sunapsis_3_softnas.yaml
---

CloudFormation template for Sunapsis SoftNAS file servers (3 of 5)

  • EC2
    • Two instances, one each in us-west-2a&b
    • Uses SoftNAS AMI from Marketplace
    • Bootstraps w/user data, calling SoftNAS API to configure
    • Security group + ingress rules allowing inter-instance communication (allowing SG to itself)
  • IAM policies, role, instance profile
  • Route53 alias for SoftNAS virtual IP
AWSTemplateFormatVersion: 2010-09-09 Description: Sunapsis (SoftNAS)

Parameters

These are the input parameters for this template. All of these parameters must be supplied for this template to be deployed.

Parameters: EnvironmentType: Description: Environment type of this resource (dev, tst, rpt, trn, prd) Type: String Default: prd AllowedValues: - dev - tst - rpt - trn - prd SoftNasIamRoleAlreadyExists:

This handles SoftNAS's fixed IAM role name requirement making the role creation conditional

Description: Specify if the SoftNAS IAM role (SoftNAS_HA_IAM) already exists to skip its creation Type: String Default: no AllowedValues: - yes - no SoftNasInstanceType: Description: Instance type to use for SoftNAS EC2 instances Type: String Default: t2.large AmiId: Description: AMI to use for the SoftNAS EC2 instances Type: String Default: ami-3712c04f # SoftNAS Cloud Enterprise 1TB - General Purpose Edition 3.6 EbsVolumeSize:
  • ua-uits-general-nonprod: 200
  • ua-erp: 500
Description: The size of the EBS volumes used to create the SoftNAS storage pools (effectively avaiable storage) Type: String Default: 500 SoftNasVirtualIp: Description: The "virtual" IP address used for SoftNAS SnapHA (endpoint for file shares) Type: String Default: 11.11.11.11 ### Tags TagService: Description: Name of the service associated with this resource (as listed in the service catalog) Type: String Default: Sunapsis TagContactNetID: Description: NetID of the primary technical resource Type: String Default: dbaty TagTicketNumber: Description: Ticket number for the CLOUD Jira project Type: String Default: CLOUD-85 TagAccountNumber: Description: Account number associated with the service Type: String Default: 2433643 TagSubAccount: Description: Sub account associated with the service Type: String Default: Sunapsis

Metadata

Metadata is mostly for organizing and presenting Parameters in a better way when using CloudFormation in the AWS Web UI.

Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Environment Parameters: - EnvironmentType - Label: default: Settings Parameters: - SoftNasIamRoleAlreadyExists - SoftNasInstanceType - AmiId - EbsVolumeSize - SoftNasVirtualIp - Label: default: Tags Parameters: - TagService - TagContactNetID - TagTicketNumber - TagAccountNumber - TagSubAccount ParameterLabels: {}

Conditions

Establishes conditions based on input parameters.

Conditions: SoftNasIamRoleDoesNotExist: !Equals [ !Ref SoftNasIamRoleAlreadyExists, false ] IsT2InstanceFamily: !Or [ !Equals [ !Ref SoftNasInstanceType, t2.large ], !Equals [!Ref SoftNasInstanceType, t2.2xlarge ] ]

Resources

Resources:

EC2

SoftNAS EC2 instances

