--- AWSTemplateFormatVersion: '2010-09-09' Description: >- This workload template deploys a WSUS instance in an existing VPC. **WARNING** This template creates EC2 instances and related resources. You will be billed for the AWS resources used if you create a stack from this template. (qs-1q77ks439) Metadata: cfn-lint: config: ignore_checks: - W9006 - E9101 AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Network configuration Parameters: - VPCID - VPCCIDR - PrivateSubnet1ID - Label: default: Microsoft Active Directory configuration Parameters: - DomainJoin - DomainDNSName - DomainNetBIOSName - DomainAdminUser - DomainAdminPassword - DomainMemberSGID - Label: default: Amazon EC2 configuration Parameters: - WSUSServerNetBIOSName - KeyPairName - WindowsVersion - WorkloadInstanceType - StorageVolumeSize - Label: default: WSUS configuration Parameters: - WSUSLanguagesList - WSUSProductList - WSUSClassification - WSUSNumOfSyncsPerDay - Label: default: AWS Quick Start configuration Parameters: - QSS3BucketName - QSS3BucketRegion - QSS3KeyPrefix ParameterLabels: KeyPairName: default: SSH key name StorageVolumeSize: default: Storage volume size PrivateSubnet1ID: default: Private subnet 1 ID QSS3BucketName: default: Quick Start S3 bucket name QSS3BucketRegion: default: Quick Start S3 bucket region QSS3KeyPrefix: default: Quick Start S3 key prefix VPCID: default: VPC ID VPCCIDR: default: VPC CIDR WorkloadInstanceType: default: Workload server instance type DomainJoin: default: Active Directory domain DomainAdminPassword: default: Domain admin password DomainAdminUser: default: Domain admin user name DomainDNSName: default: Domain DNS name DomainMemberSGID: default: Security group ID for AD domain members DomainNetBIOSName: default: Domain NetBIOS name WSUSServerNetBIOSName: default: Server NetBIOS name WSUSLanguagesList: default: Supported languages WSUSProductList: default: WSUS products WSUSClassification: default: WSUS classifications WSUSNumOfSyncsPerDay: default: Number of WSUS synchronizations per day WindowsVersion: default: Windows version Parameters: KeyPairName: Description: Name of an existing EC2 key pair. All instances will launch with this key pair. Type: AWS::EC2::KeyPair::KeyName StorageVolumeSize: Default: '500' Description: Capacity of the volume used to store update files (GB). Type: Number PrivateSubnet1ID: Description: ID of private subnet 1 in Availability Zone 1 for the workload (e.g., subnet-a0246dcd). 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. This string 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-wsus/ 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 VPCID: Description: ID of your existing VPC for deployment. Type: AWS::EC2::VPC::Id 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 WSUSServerNetBIOSName: AllowedPattern: "[a-zA-Z0-9\\-]+" Default: WSUS Description: NetBIOS name of the WSUS server (up to 15 characters). MaxLength: '15' MinLength: '1' Type: String DomainJoin: AllowedValues: - "Yes. Please join WSUS instance to my Active Directory domain." - "No. Please keep WSUS as a standalone Windows instance." ConstraintDescription: If no is selected, other AD related parameters will be ignored. Default: "No. Please keep WSUS as a standalone Windows instance." Description: Choose Yes to join the WSUS instance to an Active Directory domain. Type: String DomainAdminPassword: Description: Password for the Domain Administrator. MaxLength: '32' MinLength: '0' NoEcho: 'true' Type: String DomainAdminUser: AllowedPattern: "[a-zA-Z0-9]*" Default: StackAdmin Description: User name for the account that will be used as Domain Administrator. This is separate from the default "Administrator" account. MaxLength: '25' MinLength: '5' Type: String DomainDNSName: AllowedPattern: "[a-zA-Z0-9\\-]+\\..+" Default: example.com Description: Fully qualified domain name (FQDN). MaxLength: '255' MinLength: '2' Type: String DomainMemberSGID: Description: ID of the domain member security group (e.g., sg-7f16e910). # Type: AWS::EC2::SecurityGroup::Id Type: String DomainNetBIOSName: AllowedPattern: "[a-zA-Z0-9\\-]+" Default: EXAMPLE Description: NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows. MaxLength: '15' MinLength: '1' Type: String WSUSLanguagesList: Default: en Description: Comma-delimited list of WSUS enabled languages. Type: String WSUSProductList: Default: SQL Server 2012 R2, SQL Server 2016, SQL Server 2017, Windows Server 2012 R2, Windows Server 2016, Windows Server 2019 Description: The platforms for which you want WSUS to receive updates. Type: String WSUSClassification: Default: Applications, Update Rollups, Security Updates, Critical Updates, Service Packs, Updates, Drivers, Driver Sets Description: Types of updates that can be obtained through WSUS. Type: String WSUSNumOfSyncsPerDay: Default: '1' Description: The number of WSUS synchronizations per day. Type: Number WindowsVersion: AllowedValues: - Windows2019Full - Windows2019Core - Windows2019Full_MSSQL2017Standard ConstraintDescription: Must be one of supported Windows versions Default: Windows2019Core Description: The Windows version for running WSUS. Windows Core is recommended for production environments. Type: String WorkloadInstanceType: AllowedValues: - t3a.2xlarge - t3.2xlarge - r5a.xlarge - r5a.2xlarge - r5a.4xlarge - r5a.8xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.8xlarge - r4.xlarge - r4.2xlarge - r4.4xlarge - r4.8xlarge - z1d.large - z1d.xlarge ConstraintDescription: Must contain valid instance type Default: r5.2xlarge Description: Type of EC2 instance for the workload instance. Type: String Conditions: WindowsFullSelected: Fn::Equals: - Ref: WindowsVersion - Windows2019Full WindowsCoreSelected: Fn::Equals: - Ref: WindowsVersion - Windows2019Core ADJoinSelected: Fn::Equals: - Ref: DomainJoin - "Yes. Please join WSUS instance to my Active Directory domain." UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] Rules: KeyPairsNotEmpty: Assertions: - Assert: Fn::Not: - Fn::EachMemberEquals: - Fn::RefAll: AWS::EC2::KeyPair::KeyName - '' AssertDescription: All key pair parameters must not be empty SubnetsInVPC: Assertions: - Assert: Fn::EachMemberIn: - Fn::ValueOfAll: - AWS::EC2::Subnet::Id - VpcId - Fn::RefAll: AWS::EC2::VPC::Id AssertDescription: All subnets must in the VPC Resources: ADAdminSecrets: Type: AWS::SecretsManager::Secret Properties: Name: !Sub 'AWSQuickStart/${AWS::StackName}/ADAdminSecrets' Description: Active Directory administrator Credentials for Quick Start SecretString: !Sub '{ "username" : "${DomainAdminUser}", "password" : "${DomainAdminPassword}" }' QuickStartLogs: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' RetentionInDays: 30 WinFullBaseAMIInfo: Type: Custom::WinFullBaseAMIInfo Properties: ServiceToken: Fn::GetAtt: - AMIInfoFunction - Arn Region: Ref: AWS::Region AMIName: Windows_Server-2019-English-Full-Base* WinCoreBaseAMIInfo: Type: Custom::WinCoreBaseAMIInfo Properties: ServiceToken: Fn::GetAtt: - AMIInfoFunction - Arn Region: Ref: AWS::Region AMIName: Windows_Server-2019-English-Core-Base* WinFullSQL2017StandardAMIInfo: Type: Custom::WinFullSQL2017StandardAMIInfo Properties: ServiceToken: Fn::GetAtt: - AMIInfoFunction - Arn Region: Ref: AWS::Region AMIName: Windows_Server-2019-English-Full-SQL_2017_Standard* AMIInfoFunction: Type: AWS::Lambda::Function Properties: Code: ZipFile: Fn::Join: - '' - - "/**\n" - "* A Lambda function that looks up the latest AMI ID for a given region and architecture.\n" - "**/\n" - 'var aws = require("aws-sdk");' - 'exports.handler = function(event, context) {' - ' console.log("REQUEST RECEIVED:\n" + JSON.stringify(event));' - " // For Delete requests, immediately send a SUCCESS response.\n" - ' if (event.RequestType == "Delete") {' - ' sendResponse(event, context, "SUCCESS");' - " return;\n" - " }\n" - ' var responseStatus = "FAILED";' - " var responseData = {};\n" - " var ec2 = new aws.EC2({region: event.ResourceProperties.Region});\n" - " var describeImagesParams = {\n" - ' Filters: [{ Name: "name", Values: [event.ResourceProperties.AMIName]}],' - ' Owners: ["amazon"]' - " };\n" - " // Get AMI IDs with the specified name pattern and owner\n" - " ec2.describeImages(describeImagesParams, function(err, describeImagesResult) {\n" - " if (err) {\n" - ' responseData = {Error: "DescribeImages call failed"};' - ' console.log(responseData.Error + ":\n", err);' - " }\n" - " else {\n" - " var images = describeImagesResult.Images;\n" - " // Sort images by name in decscending order. The names contain the AMI version, formatted as YYYY.MM.Ver.\n" - " images.sort(function(x, y) { return y.CreationDate.localeCompare(x.CreationDate); });\n" - " for (var j = 0; j < images.length; j++) {\n" - " if (isBeta(images[j].Name)) continue;\n" - ' responseStatus = "SUCCESS";' - ' responseData["Id"] = images[j].ImageId;' - " break;\n" - " }\n" - " }\n" - " sendResponse(event, context, responseStatus, responseData);\n" - " });\n" - "};\n" - "\n" - "// Check if the image is a beta or rc image. The Lambda function won't return any of those images.\n" - 'function isBeta(imageName) {' - ' return imageName.toLowerCase().indexOf("beta") > -1 || imageName.toLowerCase().indexOf(".rc") > -1;' - "}\n" - "// Send response to the pre-signed S3 URL \n" - 'function sendResponse(event, context, responseStatus, responseData) {' - " var responseBody = JSON.stringify({\n" - " Status: responseStatus,\n" - ' Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,' - " PhysicalResourceId: context.logStreamName,\n" - " StackId: event.StackId,\n" - " RequestId: event.RequestId,\n" - " LogicalResourceId: event.LogicalResourceId,\n" - " Data: responseData\n" - " });\n" - ' console.log("RESPONSE BODY:\n", responseBody);' - ' var https = require("https");' - ' var url = require("url");' - " var parsedUrl = url.parse(event.ResponseURL);\n" - " var options = {\n" - " hostname: parsedUrl.hostname,\n" - " port: 443,\n" - " path: parsedUrl.path,\n" - ' method: "PUT",' - " headers: {\n" - ' "content-type": "",' - ' "content-length": responseBody.length' - " }\n" - " };\n" - ' console.log("SENDING RESPONSE...\n");' - " var request = https.request(options, function(response) {\n" - ' console.log("STATUS: " + response.statusCode);' - ' console.log("HEADERS: " + JSON.stringify(response.headers));' - " // Tell AWS Lambda that the function execution is done \n" - " context.done();\n" - " });\n" - ' request.on("error", function(error) {' - ' console.log("sendResponse Error:" + error);' - " // Tell AWS Lambda that the function execution is done \n" - " context.done();\n" - " });\n" - " // write data to request body\n" - " request.write(responseBody);\n" - " request.end();\n" - "}" Handler: index.handler Role: Fn::GetAtt: - AMIInfoFunctionLambdaExecutionRole - Arn Runtime: nodejs12.x Timeout: 30 AMIInfoFunctionLambdaExecutionRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: "* required per EC2 documentation" Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:*:*:* - Effect: Allow Action: - ec2:DescribeImages Resource: "*" AWSQuickstartWSUSRole: Type: AWS::IAM::Role Metadata: cfn-lint: config: ignore_checks: - EIAMPolicyWildcardResource - EIAMPolicyActionWildcard cfn_nag: rules_to_suppress: - id: F3 reason: "* required per EC2 documentation" - id: W11 reason: "* required per EC2 documentation" Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject - s3:ListBucket Resource: !Sub - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] Effect: Allow PolicyName: aws-quick-start-s3-policy - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - cloudformation:SignalResource Resource: !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*' - Effect: Allow Action: - ec2:DescribeInstances - ec2:DescribeInstanceStatus - ssm:* Resource: '*' PolicyName: WSUS-SSM-AutomationExecution Path: / AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Principal: Service: - ec2.amazonaws.com - ssm.amazonaws.com Effect: Allow Version: '2012-10-17' WSUSAutomation: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: schemaVersion: "0.3" description: Deploy WSUS with SSM Automation # Role that is utilized to perform the steps within the Automation Document. assumeRole: "{{AutomationAssumeRole}}" # Gathering parameters needed to configure DCs in the Quick Start parameters: DomainDNSName: default: !Sub "${DomainDNSName}" description: "Fully qualified domain name (FQDN) of the forest root domain e.g. example.com" type: "String" DomainNetBIOSName: default: !Sub "${DomainNetBIOSName}" description: "NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE" type: "String" AdminSecrets: default: !Sub "${ADAdminSecrets}" description: "Domain admin credentials" type: "String" WSUSInstanceId: default: "" description: "ID of the WSUS instance" type: "String" StackName: default: !Sub "${AWS::StackName}" description: "CloudFormation stack name" type: "String" AutomationAssumeRole: default: "" description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf." 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-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" URLSuffix: default: "amazonaws.com" description: "AWS URL suffix" type: "String" WSUSLanguagesList: default: !Sub "${WSUSLanguagesList}" description: "Configure WSUS enabled languages with a comma delimited list of languages." type: "String" WSUSProductList: default: !Sub "${WSUSProductList}" description: "Configure the Platforms that you want WSUS to receive updates." type: "String" WSUSClassification: default: !Sub "${WSUSClassification}" description: "Configure WSUS classifications." type: "String" WSUSNumOfSyncsPerDay: default: !Sub "${WSUSNumOfSyncsPerDay}" description: "Configure number of WSUS synchronizations per day." type: "String" WSUSServerNetBIOSName: default: !Sub "${WSUSServerNetBIOSName}" description: "WSUS Server NetBIOS name" type: "String" WindowsVersion: default: !Sub "${WindowsVersion}" description: "The AMI used to deploy WSUS instance." type: "String" mainSteps: - name: "InitializeDisk" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: commands: - | C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeDisks.ps1 - name: "RenameComputer" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunPowerShellScript" InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: commands: !Sub "Rename-Computer -Restart -NewName '${WSUSServerNetBIOSName}'" - name: "sleepRename" action: "aws:sleep" inputs: Duration: PT480S - name: "InstallDscModules" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/install-dsc-modules.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./install-dsc-modules.ps1" - name: "LCMConfig" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/LCM-Config.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./LCM-Config.ps1" - name: "BranchDomainJoin" action: aws:branch inputs: Choices: - NextStep: DomainJoin Variable: !Ref 'DomainJoin' StartsWith: "Yes" Default: "InstallWSUS" - name: "DomainJoin" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/DomainJoin.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./DomainJoin.ps1 -InstanceName '{{WSUSServerNetBIOSName}}' -DomainNetBIOSName '{{DomainNetBIOSName}}' -DomainDNSName '{{DomainDNSName}}' -AdminSecret '{{AdminSecrets}}'" - name: "DomainConfig" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\DomainJoin' -Wait -Verbose -Force DscStatusCheck - name: "RestartComputer" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: commands: - Restart-Computer - name: "InstallWSUS" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: commands: - Install-WindowsFeature -Name UpdateServices -IncludeManagementTools - name: "ConfigureWSUS" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/configure-wsus.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./configure-wsus.ps1 -langs '{{WSUSLanguagesList}}' -products '{{WSUSProductList}}' -classifications '{{WSUSClassification}}' -numOfSyncsPerDay {{WSUSNumOfSyncsPerDay}}" - name: "InstallSqlCmd" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/install-sqlcmd.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./install-sqlcmd.ps1" - name: "BranchMSSQL" action: aws:branch inputs: Choices: - NextStep: MigrateSUSDB Variable: "{{WindowsVersion}}" Contains: "MSSQL" Default: "CFNSignalEnd" - name: "MigrateSUSDB" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{WSUSInstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Ref 'QuickStartLogs' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/migrate-susdb.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./migrate-susdb.ps1" # Determines if CFN Needs to be Signaled or if Work flow should just end - name: CFNSignalEnd action: aws:branch inputs: Choices: - NextStep: signalsuccess Not: Variable: "{{StackName}}" StringEquals: "" - NextStep: sleepend Variable: "{{StackName}}" StringEquals: "" # If all steps complete successfully signals CFN of Success - name: "signalsuccess" action: "aws:executeAwsApi" isEnd: True inputs: Service: cloudformation Api: SignalResource LogicalResourceId: "SSMWaitCondition" StackName: "{{StackName}}" Status: SUCCESS UniqueId: "{{WSUSInstanceId}}" # If CFN Signl Not Needed this sleep ends work flow - name: "sleepend" action: "aws:sleep" isEnd: True inputs: Duration: PT1S # If any steps fails signals CFN of Failure - name: "signalfailure" action: "aws:executeAwsApi" inputs: Service: cloudformation Api: SignalResource LogicalResourceId: "SSMWaitCondition" StackName: "{{StackName}}" Status: FAILURE UniqueId: "{{WSUSInstanceId}}" WSUSSSMPassRolePolicy: Type: AWS::IAM::Policy Properties: PolicyName: WSUS-SSM-PassRole PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - iam:PassRole Resource: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartWSUSRole}' Roles: - !Ref 'AWSQuickstartWSUSRole' WSUSInstance: Type: AWS::EC2::Instance Properties: ImageId: Fn::If: - WindowsFullSelected - Fn::GetAtt: - WinFullBaseAMIInfo - Id - Fn::If: - WindowsCoreSelected - Fn::GetAtt: - WinCoreBaseAMIInfo - Id - Fn::GetAtt: - WinFullSQL2017StandardAMIInfo - Id IamInstanceProfile: Ref: WSUSProfile InstanceType: Ref: WorkloadInstanceType NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: '0' SubnetId: Ref: PrivateSubnet1ID GroupSet: Fn::If: - ADJoinSelected - - Ref: DomainMemberSGID - Ref: WorkloadSecurityGroup - - Ref: WorkloadSecurityGroup Tags: - Key: Name Value: Ref: WSUSServerNetBIOSName BlockDeviceMappings: - DeviceName: "/dev/sda1" Ebs: VolumeSize: 100 VolumeType: gp2 - DeviceName: "/dev/sdm" Ebs: VolumeSize: !Sub "${StorageVolumeSize}" VolumeType: gp2 KeyName: Ref: KeyPairName UserData: !Base64 Fn::Join: - '' - - "\n" - "New-Item -Path C:\\ -Name log -ItemType Directory \n" - "$transcriptPath = 'C:\\log\\user-data-transcript.txt' \n" - "Start-Transcript $transcriptPath \n" - "$instanceId = (New-Object System.Net.WebClient).DownloadString('http://169.254.169.254/latest/meta-data/instance-id')\n" - 'Start-SSMAutomationExecution -DocumentName ' - !Sub '"${WSUSAutomation}"' - ' -Parameter @{' - '"WSUSInstanceId"=' - "$instanceId" - ';"URLSuffix"=' - !Sub '"${AWS::URLSuffix}"' - ';"QSS3BucketName"=' - !If [UsingDefaultBucket, !Sub '"${QSS3BucketName}-${AWS::Region}"', !Sub '"${QSS3BucketName}"'] - ';"QSS3BucketRegion"=' - !If [UsingDefaultBucket, !Sub '"${AWS::Region}"', !Sub '"${QSS3BucketRegion}"'] - ';"QSS3KeyPrefix"=' - !Sub '"${QSS3KeyPrefix}"' - ';"AutomationAssumeRole"=' - !Sub '"arn:aws:iam::${AWS::AccountId}:role/${AWSQuickstartWSUSRole}"' - '}' - "\n" - "\n" WSUSRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: "* required per EC2 documentation" cfn_lint: config: ignore_checks: - EIAMPolicyWildcardResource Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject - s3:ListBucket Resource: !Sub - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] Effect: Allow PolicyName: aws-quick-start-s3-policy - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - secretsmanager:GetSecretValue - secretsmanager:DescribeSecret Resource: - !Ref 'ADAdminSecrets' - Effect: Allow Action: - ssm:StartAutomationExecution Resource: '*' PolicyName: QS-WSUS-SSM - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - iam:PassRole Resource: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartWSUSRole}' PolicyName: QS-WSUS-SSM-PassRole ManagedPolicyArns: - !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore" Path: "/" AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Principal: Service: - ec2.amazonaws.com Effect: Allow Version: '2012-10-17' WSUSProfile: Type: AWS::IAM::InstanceProfile Properties: Roles: - Ref: WSUSRole Path: "/" SSMWaitHandle: Type: AWS::CloudFormation::WaitConditionHandle SSMWaitCondition: Type: AWS::CloudFormation::WaitCondition CreationPolicy: ResourceSignal: Timeout: PT120M Count: 1 DependsOn: "WSUSInstance" Properties: Handle: Ref: "SSMWaitHandle" Timeout: "7200" Count: 1 WorkloadSecurityGroup: Type: AWS::EC2::SecurityGroup Metadata: cfn_nag: rules_to_suppress: - id: F1000 reason: "Standard Amazon practice" - id: W36 reason: "Group description is sufficient" Properties: GroupDescription: Allow access to the Workload instances VpcId: Ref: VPCID SecurityGroupIngress: - IpProtocol: tcp FromPort: 8530 ToPort: 8530 CidrIp: !Ref VPCCIDR - IpProtocol: tcp FromPort: 8531 ToPort: 8531 CidrIp: !Ref VPCCIDR Outputs: InstanceIP: Description: "Private IP of WSUS Instance" Value: !GetAtt WSUSInstance.PrivateIp