SOA

古いAMIを定期的に削除する – SSM Automationランブックバージョン

古いAMIを定期的に削除する – SSM Automationランブックバージョン

以下のページで、DLMを使用して、定期的に古いAMIを削除する方法をご紹介しました。

またStep Functionsを使用して、定期的に古いAMIを削除する方法もご紹介しました。

今回は上記と同じ内容を、SSM Automationランブックを使用して実行します。

構築する環境

Diagram of periodically delete old AMIs - SSM Automation runbook version

SSM Automationランブックを作成します。
ランブックの実行内容ですが、以下を実行します。

  • 作成されてから1時間以上経過したAMIを検索して、タグを設定する。
  • タグが付与されているAMIに対して、AWSが所有するランブックAWS-DeleteImageを実行する。

EventBridgeルールを作成します。
定期的(1時間に1回)、ランブックを実行するように設定します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

awstut-soa/02/005 at main · awstut-an-r/awstut-soa
Contribute to awstut-an-r/awstut-soa development by creating an account on GitHub.

テンプレートファイルのポイント解説

SSM Automationランブック

Resources:
  DeleteImageRunbook:
    Type: AWS::SSM::Document
    Properties: 
      Content:
        assumeRole: "{{AutomationAssumeRole}}"
        description: Create an AMI for an EC2 instance with a specific tag.
        schemaVersion: "0.3"
        parameters:
          AutomationAssumeRole:
            type: String
            description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
            default: !GetAtt DeleteImageRunbookRole.Arn
          AccountId:
            type: String
            description: (Required) The ID of Account.
            default: !Ref AWS::AccountId
          Region:
            type: String
            description: (Required) The Region.
            default: !Ref AWS::Region
          TagKey:
            type: String
            description: (Required) The tag key of Image.
            default: !Ref TagKey
          TagValue:
            type: String
            description: (Required) The tag value of Image.
            default: !Ref TagValue
          ValidHours:
            type: Integer
            description: (Required) The expired hours.
            default: !Ref ValidHours
        mainSteps:
          - name: setTagToExpiredImages
            action: aws:executeScript
            inputs:
              Runtime: python3.8
              Handler: handler
              InputPayload:
                AccountId: "{{AccountId}}"
                Region: "{{Region}}"
                TagKey: "{{TagKey}}"
                TagValue: "{{TagValue}}"
                ValidHours: "{{ValidHours}}"
              Script: |-
                import boto3
                import datetime
                import json
                
                def handler(events, context):
                  account_id = events['AccountId']
                  region = events['Region']
                  tag_key = events['TagKey']
                  tag_value = events['TagValue']
                  valid_hours = int(events['ValidHours'])
                  
                  client = boto3.client('ec2', region_name=region)
                  
                  now = datetime.datetime.now(datetime.timezone.utc)
                
                  response = client.describe_images(
                    Owners=[account_id])
                  create_tags_results = []
                  for image in response['Images']:
                    creation_date_str = image['CreationDate']
                    creation_date_dt = datetime.datetime.fromisoformat(creation_date_str.replace('Z', '+00:00'))
                    
                    diff = now - creation_date_dt
                    diff_hour = diff.seconds / (60 * 60)
                    
                    if diff_hour > valid_hours:
                      result = client.create_tags(
                        Resources=[
                          image['ImageId']],
                        Tags=[
                          {
                            'Key': tag_key,
                            'Value': tag_value
                          }],
                        )
                      create_tags_results.append(result)
                  
                  return {'createTagResults': json.dumps(create_tags_results)}
            outputs:
              - Name: createTagResults
                Selector: $.Payload.creataTagResults
                Type: String
          - name: deleteTagedImages
            action: aws:executeAutomation
            maxAttempts: 1
            timeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
            onFailure: Abort
            inputs:
              DocumentName: AWS-DeleteImage
              TargetParameterName: ImageId
              Targets:
                - Key: "tag:{{TagKey}}"
                  Values:
                    - "{{TagValue}}"
            outputs:
              - Name: deleteImage
                Selector: "$"
                Type: String
      DocumentFormat: YAML
      DocumentType: Automation
      Name: !Sub "${Prefix}-DeleteImageRunbook"
Code language: YAML (yaml)

古いAMIを削除するために、SSM Automationランブックを作成します。

ランブックを自作する方法については、以下のページをご確認ください。

ポイントはステップに関する設定です。

1つ目のステップ

このステップでは、古いAMIにタグを設定します。
actionsプロパティに「aws:executeScript」と設定することで、任意のコードを実行することができます。

指定されたランタイムとハンドラを使用して、提供された Python または PowerShell スクリプトを実行します。

aws:executeScript – スクリプトを実行する

実行するコードは、Step Functionsバージョンと概ね同様です。

変更点としては、作成されてから1時間以上経過したAMIを発見した場合は、タグを設定するという点です。
以下のタグを設定します。

  • タグ名:DeleteImage
  • タグの値:true

2つ目のステップ

このステップでは、AMIを削除します。