Ec2InstanceSoftNasA: Type: AWS::EC2::Instance DependsOn: Ec2InstanceSoftNasB # ensure that Ec2InstanceSoftNasB is created before this as we reference its properties in the user data script Properties: ImageId: !Ref AmiId KeyName: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-ec2-keypair CreditSpecification: !If - IsT2InstanceFamily - CPUCredits: unlimited - !Ref AWS::NoValue DisableApiTermination: true InstanceType: !Ref SoftNasInstanceType IamInstanceProfile: !Ref IAMProfileSoftNAS NetworkInterfaces: - DeviceIndex: 0 Description: SoftNAS admin and replication traffic SubnetId: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-private-subnet-a GroupSet: - !Ref SecurityGroupForSoftNAS - DeviceIndex: 1 Description: SoftNAS storage and virtual IP traffic SubnetId: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-private-subnet-a GroupSet: - !Ref SecurityGroupForSoftNAS BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeType: gp2 DeleteOnTermination: true VolumeSize: 30 - DeviceName: /dev/xvdf Ebs: VolumeType: gp2 DeleteOnTermination: false # if we lose the EC2 instance we want to keep the volumes to use VolumeSize: !Ref EbsVolumeSize - DeviceName: /dev/xvdg Ebs: VolumeType: gp2 DeleteOnTermination: false # if we lose the EC2 instance we want to keep the volumes to use VolumeSize: !Ref EbsVolumeSize UserData: Fn::Base64: !Sub | #!/bin/bash -xe service mysqld restart >> /tmp/cf.tmp 2>&1 service httpd restart >> /tmp/cf.tmp 2>&1 /var/www/softnas/scripts/rc.startup.sh >> /tmp/cf.tmp 2>&1 tail /tmp/softnas.boot >> /tmp/cf.tmp 2>&1

Upgrade AWS CLI (version shipped with SoftNAS 3.6 is quite out of date)

pip install awscli --upgrade --user

Retrieve the EC2 instanceId from metadata

