AWS::SSM::Association - AWS CloudFormation

AWS::SSM::Association

AWS::SSM::Association リソースによって、管理対象インスタンスに対するステートマネージャーの関連付けが作成されます。ステートマネージャーの関連付けは、インスタンスで維持する状態を定義します。たとえば、関連付けでは、アンチウイルスソフトウェアをインスタンスにインストールして実行する必要があること、または特定のポートを閉じる必要があることを指定できます。静的ターゲットの場合、関連付けは設定を再適用するスケジュールを指定します。AWS リソースグループや AWS Autoscaling グループなどの動的ターゲットの場合、ステートマネージャーは新しいインスタンスがグループに追加されたときに設定を適用します。関連付けは、設定を適用するときに実行するアクションも指定します。たとえば、アンチウイルスソフトウェアの関連付けが 1 日に 1 回実行されることがあります。ソフトウェアがインストールされていない場合は、ステートマネージャーがインストールされます。ソフトウェアがインストールされているがサービスが実行されていない場合、関連付けはステートマネージャーにサービスを開始するよう指示することがあります。

構文

AWS CloudFormation テンプレートでこのエンティティを宣言するには、次の構文を使用します。

JSON

{ "Type" : "AWS::SSM::Association", "Properties" : { "AssociationName" : String, "AutomationTargetParameterName" : String, "ComplianceSeverity" : String, "DocumentVersion" : String, "InstanceId" : String, "MaxConcurrency" : String, "MaxErrors" : String, "Name" : String, "OutputLocation" : InstanceAssociationOutputLocation, "Parameters" : {Key : Value, ...}, "ScheduleExpression" : String, "SyncCompliance" : String, "Targets" : [ Target, ... ], "WaitForSuccessTimeoutSeconds" : Integer } }

YAML

Type: AWS::SSM::Association Properties: AssociationName: String AutomationTargetParameterName: String ComplianceSeverity: String DocumentVersion: String InstanceId: String MaxConcurrency: String MaxErrors: String Name: String OutputLocation: InstanceAssociationOutputLocation Parameters: Key : Value ScheduleExpression: String SyncCompliance: String Targets: - Target WaitForSuccessTimeoutSeconds: Integer

プロパティ

AssociationName

関連付けの名前。

必須: いいえ

タイプ: 文字列

パターン: ^[a-zA-Z0-9_\-.]{3,128}$

Update requires: No interruption

AutomationTargetParameterName

関連付けのターゲットを指定します。このターゲットは、レート制御を使用した、自動化ドキュメントおよびターゲットリソースを使用する関連付けに必要です。

必須: いいえ

タイプ: 文字列

最小: 1

最大: 50

Update requires: No interruption

ComplianceSeverity

関連付けに割り当てられる重大度。

必須: いいえ

タイプ: 文字列

許可された値: CRITICAL | HIGH | LOW | MEDIUM | UNSPECIFIED

Update requires: No interruption

DocumentVersion

ターゲットと関連付ける SSM ドキュメントのバージョン。

必須: いいえ

タイプ: 文字列

パターン: ([$]LATEST|[$]DEFAULT|^[1-9][0-9]*$)

Update requires: No interruption

InstanceId

SSM ドキュメントが関連付けられるインスタンスの ID。InstanceId または Targets プロパティを指定する必要があります。

注記

InstanceId は廃止されています。関連付けのインスタンス ID を指定するには、Targets パラメータを使用します。パラメータを使用する場合 InstanceId、パラメータ AssociationNameDocumentVersionMaxErrorsMaxConcurrencyOutputLocationScheduleExpression を使用することはできません。これらのパラメータを使用するには、 Targets パラメータを使用する必要があります。

必須: 条件付き

タイプ: 文字列

パターン: (^i-(\w{8}|\w{17})$)|(^mi-\w{17}$)

Update requires: No interruption

MaxConcurrency

関連付けを同時に実行できるターゲットの最大数。数値 (10 など) またはターゲットセットのパーセント数 (10% など) を指定できます。デフォルト値は 100% です。これは、すべてのターゲットが同時に関連付けを実行することを意味します。

Systems Manager が MaxConcurrency の関連付けを実行している間に、新しいインスタンスが起動して関連付けを実行しようとすると、関連付けの実行が許可されます。次の関連付け間隔の間、新しいインスタンスは、MaxConcurrency に指定された制限内でその関連付けを処理します。

必須: いいえ

タイプ: 文字列

最小: 1

最大: 7

パターン: ^([1-9][0-9]*|[1-9][0-9]%|[1-9]%|100%)$

Update requires: No interruption

MaxErrors

追加のターゲットで関連付けを実行するリクエストの送信がシステムで停止されるまでに許容されるエラー数。エラーの絶対数 (10 など) またはターゲットセットのパーセント数 (10% など) を指定できます。たとえば、3 を指定した場合、4 番目のエラーが受信されると、システムからリクエストが送信されなくなります。0 を指定した場合、最初のエラーが返された後、システムでリクエストの送信が停止されます。50 個のインスタンスで関連付けを実行して MaxError を 10% に設定した場合、6 番目のエラーを受信すると、システムでリクエストの送信が停止されます。

MaxErrors に達したときにすでに関連付けを実行中の実行については、完了はできますが、一部の実行が失敗する場合があります。実行が失敗した max-errors を超えないようにする必要がある場合は、MaxConcurrency を 1 に設定して、実行が一度に 1 つずつ実行されるようにします。

必須: いいえ

タイプ: 文字列

最小: 1

最大: 7

パターン: ^([1-9][0-9]*|[0]|[1-9][0-9]%|[0-9]%|100%)$

Update requires: No interruption

Name

Systems Manager ドキュメントの名前。

必須: はい

タイプ: 文字列

パターン: ^[a-zA-Z0-9_\-.:/]{3,128}$

Update requires: No interruption

OutputLocation

このリクエストのアウトプット詳細を保存する S3 バケット。

必須: いいえ

タイプ: InstanceAssociationOutputLocation

Update requires: No interruption

Parameters

ドキュメントのランタイム設定のパラメータ。

必須: いいえ

タイプ: ParameterValues のマップ

Update requires: No interruption

ScheduleExpression

関連付けが実行されるときにスケジュールを指定する Cron 式。

必須: いいえ

タイプ: 文字列

最小: 1

最大: 256

Update requires: No interruption

SyncCompliance

Not currently supported by AWS CloudFormation.

必須: いいえ

タイプ: 文字列

Update requires: No interruption

Targets

関連付けのターゲット。InstanceId または Targets プロパティを指定する必要があります。

必須: 条件付き

タイプ: Target のリスト

最大: 5

Update requires: No interruption