actionsプロパティに「aws:executeAutomation」と設定することで、ランブック内から別のランブックを実行することができます。
inputs内のDocumentNameプロパティに「AWS-DeleteImage」を指定することで、AMIを削除します。

Targetsプロパティに先述のタグ情報を、TargetParameterNameに「ImageId」を指定することで、タグが付与されているAMIを対象として、AWS-DeleteImageが実行されます。

ランブック用IAMロール

Resources:
  DeleteImageRunbookRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - ssm.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole
      Policies:
        - PolicyName: SetTagAndDeleteImagePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - tag:GetResources
                Resource:
                  - "*"
Code language: YAML (yaml)

AWS管理ポリシーAmazonSSMAutomationRoleに加えて、インラインポリシーでtag:GetResourcesを許可するように設定します。

EventBridgeルール

Resources:
  EventsRule:
    Type: AWS::Events::Rule
    Properties: 
      Name: !Sub "${Prefix}-EventsRule"
      ScheduleExpression: rate(1 hour)
      State: ENABLED
      Targets: 
        - Arn: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${Runbook}:$DEFAULT"
          Id: !Ref Runbook
          RoleArn: !GetAtt EventsRuleRole.Arn
Code language: YAML (yaml)

ランブックを定期的に実行するために、EventBridgeルールを作成します。

ScheduleExpressionプロパティに「rate(1 hour)」と指定することで、1時間ごとにランブックを実行できます。

Targetsプロパティに、先述のランブックのARN等を指定します。

Resources:
  EventsRuleRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: events.amazonaws.com
      Policies:
        - PolicyName: !Sub "${Prefix}-StartAutomationExecutionPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: ssm:StartAutomationExecution
                Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${Runbook}:$DEFAULT"
              - Effect: Allow
                Action: iam:PassRole
                Resource: !Ref RunbookRoleArn
Code language: YAML (yaml)

インラインポリシーで2つのアクションを許可します。

1つ目はssm:StartAutomationExecutionです。
文字通り、ランブックの実行を許可する内容です。

2つ目はiam:PassRoleです。
先ほど確認したSSM Automationランブック用のIAMロールを、ランブックに渡すためです。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • SSM Automationランブック:soa-02-005-DeleteImageRunbook
  • EventBridgeルール:soa-02-005-EventsRule

AWSマネージメントコンソールから各種リソースを確認します。

SSM Automaitonランブックを確認します。

Detail of SSM 1.
Detail of SSM 2.
Detail of SSM 3.

正常にランブックが作成されています。

EventBridgeルールを確認します。

Detail of EventBridge 1.
Detail of EventBridge 2.

1時間ごとにランブックを実行する内容です。

動作確認

AMI作成

任意のEC2インスタンスからAMIを作成します。

Detail of EC2 1.
Detail of EC2 2.

AMIとともにスナップショットも作成されました。

ランブック実行1回目

1回目のランブック実行が自動的に始まります。

Detail of SSM 4.

実行の詳細を確認します。

Detail of SSM 5.

ランブック内の2ステップが正常に終了したことがわかります。

両ステップの状況を確認します。

1つ目のステップです。

Detail of SSM 6.

Outputsを見ると、スクリプトの実行結果が空です。
つまり作成から1時間以上経過しているAMIが存在しなかったため、タグ付け等の処理を実行せずに終了しました。

2つ目のステップです。

Detail of SSM 7.

タグ名が「DeleteImage」で、値が「true」のAMIに対して、SSM AutomationランブックAWS-DeleteImageを実行するステップです。

以下がこのランブックの実行結果です。

Detail of SSM 8.

該当のタグが付与されているAMIがなかったため、何も実行されずに終了しました。

このように削除条件を満たすAMIがない場合は、何も実行せずに終了します。

ランブック実行2回目

1時間後、2回目のランブック実行が自動的に始まります。

Detail of SSM 9.

実行の詳細を確認します。

Detail of SSM 10.

今回もランブック内の2ステップが正常に終了したことがわかります。

両ステップの状況を確認します。

1つ目のステップです。

Detail of SSM 11.

Outputsを見ると、スクリプトの実行結果が1つありました。
つまり作成から1時間以上経過したため、先ほどのAMIにタグが設定されたということです。

2つ目のステップです。

Detail of SSM 12.

タグ名が「DeleteImage」で、値が「true」のAMIに対して、SSM AutomationランブックAWS-DeleteImageを実行するステップです。

以下がこのランブックの実行結果です。

Detail of SSM 13.

Step nameに先ほど生成したAMIのIDが確認できます。

ステップの実行内容を確認します。

Detail of SSM 14.

ランブックAWS-DeleteImageの実行が開始されました。

このランブックで実行された内容を確認します。

Detail of SSM 15.

タグ付けしたAMIの削除が正常に実行されました。

最後にAMIやスナップショットを確認します。

Detail of EC2 3.
Detail of EC2 4.

確かにAMI等が削除されました。

まとめ

SSM Automationランブックを使用して、定期的に古いAMIを削除する方法もご紹介しました。

タイトルとURLをコピーしました