Systems Manager ステートマネージャーを使用してよくあるWindowsサーバの初期設定をCloudFormation一撃化してみた

2021.02.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS事業本部の梶原@福岡オフィスです

CloudFormationでEC2インスタンスを立ち上げる場合、初期設定もまとめてやりたい時があります 以前からCloudFormationで初期設定をする場合はcfn-initでの実施やサンプルが多い状況かと思いますが 最近、AWSのセッションや、ブログ等で紹介されているAWS Systems Manager ステートマネージャーでの初期化 処理が紹介されていたので、ステートマネージャーでの設定の初期化をやってみたので、ご共有します。

ご共有しているテンプレテートでは

  • 最新のAMIでWindows2019インスタンスを起動
  • SSMに必要なRole(Profile)を作成
  • Systems Manager ステートマネージャーで設定処理
  • ログ用のS3バケットの作成

などを実施しています。ご参考ください

AWS Systems Manager ステートマネージャーとは?

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-state.html

AWS Systems Manager ステートマネージャー は、安全でスケーラブルな設定管理サービスであり、Amazon EC2 およびハイブリッドインフラストラクチャを、定義された状態に保つプロセスを自動化します。

ということで、本来は1度きりの初期化ではなく、定期的にEC2インスタンス、オンプレミスサーバなどをスキャンして、自動化処理を各インスタンスに適用することができます。 紹介されている処理としては、

  • 起動時に特定のソフトウェアを使用してインスタンスをブートストラップする
  • 定義済みのスケジュールに従ってエージェント (SSM エージェント など) をダウンロードして更新する
  • ネットワーク設定を構成する
  • インスタンスを Windowsドメインに結合する (Windows Server インスタンスのみ)。
  • ライフサイクルを通じてソフトウェアの更新でインスタンスをパッチする
  • ライフサイクルを通じて Linux および Windows マネージドインスタンスでスクリプトを実行する

といった設定処理の自動化を行うことができます。

AWS Systems Managerステートマネージャーを使用する場合(対象のインスタンス)AWS Systems Managerの1機能になりますので、 実行にはAWS Systems Managerの前提条件を満たす必要がありますので、動かないなどといった場合は、こちらの条件をご確認いただき 割り当てるRoleなどを確認してみてください

AWS Systems Managerの前提条件

Systems Manager の前提条件

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-prereqs.html

こちらを満たす必要があります。具体的には操作対象のEC2には以下のような条件が必要です

  • 適切なRole(ProfileがEC2へ割り当たっている事)
  • インターネットへの接続、もしくはSSM Endpointsへの接続が可能なこと
  • ステートマネージャーでログを出力しますので、ログ用のバケットへの書き込み権限

AWS Systems Manager Quick Setup

今回は、Role等は、CloudFormationの中で、全部作ってしまいますが、既存のEC2に実施する場合はAWS Systems Manager Quick Setupを使うと AWS Systems ManagerのRoleの作成、適用まで実施してくれます。

https://dev.classmethod.jp/articles/systems-manager-quick-setup/

ちなみに、こちらの機能もまさに、本ブログ同様にCloudFormationとAWS Systems Manager ステートマネージャーの機能で実現されており、参考になります

やってみる

起動するWindowsサーバ

  • OS: Windows Server 2019 Japanese-Full-Base
  • AMI: 上記OSの最新
  • インスタンスタイプ: t3.small
  • VPC: default
  • SecurityGroup: default

初期設定する内容

  • タイムゾーンをJST(Tokyo)
  • Windows Updateの実施(オプション)
  • Windows Firewallの無効化
  • NTP Serverの設定
  • 時刻同期の実行
  • EC2Launchの更新
  • SSM Agentの更新

Windows Updateは時間がかかる、また、同時実行されて、失敗する場合があるので、オプション化しました。 スタックの更新で、UpdateをYesに変更すると後付け実行できます。 また、それぞれのスクリプトの実行成否に関しては、実行時のログを確認し、実際に適用されているかは確認するようにしてください

CloudFormationのスタック作成

  1. AWSコンソールへログイン
  2. 以下リンクをぽちっとする
    [クイック作成リンク]
  3. □AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。にチェックをいれて
  4. 「スタックの作成」を選択するとWindowsインスタンスが起動し、初期設定までやってしまいます。

全テンプレートはこちら

テンプレート抜粋(RunPowerShellScriptを使用)