WaitForSuccessTimeoutSeconds

スタックの実行を続行する前に、関連付けのステータスが「Success」と表示されるまでサービスが待機する秒数。指定した秒数が経過しても関連付けステータスが「Success」と表示されない場合、スタックの作成は失敗します。

必須: いいえ

タイプ: 整数

Update requires: No interruption

戻り値

Fn::GetAtt

AssociationId

Not currently supported by AWS CloudFormation.

特定のインスタンスの関連付けを作成する

次の例では、AWS-RunShellScript SSM ドキュメントを使用する関連付けを作成します。関連付けは、特定のインスタンスに対して単純なコマンドを実行します。

JSON

{ "Resources": { "SpecificInstanceIdAssociation": { "Type": "AWS::SSM::Association", "Properties": { "Name": "AWS-RunShellScript", "Targets": [ { "Key": "InstanceIds", "Values": [ "i-1234567890abcdef0" ] } ], "Parameters": { "commands": [ "ls" ], "workingDirectory": [ "/" ] }, "WaitForSuccessTimeoutSeconds": 300 } } } }

YAML

--- Resources: SpecificInstanceIdAssociation: Type: AWS::SSM::Association Properties: Name: AWS-RunShellScript Targets: - Key: InstanceIds Values: - i-1234567890abcdef0 Parameters: commands: - ls workingDirectory: - "/" WaitForSuccessTimeoutSeconds: 300

AWS アカウントのすべてのマネージドインスタンスの関連付けを作成する

次の例では、AWS-UpdateSSMAgent SSM ドキュメントを使用する関連付けを作成します。関連付けは、指定された CRON スケジュールに従って、ユーザーの AWS アカウント内のすべてのマネージドインスタンス (Systems Manager 用に設定されたインスタンス) の SSM エージェントを更新します。

JSON

{ "Resources": { "AllInstanceIdsAssociation": { "Type": "AWS::SSM::Association", "Properties": { "AssociationName": "UpdateSSMAgent", "Name": "AWS-UpdateSSMAgent", "ScheduleExpression": "cron(0 2 ? * SUN *)", "Targets": [ { "Key": "InstanceIds", "Values": [ "*" ] } ], "WaitForSuccessTimeoutSeconds": 300 } } } }

YAML

--- Resources: AllInstanceIdsAssociation: Type: AWS::SSM::Association Properties: AssociationName: UpdateSSMAgent Name: AWS-UpdateSSMAgent ScheduleExpression: cron(0 2 ? * SUN *) Targets: - Key: InstanceIds Values: - "*" WaitForSuccessTimeoutSeconds: 300

特定のタグの関連付けを作成する

次の例では、AWS-UpdateSSMAgent SSM ドキュメントを使用する関連付けを作成します。関連付けは、Environment のタグキーと Production の値が割り当てられているすべてのマネージドインスタンスで SSM エージェントを更新します。関連付けは、指定されたレート式に従って 7 日ごとに実行されます。

JSON

{ "Resources": { "TaggedInstancesAssociation": { "Type": "AWS::SSM::Association", "Properties": { "AssociationName": "UpdateSSMAgent", "Name": "AWS-UpdateSSMAgent", "ScheduleExpression": "rate(7 days)", "Targets": [ { "Key": "tag:Environment", "Values": [ "Production" ] } ], "WaitForSuccessTimeoutSeconds": 300 } } } }

YAML

--- Resources: TaggedInstancesAssociation: Type: AWS::SSM::Association Properties: AssociationName: UpdateSSMAgent Name: AWS-UpdateSSMAgent ScheduleExpression: rate(7 days) Targets: - Key: tag:Environment Values: - Production WaitForSuccessTimeoutSeconds: 300

インスタンスに自動化ドキュメントを関連付ける

次の例では、AWS-StopEC2Instance 自動化ドキュメントを特定のインスタンスに割り当てる関連付けを作成します。

注記

この例では、Amazon リソースネーム (ARN) (arn:${AWS::Partition}:iam::aws:policy/AmazonEC2FullAccess) を指定します。このポリシーは、インスタンスの停止に必要なアクセス許可以上のものを実現します。アクセス許可がより制限されたポリシーを使用することをお勧めします。

JSON

