AWSTemplateFormatVersion: '2010-09-09'
Description: >-
  This template creates 2 Windows Server instances into private subnets in separate Availability Zones inside a VPC. After extending your on-premises
  network to the VPC, you can promote the Windows Server instances to Domain Controllers in your AD forest. Or optionally you can choose to have automation join and promote them to an existing domain.
  **WARNING** This template creates Amazon EC2 Windows instance and related resources. You will be billed for the AWS resources used if you create a stack from this template. (qs-1qup6radg)
Metadata:
  cfn-lint: { config: { ignore_checks: [W9004, W9006, W8001] } }
  QuickStartDocumentation:
    EntrypointName: 'Parameters for extending on-premises AD into an existing VPC'
    Order: '4'
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Network configuration
        Parameters:
          - VPCCIDR
          - VPCID
          - Subnet1ID
          - Subnet2ID
          - ExistingDomainControllersSG
      - Label:
          default: Amazon EC2 configuration
        Parameters:
          - ADServerInstanceType
          - ADServer1NetBIOSName
          - ADServer1PrivateIP
          - ADServer2NetBIOSName
          - ADServer2PrivateIP
          - DataDriveSizeGiB
          - EbsEncryptionKmsKeyId
          - EnableAdvancedAudtingandMetrics
          - KeyPairName
          - LatestAmiId
      - Label:
          default: Microsoft Active Directory Domain Services configuration
        Parameters:
          - JoinAndPromote
          - AdministratorSecret
          - RestoreModeSecret
          - ExistingDomainController1IP
          - ExistingDomainController2IP
          - DomainDNSName
          - DomainNetBIOSName
          - SetupAppInsightsMonitoring
          - AppInsightsApplicationName
      - Label:
          default: AWS Quick Start configuration
        Parameters:
          - QSS3BucketName
          - QSS3BucketRegion
          - QSS3KeyPrefix
    ParameterLabels:
      ADServer1NetBIOSName:
        default: Domain Controller 1 NetBIOS Name
      ADServer1PrivateIP:
        default: Domain Controller 1 Private IP Address
      ADServer2NetBIOSName:
        default: Domain Controller 2 NetBIOS Name
      ADServer2PrivateIP:
        default: Domain Controller 2 Private IP Address
      ADServerInstanceType:
        default: Domain Controllers Instance Type
      AdministratorSecret:
        default: Secret ARN Containing Administrator Credentials
      AppInsightsApplicationName:
        default: Registered Application Name
      DataDriveSizeGiB:
        default: SYSVOL and NTDS Data Drive Size
      DomainDNSName:
        default: Domain DNS Name
      DomainNetBIOSName:
        default: Domain NetBIOS Name
      EbsEncryptionKmsKeyId:
        default: KMS Key for EBS Encryption
      EnableAdvancedAudtingandMetrics:
        default: Advanced Auditing and Metrics
      ExistingDomainController1IP:
        default: IP used for DNS (Must be accessible)
      ExistingDomainController2IP:
        default: IP used for DNS (Must be accessible)
      ExistingDomainControllersSG:
        default: Exiting Domain Controllers Security Group ID
      JoinAndPromote:
        default: Join and Promote to Domain Controllers
      KeyPairName:
        default: Key Pair Name
      LatestAmiId:
        default: SSM Parameter Value for latest AMI ID
      QSS3BucketName:
        default: Quick Start S3 Bucket Name
      QSS3BucketRegion:
        default: Quick Start S3 Bucket Region
      QSS3KeyPrefix:
        default: Quick Start S3 Key Prefix
      RestoreModeSecret:
        default: Secret ARN Containing Restore Mode Credentials
      Subnet1ID:
        default: Subnet 1 ID
      Subnet2ID:
        default: Subnet 2 ID
      SetupAppInsightsMonitoring:
        default: Setup Application Insights Monitoring
      VPCCIDR:
        default: VPC CIDR
      VPCID:
        default: VPC ID
