AWS

SSM AgentをインストールしたEC2にLambdaからコマンドを実行しWEBサーバを構築する

2024年5月30日

aws-systems-manager-icon

前にSession Managerを使用してEC2へ接続する方法を記事にした際に「Lambda関数からEC2にコマンドを実行できるのでは?」と思ったことを検証してみた。AWS Lambda関数を使用してEC2インスタンスにApacheをインストールし、WEBサーバを構築することができたため、気になる人は読んでほしい。ネタみたいな記事だが個人的に結構面白かった。

「サリー」です。AWSパートナー企業でエンジニアとして働いています。
お問い合わせ自己紹介

EC2環境

まずEC2インスタンスを以下設定にて起動する。

項目設定値
名前任意の名前
Amazon マシンイメージ (AMI)Amazon Linux 2023
インスタンスタイプt2.micro
キーペア任意のキーペア
VPC任意のVPC
サブネットパブリックサブネット
パブリックIPの自動割り当て有効化(後で無効化)
セキュリティグループタイプ:SSH、ソースタイプ:自分のIP
ストレージgp3、8GiB
IAM インスタンスプロファイル事前に作成したIAMロール(AmazonSSMManagedInstanceCoreポリシーをアタッチ)

事前準備

以前の記事で紹介したとおりSSMできる環境を準備しておく。

今回EC2で設定が必要なのはIAMロール(AmazonSSMManagedInstanceCoreポリシー)をアタッチするのみ。設定方法の詳細は上記の記事を参照してほしい。また、Amazon Linux 2023はデフォルトでSSM Agentがインストールされているためインストール不要となる。あと、パブリックサブネットに配置しているためVPCエンドポイントの設定も不要。

やること

Lambda関数でEC2にApacheをインストールし、WEBサーバを構築する。

現状確認

Session Managerにて接続しApacheがインストールされていないことを確認。

sh-5.2$ sudo systemctl status httpd
Unit httpd.service could not be found.

Lambda関数の設定

関数の作成

まずLambda関数を作成する。以下設定にて「関数の作成」をクリック

項目設定値
関数名任意の名前
ランタイムPython 3.12
アーキテクチャx86_64
アクセス権限設定不要
※後からポリシーを付与

IAMポリシーの作成

Lamdba実行ロールがコマンドを送ることができる権限を持つIAMポリシーを作成する。後にLambda実行ロールにアタッチする。

IAMポリシーは以下のとおり。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Action": [
				"ssm:SendCommand"
			],
			"Resource": [
				"arn:aws:ec2:*:<account_id>:instance/*",
				"arn:aws:ssm:*::document/AWS-RunShellScript"
			]
		},
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetCommandInvocation",
                "ssm:ListCommandInvocations"
            ],
            "Resource": "*"
        }

	]
}

Lambda実行ロールにポリシーをアタッチ

作成したLamdbaの「設定」タブから「アクセス権限」タブをクリック。実行ロールからロール名をクリックして対象のIAMロールを表示させる。

IAMロールが表示されたら、「許可を追加 > ポリシーをアタッチ 」から先ほど作成したIAMポリシーをアタッチする。

コード作成

今回はboto3のsend_commandを使用してコードを作成する。

send_command - Boto3 1.34.115 documentation

まずはコマンドが実行できているかテスト

以下コードを実行

import json
import boto3

def lambda_handler(event, context):

    # SSMクライアントの作成
    ssm_client = boto3.client('ssm')

    # コマンドの実行
    response = ssm_client.send_command(
        InstanceIds=[
            '<instance_Id>',
        ],
        DocumentName='AWS-RunShellScript',
        Parameters={
            'commands': [
                'echo "Hello AWS"',
            ]
        }
    )

    print(response["Command"]["Parameters"])

    return {
        'statusCode': 200,
        'body': json.dumps('Success!')
    }

実行結果

実行できていることを確認。

{
  "statusCode": 200,
  "body": "\\"Success!\\""
}

# レスポンス(コマンドのみ抜粋)
{'commands': ['echo "Hello AWS"']}

複数のコマンドを実行できるように修正

Apacheをインストールして有効にするには複数のコマンドを実行する必要があるためコマンドの数だけコマンド実行をループするよう修正。

import json
import boto3

def lambda_handler(event, context):

    # SSMクライアントの作成
    ssm_client = boto3.client('ssm')

    # コマンドを定義
    commands = [
        'echo "Hello AWS"',
        'echo "Hello Thiamai"'
    ]
    
    # コマンドの実行
    for command in commands:
        response = ssm_client.send_command(
            InstanceIds=[
                'i-05d78cf03b58a6dc1',
            ],
            DocumentName='AWS-RunShellScript',
            Parameters={
                'commands': [
                    command
                ]
            }
        )
        print(response["Command"]["Parameters"])

    return {
        'statusCode': 200,
        'body': json.dumps('Success!')
    }

実行結果

ループしていることが確認できる

{
  "statusCode": 200,
  "body": "\\"Success!\\""
}

# レスポンス
{'commands': ['echo "Hello AWS"']}
{'commands': ['echo "Hello Thiamai"']}

Apacheをインストールするようコードを修正

先ほど紹介したループするコードのコマンド部分のみ以下のとおり修正

    # コマンドを定義
    commands = [
        'sudo dnf update -y',
        'sudo dnf install httpd -y',
        'sudo systemctl start httpd',
        'sudo systemctl enabled httpd'
    ]
    

実行結果

全てのコマンドが実行されていることを確認できる。3秒くらいで終了した。

# レスポンス
{'commands': ['sudo dnf update -y']}
{'commands': ['sudo dnf install httpd -y']}
{'commands': ['sudo systemctl start httpd']}
{'commands': ['sudo systemctl enabled httpd']}

Apacheがインストールされていることを確認

sudo systemctl status httpdでApacheがインストールされており、稼働していることを確認できる。

sh-5.2$ sudo systemctl status httpd
● httpd.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; preset: disabled)
     Active: active (running) since Thu 2024-05-30 14:17:00 UTC; 4s ago
       Docs: man:httpd.service(8)
   Main PID: 27377 (httpd)
     Status: "Started, listening on: port 80"
      Tasks: 177 (limit: 1114)
     Memory: 12.9M
        CPU: 65ms
     CGroup: /system.slice/httpd.service
             ├─27377 /usr/sbin/httpd -DFOREGROUND
             ├─27379 /usr/sbin/httpd -DFOREGROUND
             ├─27380 /usr/sbin/httpd -DFOREGROUND
             ├─27381 /usr/sbin/httpd -DFOREGROUND
             └─27382 /usr/sbin/httpd -DFOREGROUND

May 30 14:17:00 ip-10-0-14-94.ec2.internal systemd[1]: Starting httpd.service - The Apache HTTP Server...
May 30 14:17:00 ip-10-0-14-94.ec2.internal systemd[1]: Started httpd.service - The Apache HTTP Server.
May 30 14:17:00 ip-10-0-14-94.ec2.internal httpd[27377]: Server configured, listening on: port 80

パブリックIPにアクセス

It works!」と表示されており問題なく稼働している。

まとめ

ネタではあるがLamdba関数経由でEC2にApacheをインストールし、WEBサーバを構築することができた。少し面白い使い方も思いついているためまた記事にする。

-AWS
-, , ,