constructor()

in iis-smbshare-sqlserver/typescript/lib/constructs/windows-autoscaling-stack.ts [40:206]


  constructor(scope: cdk.Construct, id: string, props: cdk.StackProps, autoScalingProps: AutoScalingProps) {
    super(scope, id, props);
    // use the vpc we just created
    const customVPC = autoScalingProps.vpc

    // define a role for the windows instances
    const signalCloudFormation = new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          resources: ['*'],
          actions: [
            'cloudformation:DescribeStackResource',
            'cloudformation:SignalResource'
          ],
          effect: iam.Effect.ALLOW
        }),
      ],
    });
    const role = new iam.Role(scope, `${autoScalingProps.prefix}-windows-instance-role`, {
      assumedBy: new iam.CompositePrincipal(
        new iam.ServicePrincipal('ec2.amazonaws.com'),
        new iam.ServicePrincipal('ssm.amazonaws.com')
      ),
      managedPolicies: [
        // allows us to access instance via SSH using IAM and SSM
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          'AmazonSSMManagedInstanceCore'
        ),
        // allows ec2 instance to access secrets manager and retrieve secrets
        // needed to add windows host to domain
        iam.ManagedPolicy.fromAwsManagedPolicyName('SecretsManagerReadWrite'),
        //allows ec2 instance to download source code from s3 bucket
        iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'),
        //allows ssm session to be established on instance, allows instance to access parameter store
        iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMFullAccess'),
        // allows ec2 instance cloudwatch agent to send logs to cloudwatch
        iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchAgentServerPolicy')
      ],
      inlinePolicies:{
          "signalCloudFormation" : signalCloudFormation
        }
    })

    // lets create a security group for the windows instances
    this.securityGroup = new ec2.SecurityGroup(
      scope,
      `${autoScalingProps.prefix}-windows-instances-sg`,
      {
        vpc: customVPC,
        allowAllOutbound: true,
        securityGroupName: `${autoScalingProps.prefix}-windows-instances-sg`,
      }
    )
    if (autoScalingProps.albSecurityGroup)
      this.securityGroup.connections.allowFrom(autoScalingProps.albSecurityGroup, ec2.Port.tcpRange(0, 65535), 'Allow incoming connections from Load Balancer');
    if(autoScalingProps.bastionSecurityGroup)
        this.securityGroup.connections.allowFrom(autoScalingProps.bastionSecurityGroup, ec2.Port.tcp(3389), 'Allow incoming RDP from Bastion host');

    // create and export out autoscaling group
    
    this.asg = new autoscaling.AutoScalingGroup(scope, `${autoScalingProps.prefix}-asg`, {
      autoScalingGroupName: `${autoScalingProps.prefix}-asg`,
      vpc: autoScalingProps.vpc,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.C3,
        ec2.InstanceSize.LARGE
      ),
      machineImage: ec2.MachineImage.latestWindows(ec2.WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_BASE),
      securityGroup: this.securityGroup,
      role: role,
      minCapacity: 2,
      maxCapacity: 4,
      vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_NAT },
      keyName: autoScalingProps.keyName,
      signals: autoscaling.Signals.waitForAll({
        // Configure the timeout based on the time taken by all init scripts to complete including restarting the machine step
        timeout: cdk.Duration.minutes(90)
      })
    });

    const autoScalingGroupCfn = <CfnAutoScalingGroup> this.asg.node.tryFindChild('ASG');

    // Store CloudWatchConfig in ParameterStore and use it to configure Cloud Watch Agent in EC2 instance
    let cloudWatchAgentConfigParameterName = '';
    if(autoScalingProps.cloudWatchAgentConfigPath !== ''){
      cloudWatchAgentConfigParameterName = 'WindowsCloudWatchAgentConfig';
      const cloudWatchAgentConfig = fs.readFileSync(autoScalingProps.cloudWatchAgentConfigPath, 'utf8');
      // console.log(cloudWatchAgentConfig);
      const cloudWatchAgentConfigParameter = new ssm.StringParameter(scope, 'cloud-watch-agent-config', {
        parameterName: cloudWatchAgentConfigParameterName,
        stringValue: cloudWatchAgentConfig,
        description: 'cloud watch agent config used to configure cloud watch agent in EC2 instance',
        type: ssm.ParameterType.STRING,
        tier: ssm.ParameterTier.STANDARD
      });
    }

    const initData = ec2.CloudFormationInit.fromElements(
      ec2.InitFile.fromAsset("c:\\cfn\\1-setup-website.ps1", "lib/scripts/1-setup-website.ps1"),
      // Install any other softwares needed
      // Included script will install sql management studio for testing the connection to SQL RDS
      // Included script will install code deploy agent so that code will be deployed from build pipeline
      ec2.InitFile.fromAsset("c:\\cfn\\2-install-softwares.ps1",'lib/scripts/2-install-softwares.ps1'),

      ec2.InitFile.fromString("c:\\cfn\\3-add-machine-to-domain-map-network-drive.ps1",
        fs.readFileSync('lib/scripts/3-add-machine-to-domain-map-network-drive.ps1', 'utf8')
          .replace(/##replace_with_domain##/g, autoScalingProps.domain)
          .replace(/##replace_file_share_dns_name##/g, autoScalingProps.fileSystemDnsName)
        ),

        ec2.InitFile.fromString("c:\\cfn\\6-create-add-AD-user.ps1",
        fs.readFileSync('lib/scripts/6-create-add-AD-user.ps1', 'utf8')
          .replace(/##replace_with_domain##/g, autoScalingProps.domain)
          .replace(/##replace_with_domain_user_name##/g, autoScalingProps.domainUserName)),

          ec2.InitFile.fromString("c:\\cfn\\7-import-certificate-from-s3.ps1",
        fs.readFileSync('lib/scripts/7-import-certificate-from-s3.ps1', 'utf8')
          .replace(/##replace_with_certificate_bucket_name##/g, autoScalingProps.s3CertificateBucketname)
          .replace(/##replace_with_certificate_password##/, autoScalingProps.certificatePassword)
      ),
      ec2.InitFile.fromString("c:\\cfn\\8-create-webapp-set-apppool.ps1",
        fs.readFileSync('lib/scripts/8-create-webapp-set-apppool.ps1', 'utf8')
          .replace(/##replace_with_app_pool_name##/g, autoScalingProps.iisAppPoolName)
          .replace(/##replace_with_domain##/g, autoScalingProps.domain)
          .replace(/##replace_with_bindinghost##/g, autoScalingProps.iisbindingHostName)
          .replace(/##replace_with_ssl_cert_host_name##/g, autoScalingProps.sslCertHostname)
          .replace(/##replace_with_certificate_bucket_name##/g, autoScalingProps.s3CertificateBucketname)
      ),

      ec2.InitFile.fromString("c:\\cfn\\9-install-configure-cloudwatch-agent.ps1",
        fs.readFileSync('lib/scripts/9-install-configure-cloudwatch-agent.ps1', 'utf8')
          .replace(/##replace_with_agent_config_parameter_name##/g, cloudWatchAgentConfigParameterName)
      ),

      ec2.InitFile.fromString("c:\\cfn\\90-install-website-from-buildoutput.ps1",
        fs.readFileSync('lib/scripts/90-install-website-from-buildoutput.ps1', 'utf8')
          .replace(/##replace_with_s3_build_output_msi_uri##/g, autoScalingProps.s3BuildOutputMsiUri)
      ),

      // Scripts order is determined by sorting alphabetically in ascending order
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\1-setup-website.ps1"', { key: "1-setup-website", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\2-install-softwares.ps1"', { key: "2-install-softwares", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\3-add-machine-to-domain-map-network-drive.ps1"', { key: "3-add-machine-to-domain-map-network-drive", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -Command Install-WindowsFeature RSAT-ADDS', { key: "4-Install-Rsat", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(120)) }),
      // //wait forever to make sure command resume once restart
      ec2.InitCommand.shellCommand('powershell.exe -Command Restart-Computer -force', { key: "5-Restart", waitAfterCompletion: ec2.InitCommandWaitDuration.forever() }),
      
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\6-create-add-AD-user.ps1"', { key: "6-create-add-AD-user.ps1", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\7-import-certificate-from-s3.ps1"', { key: "7-import-certificate-from-s3", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\8-create-webapp-set-apppool.ps1"', { key: "8-create-webapp-set-apppool", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\9-install-configure-cloudwatch-agent.ps1"', { key: "9-install-configure-cloudwatch-agent", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -File "c:\\cfn\\90-install-website-from-buildoutput.ps1"', { key: "90-install-website-from-buildoutput", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(10)) }),
      ec2.InitCommand.shellCommand('powershell.exe -Command Restart-Computer -force', { key: "91-Restart", waitAfterCompletion: ec2.InitCommandWaitDuration.forever() }),
      ec2.InitCommand.shellCommand('cfn-signal.exe -e %ERRORLEVEL% --resource ' + autoScalingGroupCfn.logicalId + ' --stack ' + autoScalingProps.parentStackId + ' --region ' + this.region  + ' --role ' + role.roleName , { key: "92-Signal", waitAfterCompletion: ec2.InitCommandWaitDuration.of(cdk.Duration.seconds(5)) })
      );
    this.asg.applyCloudFormationInit(
      initData,{
        //If Init steps need to be debug, then follow the steps below
        // 1. Set this flag to true
        // 2. Wait for Windows EC2 instance to be fully created in AWS Console
        // 3. Connect to Windows EC2 instance using SSM Manager
        // 4. All the scripts and execution logs located in c:\cfn folder
        // 5. Inspect c:\cfn:\cfn-init.log for any errors
        // Once the errors are resolved, Set the flag to false. Otherwise Stack creation will be successful even when EC2 instances are failed to create
        ignoreFailures: true,
      });
  }