INSID=$(curl http://169.254.169.254/latest/meta-data/instance-id)

Login (authenticate) to SoftNAS to allow administration

/usr/local/bin/softnas-cmd login softnas $INSID --base_url https://localhost/softnas -t >> /tmp/cf.tmp 2>&1

Acknowledge EULA (necessary for software to work)

/usr/local/bin/softnas-cmd ackagreement >> /tmp/cf.tmp 2>&1

SoftNAS defaults to the EC2 instanceId as its root & admin (softnas) password Here we retrieve new passwords from EC2 Parameter Store

passSoftNAS=$(aws ssm get-parameters --region ${AWS::Region} --names "sunapsisSoftnasUserSoftnasPassword-${EnvironmentType}" --with-decryption --query "Parameters[0].Value" --output text) passRoot=$(aws ssm get-parameters --region ${AWS::Region} --names "sunapsisSoftnasUserRootPassword-${EnvironmentType}" --with-decryption --query "Parameters[0].Value" --output text)

Then update the user passwords

/usr/local/bin/softnas-cmd userpassword user=softnas oldpassword=$INSID newpassword=$passSoftNAS >> /tmp/cf.tmp 2>&1 /usr/local/bin/softnas-cmd userpassword user=root oldpassword=$INSID newpassword=$passRoot >> /tmp/cf.tmp 2>&1

Partition all available disks

/usr/local/bin/softnas-cmd parted_command partition_all -t >> /tmp/cf.tmp 2>&1

Create a storage pool configured as RAID 1

/usr/local/bin/softnas-cmd createpool /dev/xvdf:/dev/xvdg sunapsis-pool 1 on off nopass standard on -t >> /tmp/cf.tmp 2>&1

Create a storage volume using the storage pool

/usr/local/bin/softnas-cmd createvolume vol_name=sunapsis pool=sunapsis-pool vol_type=filesystem provisioning=thin exportNFS=on shareCIFS=on compression=on dedup=off enable_snapshot=off -t >> /tmp/cf.tmp 2>&1

Initiate replicaiton (SNAPReplicate)

/usr/local/bin/softnas-cmd snaprepcommand initsnapreplicate type=source remotenode=${Ec2InstanceSoftNasB.PrivateIp} userid=softnas password=$passSoftNAS -t >> /tmp/cf.tmp 2>&1

Install the HA service

/usr/local/bin/softnas-cmd hacommand install

Initiate high availability (SNAP HA)

/usr/local/bin/softnas-cmd hacommand add nokey nokey VIP ${SoftNasVirtualIp} -t >> /tmp/cf.tmp 2>&1

Replace the sender for notification emails with a real email address in the SoftNAS monitoring config generator script (necessary as we are using SES)

sed -i "s/softnas@localhost/sunapsis@email.arizona.edu/g" /var/www/softnas/scripts/config-generator-monit.sh

Run the SoftNAS monitoring config generator script to generate an updated config

/var/www/softnas/scripts/config-generator-monit.sh >> /tmp/cf.tmp 2>&1

Change NTP config to use Amazon's NTP server (NTP is blocked at the campus border but using Amazon's NTP doesn't route out the VPN)

sed -i "s/time-c.nist.gov/169.254.169.123/g" /etc/ntp.conf >> /tmp/cf.tmp 2>&1

Restart NTPD for it to take effect

service ntpd restart >> /tmp/cf.tmp 2>&1

Stop SnapReplicate replication (required to do a software update)

/usr/local/bin/softnas-cmd snaprepcommand deactivate -t >> /tmp/cf.tmp 2>&1

Update SoftNAS to the latest version (note that this will restart the instance)

/usr/local/bin/softnas-cmd executeupdate -t >> /tmp/cf.tmp 2>&1 Tags: - Key: Name Value: !Sub sunapsis-${EnvironmentType}-softnas-a - Key: environment Value: !Ref EnvironmentType - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService - Key: subaccount Value: !Ref TagSubAccount Ec2InstanceSoftNasB: Type: AWS::EC2::Instance Properties: ImageId: !Ref AmiId DisableApiTermination: true KeyName: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-ec2-keypair CreditSpecification: !If - IsT2InstanceFamily - CPUCredits: unlimited - !Ref AWS::NoValue InstanceType: !Ref SoftNasInstanceType IamInstanceProfile: !Ref IAMProfileSoftNAS NetworkInterfaces: - DeviceIndex: 0 Description: SoftNAS admin and replication traffic SubnetId: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-private-subnet-b GroupSet: - !Ref SecurityGroupForSoftNAS - DeviceIndex: 1 Description: SoftNAS storage and virtual IP traffic SubnetId: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-private-subnet-b GroupSet: - !Ref SecurityGroupForSoftNAS BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeType: gp2 DeleteOnTermination: true VolumeSize: 30 - DeviceName: /dev/xvdf Ebs: VolumeType: gp2 DeleteOnTermination: false # if we lose the EC2 instance we want to keep the volumes to use VolumeSize: !Ref EbsVolumeSize - DeviceName: /dev/xvdg Ebs: VolumeType: gp2 DeleteOnTermination: false # if we lose the EC2 instance we want to keep the volumes to use VolumeSize: !Ref EbsVolumeSize UserData: Fn::Base64: !Sub | #!/bin/bash -xe service mysqld restart >> /tmp/cf.tmp 2>&1 service httpd restart >> /tmp/cf.tmp 2>&1 /var/www/softnas/scripts/rc.startup.sh >> /tmp/cf.tmp 2>&1 tail /tmp/softnas.boot >> /tmp/cf.tmp 2>&1

Upgrade AWS CLI (version shipped with SoftNAS 3.6 is quite out of date)

pip install awscli --upgrade --user

Retrieve the EC2 instanceId from metadata

INSID=$(curl http://169.254.169.254/latest/meta-data/instance-id)

Login (authenticate) to SoftNAS to allow administration

/usr/local/bin/softnas-cmd login softnas $INSID --base_url https://localhost/softnas -t >> /tmp/cf.tmp 2>&1

Acknowledge EULA (necessary for software to work)

/usr/local/bin/softnas-cmd ackagreement >> /tmp/cf.tmp 2>&1

SoftNAS defaults to the EC2 instanceId as its root & admin (softnas) password Here we retrieve new passwords from EC2 Parameter Store

passSoftNAS=$(aws ssm get-parameters --region ${AWS::Region} --names "sunapsisSoftnasUserSoftnasPassword-${EnvironmentType}" --with-decryption --query "Parameters[0].Value" --output text) passRoot=$(aws ssm get-parameters --region ${AWS::Region} --names "sunapsisSoftnasUserRootPassword-${EnvironmentType}" --with-decryption --query "Parameters[0].Value" --output text)

Then update the user passwords

/usr/local/bin/softnas-cmd userpassword user=softnas oldpassword=$INSID newpassword=$passSoftNAS >> /tmp/cf.tmp 2>&1 /usr/local/bin/softnas-cmd userpassword user=root oldpassword=$INSID newpassword=$passRoot >> /tmp/cf.tmp 2>&1

Partition all available disks

/usr/local/bin/softnas-cmd parted_command partition_all -t >> /tmp/cf.tmp 2>&1

Create a storage pool configured as RAID 1

/usr/local/bin/softnas-cmd createpool /dev/xvdf:/dev/xvdg sunapsis-pool 1 on off nopass standard on -t >> /tmp/cf.tmp 2>&1

Create a storage volume using the storage pool

/usr/local/bin/softnas-cmd createvolume vol_name=sunapsis pool=sunapsis-pool vol_type=filesystem provisioning=thin exportNFS=on shareCIFS=on compression=on dedup=off enable_snapshot=off -t >> /tmp/cf.tmp 2>&1

Replace the sender for notification emails with a real email address in the SoftNAS monitoring config generator script (necessary as we are using SES)

sed -i "s/softnas@localhost/sunapsis@email.arizona.edu/g" /var/www/softnas/scripts/config-generator-monit.sh

Run the SoftNAS monitoring config generator script

/var/www/softnas/scripts/config-generator-monit.sh >> /tmp/cf.tmp 2>&1

Change NTP config to use Amazon's NTP server (NTP is blocked at the campus border but using Amazon's NTP doesn't route out the VPN)

sed -i "s/time-c.nist.gov/169.254.169.123/g" /etc/ntp.conf

Restart NTPD for it to take effect

service ntpd restart >> /tmp/cf.tmp 2>&1

Sleep for 2 minutes to allow the other instance to initiate replication before we do a software update

sleep 2m

Stop SnapReplicate replication (required to do a software update)

/usr/local/bin/softnas-cmd snaprepcommand deactivate -t >> /tmp/cf.tmp 2>&1

Update SoftNAS to the latest version (note that this will restart the instance)

/usr/local/bin/softnas-cmd executeupdate -t >> /tmp/cf.tmp 2>&1 Tags: - Key: Name Value: !Sub sunapsis-${EnvironmentType}-softnas-b - Key: environment Value: !Ref EnvironmentType - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService - Key: subaccount Value: !Ref TagSubAccount

EC2 Security Groups

EC2 Security Group for the SoftNAS EC2 instances

SecurityGroupForSoftNAS: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: !Sub EC2 Security Group for Sunapsis SoftNAS EC2 instances (${EnvironmentType}) VpcId: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-vpc SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 150.135.112.64/27 Description: InfraDev VPN - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 150.135.112.64/27 Description: InfraDev VPN Tags: - Key: Name Value: !Sub sunapsis-${EnvironmentType}-sg-softnas-ec2 - Key: environment Value: !Ref EnvironmentType - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService - Key: subaccount Value: !Ref TagSubAccount

EC2 Security Group Ingress(es) to update the existing SoftNAS Security Group to allow communication with itself (and thus other SoftNAS EC2 instances)

SecurityGroupIngressInstanceIcmpEchoReply: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref SecurityGroupForSoftNAS IpProtocol: icmp FromPort: 0 ToPort: -1 SourceSecurityGroupId: !Ref SecurityGroupForSoftNAS Description: Allow ICMP Echo Reply from other SoftNAS instances SecurityGroupIngressInstanceIcmpEchoRequest: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref SecurityGroupForSoftNAS IpProtocol: icmp FromPort: 8 ToPort: -1 SourceSecurityGroupId: !Ref SecurityGroupForSoftNAS Description: Allow ICMP Echo Request from other SoftNAS instances SecurityGroupIngressInstanceSsh: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref SecurityGroupForSoftNAS IpProtocol: tcp FromPort: 22 ToPort: 22 SourceSecurityGroupId: !Ref SecurityGroupForSoftNAS Description: Allow SSH from other SoftNAS instances SecurityGroupIngressInstanceSsl: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref SecurityGroupForSoftNAS IpProtocol: tcp FromPort: 443 ToPort: 443 SourceSecurityGroupId: !Ref SecurityGroupForSoftNAS Description: Allow HTTPS from other SoftNAS instances

IAM

IAM Roles

IAMRoleSoftNAS: Type: AWS::IAM::Role Condition: SoftNasIamRoleDoesNotExist # don't create it if SoftNAS role already exists Properties: RoleName: SoftNAS_HA_IAM # role name requirement is from vendor https://www.softnas.com/docs/softnas/v3/html/specifying_the_iam_user_for_softnas_cloud_.html AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: /

IAM Instance Profiles

IAMProfileSoftNAS: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !If [ SoftNasIamRoleDoesNotExist, !Ref IAMRoleSoftNAS, SoftNAS_HA_IAM ]

IAM Policies

Note that this policy comes from the vendor: https://www.softnas.com/docs/softnas/v3/html/specifying_the_iam_user_for_softnas_cloud_.html It checks each AWS API to verify access regardless of whether or you'll actually use them so they are all required

IAMPolicySoftNAS: Type: AWS::IAM::Policy Properties: PolicyName: !Sub sunapsis-${EnvironmentType}-policy-softnas PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ec2:ModifyInstanceAttribute - ec2:DescribeInstances - ec2:CreateVolume - ec2:DeleteVolume - ec2:CreateSnapshot - ec2:DeleteSnapshot - ec2:CreateTags - ec2:DeleteTags - ec2:AttachVolume - ec2:DetachVolume - ec2:DescribeInstances - ec2:DescribeVolumes - ec2:DescribeSnapshots - aws-marketplace:MeterUsage - ec2:DescribeRouteTables - ec2:DescribeAddresses - ec2:DescribeTags - ec2:DescribeInstances - ec2:ModifyNetworkInterfaceAttribute - ec2:ReplaceRoute - ec2:CreateRoute - ec2:DeleteRoute - ec2:AssociateAddress - ec2:DisassociateAddress - s3:CreateBucket - s3:Delete* - s3:Get* - s3:List* - s3:Put* Resource: - "*" Roles: - !If [ SoftNasIamRoleDoesNotExist, !Ref IAMRoleSoftNAS, SoftNAS_HA_IAM ] IAMPolicySSM: Type: AWS::IAM::Policy Properties: PolicyName: !Sub sunapsis-${EnvironmentType}-policy-ssm PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ssm:GetParameters Resource: - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/sunapsis*" Roles: - !If [ SoftNasIamRoleDoesNotExist, !Ref IAMRoleSoftNAS, SoftNAS_HA_IAM ] IAMPolicyDecryptKMS: Type: AWS::IAM::Policy Properties: PolicyName: !Sub sunapsis-${EnvironmentType}-policy-kms PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - kms:Decrypt Resource: - !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/alias/aws/ssm Roles: - !If [ SoftNasIamRoleDoesNotExist, !Ref IAMRoleSoftNAS, SoftNAS_HA_IAM ]

Route53

Route53 Record Set Group

Route53RecordSetGroup: Type: AWS::Route53::RecordSetGroup Properties: HostedZoneName: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-hostedzone-name Comment: !Sub Sunapsis (${EnvironmentType}) - SoftNAS Virtual IP alias RecordSets: - Name: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-fqdn-for-files Type: A TTL: 3600 ResourceRecords: - !Ref SoftNasVirtualIp

Outputs

Output values that can be viewed from the AWS CloudFormation console.

Outputs: SoftNasSecurityGroupId: Description: GroupId of the SoftNAS Security Group Value: !GetAtt SecurityGroupForSoftNAS.GroupId Export: Name: !Sub sunapsis-${EnvironmentType}-SoftNasSecurityGroupId SoftNasVirtualIp: Description: Virtual IP of the SoftNAS SnapHA instance Value: !Sub ${SoftNasVirtualIp} Export: Name: !Sub sunapsis-${EnvironmentType}-SoftNasVirtualIp