AWS Systems Manager オートメーションによる AutoScaling グループのAMI更新

EC2 AutoScalingを利用している環境では、OS内の更新に伴い起動元AMIをアップデートする必要が出てきます。
例えば、下記のようにAMI更新用のインスタンス(AutoScalingグループに所属しない)を用意しておき、

  1. AMI更新用インスタンスで、OS内の更新作業を実施
  2. AMI更新用インスタンスを元にAMIを作成
  3. 作成したAMIを元にAutoScaling起動設定を作成
  4. 対象のAutoScalingグループを、作成した起動設定を参照するよう更新

といった流れで行います。
今回は、SSM Automation + CloudWatch Events を利用し、(2)~(4)をスケジュール実行させてみます。

設定方法

CloudFormationスタック作成

以下のテンプレートを利用しCloudFormationスタックを作成してください。

AWSTemplateFormatVersion: "2010-09-09"
Description: 
  IAM Role and SSM Automation Document for Updating AutoScaling AMI.

# ------------------------------------------------------------#
# METADATA
# ------------------------------------------------------------#
Metadata: 
  "AWS::CloudFormation::Interface": 
    ParameterGroups: 
      - Label: 
          default: "IAM Role: CloudWatch Events executes SSM Automation"
        Parameters: 
          - CloudWatchEventsRoleName
          - CloudWatchEventsRolePolicyName
      - Label: 
          default: "IAM Role: SSM Automation updates AutoScalingGroup AMI"
        Parameters: 
          - SSMAutomationRoleName
          - SSMAutomationRolePolicyName

    ParameterLabels: 
      CloudWatchEventsRoleName: 
        default: "IAM Role Name"
      CloudWatchEventsRolePolicyName: 
        default: "IAM Policy Name"
      SSMAutomationRoleName: 
        default: "IAM Role Name"
      SSMAutomationRolePolicyName: 
        default: "IAM Policy Name"

# ------------------------------------------------------------#
# PARAMETERS
# ------------------------------------------------------------# 
Parameters:
  CloudWatchEventsRoleName:
    Type: String
    Default: "EventsRoleForExecSSMAutomation"

  CloudWatchEventsRolePolicyName:
    Type: String
    Default: "PolicyForExecSSMAutomation"

  SSMAutomationRoleName:
    Type: String
    Default: "SSMRoleForUpdateAutoScalingGroupAMI"

  SSMAutomationRolePolicyName:
    Type: String
    Default: "PolicyForUpdateAutoScalingGroupAMI"

# ------------------------------------------------------------#
# RESOURCES
# ------------------------------------------------------------#
Resources: 
# IAM Role: CloudWatch Events executes SSM Automation
  CloudWatchEventsRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref CloudWatchEventsRoleName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - events.amazonaws.com
          Action:
          - sts:AssumeRole
      Policies: 
        - 
          PolicyName: !Ref CloudWatchEventsRolePolicyName
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - 
                Effect: "Allow"
                Action: 
                  - "ssm:StartAutomationExecution"
                  - "iam:PassRole"
                Resource: "*"

# IAM Role: SSM Automation updates AutoScalingGroup AMI
  SSMAutomationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref SSMAutomationRoleName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ssm.amazonaws.com
          Action:
          - sts:AssumeRole
      Policies: 
        - 
          PolicyName: !Ref SSMAutomationRolePolicyName
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - 
                Effect: "Allow"
                Action: 
                  - "autoscaling:CreateLaunchConfiguration"
                  - "autoscaling:UpdateAutoScalingGroup"
                  - "ec2:CreateImage"
                  - "ec2:DescribeImages"
                Resource: "*"

# SSM Automation Document
  AutomationDocument:
    Type: "AWS::SSM::Document"
    Properties:
      DocumentType: Automation
      Content:
        description: Automation Document for Updating AMI for AutoScaling Group
        schemaVersion: '0.3'
        assumeRole: "{{ AutomationAssumeRole }}"
        parameters:
          AutomationAssumeRole:
            type: String
            description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
            default: !GetAtt SSMAutomationRole.Arn
          InstanceId:
            type: String
            description: "(Required) Instance id of instance that ami is created from."
          LaunchConfigPrefix:
            type: String
            description: "(Required) Name Prefix of Launch Configuration to create."
          AutoScalingGroupName:
            type: String
            description: "(Required) Name of AutoScaling Group which already exists."
        mainSteps:
        - name: createImage
          action: aws:createImage
          inputs:
            InstanceId: "{{ InstanceId }}"
            ImageName: "{{ InstanceId }}_{{ global:DATE }}"
            NoReboot: true
            ImageDescription: "{{ InstanceId }}_{{ global:DATE }}"
        - name: CreateLaunchConfiguration
          action: aws:executeAwsApi
          inputs:
            Service: autoscaling
            Api: CreateLaunchConfiguration
            InstanceId: "{{ InstanceId }}"
            ImageId: "{{ createImage.ImageId }}"
            LaunchConfigurationName: "{{ LaunchConfigPrefix }}_{{ global:DATE }}"
        - name: UpdateAutoScalingGroup
          action: aws:executeAwsApi
          inputs:
            Service: autoscaling
            Api: UpdateAutoScalingGroup
            AutoScalingGroupName: "{{ AutoScalingGroupName }}"
            LaunchConfigurationName: "{{ LaunchConfigPrefix }}_{{ global:DATE }}"

# ------------------------------------------------------------#
# OUTPUTS
# ------------------------------------------------------------#
Outputs:
  AutomationDocument:
    Description: Automation Document Name
    Value: !Ref AutomationDocument

