schemaVersion: "0.3" description: "Configure gMSA Group, Account and generate CredSpec file to s3 or Systems Manager parameter store" assumeRole: "{{AutomationAssumeRole}}" parameters: DomainAdminPassword: description: Password for the domain admin user. Must be at least 8 characters, containing letters, numbers, and symbols. type: "String" DomainAdminUser: default: Admin description: User name for the account that will be used as domain administrator. This is separate from the default "Administrator" account. type: "String" DomainDNSName: description: "Domain DNS Name used to join Active Directory in order to perform gMSA configuration." type: "String" DomainMemberSGID: description: ID of the domain member security group (e.g., sg-7f16e910). type: "String" DomainNetBIOSName: default: EXAMPLE description: NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows. type: "String" EC2InstanceType: default: t3.2xlarge description: Amazon EC2 instance type type: "String" LatestAmiId: type: 'AWS::SSM::Parameter::Value' default: "/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-Base" SubnetId: description: "Subnet ID that give the ability to communicate to Active Directoy" type: "String" SecurityGroupId: description: "Security Group that gives the ability to communicate with Active Directory" type: "String" AutomationAssumeRole: default: "" description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf." type: "String" mainSteps: - name: createStack action: aws:createStack inputs: StackName: "EC2Windows-{{automation:EXECUTION_ID}}" Capabilities: [ "CAPABILITY_IAM" ] TemplateBody: | AWSTemplateFormatVersion: '2010-09-09' Description: "Deploy single windows EC2 Instance and join domain with SSM Association" Parameters: DomainAdminPassword: AllowedPattern: (?=^.{6,255}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.* Description: Password for the domain admin user. Must be at least 8 characters, containing letters, numbers, and symbols. MaxLength: '32' MinLength: '8' NoEcho: 'true' Type: String DomainAdminUser: AllowedPattern: '[a-zA-Z0-9]*' Default: Admin 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 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 EC2InstanceType: AllowedValues: - t3.nano - t3.micro - t3.small - t3.medium - t3.large - t3.xlarge - t3.2xlarge - m5.large - m5.xlarge - m5.2xlarge Default: m5.large Description: Amazon EC2 instance type Type: String LatestAmiId: Type: 'AWS::SSM::Parameter::Value' Default: "/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-Base" SubnetID: Description: ID of a Subnet. Type: AWS::EC2::Subnet::Id Resources: DSCBucket: Type: AWS::S3::Bucket Properties: LifecycleConfiguration: Rules: - Id: DeleteAfter30Days ExpirationInDays: 30 Status: Enabled Prefix: 'logs/' DomainJoinSecrets: Type: AWS::SecretsManager::Secret Properties: Name: !Sub 'DomainJoinSecrets-${AWS::StackName}' Description: Secrets to join AD domain SecretString: !Sub '{"username":"${DomainNetBIOSName}\\${DomainAdminUser}","password":"${DomainAdminPassword}"}' LambdaSSMRole: Type: AWS::IAM::Role Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:PutObject Resource: - !Sub "${DSCBucket.Arn}" - !Sub "${DSCBucket.Arn}/*" PolicyName: write-mof-s3 Path: / AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' WriteMOFFunction: Type: AWS::Lambda::Function Properties: Code: ZipFile: > var AWS = require('aws-sdk'), s3 = new AWS.S3(); const response = require("cfn-response"); exports.handler = async (event, context) => { console.log(JSON.stringify(event)); if (event.RequestType === 'Delete') { await postResponse(event, context, response.SUCCESS, {}) return; } function postResponse(event, context, status, data){ return new Promise((resolve, reject) => { setTimeout(() => response.send(event, context, status, data), 5000) }); } await s3.putObject({ Body: event.ResourceProperties.Body, Bucket: event.ResourceProperties.Bucket, Key: event.ResourceProperties.Key }).promise(); await postResponse(event, context, response.SUCCESS, {}); }; Handler: index.handler Role: !GetAtt LambdaSSMRole.Arn Runtime: nodejs14.x Timeout: 10 WriteDomainJoinMOF: Type: Custom::WriteMOFFile Properties: ServiceToken: !GetAtt WriteMOFFunction.Arn Bucket: !Ref DSCBucket Key: !Sub "DomainJoin-${AWS::StackName}.mof" Body: !Sub | /* @TargetNode='localhost' */ instance of MSFT_Credential as $MSFT_Credential1ref { Password = "managementgovernancesample"; UserName = "${DomainJoinSecrets}"; }; instance of DSC_Computer as $DSC_Computer1ref { ResourceID = "[Computer]JoinDomain"; Credential = $MSFT_Credential1ref; DomainName = "{tag:DomainToJoin}"; Name = "{tag:Name}"; ModuleName = "ComputerManagementDsc"; ModuleVersion = "8.0.0"; ConfigurationName = "DomainJoin"; }; instance of OMI_ConfigurationDocument { Version="2.0.0"; MinimumCompatibleVersion = "1.0.0"; CompatibleVersionAdditionalProperties= {"Omi_BaseResource:ConfigurationName"}; Name="DomainJoin"; }; SSMInstanceRole: Type : AWS::IAM::Role Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject Resource: - !Sub 'arn:aws:s3:::aws-ssm-${AWS::Region}/*' - !Sub 'arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*' - !Sub 'arn:aws:s3:::amazon-ssm-${AWS::Region}/*' - !Sub 'arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*' - !Sub 'arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*' - !Sub 'arn:aws:s3:::patch-baseline-snapshot-${AWS::Region}/*' Effect: Allow PolicyName: ssm-custom-s3-policy - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - secretsmanager:GetSecretValue - secretsmanager:DescribeSecret Resource: - !Ref 'DomainJoinSecrets' PolicyName: ssm-secrets-policy - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject - s3:PutObject - s3:PutObjectAcl - s3:ListBucket Resource: - !Sub 'arn:${AWS::Partition}:s3:::${DSCBucket}/*' - !Sub 'arn:${AWS::Partition}:s3:::${DSCBucket}' Effect: Allow PolicyName: s3-instance-bucket-policy Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ReadOnlyAccess' AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "ec2.amazonaws.com" - "ssm.amazonaws.com" Action: "sts:AssumeRole" SSMInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Roles: - !Ref SSMInstanceRole WINEC2Instance: Type: "AWS::EC2::Instance" Properties: ImageId: !Ref LatestAmiId InstanceType: !Ref EC2InstanceType IamInstanceProfile: !Ref SSMInstanceProfile NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: '0' SubnetId: !Ref 'SubnetID' GroupSet: - !Ref DomainMemberSGID Tags: - Key: "Name" Value: "WindowsBox0" - Key: "DomainToJoin" Value: !Ref "DomainDNSName" - name: "ApplyDSCMof" action: aws:runCommand onFailure: "step:terminateconfigurator" inputs: DocumentName: AWS-ApplyDSCMofs InstanceIds: - "{{launchInstance.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: "/DemoWindows" Parameters: MofsToApply: - !Sub "s3:${DSCBucket}:DomainJoin-${AWS::StackName}.mof" ServicePath: - default MofOperationMode: - Apply ComplianceType: - Custom:DomainJoinSample ModuleSourceBucketName: - "NONE" AllowPSGalleryModuleSource: - "True" RebootBehavior: - "AfterMof" UseComputerNameForReporting: - "False" EnableVerboseLogging: - "False" EnableDebugLogging: - "False"