タイムゾーンをJST(Tokyo)に設定

  TimeZoneAssociation:
    # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: TimeZoneAssociation
      # Command Document that this Association will run
      Name: AWS-RunPowerShellScript
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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
      Parameters:
        commands: 
          - |
            # 現在のタイムゾーンを取得
            Write-Host 'Get current timezone...'
            Get-TimeZone | Select-Object Id, BaseUtcOffset | Out-String
            
            # タイムゾーンの変更
            $timeZoneId = 'Tokyo Standard Time' # JSTに設定する場合(必要に応じて変更する)
            Write-Host 'Configure timezone...'
            Set-TimeZone -Id $timeZoneId | Out-String
            
            # 変更した結果を確認
            Write-Host 'Get new timezone...'
            Get-TimeZone | Select-Object Id, BaseUtcOffset | Out-String

State Manager Associationを作成して、作成したEC2と紐づけています。 S3バケットへのログ出力 パラメータはSystems Manger のRunCommandの実行でドキュメントAWS-RunShellScriptを実行し、commandsの引数(実際のPowereShellのコマンド)を渡して実行しています

テンプレート抜粋(定義済みのドキュメントを使用)

  InstallWindowsUpdatesAssociation:
    Condition: InstallWindowsUpdates
    # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: InstallWindowsUpdatesAssociation
      # Command Document that this Association will run
      Name: AWS-InstallWindowsUpdates
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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/'

AWS-InstallWindowsUpdates を使用しています。 今回とくにパラメータは指定していないですが、詳細なパラメータが必要な場合は 該当するドキュメントのコンテンツなどを参考にして設定します

https://ap-northeast-1.console.aws.amazon.com/systems-manager/documents/AWS-InstallWindowsUpdates/content?region=ap-northeast-1

他の定義済みのドキュメントを使用する場合は

Name: AWS-InstallWindowsUpdates

の部分を定義済みのドキュメントに変更し、必要に応じてパラメータを追記します

実行結果

無事にCloudFormationテンプレートが実行されますとWindwosインスタンスが起動し、Systems Manger のステートマネージャーとの紐づけが作成されます。

AWS コンソール(Systems Manger のステートマネージャー) https://ap-northeast-1.console.aws.amazon.com/systems-manager/state-manager?region=ap-northeast-1

関連付けが成功し、実行がうまくいくとSucccess:1 となります。 複数インスタンスを起動して、参照付けをした場合は、Succcessの後の数値が増えていきます。

まとめ

従来の方法(cfn-init)でなく、ステートマネージャーでの設定を実施してみました。 メリットとしては、ログがS3にしっかり残ること、CloudFormationの作成と、設定の実行処理タイミングを分離できるので、時間がかかる処理などでタイムアウトを避けることもできるかとおもいます。また失敗していてもコマンドが冪等性をもった組み方をしていれば、再実行なども手軽にできます。

ステートマネージャーの組み方によっては、順序だてて実施したり、定期的にUpdateを実行したりする設定をいれたりできるので、一度きりの処理よりも設定の柔軟性が高いと感じました。

ステートマネージャー本来の能力は、1つのインスタンスの初期化というよりも、複数のインスタンス、オートスケールなどと組み合わせてやると便利さが実感できそうです。 実際、今回のようにCloudFormationで一撃可する使い方よりも、ステートマネージャーの処理を別にした方が有用な気がしますので、いい感じに利用してください

参考ページ

CloudFormation で cfn-init に代えて State Manager を利用する方法とその利点

Run Command を使用した Windows の更新プログラムの管理

[PowerShell] EC2Launchを最新バージョンにするスクリプトを作ってみた

テンプレート全文

AWSTemplateFormatVersion: "2010-09-09"
Description: ""
Parameters:
  WindowsLatestAmi:
    Type : AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-windows-latest/Windows_Server-2019-Japanese-Full-Base

  InstanceType:
    Type: String
    Default: t3.small

  KeyName:
    Type: String

  InstallWindowsUpdates:
    Type: String
    Default: no
    AllowedValues: [yes,no]

