asis-linux-server.yaml
---

ASIS Linux Server Template

This CloudFormation template will deploy an Amazon Linux2 server with ssh permitted from limited networks, and a set of Web specific ports open to the world

AWSTemplateFormatVersion: '2010-09-09' Description: ASIS Linux + Web Instance

Parameters

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

Parameters: ASISLinuxFoundationStack: Type: String Description: Name of the ASIS Linux foundation stack. Default: ASIS-Linux-Foundation ServerName: Type: String Description: >- Enter the name of the host or service. Max 15 alphanumeric characters plus dash and underscore. The dash and underscore cannot be first or last in the name. AllowedPattern: '(^((?![-_])(?=.*[a-z].*)[a-z0-9-_]{1,15}(?<![-_]))$)' ConstraintDescription: Max 15 alphanumeric characters plus dash and underscore. The dash and underscore cannot be first or last in the name.

Default Operating System for EC2 instance.

OSType: Type: AWS::SSM::Parameter::Value<String> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Description: Amazon Linux Latest AMI ID AllowedValues: - /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

Default EC2 Instance Type for Application instances.

InstanceType: Description: >- EC2 Instance Type t3.micro (2vCPU x 1GB) t3.small (2vCPU x 2GB) t3.medium (2vCPU x 4GB) t3.large (2vCPU x 8GB) t3.xlarge (4vCPU x 16GB) m5.large (2vCPU x 8GB) m5.xlarge (4vCPU x 16GB) Type: String Default: t3.small AllowedValues: [t3.micro, t3.small, t3.medium, t3.large, t3.xlarge, m5.large, m5.xlarge] RootVolumeSize: Description: Root Volume Size (/dev/xvda or /dev/nvme0n1) Type: Number MinValue: 20 MaxValue: 2048 Default: 40 FirstOptionalDataVolumeDesired: Description: Add Data Disk 1 (/dev/xvdb or /dev/nvme1n1)? Type: String Default: false AllowedValues: [false, true] FirstOptionalDataVolumeSize: Description: Data Disk 1 Volume Size (/dev/xvdb or /dev/nvme1n1) Type: Number MinValue: 10 MaxValue: 2048 Default: 10 SecondOptionalDataVolumeDesired: Description: Add Data Disk 2 (/dev/xvdc or /dev/nvme2n1)? Type: String Default: false AllowedValues: [false, true] SecondOptionalDataVolumeSize: Description: Data Disk 2 Volume Size (/dev/xvdc or /dev/nvme2n1) Type: Number MinValue: 10 MaxValue: 2048 Default: 10 KeyName: Description: Amazon EC2 Key Pair Type: AWS::EC2::KeyPair::KeyName AsgMaxSize: Description: >- Auto Scaling Group Max Inst Count [0-2]. Will be used for Desired Count too. Type: Number MinValue: 0 MaxValue: 2 Default: 1 HostedZoneName: Type: String Description: route53 hosted zone name (lowercase only) Default: ecs-nonprod-aws.arizona.edu AllowedPattern: '(?=^.{4,253}$)(^((?!-)[a-z0-9-]{1,63}(?<!-)\.)+[a-z]{2,63}$)' ConstraintDescription: A dot-delimited DNS zone name, permitting numbers, lowercase letters, and dashes GroupName: Type: String Description: >- Enter the name of the LDAP Group of the owners. AllowedPattern: '(^((?![._-])[a-zA-Z0-9._-]{1,63}(?<![._-]))$)' Default: invalidplaceholdername WebServerDesired: Description: Web server? Type: String Default: false AllowedValues: [false, true]

Tags

The following tags are applied to all resources created by this template.

TagService: Type: String Description: Exact name of the Service as defined in the service catalog. Default: ASIS Linux Server TagEnvironment: Type: String Description: >- Used to distinguish between development, test, production,etc. environment types. AllowedValues: [dev, tst, prd] Default: tst TagContactNetID: Type: String Description: >- Used to identify the netid of the person most familiar with the usage of the resource. TagAccountNumber: Type: String Description: Identifies the financial system account number. TagTicketNumber: Type: String Description: >- Used to identify the Jira, Cherwell, or other ticketing system ticket number to link to more information about the need for the resource. TagBackupSLA: Type: String Description: >- Select Backup SLA from the following UITS Standard - Backup policy includes daily backups retained for one month and monthly backups retained for one year None - No backups performed AllowedValues: ["UITS Standard", "None"] Default: "UITS Standard"

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 Settings Parameters: - ASISLinuxFoundationStack - Label: default: Server Settings Parameters: - ServerName - OSType - InstanceType - RootVolumeSize - FirstOptionalDataVolumeDesired - FirstOptionalDataVolumeSize - SecondOptionalDataVolumeDesired - SecondOptionalDataVolumeSize - KeyName - AsgMaxSize - HostedZoneName - GroupName - WebServerDesired - Label: default: Tagging Parameters: - TagService - TagEnvironment - TagContactNetID - TagAccountNumber - TagTicketNumber - TagBackupSLA ParameterLabels: TagEnvironment: default: 'Environment Type:' TagTicketNumber: default: 'Ticket Number:' TagContactNetID: default: 'Contact NetID:'

