d2lcsr_4_web.yaml
---

D2L Course Site Request - CloudFormation Template (4 of 4)

Deploys D2L Course Site Request remaining infrastructure.

  • assumes existance of:

    S3 bucket (template 1) RDS SQL instance (template 2) CloudFront Distribution (template 3)

AWSTemplateFormatVersion: 2010-09-09 Description: D2L Course Site Request (web & other)

Parameters

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

Parameters:

ScheduledTasksDisabled is a flag used to determine if scheduled tasks should be disabled when task server is created

ScheduledTasksDisabled: Description: Disables scheduled tasks on Task Server creation (true/false) Type: String Default: false AllowedValues: - true - false

S3StackName is the name of the stack that created the S3 buckets

S3StackName: Description: Name of the stack that created the S3 buckets (template 1 of 4) Type: String Default: d2lcsr-prd-s3

RdsStackName is the name of the stack that created the RDS SQL Server instance

RdsStackName: Description: Name of the stack that created the RDS SQL Server instance (template 2 of 4) Type: String Default: d2lcsr-prd-rds

CloudFrontStackName is the name of the stack that created the CloudFront Distribution

CloudFrontStackName: Description: Name of the stack that created the CloudFront Distribution (template 3 of 4) Type: String Default: d2lcsr-prd-cloudfront

BucketNamePrefix is the prefix of the name of the S3 buckets

BucketNamePrefix: Description: Prefix of the name of the S3 buckets (environment type will be appended) Type: String Default: edu-arizona-sls-prod-d2lcsr

AmiId is the AMI ID to use for the Windows EC2 instances (Windows Server 2012 R2 Base)

AmiId: Description: AMI ID to use for the Windows EC2 instances (Windows Server 2012 R2 Base) Type: AWS::EC2::Image::Id Default: ami-2ad04f4a