Parameters:
  ADServer1NetBIOSName:
    AllowedPattern: '^[a-zA-Z0-9]+$'
    Default: DC3
    Description: NetBIOS name of the first additional Active Directory Domain Controller (up to 15 characters)
    MaxLength: '15'
    MinLength: '1'
    Type: String
  ADServer1PrivateIP:
    AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
    Default: 10.0.0.11
    Description: Fixed private IP for the first additional Active Directory Domain Controller located in subnet 1
    Type: String
  ADServer2NetBIOSName:
    AllowedPattern: '^[a-zA-Z0-9]+$'
    Default: DC4
    Description: NetBIOS name of the second additional Active Directory Domain Controller (up to 15 characters)
    MaxLength: '15'
    MinLength: '1'
    Type: String
  ADServer2PrivateIP:
    AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
    Default: 10.0.32.11
    Description: Fixed private IP for the second additional Active Directory Domain Controller located in subnet 2
    Type: String
  ADServerInstanceType:
    AllowedValues:
      - t3.medium
      - t3.large
      - t3.xlarge
      - t3.2xlarge
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5.4xlarge
    Default: m5.large
    Description: Amazon EC2 instance type for Active Directory Controller instances
    Type: String
  AdministratorSecret:
    AllowedPattern: '(^arn:[^:]+:secretsmanager:[^:]+:\d{12}:secret:[\w\/+=.@-]{1,512})?$'
    Description: ARN for the Administrator credentials Secret used to join and promote domain controllers (Only used if JoinAndPromote is Yes)
    Type: String
    Default: ''
  AppInsightsApplicationName:
    Description: Application name to be used with AppInsights monitoring
    Type: String
    Default: ''
    MaxLength: '300'
    AllowedPattern: '^[a-zA-Z0-9\-]*$'
  DataDriveSizeGiB:
    Default: '10'
    Description: Size of SYSVOL and NTDS data drive in GiB
    Type: Number
    MinValue: '1' 
    MaxValue: '16384'
  DomainDNSName:
    AllowedPattern: '^([a-zA-Z0-9]+[\.\-])+([a-zA-Z0-9])+$'
    Default: example.com
    Description: Fully qualified domain name (FQDN) of the domain you would like to join and promote to e.g. example.com
    MaxLength: '64'
    MinLength: '2'
    Type: String
  DomainNetBIOSName:
    AllowedPattern: '^[a-zA-Z0-9]+$'
    Default: example
    Description: NetBIOS name of the domain (up to 15 characters) you would like to join and promote to for users of earlier versions of Windows e.g. EXAMPLE
    MaxLength: '15'
    MinLength: '1'
    Type: String
  EbsEncryptionKmsKeyId:
    AllowedPattern: ^(arn:[^:]+:kms:[^:]+:\d{12}:(key|alias)\/[\w\-\/]+)$|^(alias\/)?[\w\-\/]+$
    Default: alias/aws/ebs
    Description: The identifier of the AWS KMS key to use for Amazon EBS encryption. You can specify the KMS key using any of the following; Key ID, Key alias, Key ARN, Alias ARN
    Type: String
  EnableAdvancedAudtingandMetrics:
    Description: Enable advanced auditing and metrics and upload them to CloudWatch using the Amazon Kinesis Agent for Microsoft Windows
    AllowedValues:
      - 'true'
      - 'false'
    Type: String
    Default: 'false'
  ExistingDomainController1IP:
    AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
    Default: 10.0.0.10
    Description: IP of DNS server that can resolve domain (Must be accessible)
    Type: String
  ExistingDomainController2IP:
    AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
    Default: 10.0.32.10
    Description: IP of DNS server that can resolve domain (Must be accessible)
    Type: String
  ExistingDomainControllersSG:
    AllowedPattern: '^sg-[a-f0-9]{8}(?:[a-f0-9]{9})?$'
    Description: Security Group ID for existing Domain Controllers Security Group. (Only used when JoinAndPromote equals Yes)
    Type: String
    Default: sg-1234567890abcdef0
  JoinAndPromote:
    AllowedValues:
      - 'Yes'
      - 'No'
    Default: 'No'
    Description: Do you want to join and promote these instances to be Active Directory Domain Controllers
    Type: String
  KeyPairName:
    Description: Public/private key pairs allow you to securely connect to your instance after it launches
    Type: AWS::EC2::KeyPair::KeyName
  LatestAmiId:
    Default: /aws/service/ami-windows-latest/Windows_Server-2022-English-Full-Base
    Description: Systems Manager parameter value for latest Windows Server AMI
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
  QSS3BucketName:
    AllowedPattern: '^[a-z0-9]+[a-z0-9\.\-]*[a-z0-9]+$'
    ConstraintDescription: Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).
    Default: aws-ia
    Description: S3 bucket name for CA CRL storage. Bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-)
    Type: String
  QSS3BucketRegion:
    AllowedPattern: '^[a-z]+\-[a-z\-]+\-[0-9]{1}$'
    Default: us-east-1
    Description: The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value
    Type: String
  QSS3KeyPrefix:
    AllowedPattern: '^[a-zA-Z0-9\-\/]+$'
    ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/)
    Default: cfn-ps-microsoft-activedirectory/
    Description: S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/)
    Type: String
  RestoreModeSecret:
    AllowedPattern: '(^arn:[^:]+:secretsmanager:[^:]+:\d{12}:secret:[\w\/+=.@-]{1,512})?$'
    Description: ARN for the Restore Mode credentials Secret used to join and promote domain controllers (Only used if JoinAndPromote is Yes)
    Type: String
    Default: ''
  Subnet1ID:
    Description: ID of subnet 1 in Availability Zone 1 (e.g., subnet-a0246dcd)
    Type: AWS::EC2::Subnet::Id
  Subnet2ID:
    Description: ID of subnet 2 in Availability Zone 2 (e.g., subnet-a0246dcd)
    Type: AWS::EC2::Subnet::Id
  SetupAppInsightsMonitoring:
    AllowedValues:
      - 'true'
      - 'false'
    Default: 'false'
    Type: String
  VPCCIDR:
    AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$'
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/16
    Description: CIDR Block for the VPC
    Type: String
  VPCID:
    Description: ID of the VPC (e.g., vpc-0343606e)
    Type: AWS::EC2::VPC::Id
Rules:
  SubnetsInVPC:
    Assertions:
      - Assert: !EachMemberIn
          - !ValueOfAll
            - AWS::EC2::Subnet::Id
            - VpcId
          - !RefAll 'AWS::EC2::VPC::Id'
        AssertDescription: All subnets must in the VPC
  SecretValidation:
    RuleCondition: !Equals [!Ref JoinAndPromote, 'Yes']
    Assertions:
      - AssertDescription: The Administrator Secret must be valid
        Assert: !Not [!Equals [!Ref AdministratorSecret, '']]
      - AssertDescription: The Restore Mode Secret must be valid
        Assert: !Not [!Equals [!Ref RestoreModeSecret, '']]
Conditions:
  DoJoinAndPromote: !Equals [!Ref JoinAndPromote, 'Yes']
  DoNOTJoinAndPromote: !Equals [!Ref JoinAndPromote, 'No']
  UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-ia']
  AppInsightsEnabled: !Equals [!Ref SetupAppInsightsMonitoring, 'true']
  UsingDefaultAppName: !Equals [!Ref AppInsightsApplicationName, '']