Conditions:
  InstallWindowsUpdates: !Equals [ !Ref InstallWindowsUpdates, yes ]

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

  EC2:
    Type: "AWS::EC2::Instance"
    Properties:
      ImageId: !Ref WindowsLatestAmi
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      Tenancy: "default"
      EbsOptimized: true
      SourceDestCheck: true
      BlockDeviceMappings: 
        - 
          DeviceName: !Sub "/dev/sda1"
          Ebs: 
            Encrypted: true
            VolumeSize: 100
            VolumeType: "gp3"
            DeleteOnTermination: true
      IamInstanceProfile: !Ref SSMInstanceProfile
      HibernationOptions: 
        Configured: false

  EC2EIP:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: "vpc"
      InstanceId: !Ref EC2

  EC2EIPAssociation:
    Type: "AWS::EC2::EIPAssociation"
    Properties:
      AllocationId: !GetAtt EC2EIP.AllocationId
      InstanceId: !Ref EC2

  TimeZoneAssociation:
    # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: TimeZoneAssociation
      # Command Document that this Association will run
      Name: AWS-RunPowerShellScript
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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
      Parameters:
        commands: 
          - |
            # 現在のタイムゾーンを取得
            Write-Host 'Get current timezone...'
            Get-TimeZone | Select-Object Id, BaseUtcOffset | Out-String
            
            # タイムゾーンの変更
            $timeZoneId = 'Tokyo Standard Time' # JSTに設定する場合(必要に応じて変更する)
            Write-Host 'Configure timezone...'
            Set-TimeZone -Id $timeZoneId | Out-String
            
            # 変更した結果を確認
            Write-Host 'Get new timezone...'
            Get-TimeZone | Select-Object Id, BaseUtcOffset | Out-String

  InstallWindowsUpdatesAssociation:
    Condition: InstallWindowsUpdates
    # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: InstallWindowsUpdatesAssociation
      # Command Document that this Association will run
      Name: AWS-InstallWindowsUpdates
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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/'

  NetFirewallAssociation:
    # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: NetFirewallAssociation
      # Command Document that this Association will run
      Name: AWS-RunPowerShellScript
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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
      Parameters:
        commands: 
          - |
            # 変更前設定を取得
            Write-Host 'Get current Firewall configuration...'
            Get-NetFirewallProfile | Select-Object Name, Enabled | Out-String
            
            # Firewallを無効化
            Write-Host 'Update Firewall configuration...'
            Get-NetFirewallProfile | Set-NetFirewallProfile -Enabled False | Out-String
            
            # 変更後設定を取得
            Write-Host 'Get changed Firewall configuration...'
            Get-NetFirewallProfile | Select-Object Name, Enabled | Out-String

  NtpServerAssociation:
    # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: NtpServerAssociation
      # Command Document that this Association will run
      Name: AWS-RunPowerShellScript
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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
      Parameters:
        commands: 
          - |
            # Windows Timeサービスが起動していない場合は起動しておく必要がある
            if ((Get-Service -Name W32Time).Status -eq 'Stopped') {
                Start-Service -Name W32Time
            }
            
            # 現在のNTPサーバーを取得
            Write-Host 'Get current NTP Server...'
            Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\services\w32time\Parameters' -Name 'NtpServer' | Select-Object NtpServer | Out-String
            
            # NTPサーバーを更新
            Write-Host 'Update NTP Server...'
            w32tm /config /manualpeerlist:169.254.169.123 /syncfromflags:manual /update | Out-String
            if (-not $?) {
                exit $LASTEXITCODE
            }
            
            # 変更後のNTPサーバーを取得
            Write-Host 'Get changed NTP Server...'
            Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\services\w32time\Parameters' -Name 'NtpServer' | Select-Object NtpServer | Out-String

            # 時刻同期
            w32tm /resync
            # 結果の確認
            w32tm /query /status

  EC2LaunchUpdateAssociation:
     # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: EC2LaunchUpdateAssociation
      # Command Document that this Association will run
      Name: AWS-RunPowerShellScript
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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
      Parameters:
        commands:
          - |
            #
            # 最新バージョンの EC2Launch をインストールするスクリプト
            # Ref : https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2launch-download.html
            #
            # ※ EC2Launch 設定ファイルのバックアップには対応していません。
            #

            <# 
            .SYNOPSIS
                EC2Launchの最新バージョンを取得します
            #>
            function Get-LatestEC2LaunchVersion() {
                # https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2launch-version-details.html
                # より最新バージョンを取得
                #
                # ※いつHTMLの構造が変わるかわからないので最悪
                # return [Version]"1.3.2003150"
                # の様に固定値を設定しても良いと思われる
                try {
                    $res = Invoke-WebRequest -Uri 'https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2launch-version-details.html' -UseBasicParsing
                    $versions = ([Xml]$res.Content).html.body.GetElementsByTagName("table") |
                    Where-Object { $_.id -like 'w???????????????????' } |
                        ForEach-Object { $_.tr } |
                        Select-Object -Skip 1 |
                        ForEach-Object {
                            if ($_.td[0].p) {
                                [Version]($_.td[0].p)
                            }
                            else {
                                [Version]($_.td[0])
                            }
                        }
                    $latestVersion = $versions |
                        Sort-Object -Descending |
                        Select-Object -First 1
                    return $latestVersion
                }
                catch {
                    Write-Host $_
                    return $null
                }
            }

            <#
            .SYNOPSIS
                現在インストールされているEC2Launchのバージョンを取得します。
                EC2Launchがインストールされていない場合は $null を返します。
            #>
            function Get-EC2LaunchVersion() {
                $psdPath = 'C:\ProgramData\Amazon\EC2-Windows\Launch\Module\Ec2Launch.psd1'
                if (-not (Test-Path -Path $psdPath -PathType Leaf)) {
                    Write-Warning "Ec2Launch isn't installed."
                    return $null
                }
                return [Version](Import-PowerShellDataFile $psdPath).ModuleVersion
            }

            <#
            .SYNOPSIS
                EC2Launchの最新パッケージをダウンロードします。
            #>
            function Save-LatestEC2LaunchPackage([string]$SavePath = $env:TEMP) {
                $packages = @(
                    @{FileName = 'EC2-Windows-Launch.zip'; Uri = 'https://s3.amazonaws.com/ec2-downloads-windows/EC2Launch/latest/EC2-Windows-Launch.zip' },
                    @{FileName = 'install.ps1'; Uri = 'https://s3.amazonaws.com/ec2-downloads-windows/EC2Launch/latest/install.ps1' }
                )
                foreach ($package in $packages) {
                    Write-Host "Download $($package.FileName) ..."
                    $outFile = Join-Path $SavePath ($package.FileName)
                    Invoke-WebRequest -Uri $package.Uri -OutFile $outFile
                }
            }

            <#
            .SYNOPSIS
                ダウンロードしたEC2Launchの最新パッケージを削除します。
            #>
            function Clear-LatestEC2LaunchPackage([string]$InstallerPath = $env:TEMP) {
                $items = @(
                    (Join-Path $InstallerPath 'EC2-Windows-Launch.zip'),
                    (Join-Path $InstallerPath 'install.ps1')
                )
                foreach ($item in $items) {
                    if (Test-Path -LiteralPath $item) {
                        Write-Host "Remove $item ..."
                        Remove-Item -LiteralPath $item -Force
                    }
                }
            }

            <#
            .SYNOPSIS
                EC2Launchのインストーラーを実行します。
            #>
            function Invoke-EC2LaunchInstaller([string]$InstallerPath = $env:TEMP) {
                $installer = Join-Path $InstallerPath 'install.ps1'
                try {
                    Push-Location $InstallerPath
                    Write-Host "Execute $installer ..."
                    Invoke-Expression $installer
                    return 0
                }
                finally {
                    Pop-Location
                }
            }

            <#
            .SYNOPSIS
                EC2LaunchがサポートされているOS(2016 Server以降)か判定します。
            #>
            function Test-SupportedVersion() {
                $ProductType_WorkStation = 1
                try {
                    $versionInfo = Get-CimInstance -ClassName Win32_OperatingSystem -Property Version, ProductType, Caption
                    Write-Host "OS : $($versionInfo.Caption)"
                    if (([Version]$versionInfo.Version).Major -lt 10) {
                        return $false
                    }
                    if ($versionInfo.ProductType -eq $ProductType_WorkStation ) {
                        return $false
                    }
                    return $true
                }
                catch {
                    return $false
                }
            }
            
            <#
            .SYNOPSIS
                Main関数です。
            #>
            function Main() {
                if (-not (Test-SupportedVersion)) {
                    Write-Host "This is unsupported OS."
                    return -1
                }
                
                # Check EC2Launch version
                $latestVersion = Get-LatestEC2LaunchVersion
                if ($null -eq $latestVersion) {
                    Write-Host 'Failed to get the latest version.'
                    return -1
                }
                Write-Host "Latest EC2Launch version is $latestVersion ..."
                $currentVersion = Get-EC2LaunchVersion
                Write-Host "Current EC2Launch version is $currentVersion ..."
                if ($currentVersion -ge $latestVersion) {
                    Write-Host "EC2Launch is the latest version."
                    return 0
                }
                
                # Update EC2Launch
                try {
                    Save-LatestEC2LaunchPackage
                    Invoke-EC2LaunchInstaller
                }
                finally {
                    Clear-LatestEC2LaunchPackage
                }
                $currentVersion = Get-EC2LaunchVersion
                Write-Host "Current EC2Launch version is $currentVersion ."
            }
            exit Main

  UpdateSSMAgentAssociation:
    # CloudFormation Resource Type that creates State Manager Associations
    Type: AWS::SSM::Association
    Properties:
      AssociationName: UpdateSSMAgentAssociation
      # Command Document that this Association will run
      Name: AWS-UpdateSSMAgent
      WaitForSuccessTimeoutSeconds: 300
      # Targeting Instance by InstanceId passed from the Logical ID of Instance being created 
      # in CloudFormation
      Targets:
        - Key: InstanceIds
          Values: [ !Ref EC2 ]
      # 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/'