Ec2KeyPairName is the EC2 KeyPair name used for instance launch (req'd to retrieve Windows admin password)

Ec2KeyPairName: Description: EC2 KeyPair name used for instance launch (req'd to retrieve Windows admin password) Type: AWS::EC2::KeyPair::KeyName Default: uits-eis-keypair

EmailForSNSSubscription is the email address to use for the SNS subscription

  • sls-nonprod: d2lcsr-aws-nonprod@list.arizona.edu
  • sls-prod: d2lcsr-aws-prod@list.arizona.edu
EmailForSNSSubscription: Description: Email address to use for the SNS subscription Type: String Default: d2lcsr-aws-prod@list.arizona.edu

InstanceTypeTask is the EC2 instance type for the task server

InstanceTypeTask: Description: Instance type for the task server Type: String Default: t2.medium

InstanceTypeWeb is the EC2 instance type for the web servers

InstanceTypeWeb: Description: Instance type for the web servers Type: String Default: t2.small

VPCID is the ID of the VPC where this template will be deployed. -sls-nonprod: vpc-2dc9b34a -sls-prod: vpc-82ee9de5

VPCID: Description: Target VPC Type: AWS::EC2::VPC::Id Default: vpc-82ee9de5

PrivateSubnetA is the private Subnet ID for us-west-2a -sls-nonprod: subnet-49338600 -sls-prod: subnet-fd862bb4

PrivateSubnetA: Description: Private Subnet (us-west-2a) Type: AWS::EC2::Subnet::Id Default: subnet-fd862bb4

PrivateSubnetB is the private Subnet ID for us-west-2b -sls-nonprod: subnet-7f019218 -sls-prod: subnet-ca49c2ad

PrivateSubnetB: Description: Private Subnet (us-west-2b) Type: AWS::EC2::Subnet::Id Default: subnet-ca49c2ad

PublicSubnetA is the public Subnet ID for us-west-2a -sls-nonprod: subnet-48338601 -sls-prod: subnet-fc862bb5

PublicSubnetA: Description: Public Subnet (us-west-2a) Type: AWS::EC2::Subnet::Id Default: subnet-fc862bb5

PublicSubnetB is the public Subnet ID for us-west-2b -sls-nonprod: subnet-fc862bb5 -sls-prod: subnet-cb49c2ac

PublicSubnetB: Description: Public Subnet (us-west-2b) Type: AWS::EC2::Subnet::Id Default: subnet-cb49c2ac

AsgAZs is the list of AZs to use for the Auto Scaling Groups

AsgAZs: Description: Comma delimited list of Availability Zones to use for the Auto Scaling Groups (MAX 2) Type: List<AWS::EC2::AvailabilityZone::Name> Default: us-west-2a,us-west-2b

SSLCertARN is the ARN of the SSL certificate to use for the ELB

  • sls-nonprod: arn:aws:acm:us-west-2:776095071695:certificate/14d7a136-05d8-47ec-a917-7cc6fba2c007
  • sls-prod: arn:aws:acm:us-west-2:918461542486:certificate/df1e7a1a-8413-4f57-9c69-baac375a06e3
SSLCertARN: Description: ARN of the SSL certificate to use for the ELB (US-West-2) Type: String Default: arn:aws:acm:us-west-2:918461542486:certificate/df1e7a1a-8413-4f57-9c69-baac375a06e3

KMSDefaultSSMKeyARN is the ARN of of the default aws/ssm KMS key -sls-nonprod: arn:aws:kms:us-west-2:998687558142:key/1ff283f9-2cf7-4d39-9af3-7c5c19c82bac -sls-prod: arn:aws:kms:us-west-2:918461542486:key/82ebf30f-2c0e-4b84-845a-21665c56e7b0

KMSDefaultSSMKeyARN: Description: ARN of of the default aws/ssm KMS key Type: String Default: arn:aws:kms:us-west-2:918461542486:key/82ebf30f-2c0e-4b84-845a-21665c56e7b0 ### Tags TagService: Description: Name of the service associated with this resource (as listed in the service catalog) Type: String Default: D2L Course Site Request TagEnvironment: Description: Environment type of this resource (dev, tst, rpt, trn, prd) Type: String Default: prd AllowedValues: - dev - tst - rpt - trn - prd TagContactNetID: Description: NetID of the primary technical resource Type: String Default: dbaty TagTicketNumber: Description: Ticket number of the associated Type: String Default: CLOUD-76 TagAccountNumber: Description: Account number associated with the service Type: String Default: Learning Management Systems

Metadata

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

Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Settings Parameters: - ScheduledTasksDisabled - S3StackName - RdsStackName - CloudFrontStackName - BucketNamePrefix - Ec2KeyPairName - EmailForSNSSubscription - InstanceTypeTask - InstanceTypeWeb - AmiId - VPCID - PrivateSubnetA - PrivateSubnetB - PublicSubnetA - PublicSubnetB - TaskServerAZ - AsgAZs - SSLCertARN - KMSDefaultSSMKeyARN - 5 - Label: default: Tags Parameters: - TagService - TagEnvironment - TagContactNetID - TagTicketNumber - TagAccountNumber ParameterLabels: {}

Conditions

Establishes conditions based on input parameters.

Conditions: IsPRD: !Equals [ !Ref TagEnvironment, prd ] IsTST: !Equals [ !Ref TagEnvironment, tst ]

Resources

Wow. This is a lot of resources to deploy. Do you really need them all?

Resources:

EC2 Security Groups

EC2 Security Groups for the EC2 instances & ELB

SecurityGroupForTaskServer: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: !Sub D2L Course Site Request - scheduled task server (${TagEnvironment}) VpcId: !Ref VPCID SecurityGroupIngress: - IpProtocol: tcp FromPort: 3389 ToPort: 3389 CidrIp: 150.135.112.64/27 # InfraDev VPN - IpProtocol: tcp FromPort: 3389 ToPort: 3389 CidrIp: 150.135.112.96/27 # EntApp VPN - IpProtocol: tcp FromPort: 445 ToPort: 445 CidrIp: 150.135.112.64/27 # InfraDev VPN - IpProtocol: tcp FromPort: 445 ToPort: 445 CidrIp: 150.135.112.96/27 # EntApp VPN Tags: - Key: Name Value: !Sub d2lcsr-${TagEnvironment}-sg-task - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService

EC2 Security Group for the Elastic Load Balancer (ELB)

SecurityGroupForELB: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: !Sub D2L Course Site Request - ELB (${TagEnvironment}) VpcId: !Ref VPCID SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: !Sub d2lcsr-${TagEnvironment}-sg-elb - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService

EC2 Security Group for the web servers

SecurityGroupForWebServers: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: !Sub D2L Course Site Request - web servers (${TagEnvironment}) VpcId: !Ref VPCID SecurityGroupIngress: - IpProtocol: tcp FromPort: 3389 ToPort: 3389 CidrIp: 150.135.112.64/27 # InfraDev VPN - IpProtocol: tcp FromPort: 3389 ToPort: 3389 CidrIp: 150.135.112.96/27 # EntApp VPN - IpProtocol: tcp FromPort: 80 ToPort: 80 SourceSecurityGroupId: !Ref SecurityGroupForELB - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 150.135.112.64/27 # InfraDev VPN - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 150.135.112.96/27 # EntApp VPN Tags: - Key: Name Value: !Sub d2lcsr-${TagEnvironment}-sg-web - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService

EC2 Security Group Ingress to update the existing Security Group for RDS to allow access to web & task servers

SecurityGroupIngressWebToSQL: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub ${RdsStackName}-RdsSecurityGroupId IpProtocol: tcp FromPort: 1433 ToPort: 1433 SourceSecurityGroupId: !Ref SecurityGroupForWebServers SecurityGroupIngressTaskToSQL: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub ${RdsStackName}-RdsSecurityGroupId IpProtocol: tcp FromPort: 1433 ToPort: 1433 SourceSecurityGroupId: !Ref SecurityGroupForTaskServer

Elastic Load Balancers (ELB) v2

 - Includes ELB, target group & listeners

ELB itself

WebServerLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Name: !Sub d2lcsr-${TagEnvironment}-elb Scheme: internet-facing LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: 180 Subnets: - Ref: PublicSubnetA - Ref: PublicSubnetB SecurityGroups: - Ref: SecurityGroupForELB Tags: - Key: Name Value: !Sub d2lcsr-${TagEnvironment}-elb - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService

ELB Target Group

ElbTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 30 UnhealthyThresholdCount: 2 HealthCheckPath: / Name: !Sub d2lcsr-${TagEnvironment}-tg Port: 80 Protocol: HTTP VpcId: Ref: VPCID TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 60 - Key: stickiness.enabled Value: true - Key: stickiness.type Value: lb_cookie - Key: stickiness.lb_cookie.duration_seconds Value: 3600 Tags: - Key: Name Value: !Sub d2lcsr-${TagEnvironment}-tg - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetID - Key: ticketnumber Value: !Ref TagTicketNumber - Key: accountnumber Value: !Ref TagAccountNumber - Key: service Value: !Ref TagService

ELB Listeners

ElbListenerHTTP: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref ElbTargetGroup LoadBalancerArn: !Ref WebServerLoadBalancer Port: 80 Protocol: HTTP ElbListenerHTTPS: Type: AWS::ElasticLoadBalancingV2::Listener Properties: Certificates: - CertificateArn: !Ref SSLCertARN DefaultActions: - Type: forward TargetGroupArn: !Ref ElbTargetGroup LoadBalancerArn: !Ref WebServerLoadBalancer Port: 443 Protocol: HTTPS

CloudWatch Logs

 - Includes Log Group & SSM Document

CloudWatch Log Group

LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub d2lcsr-${TagEnvironment} RetentionInDays: 30

SSM Document (to associate with the web servers for CloudWatch Log configuration)

WebServerSSMDocument: Type: AWS::SSM::Document Properties: Content: schemaVersion: 1.2 description: D2L - configure CloudWatch Logs for web servers runtimeConfig: aws:cloudWatch: settings: startType: Enabled properties: EngineConfiguration: PollInterval: 00:00:15 Components: - Id: ApplicationEventLog FullName: AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogName: Application Levels: 7 - Id: SystemEventLog FullName: AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogName: System Levels: 7 - Id: SecurityEventLog FullName: AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogName: Security Levels: 7 - Id: IISLogsDefault FullName: AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogDirectoryPath: D:\IISLogs\W3SVC1 TimestampFormat: yyyy-MM-dd HH:mm:ss Encoding: UTF-8 Filter: "" CultureName: en-US TimeZoneKind: UTC LineCount: 5 - Id: BootstrapLogs FullName: AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogDirectoryPath: C:\bootstrap TimestampFormat: yyyy-MM-dd HH:mm:ss Encoding: UTF-8 Filter: "" CultureName: en-US TimeZoneKind: UTC LineCount: 5 - Id: IISLogsCSR FullName: AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogDirectoryPath: D:\IISLogs\W3SVC2 TimestampFormat: yyyy-MM-dd HH:mm:ss Encoding: UTF-8 Filter: "" CultureName: en-US TimeZoneKind: UTC LineCount: 5 - Id: IISLogsGrade FullName: AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogDirectoryPath: D:\IISLogs\W3SVC3 TimestampFormat: yyyy-MM-dd HH:mm:ss Encoding: UTF-8 Filter: "" CultureName: en-US TimeZoneKind: UTC LineCount: 5 - Id: IISLogsCourse FullName: AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogDirectoryPath: D:\IISLogs\W3SVC4 TimestampFormat: yyyy-MM-dd HH:mm:ss Encoding: UTF-8 Filter: "" CultureName: en-US TimeZoneKind: UTC LineCount: 5 - Id: IISLogsValenceTest FullName: AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch Parameters: LogDirectoryPath: D:\IISLogs\W3SVC5 TimestampFormat: yyyy-MM-dd HH:mm:ss Encoding: UTF-8 Filter: "" CultureName: en-US TimeZoneKind: UTC LineCount: 5 - Id: CloudWatchLogs FullName: AWS.EC2.Windows.CloudWatch.CloudWatchLogsOutput,AWS.EC2.Windows.CloudWatch Parameters: Region: !Ref AWS::Region LogGroup: !Sub d2lcsr-${TagEnvironment} LogStream: "{instance_id}" - Id: CloudWatch FullName: AWS.EC2.Windows.CloudWatch.CloudWatch.CloudWatchOutputComponent,AWS.EC2.Windows.CloudWatch Parameters: Region: !Ref AWS::Region NameSpace: Windows/Default Flows: Flows: - (ApplicationEventLog,SystemEventLog,SecurityEventLog,BootstrapLogs,IISLogsDefault,IISLogsCSR,IISLogsGrade,IISLogsCourse,IISLogsValenceTest),CloudWatchLogs

Auto Scaling Groups

 - includes ASG, Scaling Policy, Launch Config & CW Alarms

Auto Scaling Groups

WebServerAutoScalingGroup: Type: AWS::AutoScaling::AutoScalingGroup Properties: Cooldown: 1800 HealthCheckGracePeriod: 0 HealthCheckType: EC2 LaunchConfigurationName: !Ref LaunchConfigWeb MinSize: 2 MaxSize: 6 TargetGroupARNs: - !Ref ElbTargetGroup MetricsCollection: - Granularity: 1Minute VPCZoneIdentifier: - !Ref PrivateSubnetA - !Ref PrivateSubnetB NotificationConfigurations: - TopicARN: !Ref SNSTopic NotificationTypes: - autoscaling:EC2_INSTANCE_LAUNCH - autoscaling:EC2_INSTANCE_LAUNCH_ERROR - autoscaling:EC2_INSTANCE_TERMINATE - autoscaling:EC2_INSTANCE_TERMINATE_ERROR Tags: - Key: Name Value: !Sub d2lcsr-${TagEnvironment}-asg-web PropagateAtLaunch: false - Key: environment Value: !Ref TagEnvironment PropagateAtLaunch: true - Key: contactnetid Value: !Ref TagContactNetID PropagateAtLaunch: true - Key: ticketnumber Value: !Ref TagTicketNumber PropagateAtLaunch: true - Key: accountnumber Value: !Ref TagAccountNumber PropagateAtLaunch: true - Key: service Value: !Ref TagService PropagateAtLaunch: true TaskServerAutoScalingGroup: Type: AWS::AutoScaling::AutoScalingGroup Properties: Cooldown: 900 HealthCheckGracePeriod: 0 HealthCheckType: EC2 LaunchConfigurationName: !Ref LaunchConfigTask MinSize: 1 MaxSize: 1 VPCZoneIdentifier: - !Ref PrivateSubnetA - !Ref PrivateSubnetB NotificationConfigurations: - TopicARN: !Ref SNSTopic NotificationTypes: - autoscaling:EC2_INSTANCE_LAUNCH - autoscaling:EC2_INSTANCE_LAUNCH_ERROR - autoscaling:EC2_INSTANCE_TERMINATE - autoscaling:EC2_INSTANCE_TERMINATE_ERROR Tags: - Key: Name Value: !Sub d2lcsr-${TagEnvironment}-asg-task PropagateAtLaunch: false - Key: environment Value: !Ref TagEnvironment PropagateAtLaunch: true - Key: contactnetid Value: !Ref TagContactNetID PropagateAtLaunch: true - Key: ticketnumber Value: !Ref TagTicketNumber PropagateAtLaunch: true - Key: accountnumber Value: !Ref TagAccountNumber PropagateAtLaunch: true - Key: service Value: !Ref TagService PropagateAtLaunch: true

Launch Configurations

LaunchConfigTask: Type: AWS::AutoScaling::LaunchConfiguration Properties: ImageId: !Ref AmiId KeyName: !Ref Ec2KeyPairName InstanceType: !Ref InstanceTypeTask InstanceMonitoring: true SecurityGroups: - Ref: SecurityGroupForTaskServer IamInstanceProfile: !Ref IAMProfileTaskServer BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeType: gp2 DeleteOnTermination: true VolumeSize: 80 - DeviceName: xvdd Ebs: VolumeType: gp2 DeleteOnTermination: true VolumeSize: 8 - DeviceName: xvde Ebs: VolumeType: gp2 DeleteOnTermination: true VolumeSize: 100 UserData: Fn::Base64: !Sub | <powershell> mkdir C:\bootstrap $( $environmentName = "${TagEnvironment}" #~~~ swap when in the CloudFormation template #$environmentName = "dmo" $joinDomainDomainName = "catnet.arizona.edu" $joinDomainDNofOU = "OU=D2L,OU=Enterprise,DC=catnet,DC=arizona,DC=edu" $joinDomainComputerName = "d2lcsr-$($environmentName)-task" $joinDomainParamNameUser = "d2lcsrCatnetUser" $joinDomainParamNamePassword = "d2lcsrCatnetPassword" $rootBootstrap = "C:\bootstrap" $rootApps = "D:\UA_All_Valence_Projects" $rootShib = "D:\opt\shibboleth-sp\etc\shibboleth" $s3bucket = "${BucketNamePrefix}-$($environmentName)" #~~~ swap when in the CloudFormation template #$s3bucket = "edu-arizona-sls-nonprod-d2lcsr-dev" $s3keyApps = "TaskServer\Apps\" $s3keySoftware = "TaskServer\Software\" $msiInstalls = @{}

$msiInstalls.Add("AWSCLI", "https://s3.amazonaws.com/aws-cli/AWSCLI64.msi")

$msiInstalls.Add("msodbcsql-x64-13.1", "http://download.microsoft.com/download/D/5/E/D5EEF288-A277-45C8-855B-8E2CB7E25B96/13.1.811.168/amd64/msodbcsql.msi") $msiInstalls.Add("MsSqlCmdLnUtils-x64-13.1", "http://download.microsoft.com/download/C/8/8/C88C2E51-8D23-4301-9F4B-64C8E2F163C5/Command%20Line%20Utilities%20MSI%20files/amd64/MsSqlCmdLnUtils.msi") $tasksAreDisabled = "${ScheduledTasksDisabled}" #~~~ swap when in the CloudFormation template #$tasksAreDisabled = "false" $taskApps = @{} $taskApps.Add("Feeds", "D:\D2L_Feeds") $taskApps.Add("CTMaint", "D:\UA_All_Valence_Projects\UA_CTMaint_Valence") $taskApps.Add("DataSync", "D:\UA_All_Valence_Projects\UA_DataSync_Valence") $taskApps.Add("EDS2D2L", "D:\UA_All_Valence_Projects\UA_EDS2D2L_Valence") $taskApps.Add("INS2D2L", "D:\UA_All_Valence_Projects\UA_INS2D2L_Valence") $taskApps.Add("PS2D2L", "D:\UA_All_Valence_Projects\UA_PS2D2L_Valence") $taskApps.Add("RoleSwitcher1", "D:\UA_All_Valence_Projects\UA_Role_Switcher_Valence\UA_RoleSwitcher_1") $taskApps.Add("RoleSwitcher2", "D:\UA_All_Valence_Projects\UA_Role_Switcher_Valence\UA_RoleSwitcher_2") $taskApps.Add("RoleSwitcher3", "D:\UA_All_Valence_Projects\UA_Role_Switcher_Valence\UA_RoleSwitcher_3") $taskApps.Add("RoleSwitcher4", "D:\UA_All_Valence_Projects\UA_Role_Switcher_Valence\UA_RoleSwitcher_4") $taskApps.Add("RoleSwitcher5", "D:\UA_All_Valence_Projects\UA_Role_Switcher_Valence\UA_RoleSwitcher_5") $taskApps.Add("DataVerificationUtilityCTMaint", "D:\UA_D2L_DataVerificationApps\UtilityCTMaint") $taskApps.Add("DataVerificationUtilityEDS2D2L", "D:\UA_D2L_DataVerificationApps\UtilityEDS2D2L") $taskApps.Add("DataVerificationUtilityINS2D2L", "D:\UA_D2L_DataVerificationApps\UtilityINS2D2L") $taskApps.Add("DataVerificationUtilityPS2D2L", "D:\UA_D2L_DataVerificationApps\UtilityPS2D2L") $taskApps.Add("UtilityINS2D2L", "D:\UA_D2L_UtilitiesApps\UtilityINS2D2L") $taskApps.Add("UtilityPS2D2L", "D:\UA_D2L_UtilitiesApps\UtilityPS2D2L") $taskApps.Add("BatchFiles", "D:\UA_D2L_Valence_All_Projects_Batch_Files") $tasks = @{} $tasks.Add("CTMaintenance", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_CTMaint.bat" "StartTime" = "12:00am" "RepetitionInterval" = "PT1H" })) $tasks.Add("Data_Verify", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Data_Verify.bat" "StartTime" = "3:30pm" })) $tasks.Add("DataSync", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_DataSync.bat" "StartTime" = "6:30am" })) $tasks.Add("EDS2D2L", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_EDS2D2L.bat" "StartTime" = "12:00am" "RepetitionInterval" = "PT1H" })) $tasks.Add("INS2D2L", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_INS2D2L.bat" "StartTime" = "11:30am" "RepetitionInterval" = "PT6H" })) $tasks.Add("PS2D2L", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_PS2D2L.bat" "StartTime" = "10:30am" "RepetitionInterval" = "PT2H" "RepetitionDuration" = "PT19H" })) $tasks.Add("RoleSwitcher_1", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_RoleSwitcher1.bat" "StartTime" = "1:15am" })) $tasks.Add("RoleSwitcher_2", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_RoleSwitcher2.bat" "StartTime" = "6:15am" })) $tasks.Add("RoleSwitcher_3", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_RoleSwitcher3.bat" "StartTime" = "11:15am" })) $tasks.Add("RoleSwitcher_4", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_RoleSwitcher4.bat" "StartTime" = "4:15pm" })) $tasks.Add("RoleSwitcher_5", (@{ "Action" = "D:\UA_D2L_Valence_All_Projects_Batch_Files\UA_D2L_Valence_RoleSwitcher5.bat" "StartTime" = "9:15pm" })) $tasks.Add("stageCSRandINS2D2L", (@{ "Action" = "powershell.exe" "Argument" = "-ExecutionPolicy bypass -NonInteractive -NoLogo -NoProfile D:\D2L_Feeds\scripts\stageCSRandINS2D2L.ps1" "StartTime" = "11:10am" "RepetitionInterval" = "PT6H" })) $tasks.Add("stagePS2D2L", (@{ "Action" = "powershell.exe" "Argument" = "-ExecutionPolicy bypass -NonInteractive -NoLogo -NoProfile D:\D2L_Feeds\scripts\stagePS2D2L.ps1" "StartTime" = "12:10pm" "RepetitionInterval" = "PT4H" "RepetitionDuration" = "PT12H" })) $tasks.Add("purgeStagingData", (@{ "Action" = "powershell.exe" "Argument" = "-ExecutionPolicy bypass -NonInteractive -NoLogo -NoProfile D:\D2L_Feeds\scripts\purgeStagingData.ps1" "StartTime" = "10:00am" })) try {

Add the Windows Feature "Active Directory module for Windows PowerShell"

Install-WindowsFeature RSAT-AD-PowerShell

Download and install MSIs

foreach ($msi in $msiInstalls.Keys) { ### Testing revealed just how subject to change vendor URLs are so we'll maintain the MSIs ourselves in our S3 bucket

(New-Object System.Net.WebClient).DownloadFile($msiInstalls.Item($msi), (Join-Path -Path $rootBootstrap -ChildPath "$($msi).msi"))

###

Download from S3

Read-S3Object -BucketName $s3bucket -Folder $rootBootstrap -KeyPrefix $s3keySoftware

Install MSI

if ($msi -like "msodbcsql*") { & msiexec /i (Join-Path -Path $rootBootstrap -ChildPath "$($msi).msi") /qn /log (Join-Path -Path $rootBootstrap -ChildPath "install_$($msi)-log.txt") IACCEPTMSODBCSQLLICENSETERMS=YES | Out-Null } elseif ($msi -like "MsSqlCmdLnUtils*") { & msiexec /i (Join-Path -Path $rootBootstrap -ChildPath "$($msi).msi") /qn /log (Join-Path -Path $rootBootstrap -ChildPath "install_$($msi)-log.txt") IACCEPTMSSQLCMDLNUTILSLICENSETERMS=YES | Out-Null } }

Make folders for apps

mkdir D:\UA_All_Valence_Projects mkdir D:\UA_All_Valence_Projects\Logs mkdir D:\UA_All_Valence_Projects\UA_Role_Switcher_Valence mkdir D:\UA_D2L_UtilitiesApps mkdir D:\UA_D2L_UtilitiesApps\Logs mkdir D:\UA_D2L_DataVerificationApps mkdir D:\UA_D2L_DataVerificationApps\Logs mkdir D:\UA_All_Valence_Projects\Logs\UA_CTMaint_Valence_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_DataSync_Valence_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_EDS2D2L_Valence_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_INS2D2L_Valence_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_PS2D2L_Valence_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_Role_Switcher_Valence_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_UtilityEDS2D2L_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_UtilityINS2D2L_Log mkdir D:\UA_All_Valence_Projects\Logs\UA_UtilityPS2D2L_Log foreach ($app in $taskApps.Keys) { mkdir $taskApps.Item($app) }

Download zipped apps from S3

Read-S3Object -BucketName $s3bucket -Folder $rootApps -KeyPrefix $s3keyApps

Unzip zipped apps & delete zips when done

Add-Type -As System.IO.Compression.FileSystem foreach ($zip in $taskApps.Keys) { $ZipFile = Get-Item (Join-Path -Path $rootApps -ChildPath "$($zip).zip") $Archive = [System.IO.Compression.ZipFile]::Open( $ZipFile, "Read" ) [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory( $Archive, $($taskApps.Item($zip)) ) $Archive.Dispose() } Remove-Item -Path (Join-Path -Path $rootApps -ChildPath "*.zip")

Change to use campus DNS to ease access to the Isilon

Set-DnsClientServerAddress -InterfaceIndex 12 -ServerAddresses ("128.196.11.233", "128.196.11.234")

Create the scheduled tasks

foreach ($task in $tasks.GetEnumerator()) { $taskUser = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount # -RunLevel Highest $taskTrigger = New-ScheduledTaskTrigger -Daily -At $($tasks.($task.Key).StartTime) $taskAction = New-ScheduledTaskAction -Execute "$($tasks.($task.Key).Action)" $taskObject = New-ScheduledTask -Action $taskAction -Principal $taskUser -Trigger $taskTrigger Register-ScheduledTask "$($task.Key)" -InputObject $taskObject -Force

Set argument (if value exists)

if ($tasks.($task.Key).Argument) { $taskArgumentAction = New-ScheduledTaskAction -Execute "$($tasks.($task.Key).Action)" -Argument "$($tasks.($task.Key).Argument)" Set-ScheduledTask -TaskName "$($task.Key)" -User "NT AUTHORITY\SYSTEM" -Action $taskArgumentAction }

Configure Repetition Interval (if value exists)

if ($tasks.($task.Key).RepetitionInterval) { $taskObject = Get-ScheduledTask -TaskName "$($task.Key)" $taskObject.Triggers.Repetition.Interval = "$($tasks.($task.Key).RepetitionInterval)" $taskObject | Set-ScheduledTask -User "NT AUTHORITY\SYSTEM" }

Configure Repetition Duration (if value exists)

if ($tasks.($task.Key).RepetitionDuration) { $taskObject = Get-ScheduledTask -TaskName "$($task.Key)" $taskObject.Triggers.Repetition.Duration = "$($tasks.($task.Key).RepetitionDuration)" $taskObject | Set-ScheduledTask -User "NT AUTHORITY\SYSTEM" }

Disable task if set to be disabled (value is 'true')

if ($tasksAreDisabled -Eq "true") { Disable-ScheduledTask -TaskName "$($task.Key)" } }

Get the instanceId

$awsInstanceId = Invoke-RestMethod -Uri "http://169.254.169.254/latest/meta-data/instance-id"

Assign a "Name" tag to the instance to be friendly to the console UI

$awsTags = @() $awsNameTag = New-Object Amazon.EC2.Model.Tag $awsNameTag.Key = "Name" $awsNameTag.Value = $joinDomainComputerName $awsTags += $awsNameTag New-EC2Tag -ResourceId $awsInstanceId -Tags $awsTags

Retrieve/decrypt credentials from SSM Parameter Store for user with delagated rights to create/delete computer objects in domain

$joinDomainUser = (Get-SSMParameterValue -Name $joinDomainParamNameUser).Parameters.Value $joinDomainPassword = (Get-SSMParameterValue -Name $joinDomainParamNamePassword -WithDecryption $True).Parameters.Value | ConvertTo-SecureString -asPlainText -Force $joinDomainCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $joinDomainUser, $joinDomainPassword

Check if the computer object already exists in the target OU, delete it if so

if (Get-AdComputer -Filter 'Name -eq $joinDomainComputerName' -Credential $joinDomainCred -Server $joinDomainDomainName) { Remove-ADComputer -Credential $joinDomainCred -Server $joinDomainDomainName -Identity "CN=$($joinDomainComputerName),$($joinDomainDNofOU)" -Confirm:$false }

Join to the domain and reboot

Add-Computer -DomainName $joinDomainDomainName -NewName $joinDomainComputerName -Credential $joinDomainCred -OUPath $joinDomainDNofOU -Force Restart-Computer -Force } catch { Set-Content C:\bootstrap\bootstrap-error-exception.txt -Value $Error[0].Exception $Error[0] | Export-Clixml -Path C:\bootstrap\bootstrap-error-complete.xml throw } ) *> C:\bootstrap\bootstrap-output.txt </powershell> LaunchConfigWeb: Type: AWS::AutoScaling::LaunchConfiguration Properties: ImageId: !Ref AmiId KeyName: !Ref Ec2KeyPairName InstanceType: !Ref InstanceTypeWeb InstanceMonitoring: true BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeType: gp2 DeleteOnTermination: true VolumeSize: 80 - DeviceName: xvdd Ebs: VolumeType: gp2 DeleteOnTermination: true VolumeSize: 4 IamInstanceProfile: !Ref IAMProfileWebServer SecurityGroups: - Ref: SecurityGroupForWebServers UserData: Fn::Base64: !Sub | <powershell> mkdir C:\bootstrap $( #$environmentName = "dev" #~~~ swap when in the CloudFormation template $environmentName = "${TagEnvironment}" $rootBootstrap = "C:\bootstrap" $rootApps = "D:\UA_All_Valence_Projects" $rootShib = "D:\opt\shibboleth-sp\etc\shibboleth" $s3bucket = "${BucketNamePrefix}-$($environmentName)" #~~~ swap when in the CloudFormation template

$s3bucket = "edu-arizona-sls-nonprod-d2lcsr-$($environmentName)"

$s3keyApps = "WebServer\Apps\" $s3keyShib = "WebServer\Shibboleth\" $s3keySoftware = "WebServer\Software\" $s3keyScripts = "WebServer\Scripts\" $iisLogFolder = "D:\IISLogs" $windowsFeatures = @("Web-WebServer", "Web-Mgmt-Console", "NET-Framework-45-ASPNET", "Web-IP-Security", "Web-Net-Ext45", "Web-Asp-Net45", "Web-ISAPI-Ext", "Web-ISAPI-Filter") $msiInstalls = @{}

$msiInstalls.Add("AWSCLI", "https://s3.amazonaws.com/aws-cli/AWSCLI64.msi")

$msiInstalls.Add("UrlRewrite", "http://download.microsoft.com/download/6/7/D/67D80164-7DD0-48AF-86E3-DE7A182D6815/rewrite_2.0_rtw_x64.msi") $msiInstalls.Add("Shibboleth", "http://shibboleth.net/downloads/service-provider/2.6.0/win64/shibboleth-sp-2.6.0.1-win64.msi") $iisSites = @{} $iisSites.Add("CSR", "D:\UA_All_Valence_Projects\CSR") $iisSites.Add("Grade", "D:\UA_All_Valence_Projects\UA_GradeFeed_Valence") $iisSites.Add("Course", "D:\UA_All_Valence_Projects\UA_CourseFeed_Valence") $iisSites.Add("ValenceTest", "D:\UA_All_Valence_Projects\ValenceTestTool") $ipRestrictions = @{} $ipRestrictions.Add("150.135.112.0", "255.255.252.0") $ipRestrictions.Add("150.135.94.0", "255.255.254.0") $ipRestrictions.Add("10.138.0.0", "255.255.128.0") $ipRestrictions.Add("10.139.16.0", "255.255.240.0") $ipRestrictions.Add("10.140.2.0", "255.255.255.0") $ipRestrictions.Add("10.140.12.0", "255.255.255.0") $ipRestrictions.Add("10.140.22.0", "255.255.255.0") try {

Install Windows features

Install-WindowsFeature -Name $windowsFeatures -LogPath (Join-Path -Path $rootBootstrap -ChildPath "windows_features-log.txt")

Download and install MSIs

foreach ($msi in $msiInstalls.Keys) {

Testing revealed just how subject to change vendor URLs are so we'll maintain the MSIs ourselves in our S3 bucket (New-Object System.Net.WebClient).DownloadFile($msiInstalls.Item($msi), (Join-Path -Path $rootBootstrap -ChildPath "$($msi).msi"))

Read-S3Object -BucketName $s3bucket -Folder $rootBootstrap -KeyPrefix $s3keySoftware if ($msi -eq "Shibboleth") { & msiexec /i (Join-Path -Path $rootBootstrap -ChildPath "$($msi).msi") /qn /log (Join-Path -Path $rootBootstrap -ChildPath "install_$($msi)-log.txt") REBOOT=ReallySupress INSTALL_ISAPI_FILTER=FALSE INSTALL_32BIT=TRUE INSTALLDIR=D:\opt\shibboleth-sp\ | Out-Null } else { & msiexec /i (Join-Path -Path $rootBootstrap -ChildPath "$($msi).msi") /qn /log (Join-Path -Path $rootBootstrap -ChildPath "install_$($msi)-log.txt") | Out-Null } }

Make folders for web content

mkdir D:\IISLogs mkdir D:\UA_All_Valence_Projects mkdir D:\UA_All_Valence_Projects\Logs mkdir D:\LibraryExtract foreach ($site in $iisSites.Keys) { mkdir $iisSites.Item($site) }

Create the app pools, enable 32-bit apps on the app pools & creating the web sites NOTE: forcing sort to get a consistent IIS site Id for use with Shibboleth SP

foreach ($site in $iisSites.GetEnumerator() | Sort-Object Name) { New-WebAppPool -Name $site.Key Set-ItemProperty IIS:\AppPools\$($site.Key) -Name enable32BitAppOnWin64 -Value true if ($environmentName -eq "prd") { New-Website -Name $site.Key -ApplicationPool $site.Key -Port 80 -HostHeader "$($site.Key.ToString().ToLower()).d2l.arizona.edu" -PhysicalPath $site.Value } elseif ($environmentName -eq "tst") { New-Website -Name $site.Key -ApplicationPool $site.Key -Port 80 -HostHeader "aws-test-$($site.Key.ToString().ToLower()).d2l.arizona.edu" -PhysicalPath $site.Value } else { New-Website -Name $site.Key -ApplicationPool $site.Key -Port 80 -HostHeader "aws-$($environmentName)-$($site.Key.ToString().ToLower()).d2l.arizona.edu" -PhysicalPath $site.Value } }

Create application in CSR site for Library Extract

New-WebApplication -Name Library -Site CSR -PhysicalPath D:\LibraryExtract -ApplicationPool CSR

Set default IIS logging folder

Set-WebConfigurationProperty "/system.applicationHost/sites/siteDefaults" -Name logfile.directory -Value $iisLogFolder

Create a default document on the Default Web Site for the ELB health check (lest you end up with 403.14 errors)

Set-Content C:\inetpub\wwwroot\default.htm -Value "<html><body>up</body></html>"

Download zipped apps from S3

Read-S3Object -BucketName $s3bucket -Folder $rootApps -KeyPrefix $s3keyApps

Unzip zipped apps & delete zips when done

Add-Type -As System.IO.Compression.FileSystem foreach ($zip in $iisSites.Keys) { $ZipFile = Get-Item (Join-Path -Path $rootApps -ChildPath "$($zip).zip") $Archive = [System.IO.Compression.ZipFile]::Open( $ZipFile, "Read" ) [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory( $Archive, $($iisSites.Item($zip)) ) $Archive.Dispose() } $ZipFile = Get-Item (Join-Path -Path $rootApps -ChildPath "LibraryExtract.zip") $Archive = [System.IO.Compression.ZipFile]::Open( $ZipFile, "Read" ) [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory( $Archive, "D:\LibraryExtract" ) $Archive.Dispose() Remove-Item -Path (Join-Path -Path $rootApps -ChildPath "*.zip")

Set Modify folder/file permissions for Course Export app pool requires creating/writing to a file

& ICACLS D:\UA_All_Valence_Projects\UA_CourseFeed_Valence /GRANT `"IIS AppPool\Course`":`(OI`)`(CI`)M /t

Download Shibboleth configuration from S3

Read-S3Object -BucketName $s3bucket -Folder $rootShib -KeyPrefix $s3keyShib

Override lock & set config for an ISAPI filter on CSR site for Shibboleth

Set-WebConfiguration //isapiFilters MACHINE/WEBROOT/APPHOST -Metadata overrideMode -Value Allow

* NOTE: The ISAPI filter is in the app's web.config so do not need to handle here at this time Add-WebConfiguration //isapiFilters "IIS:\sites\CSR" -Value @{ name = 'Shibboleth'; path = 'D:\opt\shibboleth-sp\lib\shibboleth\isapi_shib.dll' }

Override lock & set config for an ISAPI & CGI Restrictions for Shibboleth

Set-WebConfiguration //isapiCgiRestriction MACHINE/WEBROOT/APPHOST -Metadata overrideMode -Value Allow Add-WebConfiguration //isapiCgiRestriction "IIS:\" -Value @{ description = 'Shibboleth'; path = 'D:\opt\shibboleth-sp\lib\shibboleth\isapi_shib.dll'; allowed = 'True' }

Add a Handler Mapping for Shibboleth ** NOTE: the .sso handler mapping is in the app's web.config so do not need to handle this here at this time New-WebHandler -PSPath "IIS:\sites\CSR" -Name Shibboleth -Path .sso -ScriptProcessor "D:\opt\shibboleth-sp\lib\shibboleth\isapi_shib.dll" -Verb "" -Modules IsapiModule -ResourceType Unspecified

Restart Shibboleth service (so that config changes take effect)

Restart-Service shibd_default

Perform an IISReset (else the Shibboleth ISAPI filter won't load)

& iisreset

Override lock, set unlisted IP default deny & set allow IP range restrictions on Grade site * NOTE: the IP restriction settings are in the app's web.config so do not need to handle this here at this time (but we will unlock the section so they'll take)

Set-WebConfiguration //ipSecurity MACHINE/WEBROOT/APPHOST -Metadata overrideMode -Value Allow

Set-WebConfigurationProperty /system.webServer/security/ipSecurity -PSPath "IIS:\Sites\Grade" -Name allowUnlisted -Value "False" foreach ($ip in $ipRestrictions.Keys) { Add-WebConfiguration //ipSecurity "IIS:\Sites\Grade" -Value @{ ipAddress = "$($ip)"; subnetMask = "$($ipRestrictions.Item($ip))"; allowed = "True" }

#

Get the instanceId & availiability zone

$awsInstanceId = Invoke-RestMethod -Uri "http://169.254.169.254/latest/meta-data/instance-id" $awsAZ = Invoke-RestMethod -Uri "http://169.254.169.254/latest/meta-data/placement/availability-zone"

Create an SSM association for the CloudWatch Logs configuration

#New-SSMAssociation -InstanceId $awsInstanceId -Name "D2L-ConfigureCloudWatchLogs" #~~~ swap when in the CloudFormation template New-SSMAssociation -InstanceId $awsInstanceId -Name "${WebServerSSMDocument}"

Assign a "Name" tag to the instance to be friendly to the console UI

$awsTags = @() $awsNameTag = New-Object Amazon.EC2.Model.Tag $awsNameTag.Key = "Name" $awsNameTag.Value = "d2lcsr-$($environmentName)-web_$($awsAZ)_$($awsInstanceId)"

$awsEnvironmentTag = New-Object Amazon.EC2.Model.Tag $awsEnvironmentTag.Key = "environment" $awsEnvironmentTag.Value = $environmentName

$awsTags += $awsNameTag

$awsTags += $awsEnvironmentTag

New-EC2Tag -ResourceId $awsInstanceId -Tags $awsTags

Download PowerShell scripts and schedule a task to cleanup the IIS logs

mkdir D:\Scripts Read-S3Object -BucketName $s3bucket -Folder "D:\Scripts" -KeyPrefix $s3keyScripts $taskAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy bypass -NonInteractive -NoLogo -NoProfile D:\Scripts\Remove-IISLogsOlderThan3Days.ps1" $taskUser = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount # -RunLevel Highest $taskTrigger = New-ScheduledTaskTrigger -Daily -At 7:05am $task = New-ScheduledTask -Action $taskAction -Principal $taskUser -Trigger $taskTrigger Register-ScheduledTask "Remove-IISLogsOlderThan3Days" -InputObject $task -Force } catch { Set-Content C:\bootstrap\bootstrap-error-exception.txt -Value $Error[0].Exception $Error[0] | Export-Clixml -Path C:\bootstrap\bootstrap-error-complete.xml throw } ) *> C:\bootstrap\bootstrap-output.txt </powershell>

Scaling Policies

PolicyScaleUpWeb: Type: AWS::AutoScaling::ScalingPolicy Properties: AdjustmentType: ChangeInCapacity AutoScalingGroupName: !Ref WebServerAutoScalingGroup EstimatedInstanceWarmup: 1800 MetricAggregationType: Average PolicyType: StepScaling StepAdjustments: - MetricIntervalLowerBound: 0 ScalingAdjustment: 2 PolicyScaleDownWeb: Type: AWS::AutoScaling::ScalingPolicy Properties: AdjustmentType: ChangeInCapacity AutoScalingGroupName: !Ref WebServerAutoScalingGroup MetricAggregationType: Average PolicyType: StepScaling StepAdjustments: - MetricIntervalLowerBound: 0 ScalingAdjustment: -2

CloudWatch Alarms

AlarmHighCPU: Type: AWS::CloudWatch::Alarm Properties: EvaluationPeriods: 3 Statistic: Average Threshold: 80 AlarmDescription: Alarm if CPU too high or metric disappears indicating instance is down Period: 300 AlarmActions: - Ref: PolicyScaleUpWeb Namespace: AWS/EC2 Dimensions: - Name: AutoScalingGroupName Value: !Ref WebServerAutoScalingGroup ComparisonOperator: GreaterThanOrEqualToThreshold MetricName: CPUUtilization AlarmLowCPU: Type: AWS::CloudWatch::Alarm Properties: EvaluationPeriods: 3 Statistic: Average Threshold: 30 AlarmDescription: Alarm if CPU sufficiently low Period: 300 AlarmActions: - Ref: PolicyScaleDownWeb Namespace: AWS/EC2 Dimensions: - Name: AutoScalingGroupName Value: !Ref WebServerAutoScalingGroup ComparisonOperator: LessThanOrEqualToThreshold MetricName: CPUUtilization

Route53

Route53 Record Set Group

Route53RecordSetGroup: Type: AWS::Route53::RecordSetGroup Properties: HostedZoneName: Fn::ImportValue: !Sub ${S3StackName}-hostedzone-name Comment: !Sub D2L Course Site Request (${TagEnvironment}) - aliases for web apps to ELB & CloudFront RecordSets: - Name: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-csr Type: A AliasTarget: HostedZoneId: !GetAtt WebServerLoadBalancer.CanonicalHostedZoneID DNSName: !GetAtt WebServerLoadBalancer.DNSName EvaluateTargetHealth: true Failover: PRIMARY SetIdentifier: !Sub d2lcsr-${TagEnvironment}-csr-primary - Name: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-csr Type: A AliasTarget: HostedZoneId: Z2FDTNDATAQYW2 DNSName: Fn::ImportValue: !Sub ${CloudFrontStackName}-cloudfront-maint-fqdn Failover: SECONDARY SetIdentifier: !Sub d2lcsr-${TagEnvironment}-csr-secondary - Name: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-grade Type: A AliasTarget: HostedZoneId: !GetAtt WebServerLoadBalancer.CanonicalHostedZoneID DNSName: !GetAtt WebServerLoadBalancer.DNSName - Name: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-course Type: A AliasTarget: HostedZoneId: !GetAtt WebServerLoadBalancer.CanonicalHostedZoneID DNSName: !GetAtt WebServerLoadBalancer.DNSName - Name: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-valencetest Type: A AliasTarget: HostedZoneId: !GetAtt WebServerLoadBalancer.CanonicalHostedZoneID DNSName: !GetAtt WebServerLoadBalancer.DNSName

SNS

SNS Topic

SNSTopic: Type: AWS::SNS::Topic Properties: TopicName: !Sub d2lcsr-web-${TagEnvironment} DisplayName: !Sub AWS D2L CSR (${TagEnvironment}) Subscription: - Endpoint: !Ref EmailForSNSSubscription Protocol: email

IAM

IAM Roles

IAMRoleWebServer: Type: AWS::IAM::Role Properties: RoleName: !Sub d2lcsr-${TagEnvironment}-role-web-server AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: / IAMRoleTaskServer: Type: AWS::IAM::Role Properties: RoleName: !Sub d2lcsr-${TagEnvironment}-role-task-server AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: /

IAM Instance Profiles

IAMProfileWebServer: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref IAMRoleWebServer IAMProfileTaskServer: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref IAMRoleTaskServer

IAM Policies

IAMPolicyS3Bucket: Type: AWS::IAM::Policy Properties: PolicyName: !Sub d2lcsr-${TagEnvironment}-policy-s3bucket-full PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - s3:ListBucket - s3:GetBucketLocation Resource: - Fn::ImportValue: !Sub ${S3StackName}-bucket-app-arn - Effect: Allow Action: - s3:GetObjectMetaData - s3:GetObject - s3:ListMultipartUploadParts - s3:AbortMultipartUpload Resource: - Fn::ImportValue: !Sub ${S3StackName}-bucket-app-arn-wildcard Roles: - !Ref IAMRoleWebServer - !Ref IAMRoleTaskServer IAMPolicyCloudWatchLogs: Type: AWS::IAM::Policy Properties: PolicyName: !Sub d2lcsr-${TagEnvironment}-policy-cloudwatch-logs PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - cloudwatch:PutMetricData Resource: - "*" - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:DescribeLogGroups - logs:DescribeLogStreams - logs:PutLogEvents Resource: - "*" Roles: - !Ref IAMRoleWebServer - !Ref IAMRoleTaskServer IAMPolicySSM: Type: AWS::IAM::Policy Properties: PolicyName: !Sub d2lcsr-${TagEnvironment}-policy-ssm PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ssm:GetDocument - ssm:GetParameters - ssm:UpdateAssociationStatus - ssm:UpdateInstanceAssociationStatus - ssm:CreateAssociation Resource: "*" - Effect: Allow Action: - ec2:DescribeInstanceStatus Resource: "*" Roles: - !Ref IAMRoleWebServer - !Ref IAMRoleTaskServer IAMPolicyTagEC2: Type: AWS::IAM::Policy Properties: PolicyName: !Sub d2lcsr-${TagEnvironment}-policy-ec2-tagging PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ec2:CreateTags Resource: - "*" Roles: - !Ref IAMRoleWebServer - !Ref IAMRoleTaskServer IAMPolicyDecryptKMS: Type: AWS::IAM::Policy Properties: PolicyName: !Sub d2lcsr-${TagEnvironment}-policy-kms PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - kms:Decrypt Resource: - !Ref KMSDefaultSSMKeyARN Roles: - !Ref IAMRoleTaskServer

Outputs

Outputs are values resulting from the CloudFormation stack that can be: 1) Viewed in the AWS cosole under the CloudFormation service. 2) Marked as export to be imported into another stack allowing cross-stack references.

Outputs: FQDNforCSR: Description: FQDN for the CSR application (hosted zone) Value: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-csr Export: Name: !Sub ${AWS::StackName}-fqdn-for-csr FQDNforGradeFeed: Description: FQDN for the Grade Feed application (hosted zone) Value: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-grade Export: Name: !Sub ${AWS::StackName}-fqdn-for-grade FQDNforCourseFeed: Description: FQDN for the Course Feed application (hosted zone) Value: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-course Export: Name: !Sub ${AWS::StackName}-fqdn-for-course FQDNforValenceTest: Description: FQDN for the Valence Test Tool application (hosted zone) Value: Fn::ImportValue: !Sub ${S3StackName}-fqdn-for-valencetest Export: Name: !Sub ${AWS::StackName}-fqdn-for-valencetest