前回、SSM Automationを通じてインスタンスの停止・起動という簡単な操作について触れました。今回は起動設定の操作、起動テンプレートの操作をAutomationで実行してみたいと思います。今回はもう少し込み入った作業になってくることもあり、より自動化のメリットが伝わりやすいのではと思っています。

本題に入る前に、改めて前回の内容を整理したいと思います。AWS CLIの操作ではアウトプットがjson形式であるためによりAPIのやりとりが明確に把握できます。AWS CLI Command Referenceを参照しながら行いたい操作のコマンドを用意し、そのAWS CLIの挙動を手がかりにAutomationの各ステップを作成します。最後にAPI Referenceを参照しながら(例:DescribeInstanceStatus)調整して完成させていくというものでした。

今回する操作は次の通りです。AMIの再起動なしでの取得、起動設定作成、AutoScalingの起動設定差し替え、AutoScalingの台数増加という内容です。起動設定について触れた後、起動テンプレートにも簡単に触れたいと思います。

起動設定によるAutoScalingの更新

まずは前回同様、AWS CLIで実行した場合を想定したコマンドと、Automationのドキュメントの全体像を提示します。その後、ここのステップとCLIコマンドを比較していきます。

↓CLIコマンド

aws ec2 describe-instances --filter "Name=tag:Name,Values=test*" //頭にtestとついたインスタンスが対象になります
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name test-as --query "AutoScalingGroups[*].Instances[*]" --output table
aws ec2 create-image --instance-id i-xxxxxxxxxx --name asano-test-image --description asano-test-for-technical-investigation --no-reboot
aws autoscaling create-launch-configuration --launch-configuration-name asano-test-image \
--image-id <image-id> \
--instance-type <instance-typexxxx> --key-name asano-key \
--security-groups {sg-xxxx,sg-xxxx} \
--block-device-mappings '[{"DeviceName":"/dev/xvda","Ebs": {"SnapshotId":"<SnapshotId>","VolumeSize":20,"VolumeType":"gp2","DeleteOnTermination":true,"Encrypted": false}}]' \
--instance-monitoring Enabled=false --iam-instance-profile arn:xxxxxxxxxxxxxxx --no-ebs-optimized --associate-public-ip-address
aws autoscaling update-auto-scaling-group --auto-scaling-group-name test-as --launch-configuration-name asano-test-image --desired-capacity 2 --min-size 2 --max-size 2

↓Automationドキュメント

description: Update AutoScaling
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: ''
  ImageName:
    type: String
    description: ImageName
    default: asano-test-image
mainSteps:
  - name: createImage
    action: 'aws:createImage'
    maxAttempts: 3
    onFailure: Abort
    inputs:
      InstanceId: i-xxxxxxxxxxxxxxx
      ImageName: '{{ ImageName }}'
      NoReboot: true
      ImageDescription: '{{ ImageName }}'
    outputs:
      - Name: NewImageId
        Selector: '$.Images[0].ImageId'
        Type: String
  - name: createTags
    action: 'aws:createTags'
    maxAttempts: 1
    onFailure: Abort
    inputs:
      ResourceType: EC2
      ResourceIds:
        - '{{createImage.ImageId}}'
      Tags:
        - Key: Name
          Value: '{{ ImageName }}'
  - name: getImageId
    action: 'aws:executeAwsApi'
    inputs:
      Service: ec2
      Api: DescribeImages
      Filters:
        - Name: name
          Values:
            - '{{ ImageName }}'
    outputs:
      - Name: EBSid
        Selector: '$.Images[0].BlockDeviceMappings[0].Ebs.SnapshotId'
        Type: String
  - name: CreateLaunchConfiguration
    action: 'aws:executeAwsApi'
    inputs:
      Service: autoscaling
      Api: CreateLaunchConfiguration
      LaunchConfigurationName: '{{ ImageName }}'
      ImageId: '{{createMyImage.ImageId}}'
      InstanceType: t2.micro
      KeyName: xxxxxxxxxxxxxxxxxxx
      IamInstanceProfile: 'arn:xxxxxxxxxxxxxxxxxx'
      InstanceMonitoring:
        Enabled: false
      EbsOptimized: false
      AssociatePublicIpAddress: true
      SecurityGroups:
        - sg-xxxxxxxxxxxxxx
        - sg-xxxxxxxxxxxxxx
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            SnapshotId: '{{getImageId.EBSid}}'
            VolumeSize: 8
            VolumeType: gp2
            DeleteOnTermination: true
            Encrypted: false
  - name: UpdateAutoScaling
    action: 'aws:executeAwsApi'
    inputs:
      Service: autoscaling
      Api: UpdateAutoScalingGroup
      AutoScalingGroupName: test-as
      LaunchConfigurationName: '{{ ImageName }}'
      DesiredCapacity: 2
      MaxSize: 2
      MinSize: 2

次に、ドキュメントのそれぞれのステップとCLIコマンドを対比していきます。(説明の都合上、ステップ順ではありません。)
まずは一番最後のステップ、AutoScalingの起動設定を新たに作成したものに指定し、台数を2台に増やすステップです。

aws autoscaling update-auto-scaling-group --auto-scaling-group-name test-as \
    --launch-configuration-name asano-test-image \
    --desired-capacity 2 --min-size 2 --max-size 2
- name: UpdateAutoScaling2
    action: 'aws:executeAwsApi'
    inputs:
      Service: autoscaling
      Api: UpdateAutoScalingGroup
      AutoScalingGroupName: test-as
      LaunchConfigurationName: '{{ ImageName }}'
      DesiredCapacity: 2
      MaxSize: 2
      MinSize: 2

