AWSTemplateFormatVersion: '2010-09-09' Description: >- This template creates 2 Windows Server Active Directory Certificate Services CAs 1 Offline Root CA and 1 Enterprise Subordinate CA in a subnet inside a VPC. **WARNING** This template creates Amazon EC2 Windows instances and related resources. You will be billed for the AWS resources used if you create a stack from this template. (qs-1rtep036f) Metadata: cfn-lint: config: ignore_checks: - W9006 - E9101 AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Network Configuration Parameters: - VPCCIDR - VPCID - OrCaServerSubnet - SubCaServerSubnet - DomainMembersSG - Label: default: Amazon EC2 Configuration Parameters: - OrCaServerInstanceType - OrCaDataDriveSizeGiB - SubCaServerInstanceType - SubCaDataDriveSizeGiB - EbsEncryptionKmsKeyId - KeyPairName - AMI - Label: default: Microsoft Active Directory Domain Services Configuration Parameters: - DirectoryType - DomainDNSName - DomainNetBIOSName - DomainController1IP - DomainController2IP - AdministratorSecret - Label: default: Microsoft Active Directory Certificate Services Configuration Parameters: - OrCaServerNetBIOSName - SubCaServerNetBIOSName - OrCaKeyLength - OrCaHashAlgorithm - OrCaValidityPeriodUnits - SubCaKeyLength - SubCaHashAlgorithm - SubCaValidityPeriodUnits - UseS3ForCRL - S3CRLBucketName - EnableAdvancedAudtingandMetrics - Label: default: AWS Quick Start Configuration Parameters: - QSS3BucketName - QSS3KeyPrefix - QSS3BucketRegion ParameterLabels: AMI: default: AMI ID passed in from parent stack AdministratorSecret: default: Secret ARN Containing CA Install Credentials DirectoryType: default: Active Directory Domain Services Type DomainController1IP: default: IP used for DNS (Must be accessible) DomainController2IP: default: IP used for DNS (Must be accessible) DomainDNSName: default: Domain FQDN DNS Name DomainMembersSG: default: Domain Members Security Group ID DomainNetBIOSName: default: Domain NetBIOS Name EbsEncryptionKmsKeyId: default: KMS Key for EBS Encryption EnableAdvancedAudtingandMetrics: default: Advanced Auditing and Metrics KeyPairName: default: Key Pair Name OrCaDataDriveSizeGiB: default: Offline Root CA Data Drive Size OrCaHashAlgorithm: default: Offline Root CA Hash Algorithm OrCaKeyLength: default: Offline Root CA Key Length OrCaServerInstanceType: default: Offline Root CA Instance Type OrCaServerNetBIOSName: default: Offline Root CA NetBIOS Name OrCaServerSubnet: default: Offline Root CA Subnet ID OrCaValidityPeriodUnits: default: Offline Root CA Certificate Validity Period in Years QSS3BucketName: default: Quick Start S3 Bucket Name QSS3BucketRegion: default: Quick Start S3 Bucket Region QSS3KeyPrefix: default: Quick Start S3 Key Prefix S3CRLBucketName: default: CA CRL S3 Bucket Name SubCaDataDriveSizeGiB: default: Subordinate CA Data Drive Size SubCaHashAlgorithm: default: Subordinate CA Hash Algorithm SubCaKeyLength: default: Subordinate CA Key Length SubCaServerInstanceType: default: Subordinate CA Instance Type SubCaServerNetBIOSName: default: Subordinate CA NetBIOS Name SubCaServerSubnet: default: Subordinate CA Subnet ID SubCaValidityPeriodUnits: default: Subordinate CA Certificate Validity Period in Years UseS3ForCRL: default: Use S3 for CA CRL Location VPCCIDR: default: VPC CIDR VPCID: default: VPC ID Parameters: AMI: Default: /aws/service/ami-windows-latest/Windows_Server-2022-English-Full-Base Description: AMI ID passed in from parent stack Type: AWS::SSM::Parameter::Value AdministratorSecret: Description: ARN for the CA Install Credentials Secret used to to install the CA (Must be a member of Enterpise Admins or AWS Delegated Enterprise Certificate Authority Administrators) Type: String DirectoryType: AllowedValues: - AWSManaged - SelfManaged Default: AWSManaged Description: Type of Active Directory CA(s) will be integrated with, AWS Managed Microsoft AD or Self Managed AD Type: String DomainController1IP: 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 For Domain Controller that Offline Root & Subordinate CA Instances will use for DNS Type: String DomainController2IP: 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 For Domain Controller that Offline Root & Subordinate CA Instances will use for DNS Type: String DomainDNSName: AllowedPattern: '[a-zA-Z0-9\-]+\..+' Description: Fully qualified domain name (FQDN) of the forest root domain e.g. example.com MaxLength: '255' MinLength: '2' Type: String DomainMembersSG: Description: Security Group ID for Domain Members Security Group. Type: AWS::EC2::SecurityGroup::Id DomainNetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Description: NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE MaxLength: '15' MinLength: '1' Type: String EbsEncryptionKmsKeyId: 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' KeyPairName: Description: Name of the key pair that you will use to securely connect to your EC2 instance after it launches. Type: AWS::EC2::KeyPair::KeyName OrCaDataDriveSizeGiB: Default: '2' Description: Size of the Data Drive in GiB for the Offline Root CA Type: Number OrCaHashAlgorithm: AllowedValues: - SHA256 - SHA384 - SHA512 Default: SHA256 Description: Offline Root CA Hash Algorithm for Signing Certificates Type: String OrCaKeyLength: AllowedValues: - '2048' - '4096' Default: '2048' Description: Offline Root CA Cryptographic Provider Key Length Type: String OrCaServerInstanceType: AllowedValues: - t3.small - t3.medium - t3.large - t3.xlarge - t3.2xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge Default: t3.medium Description: Amazon EC2 instance type for the Offline Root CA instance Type: String OrCaServerNetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: ORCA1 Description: NetBIOS name of the Offline Root CA server (up to 15 characters) MaxLength: '15' MinLength: '1' Type: String OrCaServerSubnet: Description: ID of the Offline Root CA subnet (e.g., subnet-a0246dcd) Type: AWS::EC2::Subnet::Id OrCaValidityPeriodUnits: Default: '10' Description: Validity Period in Years Type: String QSS3BucketName: AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ 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 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: 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: ^[0-9a-zA-Z-/]*$ ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/) Default: quickstart-microsoft-pki/ 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 S3CRLBucketName: AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ Default: examplebucket 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 SubCaDataDriveSizeGiB: Default: '2' Description: Size of the Data Drive in GiB for the Subordinate CA Type: Number SubCaHashAlgorithm: AllowedValues: - SHA256 - SHA384 - SHA512 Default: SHA256 Description: Subordinate CA Hash Algorithm for Siging Certificates Type: String SubCaKeyLength: AllowedValues: - '2048' - '4096' Default: '2048' Description: SubordinateCA Cryptographic Provider Key Length Type: String SubCaServerInstanceType: AllowedValues: - t3.small - t3.medium - t3.large - t3.xlarge - t3.2xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge Default: t3.medium Description: Amazon EC2 instance type for the Subordinate CA instance Type: String SubCaServerNetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: SUBCA1 Description: NetBIOS name of the Subordinate CA server (up to 15 characters) MaxLength: '15' MinLength: '1' Type: String SubCaServerSubnet: Description: ID of the Subordinate CA subnet (e.g., subnet-a0246dcd) Type: AWS::EC2::Subnet::Id SubCaValidityPeriodUnits: Default: '5' Description: Validity Period in Years Type: String UseS3ForCRL: AllowedValues: - 'Yes' - 'No' Default: 'No' Description: Store CA CRLs in an S3 bucket 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 S3CRLBucketNameValidation: RuleCondition: !Equals - !Ref UseS3ForCRL - 'Yes' Assertions: - AssertDescription: CRL BucketName cannot must be valid BucketName Assert: !Not [!Equals [!Ref S3CRLBucketName, 'examplebucket']] Conditions: UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] UsingS3BucketForCrl: !Equals [!Ref UseS3ForCRL, 'Yes'] UsingLocalIisCrl: !Equals [!Ref UseS3ForCRL, 'No'] Resources: InstanceRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: "* is required" - id: W27 reason: "Standard Amazon practice" - id: F1000 reason: "Standard Amazon practice" 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: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${AWSQuickstartCA}:$DEFAULT - 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}/*' PolicyName: AWS-Mgmt-Quick-Start-Policy - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - secretsmanager:GetSecretValue - secretsmanager:DescribeSecret Resource: - !Ref AdministratorSecret PolicyName: AWS-Mgd-AD-Secret-Policy Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy' AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Principal: Service: - ec2.amazonaws.com Effect: Allow Version: '2012-10-17' InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Roles: - !Ref InstanceRole Path: / S3CrlRolePolicy: Type: AWS::IAM::Policy Condition: UsingS3BucketForCrl Properties: PolicyName: S3-CRL-PublishRole PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:GetBucketLocation - s3:ListBucket Resource: !Sub 'arn:${AWS::Partition}:s3:::${S3CRLBucketName}' - Effect: Allow Action: - s3:PutObject - s3:GetObject - s3:PutObjectAcl - s3:DeleteObject Resource: !Sub 'arn:${AWS::Partition}:s3:::${S3CRLBucketName}/*' Roles: - !Ref InstanceRole AWSQuickstartCA: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: schemaVersion: "0.3" description: Deploy Two Tier PKI with SSM Automation parameters: AdministratorSecret: description: AWS Secrets Parameter Name that has Password and User namer for the domain administrator. type: String DirectoryType: description: Type of Active Directory CA will be integrated with type: String DomainController1IP: description: Fixed private IP for the first Active Directory server located in Availability Zone 1 type: String DomainController2IP: description: Fixed private IP for the first Active Directory server located in Availability Zone 1 type: String DomainDNSName: description: Fully qualified domain name (FQDN) of the forest root domain e.g. example.com type: String DomainNetBIOSName: description: NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE type: String EnableAdvancedAudtingandMetrics: description: Enable advanced auditing and metrics type: String OrCaHashAlgorithm: description: Offline Root CA Hash Algorithm type: String OrCaKeyLength: description: Offline Root CA Key Length type: String OrCaServerNetBIOSName: description: NetBIOS name of the Offline Root CA server (up to 15 characters) type: String OrCaValidityPeriodUnits: description: Offline Root CA Certificate Validity Period in Years type: String QSS3BucketName: 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: 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: default: quickstart-microsoft-pki/ 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 S3CRLBucketName: description: S3 bucket name for Enterprise Root CA CRL type: String StackName: description: Stack Name Input for cfn resource signal type: String SubCaHashAlgorithm: description: Subordinate CA Hash Algorithm type: String SubCaKeyLength: description: Subordinate CA Key Length type: String SubCaServerNetBIOSName: description: NetBIOS name of the Subordinate CA server (up to 15 characters) type: String SubCaValidityPeriodUnits: description: Subordinate CA Certificate Validity Period in Years type: String URLSuffix: default: amazonaws.com description: AWS URL suffix type: String UseS3ForCRL: description: Use S3 for Enterprise Root CA CRL location type: String VPCCIDR: default: 10.0.0.0/16 description: CIDR block for private subnet 1 located in Availability Zone 1. type: String mainSteps: - name: CaInstanceIds action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: tag:Name Values: ['{{OrCaServerNetBIOSName}}','{{SubCaServerNetBIOSName}}'] - Name: tag:aws:cloudformation:stack-name Values: ['{{StackName}}'] - Name: instance-state-name Values: [running] outputs: - Name: InstanceIds Selector: $.Reservations..Instances..InstanceId Type: StringList nextStep: OrcaInstanceId - name: OrcaInstanceId action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: tag:Name Values: ['{{OrCaServerNetBIOSName}}'] - 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: SubCaInstanceId - name: SubCaInstanceId action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: tag:Name Values: ['{{SubCaServerNetBIOSName}}'] - 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: intializeInstances - name: intializeInstances action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{CaInstanceIds.InstanceIds}}' Parameters: commands: |- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $S3BucketName = '{{QSS3BucketName}}' $S3KeyPrefix = '{{QSS3KeyPrefix}}' $S3BucketRegion = '{{QSS3BucketRegion}}' $CustomModules = @( 'Module-Pki.psd1', 'Module-Pki.psm1' ) $Modules = @( @{ Name = 'NetworkingDsc' Version = '8.2.0' }, @{ Name = 'ComputerManagementDsc' Version = '8.5.0' }, @{ Name = 'AuditPolicyDsc' Version = '1.4.0.0' } ) Write-Output 'Creating AWSQuickstart Directory' Try { $Null = New-Item -Path 'C:\AWSQuickstart\Module-Pki' -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 Pki PowerShell Module' Foreach ($CustomModule in $CustomModules) { Try { $Null = Read-S3Object -BucketName $S3BucketName -Key "$($S3KeyPrefix)/scripts/Modules/Module-Pki/$CustomModule" -File "C:\AWSQuickstart\Module-Pki\$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: configureInstances - name: configureInstances action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{CaInstanceIds.InstanceIds}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } New-VolumeFromRawDisk Invoke-PreConfig Invoke-LcmConfig CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} nextStep: configureOrCaMof - name: configureOrCaMof action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{OrcaInstanceId.InstanceId}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } $EniConfig = Get-EniConfig Set-DscConfiguration -CaType Offline -DomainController1IP '{{DomainController1IP}}' -DomainController2IP '{{DomainController2IP}}' -GatewayAddress $EniConfig.GatewayAddress -InstanceNetBIOSName '{{OrCaServerNetBIOSName}}' -IpAddress $EniConfig.IpAddress -MacAddress $EniConfig.MacAddress CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} nextStep: runOrCaMof - name: runOrCaMof action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{OrcaInstanceId.InstanceId}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } Start-DscConfiguration 'C:\AWSQuickstart\ConfigInstance' -Wait -Verbose -Force Invoke-DscStatusCheck nextStep: InstallOrCa - name: InstallOrCa action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{OrcaInstanceId.InstanceId}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } $Secret = Get-SecretInfo -Domain '{{DomainDNSName}}' -SecretArn '{{AdministratorSecret}}' Invoke-TwoTierOrCaConfig -Credentials $Secret.Credentials -DirectoryType '{{DirectoryType}}' -DomainDNSName '{{DomainDNSName}}' -OrCaCommonName '{{OrCaServerNetBIOSName}}' -OrCaHashAlgorithm '{{OrCaHashAlgorithm}}' -OrCaKeyLength '{{OrCaKeyLength}}' -OrCaValidityPeriodUnits '{{OrCaValidityPeriodUnits}}' -S3CRLBucketName '{{S3CRLBucketName}}' -SubCaServerNetBIOSName '{{SubCaServerNetBIOSName}}' -UseS3ForCRL '{{UseS3ForCRL}}' nextStep: configureSubCaMof - name: configureSubCaMof action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{SubCaInstanceId.InstanceId}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } $EniConfig = Get-EniConfig $Secret = Get-SecretInfo -Domain '{{DomainNetBIOSName}}' -SecretArn '{{AdministratorSecret}}' Set-DscConfiguration -CaType Enterprise -Credentials $Secret.Credentials -DomainController1IP '{{DomainController1IP}}' -DomainController2IP '{{DomainController2IP}}' -DomainDNSName '{{DomainDNSName}}' -GatewayAddress $EniConfig.GatewayAddress -InstanceNetBIOSName '{{SubCaServerNetBIOSName}}' -IpAddress $EniConfig.IpAddress -MacAddress $EniConfig.MacAddress -UseS3ForCRL '{{UseS3ForCRL}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} nextStep: runSubCaMof - name: runSubCaMof action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{SubCaInstanceId.InstanceId}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } Start-DscConfiguration 'C:\AWSQuickstart\ConfigInstance' -Wait -Verbose -Force Invoke-DscStatusCheck nextStep: InstallSubCa - name: InstallSubCa action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{SubCaInstanceId.InstanceId}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } $Secret = Get-SecretInfo -Domain '{{DomainNetBIOSName}}' -SecretArn '{{AdministratorSecret}}' Invoke-TwoTierSubCaInstall -Credentials $Secret.Credentials -DirectoryType '{{DirectoryType}}' -S3CRLBucketName '{{S3CRLBucketName}}' -SubCaCommonName '{{SubCaServerNetBIOSName}}' -SubCaHashAlgorithm '{{SubCaHashAlgorithm}}' -SubCaKeyLength '{{SubCaKeyLength}}' -SubCaValidityPeriodUnits '{{SubCaValidityPeriodUnits}}' -UseS3ForCRL '{{UseS3ForCRL}}' nextStep: CertIssueSubCa - name: CertIssueSubCa action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{OrcaInstanceId.InstanceId}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } $Secret = Get-SecretInfo -Domain '{{DomainDNSName}}' -SecretArn '{{AdministratorSecret}}' Invoke-TwoTierSubCaCertIssue -Credentials $Secret.Credentials -DirectoryType '{{DirectoryType}}' -DomainDNSName '{{DomainDNSName}}' If ('{{EnableAdvancedAudtingandMetrics}}' -eq 'true') { Set-AuditDscConfiguration Set-LogsAndMetricsCollection -Stackname '{{StackName}}' } Invoke-Cleanup -VPCCIDR '{{VPCCIDR}}' nextStep: FinalizeSubCa - name: FinalizeSubCa action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{SubCaInstanceId.InstanceId}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: true CloudWatchLogGroupName: !Sub /aws/Quick_Start/${AWS::StackName} Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-Pki\Module-Pki.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to import Pki PS Module $_" Exit 1 } $Secret = Get-SecretInfo -Domain {{DomainNetBIOSName}} -SecretArn {{AdministratorSecret}} Invoke-TwoTierSubCaConfig -Credentials $Secret.Credentials -DirectoryType '{{DirectoryType}}' -S3CRLBucketName '{{S3CRLBucketName}}' -UseS3ForCRL '{{UseS3ForCRL}}' If ('{{EnableAdvancedAudtingandMetrics}}' -eq 'true') { Set-AuditDscConfiguration Set-LogsAndMetricsCollection -Stackname '{{StackName}}' } Invoke-Cleanup -VPCCIDR '{{VPCCIDR}}' - 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: SubCA StackName: '{{StackName}}' Status: SUCCESS UniqueId: '{{SubCaInstanceId.InstanceId}}' - name: sleepend action: aws:sleep isEnd: True inputs: Duration: PT1S - name: signalfailure action: aws:executeAwsApi inputs: Service: cloudformation Api: SignalResource LogicalResourceId: SubCA StackName: '{{StackName}}' Status: FAILURE UniqueId: '{{SubCaInstanceId.InstanceId}}' ORCA: Type: AWS::EC2::Instance Properties: ImageId: !Ref AMI IamInstanceProfile: !Ref InstanceProfile InstanceType: !Ref OrCaServerInstanceType SubnetId: !Ref OrCaServerSubnet Tags: - Key: Name Value: !Ref OrCaServerNetBIOSName - Key: Role Value: Offline Root CA BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 60 VolumeType: gp3 Encrypted: true KmsKeyId: !Ref EbsEncryptionKmsKeyId DeleteOnTermination: true - DeviceName: /dev/xvdf Ebs: VolumeSize: !Ref OrCaDataDriveSizeGiB VolumeType: gp3 Encrypted: true KmsKeyId: !Ref EbsEncryptionKmsKeyId DeleteOnTermination: true SecurityGroupIds: - !Ref CaSG - !Ref DomainMembersSG KeyName: !Ref KeyPairName SubCA: Type: AWS::EC2::Instance DependsOn: ORCA CreationPolicy: ResourceSignal: Timeout: PT45M Count: 1 Properties: ImageId: !Ref AMI IamInstanceProfile: !Ref InstanceProfile InstanceType: !Ref SubCaServerInstanceType SubnetId: !Ref SubCaServerSubnet Tags: - Key: Name Value: !Ref SubCaServerNetBIOSName - Key: Domain Value: !Ref DomainDNSName - Key: Role Value: Subordinate Enterprise CA BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 60 VolumeType: gp3 Encrypted: true KmsKeyId: !Ref EbsEncryptionKmsKeyId DeleteOnTermination: true - DeviceName: /dev/xvdf Ebs: VolumeSize: !Ref SubCaDataDriveSizeGiB VolumeType: gp3 Encrypted: true KmsKeyId: !Ref EbsEncryptionKmsKeyId DeleteOnTermination: true SecurityGroupIds: - !Ref CaSG - !Ref DomainMembersSG KeyName: !Ref KeyPairName UserData: Fn::Base64: !Sub - | $Params = @{ AdministratorSecret = '${AdministratorSecret}' DirectoryType = '${DirectoryType}' DomainController1IP = '${DomainController1IP}' DomainController2IP = '${DomainController2IP}' DomainDNSName = '${DomainDNSName}' DomainNetBIOSName = '${DomainNetBIOSName}' EnableAdvancedAudtingandMetrics = '${EnableAdvancedAudtingandMetrics}' OrCaHashAlgorithm = '${OrCaHashAlgorithm}' OrCaKeyLength = '${OrCaKeyLength}' OrCaServerNetBIOSName = '${OrCaServerNetBIOSName}' OrCaValidityPeriodUnits = '${OrCaValidityPeriodUnits}' QSS3BucketName = '${QSS3BucketName}' QSS3BucketRegion = '${QSS3BucketRegion}' QSS3KeyPrefix = '${QSS3KeyPrefix}' S3CRLBucketName = '${S3CRLBucketName}' StackName = '${AWS::StackName}' SubCaHashAlgorithm = '${SubCaHashAlgorithm}' SubCaKeyLength = '${SubCaKeyLength}' SubCaServerNetBIOSName = '${SubCaServerNetBIOSName}' SubCaValidityPeriodUnits = '${SubCaValidityPeriodUnits}' URLSuffix = '${AWS::URLSuffix}' UseS3ForCRL = '${UseS3ForCRL}' VPCCIDR = '${VPCCIDR}' } Start-SSMAutomationExecution -DocumentName '${AWSQuickstartCA}' -Parameter $Params - QSS3BucketName: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Sub '${QSS3BucketName}'] QSS3BucketRegion: !If [UsingDefaultBucket, !Sub '${AWS::Region}', !Sub '${QSS3BucketRegion}'] CaSG: Type: AWS::EC2::SecurityGroup Metadata: cfn_nag: rules_to_suppress: - id: F1000 reason: "Standard Amazon practice" - id: W27 reason: "Standard Amazon practice" Properties: GroupDescription: CA Security Group VpcId: !Ref VPCID SecurityGroupIngress: - IpProtocol: tcp Description: Certificate Enrollment FromPort: 135 ToPort: 135 CidrIp: !Ref VPCCIDR - IpProtocol: tcp Description: RDP FromPort: 3389 ToPort: 3389 SourceSecurityGroupId: !Ref DomainMembersSG - IpProtocol: tcp Description: Random RPC FromPort: 49152 ToPort: 65535 CidrIp: !Ref VPCCIDR Tags: - Key: Name Value: CertificateAuthoritySecurityGroup CaSGCrlHttps: Type: AWS::EC2::SecurityGroupIngress Condition: UsingLocalIisCrl Properties: Description: HTTPS for CRL GroupId: !Ref CaSG IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Ref VPCCIDR CaSGCrlHttp: Type: AWS::EC2::SecurityGroupIngress Condition: UsingLocalIisCrl Properties: Description: HTTP for CRL GroupId: !Ref CaSG IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref VPCCIDR Outputs: CASGID: Value: !Ref CaSG Description: CA Security Group ID OrCaInstanceId: Value: !Ref ORCA Description: ORCA instance ID SubCAInstanceId: Value: !Ref SubCA Description: SubCA instance ID