{ "Resources": { "AutomationExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ssm.amazonaws.com" }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/AmazonEC2FullAccess" ] } }, "AutomationAssociation": { "Type": "AWS::SSM::Association", "Properties": { "Name": "AWS-StopEC2Instance", "Parameters": { "AutomationAssumeRole": [ "AutomationExecutionRole.Arn" ] }, "Targets": [ { "Key": "ParameterValues", "Values": [ "i-1234567890abcdef0" ] } ], "AutomationTargetParameterName": "InstanceId" } } } }

YAML

--- Resources: AutomationExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: ssm.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonEC2FullAccess AutomationAssociation: Type: AWS::SSM::Association Properties: Name: AWS-StopEC2Instance Parameters: AutomationAssumeRole: - !GetAtt AutomationExecutionRole.Arn Targets: - Key: ParameterValues Values: - i-1234567890abcdef0 AutomationTargetParameterName: InstanceId

レート制御を使用して出力を Amazon S3 に記録する関連付けを作成する

次の例では、レート制御を使用する関連付けを作成します。関連付けは、一度に 20% のインスタンスのみで SSM エージェントの更新を試みます。Systems Manager は、インスタンスの合計数の 5% で実行が失敗した場合、追加のインスタンスでの関連付けの実行を停止します。また、System Manager は、関連付けの出力を Amazon S3 に記録します。

JSON

{ "Resources": { "RateControlAssociation": { "Type": "AWS::SSM::Association", "Properties": { "Name": "AWS-UpdateSSMAgent", "Targets": [ { "Key": "InstanceIds", "Values": [ "*" ] } ], "MaxConcurrency": "20%", "MaxErrors": "5%" }, "OutputLocation": { "S3Location": { "OutputS3BucketName": "MyAssociationOutputBucket", "OutputS3KeyPrefix": "my-agent-update-output" } }, "WaitForSuccessTimeoutSeconds": 300 } } }

YAML

--- Resources: RateControlAssociation: Type: AWS::SSM::Association Properties: Name: AWS-UpdateSSMAgent Targets: - Key: InstanceIds Values: - "*" MaxConcurrency: 20% MaxErrors: 5% OutputLocation: S3Location: OutputS3BucketName: MyAssociationOutputBucket OutputS3KeyPrefix: my-agent-update-output WaitForSuccessTimeoutSeconds: 300

Ansible で動作する関連付けを作成する

次の例では、Ansible とシステムマネージャーを使用して Nginx をデプロイする関連付けを作成します。このテンプレートは、Github リポジトリから Ansible Playbook をコピーします。ターゲットはインスタンス ID に基づいています。

JSON

{ "Description": "Deploy Single EC2 Linux Instance", "Parameters": { "LatestAmiId": { "Type": "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>", "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" }, "GitHubOwner": { "Type": "String" }, "GitHubRepo": { "Type": "String" }, "GitHubBranch": { "Type": "String" } }, "Resources": { "SSMAssocLogs": { "Type": "AWS::S3::Bucket" }, "SSMInstanceRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::aws-ssm-${AWS::Region}/*", "arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*", "arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*", "arn:aws:s3:::patch-baseline-snapshot-${AWS::Region}/*" ], "Effect": "Allow" } ] }, "PolicyName": "ssm-custom-s3-policy" }, { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl", "s3:ListBucket" ], "Resource": [ "arn:${AWS::Partition}:s3:::${SSMAssocLogs}/*", "arn:${AWS::Partition}:s3:::${SSMAssocLogs}" ], "Effect": "Allow" } ] }, "PolicyName": "s3-instance-bucket-policy" } ], "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore" ], "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": [ "SSMInstanceRole" ] } }, "EC2Instance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "LatestAmiId", "InstanceType": "t3.small", "IamInstanceProfile": "SSMInstanceProfile" } }, "AnsibleAssociation": { "Type": "AWS::SSM::Association", "Properties": { "Name": "AWS-ApplyAnsiblePlaybooks", "WaitForSuccessTimeoutSeconds": 300, "Targets": [ { "Key": "InstanceIds", "Values": [ "EC2Instance" ] } ], "OutputLocation": { "S3Location": { "OutputS3BucketName": "SSMAssocLogs", "OutputS3KeyPrefix": "logs/" } }, "Parameters": { "SourceType": [ "GitHub" ], "SourceInfo": [ "{\"owner\":\"${GitHubOwner}\",\n\"repository\":\"${GitHubRepo}\",\n\"path\":\"\",\n\"getOptions\":\"branch:${GitHubBranch}\"}\n" ], "InstallDependencies": [ "True" ], "PlaybookFile": [ "playbook.yml" ], "ExtraVariables": [ "SSM=True" ], "Check": [ "False" ], "Verbose": [ "-v" ] } } } }, "Outputs": { "WebServerPublic": { "Value": "EC2Instance.PublicDnsName", "Description": "Public DNS for WebServer" } } }

YAML

--- Description: "Deploy Single EC2 Linux Instance" Parameters: # Using SSM Parameter Store to fetch the Latest AMI for Amazon Linux, eliminates the need for AMI Mappings LatestAmiId: Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>' Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" GitHubOwner: Type: 'String' GitHubRepo: Type: 'String' GitHubBranch: Type: 'String' Resources: SSMAssocLogs: Type: AWS::S3::Bucket 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: - Action: - s3:GetObject - s3:PutObject - s3:PutObjectAcl - s3:ListBucket Resource: - !Sub 'arn:${AWS::Partition}:s3:::${SSMAssocLogs}/*' - !Sub 'arn:${AWS::Partition}:s3:::${SSMAssocLogs}' Effect: Allow PolicyName: s3-instance-bucket-policy Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' 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 EC2Instance: Type: "AWS::EC2::Instance" Properties: ImageId: !Ref LatestAmiId InstanceType: "t3.small" IamInstanceProfile: !Ref SSMInstanceProfile AnsibleAssociation: Type: AWS::SSM::Association Properties: # Here using the AWS-ApplyAnsiblePlaybooks Name: AWS-ApplyAnsiblePlaybooks WaitForSuccessTimeoutSeconds: 300 # Targeting Instance by InstanceId passed from the Logical ID of Instance being created # in CloudFormation Targets: - Key: InstanceIds Values: [ !Ref EC2Instance ] OutputLocation: S3Location: OutputS3BucketName: !Ref SSMAssocLogs OutputS3KeyPrefix: 'logs/' Parameters: # Getting an Ansible Playbook from a GitHub Location SourceType: - 'GitHub' # At a minimum must include the following GitHub repo information, if using a private repo # would want to include the GitHub Token option SourceInfo: - !Sub | {"owner":"${GitHubOwner}", "repository":"${GitHubRepo}", "path":"", "getOptions":"branch:${GitHubBranch}"} # Installing Ansible and its dependencies InstallDependencies: - 'True' # Playbook file we want to run PlaybookFile: - 'playbook.yml' ExtraVariables: - 'SSM=True' Check: - 'False' Verbose: - '-v' Outputs: WebServerPublic: Value: !GetAtt 'EC2Instance.PublicDnsName' Description: Public DNS for WebServer

bash スクリプトを実行する関連付けを作成する

次の例では、bash スクリプトを実行する関連付けを作成します。ターゲットはタグに基づいています。

JSON

{ "Description": "Deploy Single EC2 Linux Instance Install and Install Nginx by a State Manager Association", "Parameters": { "LatestAmiId": { "Type": "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>", "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" } }, "Resources": { "SSMAssocLogs": { "Type": "AWS::S3::Bucket" }, "SSMInstanceRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::aws-ssm-${AWS::Region}/*", "arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*", "arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*", "arn:aws:s3:::patch-baseline-snapshot-${AWS::Region}/*" ], "Effect": "Allow" } ] }, "PolicyName": "ssm-custom-s3-policy" }, { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl", "s3:ListBucket" ], "Resource": [ "arn:${AWS::Partition}:s3:::${SSMAssocLogs}/*", "arn:${AWS::Partition}:s3:::${SSMAssocLogs}" ], "Effect": "Allow" } ] }, "PolicyName": "s3-instance-bucket-policy" } ], "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore", "arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy" ], "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": [ "SSMInstanceRole" ] } }, "EC2Instance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "LatestAmiId", "InstanceType": "t3.medium", "IamInstanceProfile": "SSMInstanceProfile", "Tags": [ { "Key": "nginx", "Value": "yes" } ] } }, "NginxAssociation": { "DependsOn": "EC2Instance", "Type": "AWS::SSM::Association", "Properties": { "Name": "AWS-RunShellScript", "WaitForSuccessTimeoutSeconds": 300, "Targets": [ { "Key": "tag:nginx", "Values": [ "yes" ] } ], "OutputLocation": { "S3Location": { "OutputS3BucketName": "SSMAssocLogs", "OutputS3KeyPrefix": "logs/" } }, "Parameters": { "commands": [ "sudo amazon-linux-extras install nginx1 -y\nsudo service nginx start\n" ] } } } }, "Outputs": { "WebServerPublic": { "Value": "EC2Instance.PublicDnsName", "Description": "Public DNS for WebServer" } } }

YAML

--- Description: "Deploy Single EC2 Linux Instance Install and Install Nginx by a State Manager Association" Parameters: LatestAmiId: Type: 'AWS::SSM::Parameter::Value*<AWS::EC2::Image::Id>' Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" Resources: SSMAssocLogs: Type: AWS::S3::Bucket # Role that allows SSM Agent to communicate with SSM and allows use of all features of SSM 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: - Action: - s3:GetObject - s3:PutObject - s3:PutObjectAcl - s3:ListBucket Resource: - !Sub 'arn:${AWS::Partition}:s3:::${SSMAssocLogs}/*' - !Sub 'arn:${AWS::Partition}:s3:::${SSMAssocLogs}' Effect: Allow PolicyName: s3-instance-bucket-policy Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy' 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 EC2Instance: Type: "AWS::EC2::Instance" Properties: ImageId: !Ref LatestAmiId InstanceType: "t3.medium" IamInstanceProfile: !Ref SSMInstanceProfile Tags: - Key: 'nginx' Value: 'yes' NginxAssociation: DependsOn: EC2Instance # CloudFormation Resource Type that creates State Manager Associations Type: AWS::SSM::Association Properties: # Command Document that this Association will run Name: AWS-RunShellScript WaitForSuccessTimeoutSeconds: 300 # Targeting Instance by Tags Targets: - Key: tag:nginx Values: - 'yes' # The passing in the S3 Bucket that is created in the template that logs will be sent to OutputLocation: S3Location: OutputS3BucketName: !Ref SSMAssocLogs OutputS3KeyPrefix: 'logs/' # Parameters for the AWS-RunShellScript, in this case commands to install nginx Parameters: commands: - | sudo amazon-linux-extras install nginx1 -y sudo service nginx start Outputs: WebServerPublic: Value: !GetAtt 'EC2Instance.PublicDnsName' Description: Public DNS for WebServer

Systems Manager Automation で bash スクリプトを実行する関連付けを作成する

次の例では、ステートマネージャーとオートメーションを使用して bash スクリプトを複数のステップで実行する関連付けを作成します。ターゲットはタグに基づいています。

JSON

{ "Description": "Deploy Single EC2 Linux Instance", "Parameters": { "LatestAmiId": { "Type": "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>", "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" } }, "Resources": { "SSMAssocLogs": { "Type": "AWS::S3::Bucket" }, "nginxInstallAutomation": { "Type": "AWS::SSM::Document", "Properties": { "DocumentType": "Automation", "Content": { "schemaVersion": "0.3", "description": "Updates AMI with Linux distribution packages and installs Nginx software", "assumeRole": "{{AutomationAssumeRole}}", "parameters": { "InstanceId": { "description": "ID of the Instance.", "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": "updateOSSoftware", "action": "aws:runCommand", "maxAttempts": 3, "timeoutSeconds": 3600, "inputs": { "DocumentName": "AWS-RunShellScript", "InstanceIds": [ "{{InstanceId}}" ], "CloudWatchOutputConfig": { "CloudWatchOutputEnabled": "true" }, "Parameters": { "commands": [ "#!/bin/bash\nsudo yum update -y\nneeds-restarting -r\nif [ $? -eq 1 ]\nthen\n exit 194\nelse\n exit 0\nfi\n" ] } } }, { "name": "InstallNginx", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunShellScript", "InstanceIds": [ "{{InstanceId}}" ], "CloudWatchOutputConfig": { "CloudWatchOutputEnabled": "true" }, "Parameters": { "commands": [ "sudo amazon-linux-extras install nginx1 -y\nsudo service nginx start\n" ] } } }, { "name": "TestInstall", "action": "aws:runCommand", "maxAttempts": 3, "timeoutSeconds": 3600, "onFailure": "Abort", "inputs": { "DocumentName": "AWS-RunShellScript", "InstanceIds": [ "{{InstanceId}}" ], "Parameters": { "commands": [ "curl localhost\n" ] } } } ] } } }, "SSMExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "ssm:StartAssociationsOnce", "ssm:CreateAssociation", "ssm:CreateAssociationBatch", "ssm:UpdateAssociation" ], "Resource": "*", "Effect": "Allow" } ] }, "PolicyName": "ssm-association" } ], "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole" ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com", "ssm.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] } } }, "SSMInstanceRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::aws-ssm-${AWS::Region}/*", "arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*", "arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*", "arn:aws:s3:::patch-baseline-snapshot-${AWS::Region}/*" ], "Effect": "Allow" } ] }, "PolicyName": "ssm-custom-s3-policy" }, { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl", "s3:ListBucket" ], "Resource": [ "arn:${AWS::Partition}:s3:::${SSMAssocLogs}/*", "arn:${AWS::Partition}:s3:::${SSMAssocLogs}" ], "Effect": "Allow" } ] }, "PolicyName": "s3-instance-bucket-policy" } ], "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore", "arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy" ], "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": [ "SSMInstanceRole" ] } }, "EC2Instance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "LatestAmiId", "InstanceType": "t3.medium", "IamInstanceProfile": "SSMInstanceProfile", "Tags": [ { "Key": "nginx", "Value": true } ] } }, "NginxAssociation": { "DependsOn": "EC2Instance", "Type": "AWS::SSM::Association", "Properties": { "Name": "nginxInstallAutomation", "WaitForSuccessTimeoutSeconds": 300, "OutputLocation": { "S3Location": { "OutputS3BucketName": "SSMAssocLogs", "OutputS3KeyPrefix": "logs/" } }, "AutomationTargetParameterName": "InstanceId", "Parameters": { "AutomationAssumeRole": [ "SSMExecutionRole.Arn" ] }, "Targets": [ { "Key": "tag:nginx", "Values": [ true ] } ] } } }, "Outputs": { "WebServerPublic": { "Value": "EC2Instance.PublicDnsName", "Description": "Public DNS for WebServer" } } }

YAML

--- Description: "Deploy Single EC2 Linux Instance" Parameters: LatestAmiId: Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>' Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" Resources: SSMAssocLogs: Type: AWS::S3::Bucket nginxInstallAutomation: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: schemaVersion: "0.3" description: "Updates AMI with Linux distribution packages and installs Nginx software" assumeRole: "{{AutomationAssumeRole}}" parameters: InstanceId: description: "ID of the Instance." 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: "updateOSSoftware" action: "aws:runCommand" maxAttempts: 3 timeoutSeconds: 3600 inputs: DocumentName: "AWS-RunShellScript" InstanceIds: - "{{InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" Parameters: commands: - | #!/bin/bash sudo yum update -y needs-restarting -r if [ $? -eq 1 ] then exit 194 else exit 0 fi - name: "InstallNginx" action: "aws:runCommand" inputs: DocumentName: "AWS-RunShellScript" InstanceIds: - "{{InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" Parameters: commands: - | sudo amazon-linux-extras install nginx1 -y sudo service nginx start - name: "TestInstall" action: "aws:runCommand" maxAttempts: 3 timeoutSeconds: 3600 onFailure: "Abort" inputs: DocumentName: "AWS-RunShellScript" InstanceIds: - "{{InstanceId}}" Parameters: commands: - | curl localhost SSMExecutionRole: Type : AWS::IAM::Role Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Action: - ssm:StartAssociationsOnce - ssm:CreateAssociation - ssm:CreateAssociationBatch - ssm:UpdateAssociation Resource: '*' Effect: Allow PolicyName: ssm-association Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole' AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "ec2.amazonaws.com" - "ssm.amazonaws.com" Action: "sts:AssumeRole" 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: - Action: - s3:GetObject - s3:PutObject - s3:PutObjectAcl - s3:ListBucket Resource: - !Sub 'arn:${AWS::Partition}:s3:::${SSMAssocLogs}/*' - !Sub 'arn:${AWS::Partition}:s3:::${SSMAssocLogs}' Effect: Allow PolicyName: s3-instance-bucket-policy Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy' 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 EC2Instance: Type: "AWS::EC2::Instance" Properties: ImageId: !Ref LatestAmiId InstanceType: "t3.medium" IamInstanceProfile: !Ref SSMInstanceProfile Tags: - Key: nginx Value: Yes NginxAssociation: DependsOn: EC2Instance Type: AWS::SSM::Association Properties: Name: !Ref nginxInstallAutomation WaitForSuccessTimeoutSeconds: 300 OutputLocation: S3Location: OutputS3BucketName: !Ref SSMAssocLogs OutputS3KeyPrefix: 'logs/' AutomationTargetParameterName: InstanceId Parameters: AutomationAssumeRole: - !GetAtt 'SSMExecutionRole.Arn' Targets: - Key: tag:nginx Values: - Yes Outputs: WebServerPublic: Value: !GetAtt 'EC2Instance.PublicDnsName' Description: Public DNS for WebServer

Windows Active Directory ドメインにターゲットを結合する関連付けを作成する

次の例では、ステートマネージャーを使用して Windows Active Directory ドメインにターゲットを結合する関連付けを作成します。ターゲットはタグに基づいています。

JSON

{ "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<AWS::EC2::Image::Id>", "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": "DomainJoinSecrets-${AWS::StackName}", "Description": "Secrets to join AD domain", "SecretString": "{\"username\":\"${DomainNetBIOSName}\\\\${DomainAdminUser}\",\"password\":\"${DomainAdminPassword}\"}" } }, "LambdaSSMRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject" ], "Resource": [ "${DSCBucket.Arn}", "${DSCBucket.Arn}/*" ] } ] }, "PolicyName": "write-mof-s3" } ], "Path": "/", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] }, "ManagedPolicyArns": [ "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) => {\n console.log(JSON.stringify(event));\n if (event.RequestType === 'Delete') {\n await postResponse(event, context, response.SUCCESS, {})\n return;\n }\n function postResponse(event, context, status, data){\n return new Promise((resolve, reject) => {\n setTimeout(() => response.send(event, context, status, data), 5000)\n });\n }\n await s3.putObject({\n Body: event.ResourceProperties.Body,\n Bucket: event.ResourceProperties.Bucket,\n Key: event.ResourceProperties.Key\n }).promise();\n await postResponse(event, context, response.SUCCESS, {});\n};\n" }, "Handler": "index.handler", "Role": "LambdaSSMRole.Arn", "Runtime": "nodejs10.x", "Timeout": 10 } }, "WriteDomainJoinMOF": { "Type": "Custom::WriteMOFFile", "Properties": { "ServiceToken": "WriteMOFFunction.Arn", "Bucket": "DSCBucket", "Key": "DomainJoin-${AWS::StackName}.mof", "Body": "/*\n@TargetNode='localhost'\n*/\ninstance of MSFT_Credential as $MSFT_Credential1ref\n{\nPassword = \"managementgovernancesample\";\n UserName = \"${DomainJoinSecrets}\";\n\n};\ninstance of DSC_Computer as $DSC_Computer1ref\n{\nResourceID = \"[Computer]JoinDomain\";\n Credential = $MSFT_Credential1ref;\n DomainName = \"{tag:DomainToJoin}\";\n Name = \"{tag:Name}\";\n ModuleName = \"ComputerManagementDsc\";\n ModuleVersion = \"8.0.0\";\n ConfigurationName = \"DomainJoin\";\n};\ninstance of OMI_ConfigurationDocument\n {\n Version=\"2.0.0\";\n MinimumCompatibleVersion = \"1.0.0\";\n CompatibleVersionAdditionalProperties= {\"Omi_BaseResource:ConfigurationName\"};\n Name=\"DomainJoin\";\n }; \n" } }, "SSMInstanceRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::aws-ssm-${AWS::Region}/*", "arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*", "arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*", "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": [ "DomainJoinSecrets" ] } ] }, "PolicyName": "ssm-secrets-policy" }, { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl", "s3:ListBucket" ], "Resource": [ "arn:${AWS::Partition}:s3:::${DSCBucket}/*", "arn:${AWS::Partition}:s3:::${DSCBucket}" ], "Effect": "Allow" } ] }, "PolicyName": "s3-instance-bucket-policy" } ], "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore", "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": [ "SSMInstanceRole" ] } }, "WINEC2Instance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "LatestAmiId", "InstanceType": "EC2InstanceType", "IamInstanceProfile": "SSMInstanceProfile", "NetworkInterfaces": [ { "DeleteOnTermination": true, "DeviceIndex": "0", "SubnetId": "SubnetID", "GroupSet": [ "DomainMemberSGID" ] } ], "Tags": [ { "Key": "Name", "Value": "WindowsBox0" }, { "Key": "DomainToJoin", "Value": "DomainDNSName" } ] } }, "JoinDomainAssociation": { "DependsOn": [ "WINEC2Instance", "WriteDomainJoinMOF" ], "Type": "AWS::SSM::Association", "Properties": { "WaitForSuccessTimeoutSeconds": 300, "Name": "AWS-ApplyDSCMofs", "Targets": [ { "Key": "tag:DomainToJoin", "Values": [ "DomainDNSName" ] } ], "OutputLocation": { "S3Location": { "OutputS3BucketName": "DSCBucket", "OutputS3KeyPrefix": "logs/" } }, "ScheduleExpression": "cron(30 23 * * ? *)", "MaxErrors": 1, "MaxConcurrency": 1, "Parameters": { "MofsToApply": [ "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" ] } } } } }

YAML

--- 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<AWS::EC2::Image::Id>' 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: nodejs10.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" JoinDomainAssociation: DependsOn: - WINEC2Instance - WriteDomainJoinMOF Type: AWS::SSM::Association Properties: WaitForSuccessTimeoutSeconds: 300 Name: AWS-ApplyDSCMofs Targets: - Key: "tag:DomainToJoin" Values: - !Ref "DomainDNSName" OutputLocation: S3Location: OutputS3BucketName: !Ref DSCBucket OutputS3KeyPrefix: 'logs/' ScheduleExpression: "cron(30 23 * * ? *)" MaxErrors: 1 MaxConcurrency: 1 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"

Windows Active Directory ドメインにターゲットを結合し、Systems Manager Automation を使用する関連付けを作成する

次の例では、ステートマネージャーとオートメーションを使用して、Windows Active Directory ドメインにターゲットを結合する関連付けを作成します。ターゲットはタグに基づいています。

JSON

{ "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<AWS::EC2::Image::Id>", "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": "DomainJoinSecrets-${AWS::StackName}", "Description": "Secrets to join AD domain", "SecretString": "{\"username\":\"${DomainAdminUser}\",\"password\":\"${DomainAdminPassword}\"}" } }, "LambdaSSMRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject" ], "Resource": [ "${DSCBucket.Arn}", "${DSCBucket.Arn}/*" ] } ] }, "PolicyName": "write-mof-s3" } ], "Path": "/", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] }, "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ] } }, "WriteScriptFunction": { "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) => {\n console.log(JSON.stringify(event));\n if (event.RequestType === 'Delete') {\n await postResponse(event, context, response.SUCCESS, {})\n return;\n }\n function postResponse(event, context, status, data){\n return new Promise((resolve, reject) => {\n setTimeout(() => response.send(event, context, status, data), 5000)\n });\n }\n await s3.putObject({\n Body: event.ResourceProperties.Body,\n Bucket: event.ResourceProperties.Bucket,\n Key: event.ResourceProperties.Key\n }).promise();\n await postResponse(event, context, response.SUCCESS, {});\n};\n" }, "Handler": "index.handler", "Role": "LambdaSSMRole.Arn", "Runtime": "nodejs10.x", "Timeout": 10 } }, "WriteDomainJoinScript": { "Type": "Custom::WriteScript", "Properties": { "ServiceToken": "WriteScriptFunction.Arn", "Bucket": "DSCBucket", "Key": "DomainJoin.ps1", "Body": "[CmdletBinding()]\n# Incoming Parameters for Script, CloudFormation\\SSM Parameters being passed in\nparam(\n [Parameter(Mandatory=$true)]\n [string]$DomainNetBIOSName,\n\n [Parameter(Mandatory=$true)]\n [string]$DomainDNSName,\n\n [Parameter(Mandatory=$true)]\n [string]$AdminSecret\n)\n\n# Formatting AD Admin User to proper format for JoinDomain DSC Resources in this Script\n$DomainAdmin = 'Domain\\User' -replace 'Domain',$DomainNetBIOSName -replace 'User',$UserName\n$Admin = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString\n$AdminUser = $DomainNetBIOSName + '\\' + $Admin.UserName\n# Creating Credential Object for Administrator\n$Credentials = (New-Object PSCredential($AdminUser,(ConvertTo-SecureString $Admin.Password -AsPlainText -Force)))\n# Getting the DSC Cert Encryption Thumbprint to Secure the MOF File\n$DscCertThumbprint = (get-childitem -path cert:\\LocalMachine\\My | where { $_.subject -eq \"CN=SampleDscEncryptCert\" }).Thumbprint\n# Getting the Name Tag of the Instance\n$NameTag = (Get-EC2Tag -Filter @{ Name=\"resource-id\";Values=(Invoke-RestMethod -Method Get -Uri http://169.254.169.254/latest/meta-data/instance-id)}| Where-Object { $_.Key -eq \"Name\" })\n$NewName = $NameTag.Value\n\n# Creating Configuration Data Block that has the Certificate Information for DSC Configuration Processing\n$ConfigurationData = @{\n AllNodes = @(\n @{\n NodeName=\"*\"\n CertificateFile = \"C:\\awssample\\publickeys\\SamplePublicKey.cer\"\n Thumbprint = $DscCertThumbprint\n PSDscAllowDomainUser = $true\n },\n @{\n NodeName = 'localhost'\n }\n )\n}\n\nConfiguration DomainJoin {\n param(\n [PSCredential] $Credentials\n )\n\n Import-Module -Name PSDesiredStateConfiguration\n Import-Module -Name ComputerManagementDsc\n \n Import-DscResource -Module PSDesiredStateConfiguration\n Import-DscResource -Module ComputerManagementDsc\n\n Node 'localhost' {\n\n Computer JoinDomain {\n Name = $NewName\n DomainName = $DomainDNSName\n Credential = $Credentials\n }\n }\n}\n\nDomainJoin -OutputPath 'C:\\awssample\\DomainJoin' -ConfigurationData $ConfigurationData -Credentials $Credentials\n" } }, "WriteInstallModuleScript": { "Type": "Custom::WriteScript", "Properties": { "ServiceToken": "WriteScriptFunction.Arn", "Bucket": "DSCBucket", "Key": "install-modules.ps1", "Body": "[CmdletBinding()]\nparam()\n\n\"Setting up Powershell Gallery to Install DSC Modules\"\nInstall-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force\nSet-PSRepository -Name PSGallery -InstallationPolicy Trusted\n\n\"Installing the needed Powershell DSC modules for this Quick Start\"\nInstall-Module -Name ComputerManagementDsc\nInstall-Module -Name PSDscResources\n\n\"Creating Directory for DSC Public Cert\"\nNew-Item -Path C:\\awssample\\publickeys -ItemType directory -Force\n\n\"Setting up DSC Certificate to Encrypt Credentials in MOF File\"\n$cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'SampleDscEncryptCert' -HashAlgorithm SHA256\n# Exporting the public key certificate\n$cert | Export-Certificate -FilePath \"C:\\awssample\\publickeys\\SamplePublicKey.cer\" -Force\n" } }, "WriteLCMConfigScript": { "Type": "Custom::WriteScript", "Properties": { "ServiceToken": "WriteScriptFunction.Arn", "Bucket": "DSCBucket", "Key": "LCM-Config.ps1", "Body": "# This block sets the LCM configuration to what we need for QS\n[DSCLocalConfigurationManager()]\nconfiguration LCMConfig\n{\n Node 'localhost' {\n Settings {\n RefreshMode = 'Push'\n ActionAfterReboot = 'StopConfiguration' \n RebootNodeIfNeeded = $false\n CertificateId = $DscCertThumbprint \n }\n }\n}\n\n$DscCertThumbprint = [string](get-childitem -path cert:\\LocalMachine\\My | where { $_.subject -eq \"CN=SampleDscEncryptCert\" }).Thumbprint\n \n#Generates MOF File for LCM\nLCMConfig -OutputPath 'C:\\awssample\\LCMConfig'\n \n# Sets LCM Configuration to MOF generated in previous command\nSet-DscLocalConfigurationManager -Path 'C:\\awssample\\LCMConfig' \n" } }, "DomainJoinAutomation": { "Type": "AWS::SSM::Document", "Properties": { "DocumentType": "Automation", "Content": { "schemaVersion": "0.3", "description": "Join a Windows Domain", "assumeRole": "{{AutomationAssumeRole}}", "parameters": { "InstanceId": { "description": "ID of the Instance.", "type": "StringList" }, "DomainDNSName": { "default": "example.com", "description": "Fully qualified domain name (FQDN) of the forest root domain e.g. example.com", "type": "String" }, "DomainNetBIOSName": { "default": "example", "description": "NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE", "type": "String" }, "AdminSecrets": { "description": "AWS Secrets Parameter Name that has Password and User name for a domain administrator.", "type": "String" }, "S3BucketName": { "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" }, "AutomationAssumeRole": { "default": "", "description": "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf.", "type": "String" } }, "mainSteps": [ { "name": "InstallDSCModules", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunRemoteScript", "InstanceIds": [ "{{InstanceId}}" ], "CloudWatchOutputConfig": { "CloudWatchOutputEnabled": "true", "CloudWatchLogGroupName": "/ssm/${AWS::StackName}" }, "Parameters": { "sourceType": "S3", "sourceInfo": "{\"path\": \"https://{{S3BucketName}}.s3.amazonaws.com/install-modules.ps1\"}", "commandLine": "./install-modules.ps1" } } }, { "name": "ConfigureLCM", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunRemoteScript", "InstanceIds": [ "{{InstanceId}}" ], "CloudWatchOutputConfig": { "CloudWatchOutputEnabled": "true", "CloudWatchLogGroupName": "/ssm/${AWS::StackName}" }, "Parameters": { "sourceType": "S3", "sourceInfo": "{\"path\": \"https://{{S3BucketName}}.s3.amazonaws.com/LCM-Config.ps1\"}", "commandLine": "./LCM-Config.ps1" } } }, { "name": "GenerateDomainJoinMof", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunRemoteScript", "InstanceIds": [ "{{InstanceId}}" ], "CloudWatchOutputConfig": { "CloudWatchOutputEnabled": "true", "CloudWatchLogGroupName": "/ssm/${AWS::StackName}" }, "Parameters": { "sourceType": "S3", "sourceInfo": "{\"path\": \"https://{{S3BucketName}}.s3.amazonaws.com/DomainJoin.ps1\"}", "commandLine": "./DomainJoin.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}}" } } }, { "name": "DomainJoin", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunPowerShellScript", "InstanceIds": [ "{{InstanceId}}" ], "CloudWatchOutputConfig": { "CloudWatchOutputEnabled": "true", "CloudWatchLogGroupName": "/ssm/${AWS::StackName}" }, "Parameters": { "commands": [ "function DscStatusCheck () {\n $LCMState = (Get-DscLocalConfigurationManager).LCMState\n if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') {\n 'returning 3010, should continue after reboot'\n exit 3010\n } else {\n 'Completed'\n }\n}\n\nStart-DscConfiguration 'C:\\awssample\\DomainJoin' -Wait -Verbose -Force\n\nDscStatusCheck\n" ] } } } ] } } }, "SSMExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "ssm:StartAssociationsOnce", "ssm:CreateAssociation", "ssm:CreateAssociationBatch", "ssm:UpdateAssociation" ], "Resource": "*", "Effect": "Allow" } ] }, "PolicyName": "ssm-association" } ], "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole" ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com", "ssm.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] } } }, "SSMInstanceRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::aws-ssm-${AWS::Region}/*", "arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-${AWS::Region}/*", "arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*", "arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*", "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": [ "DomainJoinSecrets" ] } ] }, "PolicyName": "ssm-secrets-policy" }, { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl", "s3:ListBucket" ], "Resource": [ "arn:${AWS::Partition}:s3:::${DSCBucket}/*", "arn:${AWS::Partition}:s3:::${DSCBucket}" ], "Effect": "Allow" } ] }, "PolicyName": "s3-instance-bucket-policy" } ], "Path": "/", "ManagedPolicyArns": [ "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore", "arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy" ], "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": [ "SSMInstanceRole" ] } }, "WINEC2Instance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "LatestAmiId", "InstanceType": "EC2InstanceType", "IamInstanceProfile": "SSMInstanceProfile", "NetworkInterfaces": [ { "DeleteOnTermination": true, "DeviceIndex": "0", "SubnetId": "SubnetID", "GroupSet": [ "DomainMemberSGID" ] } ], "Tags": [ { "Key": "Name", "Value": "WindowsBox1" } ] } }, "DomainAssociation": { "Type": "AWS::SSM::Association", "Properties": { "AssociationName": "DomainJoin", "Name": "DomainJoinAutomation", "WaitForSuccessTimeoutSeconds": 600, "AutomationTargetParameterName": "InstanceId", "Targets": [ { "Key": "ParameterValues", "Values": [ "WINEC2Instance" ] } ], "OutputLocation": { "S3Location": { "OutputS3BucketName": "DSCBucket", "OutputS3KeyPrefix": "logs/" } }, "Parameters": { "DomainDNSName": [ "DomainDNSName" ], "DomainNetBIOSName": [ "DomainNetBIOSName" ], "AdminSecrets": [ "DomainJoinSecrets" ], "S3BucketName": [ "DSCBucket" ], "AutomationAssumeRole": [ "SSMExecutionRole.Arn" ] } } } } }

YAML

--- 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<AWS::EC2::Image::Id>' 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":"${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' WriteScriptFunction: 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: nodejs10.x Timeout: 10 WriteDomainJoinScript: Type: Custom::WriteScript Properties: ServiceToken: !GetAtt WriteScriptFunction.Arn Bucket: !Ref DSCBucket Key: "DomainJoin.ps1" Body: | [CmdletBinding()] # Incoming Parameters for Script, CloudFormation\SSM Parameters being passed in param( [Parameter(Mandatory=$true)] [string]$DomainNetBIOSName, [Parameter(Mandatory=$true)] [string]$DomainDNSName, [Parameter(Mandatory=$true)] [string]$AdminSecret ) # Formatting AD Admin User to proper format for JoinDomain DSC Resources in this Script $DomainAdmin = 'Domain\User' -replace 'Domain',$DomainNetBIOSName -replace 'User',$UserName $Admin = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString $AdminUser = $DomainNetBIOSName + '\' + $Admin.UserName # Creating Credential Object for Administrator $Credentials = (New-Object PSCredential($AdminUser,(ConvertTo-SecureString $Admin.Password -AsPlainText -Force))) # Getting the DSC Cert Encryption Thumbprint to Secure the MOF File $DscCertThumbprint = (get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=SampleDscEncryptCert" }).Thumbprint # Getting the Name Tag of the Instance $NameTag = (Get-EC2Tag -Filter @{ Name="resource-id";Values=(Invoke-RestMethod -Method Get -Uri http://169.254.169.254/latest/meta-data/instance-id)}| Where-Object { $_.Key -eq "Name" }) $NewName = $NameTag.Value # Creating Configuration Data Block that has the Certificate Information for DSC Configuration Processing $ConfigurationData = @{ AllNodes = @( @{ NodeName="*" CertificateFile = "C:\awssample\publickeys\SamplePublicKey.cer" Thumbprint = $DscCertThumbprint PSDscAllowDomainUser = $true }, @{ NodeName = 'localhost' } ) } Configuration DomainJoin { param( [PSCredential] $Credentials ) Import-Module -Name PSDesiredStateConfiguration Import-Module -Name ComputerManagementDsc Import-DscResource -Module PSDesiredStateConfiguration Import-DscResource -Module ComputerManagementDsc Node 'localhost' { Computer JoinDomain { Name = $NewName DomainName = $DomainDNSName Credential = $Credentials } } } DomainJoin -OutputPath 'C:\awssample\DomainJoin' -ConfigurationData $ConfigurationData -Credentials $Credentials WriteInstallModuleScript: Type: Custom::WriteScript Properties: ServiceToken: !GetAtt WriteScriptFunction.Arn Bucket: !Ref DSCBucket Key: "install-modules.ps1" Body: | [CmdletBinding()] param() "Setting up Powershell Gallery to Install DSC Modules" Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force Set-PSRepository -Name PSGallery -InstallationPolicy Trusted "Installing the needed Powershell DSC modules for this Quick Start" Install-Module -Name ComputerManagementDsc Install-Module -Name PSDscResources "Creating Directory for DSC Public Cert" New-Item -Path C:\awssample\publickeys -ItemType directory -Force "Setting up DSC Certificate to Encrypt Credentials in MOF File" $cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'SampleDscEncryptCert' -HashAlgorithm SHA256 # Exporting the public key certificate $cert | Export-Certificate -FilePath "C:\awssample\publickeys\SamplePublicKey.cer" -Force WriteLCMConfigScript: Type: Custom::WriteScript Properties: ServiceToken: !GetAtt WriteScriptFunction.Arn Bucket: !Ref DSCBucket Key: "LCM-Config.ps1" Body: | # This block sets the LCM configuration to what we need for QS [DSCLocalConfigurationManager()] configuration LCMConfig { Node 'localhost' { Settings { RefreshMode = 'Push' ActionAfterReboot = 'StopConfiguration' RebootNodeIfNeeded = $false CertificateId = $DscCertThumbprint } } } $DscCertThumbprint = [string](get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=SampleDscEncryptCert" }).Thumbprint #Generates MOF File for LCM LCMConfig -OutputPath 'C:\awssample\LCMConfig' # Sets LCM Configuration to MOF generated in previous command Set-DscLocalConfigurationManager -Path 'C:\awssample\LCMConfig' DomainJoinAutomation: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: schemaVersion: "0.3" description: "Join a Windows Domain" # 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: InstanceId: description: "ID of the Instance." type: "StringList" DomainDNSName: default: "example.com" description: "Fully qualified domain name (FQDN) of the forest root domain e.g. example.com" type: "String" DomainNetBIOSName: default: "example" description: "NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE" type: "String" AdminSecrets: description: "AWS Secrets Parameter Name that has Password and User name for a domain administrator." type: "String" S3BucketName: 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" AutomationAssumeRole: default: "" description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf." type: "String" mainSteps: # This step Demonstrates how to run a local script on an Instance. It can be defined or pointed to a local script. - name: "InstallDSCModules" action: "aws:runCommand" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/ssm/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: '{"path": "https://{{S3BucketName}}.s3.amazonaws.com/install-modules.ps1"}' commandLine: "./install-modules.ps1" - name: "ConfigureLCM" action: "aws:runCommand" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/ssm/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: '{"path": "https://{{S3BucketName}}.s3.amazonaws.com/LCM-Config.ps1"}' commandLine: "./LCM-Config.ps1" - name: "GenerateDomainJoinMof" action: "aws:runCommand" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/ssm/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: '{"path": "https://{{S3BucketName}}.s3.amazonaws.com/DomainJoin.ps1"}' commandLine: "./DomainJoin.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}}" - name: "DomainJoin" action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/ssm/${AWS::StackName}' 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:\awssample\DomainJoin' -Wait -Verbose -Force DscStatusCheck SSMExecutionRole: Type : AWS::IAM::Role Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Action: - ssm:StartAssociationsOnce - ssm:CreateAssociation - ssm:CreateAssociationBatch - ssm:UpdateAssociation Resource: '*' Effect: Allow PolicyName: ssm-association Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole' AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "ec2.amazonaws.com" - "ssm.amazonaws.com" Action: "sts:AssumeRole" 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/CloudWatchAgentServerPolicy' 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: "WindowsBox1" DomainAssociation: Type: AWS::SSM::Association Properties: AssociationName: DomainJoin # We are using the AWS-ApplyDSCMofs Document Name: !Ref DomainJoinAutomation WaitForSuccessTimeoutSeconds: 600 AutomationTargetParameterName: InstanceId Targets: - Key: ParameterValues Values: - !Ref WINEC2Instance OutputLocation: S3Location: OutputS3BucketName: !Ref DSCBucket OutputS3KeyPrefix: 'logs/' Parameters: DomainDNSName: - !Ref DomainDNSName DomainNetBIOSName: - !Ref DomainNetBIOSName AdminSecrets: - !Ref DomainJoinSecrets S3BucketName: - !Ref DSCBucket AutomationAssumeRole: - !GetAtt 'SSMExecutionRole.Arn'

以下の資料も参照してください。