Conditions

Conditions: CreateFirstOptionalDataVolume: !Equals [true, !Ref FirstOptionalDataVolumeDesired] CreateSecondOptionalDataVolume: !Equals [true, !Ref SecondOptionalDataVolumeDesired] EnableWebServer: !Equals [true, !Ref WebServerDesired]

Resources

This is the EC2 instance deployed by the template.

Resources:

AWS Account Information

Lambda function to introspect VPCs, subnets, and select most available

AccountInfo: Type: Custom::AccountInfo Properties: ServiceToken: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:fdn-cf-account-info" VPCInfo: - vpcid - public-subnet-a - public-subnet-b - private-subnet-a - private-subnet-b - choose-private-subnet - choose-public-subnet

EC2 Instance Launch Config

Launch Config to deploy the EC2 instance with some tags.

Ec2InstanceLc: Type: AWS::AutoScaling::LaunchConfiguration Properties: IamInstanceProfile: !Ref EnvInstanceProfile ImageId: !Ref OSType KeyName: !Ref KeyName InstanceType: !Ref InstanceType SecurityGroups: - Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-ssh-sg" - Ref: InstanceSecurityGroup - Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-other-sg" BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref RootVolumeSize VolumeType: gp2 Encrypted: true - !If - CreateFirstOptionalDataVolume - DeviceName: /dev/xvdb Ebs: VolumeSize: !Ref FirstOptionalDataVolumeSize VolumeType: gp2 Encrypted: true - !Ref "AWS::NoValue" - !If - CreateSecondOptionalDataVolume - DeviceName: /dev/xvdc Ebs: VolumeSize: !Ref SecondOptionalDataVolumeSize VolumeType: gp2 Encrypted: true - !Ref "AWS::NoValue" UserData: Fn::Base64: !Sub - | #!/bin/bash export WORKINGPATH=/tmp/cftdeployscripts

Create temporary working folder if it doesn't already exist

if [ ! -d $WORKINGPATH ]; then mkdir -p $WORKINGPATH fi

Import CFT parameters into a local parameters file if they don't already exist

if [ ! -f $WORKINGPATH/cftdeploy-params ]; then cat <<EOFPARAMS > $WORKINGPATH/cftdeploy-params HostedZoneName=${HostedZoneName} ServerName=${ServerName} AWSRegion=${AWS::Region} GroupName=${GroupName} S3BucketName=${S3BucketName}

EFSId = $ { EFSId }

WORKINGPATH=${!WORKINGPATH} SNS_NOTIFICATION_TOPIC=${SNS_NOTIFICATION_TOPIC} WEBSERVER=${WebServer} EOFPARAMS fi

Import the instance customization script from S3 if it doesn't already exist

if [ ! -f $WORKINGPATH/asis-linux-server.sh ]; then aws s3 sync s3://${S3BucketName}/scripts $WORKINGPATH/ chmod +x $WORKINGPATH/asis-linux-server.sh fi

Run the instance customization script

$WORKINGPATH/asis-linux-server.sh $WORKINGPATH/cftdeploy-params

Clean up on completion

#rm -r $WORKINGPATH - S3BucketName: Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-bucket" #EFSId:

Fn::ImportValue: !Sub "${CMFoundationStack}-fs-id"

WebServer: !Ref WebServerDesired SNS_NOTIFICATION_TOPIC: Fn::ImportValue: fdn-logging-alarm-topic

Auto Scaling Group

Define the ASG for the Cluster Instances