Resources:
  AWSQuickstartActiveDirectoryDS:
    Condition: DoNOTJoinAndPromote
    Type: AWS::SSM::Document
    Properties:
      DocumentType: Automation
      Tags:
        - Key: StackName
          Value: !Ref AWS::StackName
      Content:
        schemaVersion: '0.3'
        description: 'Deploy non-promoted domain contollers with SSM Automation'
        parameters:
          ADServer1NetBIOSName:
            allowedPattern: '^[a-zA-Z0-9]+$'
            description: NetBIOS name of the first Active Directory Domain Controller (up to 15 characters)
            type: String
            maxChars: '15'
            minChars: '1'
          ADServer2NetBIOSName:
            allowedPattern: '^[a-zA-Z0-9]+$'
            description: NetBIOS name of the first Active Directory Domain Controller (up to 15 characters)
            type: String
            maxChars: '15'
            minChars: '1'  
          DomainDNSName:
            allowedPattern: '^([a-zA-Z0-9]+[\.\-])+([a-zA-Z0-9])+$'
            description: Fully qualified domain name (FQDN) of the domain you would like to join and promote to e.g. example.com
            default: 'example.com'
            type: String
            maxChars: '64'
            minChars: '2'
          DomainNetBIOSName:
            allowedPattern: '^[a-zA-Z0-9]+$'
            description: NetBIOS name of the domain (up to 15 characters) you would like to join and promote to for users of earlier versions of Windows e.g. EXAMPLE
            type: String
            maxChars: '15'
            minChars: '1'
          EnableAdvancedAudtingandMetrics:
            allowedValues:
              - 'true'
              - 'false'
            default: 'false'
            description: Enable advanced auditing and metrics
            type: String
          ExistingDomainController1IP:
            allowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
            description: IP of DNS server that can resolve domain (Must be accessible)
            type: String
          ExistingDomainController2IP:
            allowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
            description: IP of DNS server that can resolve domain (Must be accessible)
            type: String
          QSS3BucketName:
            allowedPattern: '^[a-z0-9]+[a-z0-9\.\-]*[a-z0-9]+$'
            default: aws-ia-us-east-1
            description: S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-)
            type: String
          QSS3BucketRegion:
            allowedPattern: '^[a-z]+\-[a-z\-]+\-[0-9]{1}$'
            default: us-east-1
            description: The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value
            type: String
          QSS3KeyPrefix:
            allowedPattern: '^[a-zA-Z0-9\-\/]+$'
            default: 'cfn-ps-microsoft-activedirectory/'
            description: S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/)
            type: String
          StackName:
            allowedPattern: '^[a-zA-Z][A-Za-z0-9\-]+$'
            description: Stack Name Input for cfn resource signal
            maxChars: '128'
            minChars: '1'
            type: String
          URLSuffix:
            allowedPattern: '^[a-zA-Z0-9\-\.]+$'
            default: 'amazonaws.com'
            description: AWS URL suffix
            type: String
          VPCCIDR:
            allowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$'
            description: CIDR Block for the VPC
            type: String
        mainSteps:
          - name: dcsInstanceIds
            action: aws:executeAwsApi
            onFailure: step:signalfailure
            inputs:
              Service: ec2
              Api: DescribeInstances
              Filters:
                - Name: tag:Name
                  Values: ['{{ADServer1NetBIOSName}}', '{{ADServer2NetBIOSName}}']
                - Name: tag:aws:cloudformation:stack-name
                  Values: ['{{StackName}}']
                - Name: instance-state-name
                  Values: ['running']
            outputs:
              - Name: InstanceIds
                Selector: $.Reservations..Instances..InstanceId
                Type: StringList
            nextStep: dc1InstanceId
          - name: dc1InstanceId
            action: aws:executeAwsApi
            onFailure: step:signalfailure
            inputs:
              Service: ec2
              Api: DescribeInstances
              Filters:
                - Name: tag:Name
                  Values: ['{{ADServer1NetBIOSName}}']
                - Name: tag:aws:cloudformation:stack-name
                  Values: ['{{StackName}}']
                - Name: instance-state-name
                  Values: ['running']
            outputs:
              - Name: InstanceId
                Selector: $.Reservations[0].Instances[0].InstanceId
                Type: String
            nextStep: dc2InstanceId
          - name: dc2InstanceId
            action: aws:executeAwsApi
            onFailure: step:signalfailure
            inputs:
              Service: ec2
              Api: DescribeInstances
              Filters:
                - Name: tag:Name
                  Values: ['{{ADServer2NetBIOSName}}']
                - Name: tag:aws:cloudformation:stack-name
                  Values: ['{{StackName}}']
                - Name: instance-state-name
                  Values: ['running']
            outputs:
              - Name: InstanceId
                Selector: $.Reservations[0].Instances[0].InstanceId
                Type: String
            nextStep: intializeInstance
          - name: intializeInstance
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

                  $S3BucketName = '{{QSS3BucketName}}'
                  $S3KeyPrefix = '{{QSS3KeyPrefix}}'
                  $S3BucketRegion = '{{QSS3BucketRegion}}'

                  $CustomModules = @(
                      'Module-AD.psd1',
                      'Module-AD.psm1'
                  )

                  $Modules = @(
                      @{
                          Name = 'NetworkingDsc'
                          Version = '8.2.0'
                      },
                      @{
                          Name = 'ActiveDirectoryDsc'
                          Version = '6.0.1'
                      },
                      @{
                          Name = 'ComputerManagementDsc'
                          Version = '8.5.0'
                      },
                      @{
                          Name = 'DnsServerDsc'
                          Version = '3.0.0'
                      },
                      @{
                          Name = 'AuditPolicyDsc'
                          Version = '1.4.0.0'
                      }
                  )

                  Write-Output 'Creating AWSQuickstart Directory'
                  Try {
                      $Null = New-Item -Path 'C:\AWSQuickstart\Module-AD' -ItemType 'Directory' -ErrorAction Stop
                  } Catch [System.Exception] {
                      Write-Output "Failed to create AWSQuickstart directory $_"
                      Exit 1
                  }

                  $S3KeyPrefix = $S3KeyPrefix.Substring(0,$S3KeyPrefix.Length -1)
                  Write-Output 'Downloading AD PowerShell Module'
                  Foreach ($CustomModule in $CustomModules) {
                      Try {
                          $Null = Read-S3Object -BucketName $S3BucketName -Key "$($S3KeyPrefix)/scripts/Modules/Module-AD/$CustomModule" -File "C:\AWSQuickstart\Module-AD\$CustomModule" -Region $S3BucketRegion
                      } Catch [System.Exception] {
                          Write-Output "Failed to read and download $CustomModule.Name from S3 $_"
                          Exit 1
                      }
                  }

                  Write-Output 'Installing NuGet Package Provider'
                  Try {
                      $Null = Install-PackageProvider -Name 'NuGet' -MinimumVersion '2.8.5' -Force -ErrorAction Stop
                  } Catch [System.Exception] {
                      Write-Output "Failed to install NuGet Package Provider $_"
                      Exit 1
                  }

                  Write-Output 'Setting PSGallery Respository to trusted'
                  Try {
                      Set-PSRepository -Name 'PSGallery' -InstallationPolicy 'Trusted' -ErrorAction Stop
                  } Catch [System.Exception] {
                      Write-Output "Failed to set PSGallery Respository to trusted $_"
                      Exit 1
                  }

                  Write-Output 'Installing the needed Powershell DSC modules for this Quick Start'
                  Foreach ($Module in $Modules) {
                      Try {
                          Install-Module -Name $Module.Name -RequiredVersion $Module.Version -ErrorAction Stop
                      } Catch [System.Exception] {
                          Write-Output "Failed to Import Modules $_"
                          Exit 1
                      }
                  } 
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: configureInstance
          - name: configureInstance
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  New-VolumeFromRawDisk
                  Invoke-PreConfig
                  Invoke-LcmConfig
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: configureDc1Mof
          - name: configureDc1Mof
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dc1InstanceId.InstanceId}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  $EniConfig = Get-EniConfig
                  Set-DscConfiguration -DeploymentType 'NonPromo' -DomainDNSName '{{DomainDNSName}}' -DomainNetBIOSName '{{DomainNetBIOSName}}' -ExistingDcIP01 '{{ExistingDomainController1IP}}'-ExistingDcIP02 '{{ExistingDomainController2IP}}' -GatewayAddress $EniConfig.GatewayAddress -InstanceIP $EniConfig.IpAddress -InstanceIPDns $EniConfig.DnsIpAddress -InstanceNetBIOSName '{{ADServer1NetBIOSName}}' -MacAddress $EniConfig.MacAddress
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: runDc1Mof
          - name: runDc1Mof
            action: aws:runCommand
            onFailure: step:signalfailure
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dc1InstanceId.InstanceId}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  Start-DscConfiguration 'C:\AWSQuickstart\ConfigInstance' -Wait -Verbose -Force
                  Invoke-DscStatusCheck
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: configureDc2Mof
          - name: configureDc2Mof
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dc2InstanceId.InstanceId}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  $EniConfig = Get-EniConfig
                  Set-DscConfiguration -DeploymentType 'NonPromo' -DomainDNSName '{{DomainDNSName}}' -DomainNetBIOSName '{{DomainNetBIOSName}}' -ExistingDcIP01 '{{ExistingDomainController1IP}}'-ExistingDcIP02 '{{ExistingDomainController2IP}}' -GatewayAddress $EniConfig.GatewayAddress -InstanceIP $EniConfig.IpAddress -InstanceIPDns $EniConfig.DnsIpAddress -InstanceNetBIOSName '{{ADServer2NetBIOSName}}' -MacAddress $EniConfig.MacAddress
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: runDc2Mof
          - name: runDc2Mof
            action: aws:runCommand
            onFailure: step:signalfailure
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dc2InstanceId.InstanceId}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  Start-DscConfiguration 'C:\AWSQuickstart\ConfigInstance' -Wait -Verbose -Force
                  Invoke-DscStatusCheck
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: PostConfig
          - name: PostConfig
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  Set-AD2PostConfig

                  If ('{{EnableAdvancedAudtingandMetrics}}' -eq 'true') {
                    Set-DcAuditDscConfiguration
                    Set-LogsAndMetricsCollection -Role 'DomainController' -Stackname '{{StackName}}'
                  }

                  Invoke-Cleanup -VPCCIDR '{{VPCCIDR}}'
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
          - name: configureIMDSv2dc1
            action: 'aws:executeAwsApi'
            onFailure: step:signalfailure
            description: Set hop limit and configure IMDS version
            inputs:
              Service: ec2
              Api: ModifyInstanceMetadataOptions
              InstanceId: '{{dc1InstanceId.InstanceId}}'
              HttpEndpoint: enabled
              HttpTokens: required
            timeoutSeconds: 300
            isEnd: false
            isCritical: true
            maxAttempts: 3
          - name: configureIMDSv2dc2
            action: 'aws:executeAwsApi'
            onFailure: step:signalfailure
            description: Set hop limit and configure IMDS version
            inputs:
              Service: ec2
              Api: ModifyInstanceMetadataOptions
              InstanceId: '{{dc2InstanceId.InstanceId}}'
              HttpEndpoint: enabled
              HttpTokens: required
            timeoutSeconds: 300
            isEnd: false
            isCritical: true
            maxAttempts: 3
          - name: CFNSignalEnd
            action: aws:branch
            inputs:
              Choices:
                - NextStep: signalsuccess
                  Not:
                    Variable: '{{StackName}}'
                    StringEquals: ''
                - NextStep: sleepend
                  Variable: '{{StackName}}'
                  StringEquals: ''
          - name: signalsuccess
            action: aws:executeAwsApi
            isEnd: True
            inputs:
              Service: cloudformation
              Api: SignalResource
              LogicalResourceId: DomainController2
              StackName: '{{StackName}}'
              Status: SUCCESS
              UniqueId: '{{dc2InstanceId.InstanceId}}'
          - name: sleepend
            action: aws:sleep
            isEnd: True
            inputs:
              Duration: PT1S
          - name: signalfailure
            action: aws:executeAwsApi
            inputs:
              Service: cloudformation
              Api: SignalResource
              LogicalResourceId: DomainController2
              StackName: '{{StackName}}'
              Status: FAILURE
              UniqueId: '{{dc2InstanceId.InstanceId}}'
  AWSQuickstartActiveDirectoryDSPromote:
    Condition: DoJoinAndPromote
    Type: AWS::SSM::Document
    Properties:
      DocumentType: Automation
      Tags:
        - Key: StackName
          Value: !Ref AWS::StackName
      Content:
        schemaVersion: '0.3'
        description: 'Deploy promoted domain contollers with SSM Automation'
        parameters:
          ADAdminSecParamName:
            allowedPattern: '^arn:[^:]+:secretsmanager:[^:]+:\d{12}:secret:[\w\/+=.@-]{1,512}$'
            description: ARN for the Administrator credentials Secret used to join and promote domain controllers (Only used if JoinAndPromote is Yes)
            maxChars: '2048'
            minChars: '20'
            type: String
          ADServer1NetBIOSName:
            allowedPattern: '^[a-zA-Z0-9\-]+$'
            description: NetBIOS name of the first Active Directory Domain Controller (up to 15 characters)
            maxChars: '15'
            minChars: '1'
            type: String
          ADServer1PrivateIP:
            allowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
            description: Fixed private IP for the first additional Active Directory Domain Controller located in subnet 1
            type: String
          ADServer2NetBIOSName:
            allowedPattern: '^[a-zA-Z0-9\-]+$'
            description: NetBIOS name of the first Active Directory Domain Controller (up to 15 characters)
            maxChars: '15'
            minChars: '1'
            type: String
          ADServer2PrivateIP:
            allowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
            description: Fixed private IP for the second additional Active Directory Domain Controller located in subnet 2
            type: String
          DomainDNSName:
            allowedPattern: '^([a-zA-Z0-9]+[\.\-])+([a-zA-Z0-9])+$'
            description: Fully qualified domain name (FQDN) of the domain you would like to join and promote to e.g. example.com
            maxChars: '64'
            minChars: '2'
            type: String
          DomainNetBIOSName:
            allowedPattern: '^[a-zA-Z0-9\-]+$'
            description: NetBIOS name of the domain (up to 15 characters) you would like to join and promote to for users of earlier versions of Windows e.g. EXAMPLE
            maxChars: '15'
            minChars: '1'
            type: String
          EnableAdvancedAudtingandMetrics:
            allowedValues:
              - 'true'
              - 'false'
            description: Enable advanced auditing and metrics
            type: String
          ExistingDomainController1IP:
            allowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
            description: IP of DNS server that can resolve domain (Must be accessible)
            type: String
          RestoreModeSecParamName:
            allowedPattern: '^arn:[^:]+:secretsmanager:[^:]+:\d{12}:secret:[\w\/+=.@-]{1,512}$'
            description: ARN for the Restore Mode credentials Secret used to join and promote domain controllers (Only used if JoinAndPromote is Yes)
            maxChars: '2048'
            minChars: '20'
            type: String
          QSS3BucketName:
            allowedPattern: '^[a-z0-9]+[a-z0-9\.\-]*[a-z0-9]+$'
            description: S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-)
            type: String
          QSS3BucketRegion:
            allowedPattern: '^[a-z]+-[a-z\-]+-[0-9]{1}$'
            description: The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value
            type: String
          QSS3KeyPrefix:
            allowedPattern: '^[a-zA-Z0-9\-\/]+$'
            description: S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/)
            type: String
          StackName:
            allowedPattern: '^[a-zA-Z][A-Za-z0-9\-]*$'
            description: Stack Name Input for cfn resource signal
            maxChars: '128'
            minChars: '1'
            type: String
          URLSuffix:
            allowedPattern: '^[a-zA-Z0-9\-\.]+$'
            default: 'amazonaws.com'
            description: AWS URL suffix
            type: String
          VPCCIDR:
            allowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$'
            description: CIDR block for private subnet 1 located in Availability Zone 1
            type: String
        mainSteps:
          - name: dcsInstanceIds
            action: aws:executeAwsApi
            onFailure: step:signalfailure
            inputs:
              Service: ec2
              Api: DescribeInstances
              Filters:
                - Name: tag:Name
                  Values: ['{{ADServer1NetBIOSName}}', '{{ADServer2NetBIOSName}}']
                - Name: tag:aws:cloudformation:stack-name
                  Values: ['{{StackName}}']
                - Name: instance-state-name
                  Values: ['running']
            outputs:
              - Name: InstanceIds
                Selector: $.Reservations..Instances..InstanceId
                Type: StringList
            nextStep: dc1InstanceId
          - name: dc1InstanceId
            action: aws:executeAwsApi
            onFailure: step:signalfailure
            inputs:
              Service: ec2
              Api: DescribeInstances
              Filters:
                - Name: tag:Name
                  Values: ['{{ADServer1NetBIOSName}}']
                - Name: tag:aws:cloudformation:stack-name
                  Values: ['{{StackName}}']
                - Name: instance-state-name
                  Values: ['running']
            outputs:
              - Name: InstanceId
                Selector: $.Reservations[0].Instances[0].InstanceId
                Type: String
            nextStep: dc2InstanceId
          - name: dc2InstanceId
            action: aws:executeAwsApi
            onFailure: step:signalfailure
            inputs:
              Service: ec2
              Api: DescribeInstances
              Filters:
                - Name: tag:Name
                  Values: ['{{ADServer2NetBIOSName}}']
                - Name: tag:aws:cloudformation:stack-name
                  Values: ['{{StackName}}']
                - Name: instance-state-name
                  Values: ['running']
            outputs:
              - Name: InstanceId
                Selector: $.Reservations[0].Instances[0].InstanceId
                Type: String
            nextStep: intializeInstance
          - name: intializeInstance
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

                  $S3BucketName = '{{QSS3BucketName}}'
                  $S3KeyPrefix = '{{QSS3KeyPrefix}}'
                  $S3BucketRegion = '{{QSS3BucketRegion}}'

                  $CustomModules = @(
                      'Module-AD.psd1',
                      'Module-AD.psm1'
                  )

                  $Modules = @(
                      @{
                          Name = 'NetworkingDsc'
                          Version = '8.2.0'
                      },
                      @{
                          Name = 'ActiveDirectoryDsc'
                          Version = '6.0.1'
                      },
                      @{
                          Name = 'ComputerManagementDsc'
                          Version = '8.5.0'
                      },
                      @{
                          Name = 'DnsServerDsc'
                          Version = '3.0.0'
                      },
                      @{
                          Name = 'AuditPolicyDsc'
                          Version = '1.4.0.0'
                      }
                  )

                  Write-Output 'Creating AWSQuickstart Directory'
                  Try {
                      $Null = New-Item -Path 'C:\AWSQuickstart\Module-AD' -ItemType 'Directory' -ErrorAction Stop
                  } Catch [System.Exception] {
                      Write-Output "Failed to create AWSQuickstart directory $_"
                      Exit 1
                  }

                  $S3KeyPrefix = $S3KeyPrefix.Substring(0,$S3KeyPrefix.Length -1)
                  Write-Output 'Downloading AD PowerShell Module'
                  Foreach ($CustomModule in $CustomModules) {
                      Try {
                          $Null = Read-S3Object -BucketName $S3BucketName -Key "$($S3KeyPrefix)/scripts/Modules/Module-AD/$CustomModule" -File "C:\AWSQuickstart\Module-AD\$CustomModule" -Region $S3BucketRegion
                      } Catch [System.Exception] {
                          Write-Output "Failed to read and download $CustomModule.Name from S3 $_"
                          Exit 1
                      }
                  }

                  Write-Output 'Installing NuGet Package Provider'
                  Try {
                      $Null = Install-PackageProvider -Name 'NuGet' -MinimumVersion '2.8.5' -Force -ErrorAction Stop
                  } Catch [System.Exception] {
                      Write-Output "Failed to install NuGet Package Provider $_"
                      Exit 1
                  }

                  Write-Output 'Setting PSGallery Respository to trusted'
                  Try {
                      Set-PSRepository -Name 'PSGallery' -InstallationPolicy 'Trusted' -ErrorAction Stop
                  } Catch [System.Exception] {
                      Write-Output "Failed to set PSGallery Respository to trusted $_"
                      Exit 1
                  }

                  Write-Output 'Installing the needed Powershell DSC modules for this Quick Start'
                  Foreach ($Module in $Modules) {
                      Try {
                          Install-Module -Name $Module.Name -RequiredVersion $Module.Version -ErrorAction Stop
                      } Catch [System.Exception] {
                          Write-Output "Failed to Import Modules $_"
                          Exit 1
                      }
                  } 
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: configureInstance
          - name: configureInstance
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  New-VolumeFromRawDisk
                  Invoke-PreConfig
                  Invoke-LcmConfig
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: configureDc1Mof
          - name: configureDc1Mof
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dc1InstanceId.InstanceId}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  $EniConfig = Get-EniConfig
                  $DaSecret = Get-SecretInfo -Domain '{{DomainNetBIOSName}}' -SecretArn '{{ADAdminSecParamName}}'
                  $RmSecret = Get-SecretInfo -Domain '{{DomainNetBIOSName}}' -SecretArn '{{RestoreModeSecParamName}}'
                  Set-DscConfiguration -DaCredentials $DaSecret.DomainCredentials -DeploymentType 'SecondaryDC' -DomainDNSName '{{DomainDNSName}}' -DomainNetBIOSName '{{DomainNetBIOSName}}' -ExistingDcIP01 '{{ExistingDomainController1IP}}' -GatewayAddress $EniConfig.GatewayAddress -InstanceIP $EniConfig.IpAddress -InstanceIPDns $EniConfig.DnsIpAddress -InstanceNetBIOSName '{{ADServer1NetBIOSName}}' -MacAddress $EniConfig.MacAddress -RestoreModeCredentials $RmSecret.Credentials
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: configureDc2Mof
          - name: configureDc2Mof
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dc2InstanceId.InstanceId}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  $EniConfig = Get-EniConfig
                  $DaSecret = Get-SecretInfo -Domain '{{DomainNetBIOSName}}' -SecretArn '{{ADAdminSecParamName}}'
                  $RmSecret = Get-SecretInfo -Domain '{{DomainNetBIOSName}}' -SecretArn '{{RestoreModeSecParamName}}'
                  Set-DscConfiguration -DaCredentials $DaSecret.DomainCredentials -DeploymentType 'SecondaryDC' -DomainDNSName '{{DomainDNSName}}' -DomainNetBIOSName '{{DomainNetBIOSName}}' -ExistingDcIP01 '{{ExistingDomainController1IP}}' -GatewayAddress $EniConfig.GatewayAddress -InstanceIP $EniConfig.IpAddress -InstanceIPDns $EniConfig.DnsIpAddress -InstanceNetBIOSName '{{ADServer2NetBIOSName}}' -MacAddress $EniConfig.MacAddress -RestoreModeCredentials $RmSecret.Credentials
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: runDcMofs
          - name: runDcMofs
            action: aws:runCommand
            onFailure: step:signalfailure
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  Start-DscConfiguration 'C:\AWSQuickstart\ConfigInstance' -Wait -Verbose -Force
                  Invoke-DscStatusCheck
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: DisableFW
          - name: DisableFW
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  Try {
                      Get-NetFirewallProfile -ErrorAction Stop | Set-NetFirewallProfile -Enabled False -ErrorAction Stop
                  } Catch [System.Exception] {
                      Write-Output "Failed to disable Windows Firewall $_"
                      Exit 1
                  }
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: DnsConfig
          - name: DnsConfig
            action: aws:runCommand
            onFailure: step:signalfailure
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dc2InstanceId.InstanceId}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  $DaSecret = Get-SecretInfo -Domain '{{DomainNetBIOSName}}' -SecretArn '{{ADAdminSecParamName}}'
                  Set-DnsDscConfiguration -ADServer1NetBIOSName '{{ADServer1NetBIOSName}}' -ADServer2NetBIOSName '{{ADServer2NetBIOSName}}' -ADServer1PrivateIP '{{ADServer1PrivateIP}}' -ADServer2PrivateIP '{{ADServer2PrivateIP}}' -DomainDNSName '{{DomainDNSName}}' -DaCredentials $DaSecret.DomainCredentials
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
            nextStep: PostConfig
          - name: PostConfig
            action: aws:runCommand
            inputs:
              DocumentName: AWS-RunPowerShellScript
              InstanceIds:
                - '{{dcsInstanceIds.InstanceIds}}'
              Parameters:
                commands: |-
                  Try {
                      Import-Module -Name 'C:\AWSQuickstart\Module-AD\Module-AD.psm1' -Force
                  } Catch [System.Exception] {
                      Write-Output "Failed to import AWSQuickstart AD PS Module $_"
                      Exit 1
                  }
                  Set-AD2PostConfig

                  If ('{{EnableAdvancedAudtingandMetrics}}' -eq 'true') {
                    Set-DcAuditDscConfiguration
                    Set-LogsAndMetricsCollection -Role 'DomainController' -Stackname '{{StackName}}'
                  }

                  Invoke-Cleanup -VPCCIDR '{{VPCCIDR}}'
              CloudWatchOutputConfig:
                CloudWatchOutputEnabled: true
                CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName}
          - name: CFNSignalEnd
            action: aws:branch
            inputs:
              Choices:
                - NextStep: signalsuccess
                  Not:
                    Variable: '{{StackName}}'
                    StringEquals: ''
                - NextStep: sleepend
                  Variable: '{{StackName}}'
                  StringEquals: ''
          - name: signalsuccess
            action: aws:executeAwsApi
            isEnd: True
            inputs:
              Service: cloudformation
              Api: SignalResource
              LogicalResourceId: DomainController2Promote
              StackName: '{{StackName}}'
              Status: SUCCESS
              UniqueId: '{{dc2InstanceId.InstanceId}}'
          - name: sleepend
            action: aws:sleep
            isEnd: True
            inputs:
              Duration: PT1S
          - name: signalfailure
            action: aws:executeAwsApi
            inputs:
              Service: cloudformation
              Api: SignalResource
              LogicalResourceId: DomainController2
              StackName: '{{StackName}}'
              Status: FAILURE
              UniqueId: '{{dc2InstanceId.InstanceId}}'
  ADServerRole:
    Type: AWS::IAM::Role
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W11
            reason: "* required"
    Properties:
      Policies:
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: s3:GetObject
                Resource:
                  - !Sub arn:${AWS::Partition}:s3:::aws-ssm-${AWS::Region}/*
                  - !Sub arn:${AWS::Partition}:s3:::aws-windows-downloads-${AWS::Region}/*
                  - !Sub arn:${AWS::Partition}:s3:::amazon-ssm-${AWS::Region}/*
                  - !Sub arn:${AWS::Partition}:s3:::amazon-ssm-packages-${AWS::Region}/*
                  - !Sub arn:${AWS::Partition}:s3:::${AWS::Region}-birdwatcher-prod/*
                  - !Sub arn:${AWS::Partition}:s3:::patch-baseline-snapshot-${AWS::Region}/*
                  - !Sub arn:${AWS::Partition}:s3:::aws-ssm-distributor-file-${AWS::Region}/*
                  - !Sub arn:${AWS::Partition}:s3:::aws-ssm-document-attachments-${AWS::Region}/*
          PolicyName: SSMAgent
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: s3:ListBucket
                Resource: !Sub
                  - 'arn:${AWS::Partition}:s3:::${S3Bucket}'
                  - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]
              - Effect: Allow
                Action: s3:GetObject
                Resource: !Sub
                  - 'arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}*'
                  - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]
              - Effect: Allow
                Action: ssm:StartAutomationExecution
                Resource:
                  - !If
                    - DoNOTJoinAndPromote
                    - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${AWSQuickstartActiveDirectoryDS}:$DEFAULT
                    - !Ref AWS::NoValue
                  - !If
                    - DoJoinAndPromote
                    - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${AWSQuickstartActiveDirectoryDSPromote}:$DEFAULT
                    - !Ref AWS::NoValue
              - Effect: Allow
                Action: ssm:SendCommand
                Resource:
                  - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:*:document/AWS-RunRemoteScript
                  - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:*:document/AWS-RunPowerShellScript
              - Effect: Allow
                Action: ssm:SendCommand
                Resource: !Sub arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*
                Condition:
                  StringEquals:
                    'ssm:ResourceTag/aws:cloudformation:stack-name': !Ref AWS::StackName
              - Sid: ReadOperations
                Effect: Allow
                Action:
                  - ec2:DescribeInstances
                  - ssm:DescribeInstanceInformation
                  - ssm:ListCommands
                  - ssm:ListCommandInvocations
                Resource: '*'
              - Effect: Allow
                Action: cloudformation:SignalResource
                Resource: !Sub arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*
              - Sid: ConfigureIMDS
                Effect: Allow
                Action:
                 -  ec2:ModifyInstanceMetadataOptions
                Resource: !Sub arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*
          PolicyName: AWS-Mgmt-Quick-Start-Policy
      Path: /
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore
        - !Sub arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy
      Tags:
        - Key: StackName
          Value: !Ref AWS::StackName
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
        Version: '2012-10-17'
  ADServerProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref ADServerRole
      Path: /
  SecretRolePolicy:
    Condition: DoJoinAndPromote
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: AWS-Mgd-AD-Secret-Role
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - secretsmanager:GetSecretValue
              - secretsmanager:DescribeSecret
            Resource:
              - !Ref AdministratorSecret
              - !Ref RestoreModeSecret
      Roles:
        - !Ref ADServerRole
  DomainControllersSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Domain Controllers Security Group
      VpcId: !Ref VPCID
      Tags:
        - Key: Name
          Value: DomainControllersSecurityGroup
  DCSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      Description: Security Group Rule between Domain Controllers
      GroupId: !Ref DomainControllersSG
      IpProtocol: '-1'
      FromPort: -1
      ToPort: -1
      SourceSecurityGroupId: !Ref DomainControllersSG
  ExistingDCSecurityGroupIngress:
    Condition: DoJoinAndPromote
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      Description: Security Group Rule between Domain Controllers
      GroupId: !Ref ExistingDomainControllersSG
      IpProtocol: '-1'
      FromPort: -1
      ToPort: -1
      SourceSecurityGroupId: !Ref DomainControllersSG
  DomainController1:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      IamInstanceProfile: !Ref ADServerProfile
      InstanceType: !Ref ADServerInstanceType
      SubnetId: !Ref Subnet1ID
      Tags:
        - Key: Name
          Value: !Ref ADServer1NetBIOSName
        - Key: Domain
          Value: !Ref DomainDNSName
        - Key: Role
          Value: Domain Controller
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeSize: 60
            VolumeType: gp3
            Encrypted: true
            KmsKeyId: !Ref EbsEncryptionKmsKeyId
            DeleteOnTermination: true
        - DeviceName: /dev/xvdf
          Ebs:
            VolumeSize: !Ref DataDriveSizeGiB
            VolumeType: gp3
            Encrypted: true
            KmsKeyId: !Ref EbsEncryptionKmsKeyId
            DeleteOnTermination: true
      SecurityGroupIds:
        - !Ref DomainControllersSG
      PrivateIpAddress: !Ref ADServer1PrivateIP
      KeyName: !Ref KeyPairName
  DomainController2:
    Condition: DoNOTJoinAndPromote
    CreationPolicy:
      ResourceSignal:
        Timeout: PT60M
        Count: 1
    DependsOn: DomainController1
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      IamInstanceProfile: !Ref ADServerProfile
      InstanceType: !Ref ADServerInstanceType
      SubnetId: !Ref Subnet2ID
      Tags:
        - Key: Name
          Value: !Ref ADServer2NetBIOSName
        - Key: Domain
          Value: !Ref DomainDNSName
        - Key: Role
          Value: Domain Controller
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeSize: 60
            VolumeType: gp3
            Encrypted: true
            KmsKeyId: !Ref EbsEncryptionKmsKeyId
            DeleteOnTermination: true
        - DeviceName: /dev/xvdf
          Ebs:
            VolumeSize: !Ref DataDriveSizeGiB
            VolumeType: gp3
            Encrypted: true
            KmsKeyId: !Ref EbsEncryptionKmsKeyId
            DeleteOnTermination: true
      SecurityGroupIds:
        - !Ref DomainControllersSG
      PrivateIpAddress: !Ref ADServer2PrivateIP
      KeyName: !Ref KeyPairName
      UserData:
        Fn::Base64: !Sub
            - |
                <powershell>
                $Params = @{
                    ADServer1NetBIOSName = '${ADServer1NetBIOSName}'
                    ADServer2NetBIOSName = '${ADServer2NetBIOSName}'
                    DomainDNSName = '${DomainDNSName}'
                    DomainNetBIOSName = '${DomainNetBIOSName}'
                    EnableAdvancedAudtingandMetrics = '${EnableAdvancedAudtingandMetrics}'
                    ExistingDomainController1IP = '${ExistingDomainController1IP}'
                    ExistingDomainController2IP = '${ExistingDomainController2IP}'
                    QSS3BucketName = '${QSS3BucketName}'
                    QSS3BucketRegion =  '${QSS3BucketRegion}'
                    QSS3KeyPrefix = '${QSS3KeyPrefix}'
                    StackName = '${AWS::StackName}'
                    URLSuffix = '${AWS::URLSuffix}'
                    VPCCIDR = '${VPCCIDR}'
                }
                Start-SSMAutomationExecution -DocumentName '${AWSQuickstartActiveDirectoryDS}' -Parameter $Params
                </powershell>
            - QSS3BucketName: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Sub '${QSS3BucketName}']
              QSS3BucketRegion: !If [UsingDefaultBucket, !Sub '${AWS::Region}', !Sub '${QSS3BucketRegion}']
  DomainController2Promote:
    Condition: DoJoinAndPromote
    CreationPolicy:
      ResourceSignal:
        Timeout: PT60M
        Count: 1
    DependsOn: DomainController1
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      IamInstanceProfile: !Ref ADServerProfile
      InstanceType: !Ref ADServerInstanceType
      SubnetId: !Ref Subnet2ID
      Tags:
        - Key: Name
          Value: !Ref ADServer2NetBIOSName
        - Key: Domain
          Value: !Ref DomainDNSName
        - Key: Role
          Value: Domain Controller
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeSize: 60
            VolumeType: gp3
            Encrypted: true
            KmsKeyId: !Ref EbsEncryptionKmsKeyId
            DeleteOnTermination: true
        - DeviceName: /dev/xvdf
          Ebs:
            VolumeSize: !Ref DataDriveSizeGiB
            VolumeType: gp3
            Encrypted: true
            KmsKeyId: !Ref EbsEncryptionKmsKeyId
            DeleteOnTermination: true
      SecurityGroupIds:
        - !Ref DomainControllersSG
      PrivateIpAddress: !Ref ADServer2PrivateIP
      KeyName: !Ref KeyPairName
      UserData:
        Fn::Base64: !Sub
            - |
                <powershell>
                $Params = @{
                    ADAdminSecParamName = '${AdministratorSecret}'
                    ADServer1NetBIOSName = '${ADServer1NetBIOSName}'
                    ADServer1PrivateIP = '${ADServer1PrivateIP}'
                    ADServer2NetBIOSName = '${ADServer2NetBIOSName}'
                    ADServer2PrivateIP = '${ADServer2PrivateIP}'
                    DomainDNSName = '${DomainDNSName}'
                    DomainNetBIOSName = '${DomainNetBIOSName}'
                    EnableAdvancedAudtingandMetrics = '${EnableAdvancedAudtingandMetrics}'
                    ExistingDomainController1IP = '${ExistingDomainController1IP}'
                    RestoreModeSecParamName = '${RestoreModeSecret}'
                    QSS3BucketName = '${QSS3BucketName}'
                    QSS3BucketRegion =  '${QSS3BucketRegion}'
                    QSS3KeyPrefix = '${QSS3KeyPrefix}'
                    StackName = '${AWS::StackName}'
                    URLSuffix = '${AWS::URLSuffix}'
                    VPCCIDR = '${VPCCIDR}'
                }
                Start-SSMAutomationExecution -DocumentName '${AWSQuickstartActiveDirectoryDSPromote}' -Parameter $Params
                </powershell>
            - QSS3BucketName: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Sub '${QSS3BucketName}']
              QSS3BucketRegion: !If [UsingDefaultBucket, !Sub '${AWS::Region}', !Sub '${QSS3BucketRegion}']
  ADResourceGroup:
    Condition: AppInsightsEnabled
    Type: AWS::ResourceGroups::Group
    Properties:
      Name: !If [UsingDefaultAppName, !Sub "ApplicationInsights-${AWS::StackName}", !Ref AppInsightsApplicationName]
      ResourceQuery:
        Query:
          TagFilters:
            - Key: 'aws:cloudformation:stack-name'
              Values:
                - !Sub "${AWS::StackName}"
        Type: 'TAG_FILTERS_1_0'          
  ApplicationInsightsAD:
    Condition: AppInsightsEnabled
    Type: AWS::ApplicationInsights::Application
    Properties:
      ResourceGroupName: !Ref 'ADResourceGroup'
      AutoConfigurationEnabled: true 
Outputs:
  DC1InstanceId:
    Value: !Ref DomainController1
    Description: Domain Controller instance ID
