AWSTemplateFormatVersion: '2010-09-09' Description: >- This template deploys two Web Application Proxy servers into public subnets in each availability zone, as well as two ADFS servers into the private subnets in each availability zone. The Active Directory domain being joined to must include a server with the Certificate Authority role and the Secret ARN account must be able to create and deploy certificates. **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-1qup6rai9) Metadata: cfn-lint: config: ignore_checks: - W9006 - E9101 QuickStartDocumentation: EntrypointName: "Launch into an existing VPC" Order: "2" AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Network configuration Parameters: - VPCCIDR - VPCID - PrivateSubnet1ID - PrivateSubnet2ID - PublicSubnet1ID - PublicSubnet2ID - Label: default: Amazon EC2 configuration Parameters: - KeyPairName - LatestAmiId - EbsEncryptionKmsKeyId - Label: default: Microsoft Active Directory Domain Services configuration Parameters: - DirectoryType - DomainController1IP - DomainController2IP - DomainDNSName - DomainNetBIOSName - DomainAdminUserSecret - DomainMemberSGID - Label: default: ADFS and WAP configuration Parameters: - WAPADFSInstanceType - Adfs1NetBIOSName - Adfs2NetBIOSName - Wap1NetBIOSName - Wap2NetBIOSName - EnableAdvancedAudtingandMetrics - Label: default: AWS Quick Start configuration Parameters: - QSS3BucketName - QSS3BucketRegion - QSS3KeyPrefix ParameterLabels: Adfs1NetBIOSName: default: ADFS server 1 NetBIOS name Adfs2NetBIOSName: default: ADFS server 2 NetBIOS name Wap1NetBIOSName: default: WAP server 1 NetBIOS name Wap2NetBIOSName: default: WAP server 2 NetBIOS name DirectoryType: default: Active Directory Domain Services type DomainController1IP: default: IP the instances will use for DNS (must be accessible) DomainController2IP: default: IP the instances will use for DNS (must be accessible) DomainAdminUserSecret: default: Secret ARN containing Administrator credentials DomainDNSName: default: Domain DNS name DomainMemberSGID: default: Domain member security group ID DomainNetBIOSName: default: Domain NetBIOS name EbsEncryptionKmsKeyId: default: KMS Key for EBS encryption EnableAdvancedAudtingandMetrics: default: Advanced Auditing and Metrics for WAP and ADFS Instances KeyPairName: default: Key pair name LatestAmiId: default: SSM parameter to for latest AMI ID PrivateSubnet1ID: default: Private subnet 1 ID PrivateSubnet2ID: default: Private subnet 2 ID PublicSubnet1ID: default: Public subnet 1 ID PublicSubnet2ID: default: Public subnet 2 ID QSS3BucketName: default: Quick Start S3 bucket name QSS3BucketRegion: default: Quick Start S3 bucket region QSS3KeyPrefix: default: Quick Start S3 key prefix VPCCIDR: default: VPC CIDR VPCID: default: VPC ID WAPADFSInstanceType: default: WAP and ADFS server instance type Parameters: Adfs1NetBIOSName: Default: ADFS1 Description: First ADFS Server NetBIOS Name Type: String Adfs2NetBIOSName: Default: ADFS2 Description: Second ADFS Server NetBIOS Name Type: String DirectoryType: AllowedValues: - AWSManaged - SelfManaged Default: SelfManaged Description: Type of Active Directory the WAP / ADFS deployment will be integrated with, AWS Managed Microsoft AD or Self Managed AD Type: String DomainAdminUserSecret: Description: ARN for the Administrator credentials Secret with "username" and "password" key pairs. This account must be a member of Domain Admins with Self-Managed directories or a member of AWS Delegated Administrators with AWS Managed Microsoft 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])$ Default: 10.0.0.10 Description: IP of DNS server that can resolve Active Directory domain (Must be accessible) 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])$ Default: 10.0.32.10 Description: IP of DNS server that can resolve Active Directory domain (Must be accessible) Type: String DomainDNSName: AllowedPattern: '^([a-zA-Z0-9]+[\.\-])+([a-zA-Z0-9])+$' Default: example.com Description: Fully qualified domain name (FQDN) of the domain the WAP / ADFS instances will join e.g. corp.example.com MaxLength: '25' MinLength: '3' Type: String DomainMemberSGID: Description: ID of the Domain Member Security Group (e.g., sg-7f16e910) Type: AWS::EC2::SecurityGroup::Id DomainNetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: example Description: NetBIOS name of the domain the WAP / ADFS instances will join (up to 15 characters) for users of earlier versions of Windows e.g. CORP 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: AllowedValues: - 'true' - 'false' Default: 'false' Description: Enable advanced auditing and metrics and upload them to CloudWatch using the Amazon Kinesis Agent for Microsoft Windows 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 PrivateSubnet1ID: Description: ID of the private subnet 1 in Availability Zone 1 (e.g., subnet-a0246dcd) Type: AWS::EC2::Subnet::Id PrivateSubnet2ID: Description: ID of the private subnet 2 in Availability Zone 2 (e.g., subnet-a0246dcd) Type: AWS::EC2::Subnet::Id PublicSubnet1ID: Description: ID of the public subnet 1 in Availability Zone 1 (e.g., subnet-e3246d8e) Type: AWS::EC2::Subnet::Id PublicSubnet2ID: Description: ID of the public subnet 2 in Availability Zone 2 (e.g., subnet-e3246d8e) Type: AWS::EC2::Subnet::Id 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-wapadfs/ 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 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 of the VPC Type: String VPCID: Description: ID of the VPC (e.g., vpc-0343606e) Type: AWS::EC2::VPC::Id WAPADFSInstanceType: 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 the WAP and ADFS Instances Type: String Wap1NetBIOSName: Default: WAP1 Description: First WAP Server NetBIOS Name Type: String Wap2NetBIOSName: Default: WAP2 Description: Second WAP Server NetBIOS Name Type: String Rules: SubnetsInVPC: Assertions: - Assert: !EachMemberIn - !ValueOfAll - AWS::EC2::Subnet::Id - VpcId - !RefAll 'AWS::EC2::VPC::Id' AssertDescription: All subnets must in the VPC Conditions: UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] Resources: ServerRole: Type: AWS::IAM::Role 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/${WapAdfsAutomationDoc}:$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 'DomainAdminUserSecret' PolicyName: AWS-Mgd-AD-Secret-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' ServerProfile: Type: AWS::IAM::InstanceProfile Properties: Roles: - !Ref 'ServerRole' Path: / WAP1EIP: Type: AWS::EC2::EIP Properties: InstanceId: !Ref 'WAP1' Domain: vpc WAP2EIP: Type: AWS::EC2::EIP Properties: InstanceId: !Ref 'WAP2' Domain: vpc WapAdfsAutomationDoc: Type: AWS::SSM::Document Properties: DocumentType: Automation Tags: - Key: StackName Value: !Ref AWS::StackName Content: schemaVersion: '0.3' description: 'Deploy WAP Instances with SSM Automation' parameters: Adfs1NetBIOSName: description: ADFS1 NetBIOS Name type: String Adfs2NetBIOSName: description: ADFS1 NetBIOS Name type: String DirectoryType: description: Type of Active Directory ADFS will be deployed to. type: String DomainAdminUserSecret: description: Secret ARN containing Administrator credentials type: String DomainController1IP: description: IP of DNS server that can resolve domain (Must be accessible) type: 'String' DomainController2IP: description: IP of DNS server that can resolve domain (Must be accessible) type: 'String' DomainDNSName: description: Domain DNS Name type: String DomainNetBIOSName: description: Domain NetBIOS Name type: String EnableAdvancedAudtingandMetrics: allowedValues: - 'true' - 'false' default: 'false' description: Enable advanced auditing and metrics type: String QSS3BucketName: description: Quick Start S3 Bucket Name type: String QSS3BucketRegion: description: Quick Start S3 bucket region type: String QSS3KeyPrefix: description: Quick Start S3 Key Prefix type: String StackName: description: CF Stack Name Input for cfn resource signal type: String URLSuffix: default: amazonaws.com description: AWS URL suffix type: String Wap1NetBIOSName: description: WAP1 NetBIOS Name type: String Wap2NetBIOSName: description: WAP2 NetBIOS Name type: String mainSteps: - name: InstanceIds action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: 'tag:Name' Values: ['{{Adfs1NetBIOSName}}', '{{Adfs2NetBIOSName}}', '{{Wap1NetBIOSName}}', '{{Wap2NetBIOSName}}'] - Name: 'tag:aws:cloudformation:stack-name' Values: ['{{StackName}}'] - Name: 'instance-state-name' Values: ['running'] outputs: - Name: InstanceIds Selector: '$.Reservations..Instances..InstanceId' Type: 'StringList' nextStep: adfs1InstanceId - name: adfs1InstanceId action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: 'tag:Name' Values: ['{{Adfs1NetBIOSName}}'] - 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: adfs2InstanceId - name: adfs2InstanceId action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: 'tag:Name' Values: ['{{Adfs2NetBIOSName}}'] - 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: wap1InstanceId - name: wap1InstanceId action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: 'tag:Name' Values: ['{{Wap1NetBIOSName}}'] - 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: wap2InstanceId - name: wap2InstanceId action: aws:executeAwsApi onFailure: step:signalfailure inputs: Service: ec2 Api: DescribeInstances Filters: - Name: 'tag:Name' Values: ['{{Wap2NetBIOSName}}'] - 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: - '{{InstanceIds.InstanceIds}}' Parameters: commands: |- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $S3BucketName = '{{ QSS3BucketName }}' $S3KeyPrefix = '{{ QSS3KeyPrefix }}' $S3BucketRegion = '{{QSS3BucketRegion}}' $CustomModules = @( 'Module-WAPADFS.psd1', 'Module-WAPADFS.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-WAPADFS' -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 ADFS PowerShell Module' Foreach ($CustomModule in $CustomModules) { Try { $Null = Read-S3Object -BucketName $S3BucketName -Key "$($S3KeyPrefix)/scripts/Modules/Module-WAPADFS/$CustomModule" -File "C:\AWSQuickstart\Module-WAPADFS\$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 } } $OS = Get-CimInstance -ClassName 'Win32_OperatingSystem' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'Caption' If ($OS -like 'Microsoft Windows Server 2022 *') { Write-Output 'Disabling TLS 1.3 client due to bug in ADFS Deployment on Windows Server 2022.' Try { New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Client' -Force -ErrorAction Stop New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Client' -Name 'Enabled' -Value 0 –PropertyType DWORD -ErrorAction Stop New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Client' -Name 'DisabledByDefault' -Value 1 –PropertyType DWORD -ErrorAction Stop } Catch [System.Exception] { Write-Output "Failed to disable TLS 1.3 client $_" Exit 1 } } CloudWatchOutputConfig: CloudWatchOutputEnabled: 'true' CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' nextStep: configureInstances - name: configureInstances action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{InstanceIds.InstanceIds}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-WAPADFS\Module-WAPADFS.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to importing WAPADFS PS Module $_" Exit 1 } Invoke-PreConfig Invoke-LcmConfig CloudWatchOutputConfig: CloudWatchOutputEnabled: 'true' CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' nextStep: configureMofs - name: configureMofs action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{InstanceIds.InstanceIds}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-WAPADFS\Module-WAPADFS.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to importing WAPADFS PS Module $_" Exit 1 } $EniConfig = Get-EniConfig $Secret = Get-SecretInfo -DomainNetBIOSName '{{ DomainNetBIOSName }}' -SecretArn '{{ DomainAdminUserSecret }}' $InstanceId = Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-id' -UseBasicParsing | Select-Object -ExpandProperty Content $NameFromTag = Get-EC2Instance $InstanceId -ErrorAction Stop | Select-Object -ExpandProperty 'Instances' | Select-Object -ExpandProperty 'Tags' | Where-Object {$_.Key -eq 'Name'} | Select-Object -ExpandProperty 'Value' Set-DscConfiguration -Credentials $Secret.Credentials -DomainController1IP '{{DomainController1IP}}' -DomainController2IP '{{DomainController2IP}}' -DomainDNSName '{{ DomainDNSName }}' -GatewayAddress $EniConfig.GatewayAddress -InstanceNetBIOSName $NameFromTag -IpAddress $EniConfig.IpAddress -MacAddress $EniConfig.MacAddress CloudWatchOutputConfig: CloudWatchOutputEnabled: 'true' CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' nextStep: runMofs - name: runMofs action: aws:runCommand onFailure: step:signalfailure inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{InstanceIds.InstanceIds}}' CloudWatchOutputConfig: CloudWatchOutputEnabled: 'true' CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-WAPADFS\Module-WAPADFS.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to importing WAPADFS PS Module $_" Exit 1 } Start-DscConfiguration 'C:\AWSQuickstart\ConfigInstance' -Wait -Verbose -Force Invoke-DscStatusCheck nextStep: InstallAdsf1 - name: InstallAdsf1 action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{adfs1InstanceId.InstanceId}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-WAPADFS\Module-WAPADFS.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to importing WAPADFS PS Module $_" Exit 1 } $Secret = Get-SecretInfo -DomainNetBIOSName '{{ DomainNetBIOSName }}' -SecretArn '{{ DomainAdminUserSecret }}' Install-FirstADFS -Credential $Secret.Credentials -DirectoryType '{{ DirectoryType }}' -Password $Secret.UserPassword -Username $Secret.Username If ('{{EnableAdvancedAudtingandMetrics}}' -eq 'true') { Set-DcAuditDscConfiguration Set-LogsAndMetricsCollection -Role "ADFS" -Stackname '{{StackName}}' } Start-CleanUp CloudWatchOutputConfig: CloudWatchOutputEnabled: 'true' CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' nextStep: InstallAdsf2 - name: InstallAdsf2 action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{adfs2InstanceId.InstanceId}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-WAPADFS\Module-WAPADFS.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to importing WAPADFS PS Module $_" Exit 1 } $Secret = Get-SecretInfo -DomainNetBIOSName '{{ DomainNetBIOSName }}' -SecretArn '{{ DomainAdminUserSecret }}' Install-AdditionalADFS -Credential $Secret.Credentials -DirectoryType '{{ DirectoryType }}' -FirstAdfsServerBIOSName '{{ Adfs1NetBIOSName }}' -Password $Secret.UserPassword If ('{{EnableAdvancedAudtingandMetrics}}' -eq 'true') { Set-DcAuditDscConfiguration Set-LogsAndMetricsCollection -Role "ADFS" -Stackname '{{StackName}}' } Start-CleanUp CloudWatchOutputConfig: CloudWatchOutputEnabled: 'true' CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' nextStep: InstallWap - name: InstallWap action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{wap1InstanceId.InstanceId}}' - '{{wap2InstanceId.InstanceId}}' Parameters: commands: |- Try { Import-Module -Name 'C:\AWSQuickstart\Module-WAPADFS\Module-WAPADFS.psm1' -Force } Catch [System.Exception] { Write-Output "Failed to importing WAPADFS PS Module $_" Exit 1 } $Secret = Get-SecretInfo -DomainNetBIOSName '{{ DomainNetBIOSName }}' -SecretArn '{{ DomainAdminUserSecret }}' Install-WAP -Credential $Secret.Credentials -FirstAdfsServerBIOSName '{{ Adfs1NetBIOSName }}' -Password $Secret.UserPassword If ('{{EnableAdvancedAudtingandMetrics}}' -eq 'true') { Set-DcAuditDscConfiguration Set-LogsAndMetricsCollection -Role "WAP" -Stackname '{{StackName}}' } Start-CleanUp CloudWatchOutputConfig: CloudWatchOutputEnabled: 'true' CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' nextStep: CFNSignalEnd - 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: 'ADFS2' StackName: '{{StackName}}' Status: SUCCESS UniqueId: '{{adfs2InstanceId.InstanceId}}' - name: sleepend action: aws:sleep isEnd: True inputs: Duration: PT1S - name: signalfailure action: aws:executeAwsApi inputs: Service: cloudformation Api: SignalResource LogicalResourceId: 'ADFS2' StackName: '{{StackName}}' Status: FAILURE UniqueId: '{{adfs2InstanceId.InstanceId}}' WAP1: Type: AWS::EC2::Instance Properties: ImageId: !Ref 'LatestAmiId' IamInstanceProfile: !Ref 'ServerProfile' InstanceType: !Ref 'WAPADFSInstanceType' SubnetId: !Ref 'PublicSubnet1ID' Tags: - Key: Name Value: !Ref 'Wap1NetBIOSName' - Key: Domain Value: !Ref 'DomainDNSName' - Key: Role Value: Web Application Proxy BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 60 VolumeType: gp3 Encrypted: true KmsKeyId: !Ref 'EbsEncryptionKmsKeyId' DeleteOnTermination: true SecurityGroupIds: - !Ref 'WAPSecurityGroup' - !Ref 'DomainMemberSGID' KeyName: !Ref 'KeyPairName' WAP2: Type: AWS::EC2::Instance Properties: ImageId: !Ref 'LatestAmiId' IamInstanceProfile: !Ref 'ServerProfile' InstanceType: !Ref 'WAPADFSInstanceType' SubnetId: !Ref 'PublicSubnet2ID' Tags: - Key: Name Value: !Ref 'Wap2NetBIOSName' - Key: Domain Value: !Ref 'DomainDNSName' - Key: Role Value: Web Application Proxy BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 60 VolumeType: gp3 Encrypted: true KmsKeyId: !Ref 'EbsEncryptionKmsKeyId' DeleteOnTermination: true SecurityGroupIds: - !Ref 'WAPSecurityGroup' - !Ref 'DomainMemberSGID' KeyName: !Ref 'KeyPairName' ADFS1: Type: AWS::EC2::Instance Properties: ImageId: !Ref 'LatestAmiId' IamInstanceProfile: !Ref 'ServerProfile' InstanceType: !Ref 'WAPADFSInstanceType' SubnetId: !Ref 'PrivateSubnet1ID' Tags: - Key: Name Value: !Ref 'Adfs1NetBIOSName' - Key: Domain Value: !Ref 'DomainDNSName' - Key: Role Value: Active Directory Federation Services BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 60 VolumeType: gp3 Encrypted: true KmsKeyId: !Ref 'EbsEncryptionKmsKeyId' DeleteOnTermination: true SecurityGroupIds: - !Ref 'ADFSSecurityGroup' - !Ref 'DomainMemberSGID' KeyName: !Ref 'KeyPairName' ADFS2: CreationPolicy: ResourceSignal: Timeout: PT60M Count: 1 DependsOn: - ADFS1 - WAP1 - WAP2 Type: AWS::EC2::Instance Properties: ImageId: !Ref 'LatestAmiId' IamInstanceProfile: !Ref 'ServerProfile' InstanceType: !Ref 'WAPADFSInstanceType' SubnetId: !Ref 'PrivateSubnet2ID' Tags: - Key: Name Value: !Ref 'Adfs2NetBIOSName' - Key: Domain Value: !Ref 'DomainDNSName' - Key: Role Value: Active Directory Federation Services BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 60 VolumeType: gp3 Encrypted: true KmsKeyId: !Ref 'EbsEncryptionKmsKeyId' DeleteOnTermination: true SecurityGroupIds: - !Ref 'ADFSSecurityGroup' - !Ref 'DomainMemberSGID' KeyName: !Ref 'KeyPairName' UserData: Fn::Base64: !Sub - | $Params = @{ Adfs1NetBIOSName = '${Adfs1NetBIOSName}' Adfs2NetBIOSName = '${Adfs2NetBIOSName}' DirectoryType = '${DirectoryType}' DomainAdminUserSecret = '${DomainAdminUserSecret}' DomainController1IP = '${DomainController1IP}' DomainController2IP = '${DomainController2IP}' DomainDNSName = '${DomainDNSName}' DomainNetBIOSName = '${DomainNetBIOSName}' EnableAdvancedAudtingandMetrics = '${EnableAdvancedAudtingandMetrics}' QSS3BucketName = '${QSS3BucketName}' QSS3BucketRegion = '${QSS3BucketRegion}' QSS3KeyPrefix = '${QSS3KeyPrefix}' StackName = '${AWS::StackName}' URLSuffix = '${AWS::URLSuffix}' Wap1NetBIOSName = '${Wap1NetBIOSName}' Wap2NetBIOSName = '${Wap2NetBIOSName}' } Start-SSMAutomationExecution -DocumentName '${WapAdfsAutomationDoc}' -Parameter $Params - QSS3BucketName: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Sub '${QSS3BucketName}'] QSS3BucketRegion: !If [UsingDefaultBucket, !Sub '${AWS::Region}', !Sub '${QSS3BucketRegion}'] WAPSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable traffic to WAP Servers from the internet VpcId: !Ref 'VPCID' SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: '0.0.0.0/0' - IpProtocol: tcp FromPort: 3389 ToPort: 3389 SourceSecurityGroupId: !Ref 'DomainMemberSGID' Tags: - Key: Name Value: WAPSecurityGroup ADFSSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable traffic to ADFS servers VpcId: !Ref 'VPCID' SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Ref 'VPCCIDR' - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref 'VPCCIDR' - IpProtocol: tcp FromPort: 445 ToPort: 445 CidrIp: !Ref 'VPCCIDR' - IpProtocol: tcp FromPort: 3389 ToPort: 3389 SourceSecurityGroupId: !Ref 'DomainMemberSGID' Tags: - Key: Name Value: ADFSSecurityGroup