{Lambda}AMI自動削除

 

https://oc-technote.com/aws/lambda-%E4%B8%8D%E8%A6%81%E3%81%AAami%E3%82%92%E5%AE%9A%E6%9C%9F%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B/
https://oc-technote.com/aws/lambda-%e4%b8%8d%e8%a6%81%e3%81%aaami%e3%81%a8%e3%82%b9%e3%83%8a%e3%83%83%e3%83%97%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88%e3%82%92%e3%81%be%e3%81%a8%e3%82%81%e3%81%a6%e5%89%8a%e9%99%a4%e3%81%99%e3%82%8b/
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2/client/describe_images.html


https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ami-policy.html

Data Lifecycle Managerでは削除のみはできない模様

 

 


-- 1. コマンド等のインストール

-- 1.1 aws cli version 2 インストール

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

aws --version

-- 1.2 jqインストール
sudo yum -y install jq


-- 2. ポリシーの作成 

vim policy01.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DeregisterImage",
                "ec2:DeleteSnapshot",
                "ec2:DescribeSnapshots",
                "ec2:DescribeImages"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

 


aws iam create-policy \
--policy-name policy01 \
--policy-document file://policy01.json


-- 3. IAMロール作成
vim role01.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}


aws iam create-role \
--role-name role01 \
--assume-role-policy-document file://role01.json


-- 4. ポリシーをロールにアタッチ
aws iam attach-role-policy \
--policy-arn arn:aws:iam::999999999999:policy/policy01 \
--role-name role01

 

 


-- 5. Lambda関数作成

vim test.py

import boto3
import logging
import time
import datetime as dt

from botocore.exceptions import ClientError


ec2 = boto3.client('ec2')

def get_old_ami():
    # Describe target AMIs.
    try:
        response = ec2.describe_images(
            Owners=['999999999999']
        )
        if response.get('ResponseMetadata', {}).get('HTTPStatusCode', -1) != 200:
            raise('# Cannot describe images!')
        images = response.get('Images')
        print('# All delete-sample images:', len(images))
    except ClientError as e:
        print(e.response['Error']['Code'])
        print(e.response['Error']['Message'])
        logging.error("# Describe images error: %s", e)
        return
    
    age_in_seconds = 600
    old_images = []
    epoc_now = int(time.time())
    print('# epoc now:', epoc_now)
    
    for image in images:
        creation_date = image.get('CreationDate')
        datetime = dt.datetime.strptime(creation_date, "%Y-%m-%dT%H:%M:%S.000Z")
        epoc_datetime = int(dt.datetime.timestamp(datetime))

        if epoc_now - epoc_datetime > age_in_seconds:
            old_images.append(image.get('ImageId'))

    return old_images

 

def unregister_ami(delete_ami_id):
    # Unregister AMI.
    try:
        response = ec2.deregister_image(ImageId=str(delete_ami_id))
    except ClientError as e:
        if e.response['Error']['Code'] == 'InvalidAMIID.Unavailable':
            print('InvalidAMIID.Unavailable: ' + delete_ami_id)
            return True
        elif e.response['Error']['Code'] == 'InvalidAMIID.NotFound':
            print('InvalidAMIID.NotFound: ' + delete_ami_id)
            return True
        else:
            print(e.response['Error']['Code'])
            print(e.response['Error']['Message'])
            raise e
    print('Unregister AMI: ' + delete_ami_id)
    return response.get('ResponseMetadata', {}).get('HTTPStatusCode', -1) == 200


def delete_related_snapshots(delete_ami_id):
    # Get target snapshots.
    try:
        response = ec2.describe_snapshots(
            Filters=[
                {
                    'Name': 'description',
                    'Values': ['Created by CreateImage(*) for ' + delete_ami_id + '*',]
                }
            ]
        )
    except ClientError as e:
        print(e.response['Error']['Code'])
        print(e.response['Error']['Message'])
        raise e

    # Delete target snapshots.
    for snapshot in response['Snapshots']:
        try:
            response = ec2.delete_snapshot(SnapshotId=snapshot['SnapshotId'])
            if response.get('ResponseMetadata', {}).get('HTTPStatusCode', -1) != 200:
                raise('# Cannot delete snapshot: %s', snapshot)
            print('Delete Snapshot: ' + snapshot['SnapshotId'])
        except ClientError as e:
            print(e.response['Error']['Code'])
            print(e.response['Error']['Message'])
            logging.error("# Delete snapshot error: %s", e)
        
    return


def lambda_handler(event, context):
    # Get the AMI to be deleted.
    delete_amis = get_old_ami()
    print('# Target AMIs:', len(delete_amis))
    print(delete_amis)

    for delete_ami in delete_amis:
        # Delete AMI.
        unregister_ami(delete_ami)
    
        # Delete Snapshots.
        delete_related_snapshots(delete_ami)
    
    return

 

 

 

chmod 755 test.py
zip test.zip test.py

aws lambda create-function \
--function-name func01 \
--zip-file fileb://test.zip \
--handler test.lambda_handler \
--runtime python3.11 \
--role arn:aws:iam::999999999999:role/role01 \
--timeout 900

 

aws lambda list-functions | grep func01

aws lambda get-function --function-name func01

 

 


-- 6. ルールの作成

aws events put-rule \
--name rule01 \
--schedule-expression "rate(1 minute)" \
--state ENABLED \
--description rule01

 

aws events list-rules

aws events describe-rule --name rule01

-- 7. ターゲットの作成

aws events put-targets \
--rule rule01 \
--targets "Id"="1","Arn"="arn:aws:lambda:ap-northeast-1:999999999999:function:func01"

aws events list-targets-by-rule \
--rule rule01

 

-- 8. Lambda関数に権限を追加する

aws lambda add-permission \
--function-name func01 \
--statement-id events \
--action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:ap-northeast-1:999999999999:rule/rule01

aws lambda get-policy \
--function-name func01 | jq -r .Policy  | jq .

 

-- 9. 動作確認


aws ec2 create-image                  \
--description "ami001"                \
--instance-id i-11111111111111111     \
--name "ami20230808"

 

aws ec2 describe-images  \
--owners self

aws ec2 describe-snapshots   \
--owner-ids self

 


-- 10. クリーンアップ


-- ターゲットの削除
aws events list-targets-by-rule \
--rule rule01

aws events remove-targets \
--rule rule01 \
--ids 1


-- ルールの削除

aws events list-rules

aws events delete-rule \
--name rule01

 

-- Lambda関数の削除

aws lambda get-function --function-name func01
aws lambda delete-function --function-name func01


-- ロールの削除
aws iam list-roles | grep role01

aws iam detach-role-policy \
--role-name role01 \
--policy-arn arn:aws:iam::999999999999:policy/policy01

aws iam delete-role --role-name role01

 


-- ポリシーの削除
aws iam list-policies | grep policy01

aws iam delete-policy \
--policy-arn arn:aws:iam::999999999999:policy/policy01