Ec2InstanceAsg: Type: AWS::AutoScaling::AutoScalingGroup Properties: VPCZoneIdentifier: - !GetAtt AccountInfo.private-subnet-a - !GetAtt AccountInfo.private-subnet-b LaunchConfigurationName: !Ref Ec2InstanceLc MinSize: "0" MaxSize: !Ref AsgMaxSize DesiredCapacity: !Ref AsgMaxSize MetricsCollection: - Granularity: 1Minute Tags: - Key: Name Value: !Sub ASIS-Linux-${ServerName} PropagateAtLaunch: true - Key: service Value: !Ref TagService PropagateAtLaunch: true - Key: environment Value: !Ref TagEnvironment PropagateAtLaunch: true - Key: contactnetid Value: !Ref TagContactNetID PropagateAtLaunch: true - Key: accountnumber Value: !Ref TagAccountNumber PropagateAtLaunch: true - Key: ticketnumber Value: !Ref TagTicketNumber PropagateAtLaunch: true - Key: BackupSLA Value: !Ref TagBackupSLA PropagateAtLaunch: true

Instance Security Group

Security group for the EC2 instance, that allows you to ssh into the instance TODO: Needs to be updated to permit passing this as a parameter.

InstanceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ASIS Linux instance members VpcId: !GetAtt AccountInfo.vpcid Tags: - Key: Name Value: !Sub ASIS-Linux-Inst-SG-${ServerName} - Key: service Value: !Ref TagService - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetID - Key: accountnumber Value: !Ref TagAccountNumber - Key: ticketnumber Value: !Ref TagTicketNumber

EFS Security Group

Permit the instance access to the EFS export

#EFSIngress:

Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-efs-sg" IpProtocol: tcp FromPort: 2049 ToPort: 2049 SourceSecurityGroupId: !Ref InstanceSecurityGroup

SSH Security Group

Permit the instance ssh access to the other instances

SSHIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-ssh-sg" IpProtocol: tcp FromPort: 22 ToPort: 22 SourceSecurityGroupId: !Ref InstanceSecurityGroup Description: "ASIS Linux self"

Permit world access to the Web ports on the instances

WebIngress80: Condition: EnableWebServer Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-other-sg" IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: "0.0.0.0/0" Description: "HTTP from the world" WebIngress443: Condition: EnableWebServer Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-other-sg" IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: "0.0.0.0/0" Description: "HTTPS from the world"

Instance Policy

This is the IAM policy that will be attached to the instance's IAM role. Any AWS specific permissions that the node might need should be defined here. TODO: Determine what policy is needed

InstancePolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Ref ServerName Description: Access to SSM parameters for deployment PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 'ssm:DescribeParameters' Resource: - '*' - Effect: Allow Action: - 'ssm:GetParameter' Resource: - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ad_join/*" - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/agents/*" - Effect: Allow Action: - 'ec2:Describe*' Resource: - '*' - Effect: Allow Action: - s3:GetObject - s3:ListBucket Resource: !Sub - "arn:aws:s3:::${S3Bucket}*" - S3Bucket: Fn::ImportValue: !Sub "${ASISLinuxFoundationStack}-bucket" - Effect: Allow Action: - route53:ListHostedZones - route53:ListHostedZonesByName - route53:ChangeResourceRecordSets Resource: - "*" - Effect: Allow Action: - sns:publish Resource: - !ImportValue fdn-logging-alarm-topic Roles: - !Ref EnvInstanceRole

Instance Role

This is the IAM role that will be applied to the EC2 Instance. Any AWS specific permissions that the node might need should be defined here.

EnvInstanceRole: Type: AWS::IAM::Role Properties: RoleName: !Ref ServerName AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: "/" ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy

Instance Profile

This is just a little construct to connect a set of roles together into a profile. The profile is referenced by the EC2 Instance.

EnvInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - !Ref EnvInstanceRole

Instance Route53 records

This creates placeholder Route53 records that are populated at boot time by the instance itself

InstanceRoute53RecordSetGroup: Type: AWS::Route53::RecordSetGroup Properties: Comment: Creating records ASIS Linux Server instances HostedZoneName: !Sub "${HostedZoneName}." RecordSets: - Name: !Sub "${ServerName}-a.${HostedZoneName}." Type: A TTL: "200" ResourceRecords: - 127.0.0.0 - Name: !Sub "${ServerName}-b.${HostedZoneName}." Type: A TTL: "200" ResourceRecords: - 127.0.0.0

Outputs

TODO: Determine what other outputs back to the caller will be needed.

Outputs: SshSecurityGroup: Description: "Instance Security Group" Value: !Ref InstanceSecurityGroup Export: Name: !Sub "${AWS::StackName}-inst-sg" AsgID: Description: "AutoScalingGroup ID" Value: !Ref Ec2InstanceAsg Export: Name: !Sub "${AWS::StackName}-asg-id"