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: m4.xlarge AmiId: Description: AMI to use for the SoftNAS EC2 instances Type: AWS::EC2::Image::Id Default: ami-0aa4b9c1a76dc7992 # SoftNAS 4.4.1

Default: ami-3712c04f # SoftNAS Cloud Enterprise 1TB - General Purpose Edition 3.6

EbsVolumeSize: 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 FileServerPrefix: Description: File server prefix (note that environment type will be suffixed onto prefix and appended to the hosted zone to create the FQDN) Type: String Default: sunapsis-files ### 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 - FileServerPrefix - Label: default: Tags Parameters: - TagService - TagContactNetID - TagTicketNumber - TagAccountNumber - TagSubAccount ParameterLabels: {}

Conditions

Establishes conditions based on input parameters.

Conditions: SoftNasIamRoleDoesNotExist: !Equals [ !Ref SoftNasIamRoleAlreadyExists, false ]

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 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 ## We need to upgrade the AWS CLI since the version included with the SoftNAS AMI is ancient and doesn't support SSM Parameter aws_ssm_parameter_store ## BUT! We're stuck with Python 2.6 due to CentOS6 usage so need to install a compatible version (pre 1.17) pip install --upgrade --user 'awscli<1.17'

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 -n=sunapsis-pool -r=1 -sync=standard -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 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-v4-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 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 ## We need to upgrade the AWS CLI since the version included with the SoftNAS AMI is ancient and doesn't support SSM Parameter aws_ssm_parameter_store ## BUT! We're stuck with Python 2.6 due to CentOS6 usage so need to install a compatible version (pre 1.17) pip install --upgrade --user 'awscli<1.17'

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 -n=sunapsis-pool -r=1 -sync=standard -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-v4-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-v4-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_DISK_IAM
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_DISK_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}-softnas-v4-policy 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 - ec2:StopInstances - ec2:RebootInstances - 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_DISK_IAM ] IAMPolicySSM: Type: AWS::IAM::Policy Properties: PolicyName: !Sub sunapsis-${EnvironmentType}-softnas-v4-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_DISK_IAM ] IAMPolicyDecryptKMS: Type: AWS::IAM::Policy Properties: PolicyName: !Sub sunapsis-${EnvironmentType}-softnas-v4-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_DISK_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: !Sub - ${FileServerPrefix}-${EnvironmentType}.${HostedZoneName} - HostedZoneName: Fn::ImportValue: !Sub sunapsis-${EnvironmentType}-hostedzone-name 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}-SoftNasV4-SecurityGroupId SoftNasVirtualIp: Description: Virtual IP of the SoftNAS SnapHA instance Value: !Sub ${SoftNasVirtualIp} Export: Name: !Sub sunapsis-${EnvironmentType}-SoftNasV4-VirtualIp