このテンプレートで、以下リソースが作成されます。

  • (2)~(4)を行う為のSSM Automationドキュメント
  • SSM Automationドキュメントの内容(AutoScaling AMI更新)を実行する為のIAMロール
  • CloudWatch Events から SSM Automationを実行する為のIAMロール

CloudWatch Events ルールの作成

CloudWatchの操作画面で、ルールを作成します。

AutoScalingの起動元AMI更新を実施するスケジュールを設定します。
以下の場合、日本時間で毎日14:30になります。

また[ターゲットの追加]を選択しておきます。

以下の通りターゲットを設定します。

ドキュメント [スタック名]-AutomationDocument-[ランダム文字列]

(CFnにより作成されたSSMドキュメント名)

InstanceId AMI更新用インスタンスのID
AutoScalingGroupName 対象のAutoScalingグループ名
LaunchConfigPrefix 新しく作成される起動設定の接頭辞(プレフィックス)
既存のロールを使用 EventsRoleForExecSSMAutomation (既定のIAMロール名)

適当な名前を設定し、ルールを作成します。

 

稼働確認

実際に起動元AMIが更新されているか確認してみましょう。
[インスタンスID]_[YY-MM-DD] という名前でAMIが作成されていて、

そのAMIを指定した起動設定が [指定したプレフィックス]_[YY-MM-DD] という名前で作成されていて、

対象のAutoScalingグループでその起動設定が参照されていればOKです。

更新処理の詳細はSystems Manager オートメーションの画面で確認できます。
実行IDを選択するとより詳細な内容が確認できます。

ステップごと(AMI作成、起動設定作成、AutoScalingグループ更新)の状況を確認したい場合はステップIDを選択します。

ステップ(AMI作成)の詳細が確認できました。

設定内容(CloudFormationテンプレート内容解説)

SSM Automationを実行する為のCloudWatch Events用IAMロール

CloudWatch Events(events.amazonaws.com)から、SSM Automationが実行可能なロールになっています。

...
# IAM Role: CloudWatch Events executes SSM Automation
  CloudWatchEventsRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref CloudWatchEventsRoleName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - events.amazonaws.com
          Action:
          - sts:AssumeRole
      Policies: 
        - 
          PolicyName: !Ref CloudWatchEventsRolePolicyName
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - 
                Effect: "Allow"
                Action: 
                  - "ssm:StartAutomationExecution"
                  - "iam:PassRole"
                Resource: "*"
...

AutoScalingグループを更新する為のSSM用IAMロール

Systems Manager(ssm.amazonaws.com)から、AMI作成・起動設定作成・AutoScalingグループ更新が実行可能なロールになっています。

...
# IAM Role: SSM Automation updates AutoScalingGroup AMI
  SSMAutomationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref SSMAutomationRoleName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ssm.amazonaws.com
          Action:
          - sts:AssumeRole
      Policies: 
        - 
          PolicyName: !Ref SSMAutomationRolePolicyName
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - 
                Effect: "Allow"
                Action: 
                  - "autoscaling:CreateLaunchConfiguration"
                  - "autoscaling:UpdateAutoScalingGroup"
                  - "ec2:CreateImage"
                  - "ec2:DescribeImages"
                Resource: "*"
...

SSM Automationドキュメント

CloudFormationテンプレートもSSMドキュメントもYAMLなので分かりにくいですが、
Content以下がSSMドキュメントの内容です。

...
# SSM Automation Document
  AutomationDocument:
    Type: "AWS::SSM::Document"
    Properties:
      DocumentType: Automation
      Content:
        description: Automation Document for Updating AMI for AutoScaling Group
        schemaVersion: '0.3'
        assumeRole: "{{ AutomationAssumeRole }}"
        parameters:
          AutomationAssumeRole:
            type: String
            description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
            default: !GetAtt SSMAutomationRole.Arn
          InstanceId:
            type: String
            description: "(Required) Instance id of instance that ami is created from."
          LaunchConfigPrefix:
            type: String
            description: "(Required) Name Prefix of Launch Configuration to create."
          AutoScalingGroupName:
            type: String
            description: "(Required) Name of AutoScaling Group which already exists."
        mainSteps:
        - name: createImage
          action: aws:createImage
          inputs:
            InstanceId: "{{ InstanceId }}"
            ImageName: "{{ InstanceId }}_{{ global:DATE }}"
            NoReboot: true
            ImageDescription: "{{ InstanceId }}_{{ global:DATE }}"
        - name: CreateLaunchConfiguration
          action: aws:executeAwsApi
          inputs:
            Service: autoscaling
            Api: CreateLaunchConfiguration
            InstanceId: "{{ InstanceId }}"
            ImageId: "{{ createImage.ImageId }}"
            LaunchConfigurationName: "{{ LaunchConfigPrefix }}_{{ global:DATE }}"
        - name: UpdateAutoScalingGroup
          action: aws:executeAwsApi
          inputs:
            Service: autoscaling
            Api: UpdateAutoScalingGroup
            AutoScalingGroupName: "{{ AutoScalingGroupName }}"
            LaunchConfigurationName: "{{ LaunchConfigPrefix }}_{{ global:DATE }}"
...
  • createImageステップで(インスタンス再起動無しで) [インスタンスID]_[YY-MM-DD] というAMIを作成し、
  • CreateLaunchConfigurationステップでそのAMIを元に [指定したプレフィックス]_[YY-MM-DD] という起動設定を作成し、
  • UpdateAutoScalingGroupステップで、その起動設定を参照するよう対象のAutoScalingグループを更新しています。

また、CreateLaunchConfigurationステップではInstanceIdというオプションを指定しています。
これは指定したインスタンスのプロパティを引き継いだ起動設定を作成してくれるオプションです。

EC2 インスタンスを使用した起動設定の作成

以上です。