基本的にはAWS CLIの表記とAutomationのドキュメントはほぼ同じです。しかし、update-auto-scaling-group→UpdateAutoScalingGroupなど、微妙な違いがあります。そこはCLIを参考にしつつ大まかなコマンドを作成し、APIリファレンスを参考に細かい調整が必要です。

次にEBSのID取得のステップです。

  - name: getImageId
    action: 'aws:executeAwsApi'
    inputs:
      Service: ec2
      Api: DescribeImages
      Filters:
        - Name: name
          Values:
            - '{{ ImageName }}'
    outputs:
      - Name: EBSid
        Selector: '$.Images[0].BlockDeviceMappings[0].Ebs.SnapshotId'
        Type: String

コンソールのReadonlyで確認しながらCLIで作業をしていると、コンソール上で生成したコマンドの結果を確認しながら作業することがあると思います。Automationに記述しようとしたとき、このコマンドなどが抜け落ちることがあるので、検証しながらそれらを補完して記述することが必要です。
また、このコマンドでoutputsの変数を用意し、そこに入れた情報を起動設定作成のステップに渡しています。このoutputsはどのように指定するとよいかですが、コマンドを打ちつつ、jsonファイルを見てほしいデータがjson構造の中のどこにあるかを指定するように書いていきます。

$ aws ec2 describe-images --filters "Name=name,Values=asano-test"
{
    "Images": [
        {
            "Architecture": "x86_64",
            "CreationDate": "2023-xx-xxxxxxxxx",
            "ImageId": "ami-xxxxxxxxxxxxxxxx",
            "ImageLocation": "xxxxxxxxxxxx/asano-test",
            "ImageType": "machine",
            "Public": false,
            "OwnerId": "xxxxxxxxxxxxx",
            "PlatformDetails": "Linux/UNIX",
            "UsageOperation": "RunInstances",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "SnapshotId": "snap-xxxxxxxxxxxx",
                        "VolumeSize": 8,
                        "VolumeType": "gp2",
                        "Encrypted": false
                    }
                }
            ],
            "EnaSupport": true,
            "Hypervisor": "xen",
            "Name": "asano-test2",
            "RootDeviceName": "/dev/xvda",
            "RootDeviceType": "ebs",
            "SriovNetSupport": "simple",
            "Tags": [
                {
                    "Key": "Name",
                    "Value": "asano-test"
                }
            ],
            "VirtualizationType": "hvm"
        }
    ]
}
$ 

Images→BlockDeviceMappings→Ebs→SnapshotIdという構造になっており、Automationのステップではその構造に合わせてSelector: '$.Images[0].BlockDeviceMappings[0].Ebs.SnapshotId'と記述しています。

次に起動設定作成です。

  - name: CreateLaunchConfiguration
    action: 'aws:executeAwsApi'
    inputs:
      Service: autoscaling
      Api: CreateLaunchConfiguration
      LaunchConfigurationName: '{{ ImageName }}'
      ImageId: '{{createMyImage.ImageId}}'
      InstanceType: t2.micro
      KeyName: test-key
      IamInstanceProfile: 'arn:xxxxxxxxxxxxxxxxxx'
      InstanceMonitoring:
        Enabled: false
      EbsOptimized: false
      AssociatePublicIpAddress: true
      SecurityGroups:
        - sg-xxxxxxxxxxxxxx
        - sg-xxxxxxxxxxxxxx
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            SnapshotId: '{{getImageId.EBSid}}'
            VolumeSize: 8
            VolumeType: gp2
            DeleteOnTermination: true
            Encrypted: false

指定する要素が多いですが、基本的にはこれまで出てきた要素の組み合わせで記述することができると思います。コンソール上では起動設定のコピーはありますが、CLIではそれは用意されていません。ですので都度起動設定の作成をする必要があります。正しくコピーしたようにできているか不安な場合はAWS CLIでdescribeのコマンドを打って結果をjsonファイルで保存し、比較するなどの手段もあります。

起動テンプレートの場合

  - name: CreateLaunchTemplateVersion
    action: 'aws:executeAwsApi'
    inputs:
      Service: ec2
      Api: CreateLaunchTemplateVersion
      LaunchTemplateId: lt-xxxxxxxxxxxxx
      SourceVersion: '1'
      LaunchTemplateData:
        ImageId: '{{createMyImage.ImageId}}'
  - name: UpdateLaunchTemplate
    action: 'aws:executeAwsApi'
    inputs:
      Service: autoscaling
      Api: UpdateAutoScalingGroup
      AutoScalingGroupName: xxxxxxxxxxxx
      LaunchTemplate:
        LaunchTemplateId: lt-xxxxxxxxxxxxxxxxxx
        Version: $Latest

起動テンプレートもこれまで出てきた要素の組み合わせでしかありません。

このように、AWS CLIのコマンドを用意しつつ、Automationのドキュメントを作成するという方法をご紹介しました。

最後になりますが、起動設定は徐々に使われなくなっていく傾向になると思っています。(参照)そのため、私自身検索してもなかなか起動設定のコマンドが見つからず苦戦したので、あえてこれを選択しました。また、起動設定の作成の自動化には変数やJSONの構造など、様々な要素が必要になってきます。Automationの理解を深めるという目的でも起動設定はいい題材となると思い、これを書くことにしました。