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: - W9006 - E9101 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 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-quickstart 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: quickstart-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-quickstart'] 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-quickstart 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: 'quickstart-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 - | $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 - 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 - | $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 - 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 DependsOn: ADResourceGroup Outputs: DC1InstanceId: Value: !Ref DomainController1 Description: Domain Controller instance ID