0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS APIGateway からの SNS 連携

0
Posted at

AWS APIGateway からの SNS 連携

http post をAPIGatewayを経由して直接SNSへ連携します

以降の内容は以下の記事を前提とします

ロールのポリシー更新

ロールをAPIGatewayに適用できるようポリシー設定を更新します

import json
import boto3

ROLE_NAME = 'ResourcesEnvMainRole'
AWS_PROFILE = "resources_dev_admin"

session1 = boto3.Session()

sts_client = session1.client('sts')
AWS_ROOT_ACCOUNT_ID = sts_client.get_caller_identity()['Account']

session2 = boto3.Session(profile_name=AWS_PROFILE)
client = session2.client('iam')

trust_policy = {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": f"arn:aws:iam::{AWS_ROOT_ACCOUNT_ID}:root",
                "Service": [
                    "lambda.amazonaws.com",
                    "apigateway.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

response = client.update_assume_role_policy(
    RoleName=ROLE_NAME,
    PolicyDocument=json.dumps(trust_policy),
)

print(response)

ロールへの権限付与

APIGatewayとの連携に必要な権限をロールに付与します。

import boto3
from botocore.exceptions import ClientError

PROFILE_NAME = 'resources_dev_admin'
ROLE_NAME = 'ResourcesEnvMainRole'

POLICY_ARNS = [
    'arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator'
]

session = boto3.Session(profile_name=PROFILE_NAME)
iam_client = session.client('iam')

print(f"Starting to attach policies to role '{ROLE_NAME}' using profile '{PROFILE_NAME}'...")

for policy_arn in POLICY_ARNS:
    try:
        iam_client.attach_role_policy(
            RoleName=ROLE_NAME,
            PolicyArn=policy_arn
        )
        print(f"Success: Attached {policy_arn.split('/')[-1]}.")
    except ClientError as e:
        print(f"Error: Failed to attach {policy_arn.split('/')[-1]}.")
        print(e.response['Error']['Message'])

print("Process completed.")

ロールへのiam:PassRole権限の付与

API Gatewayがバックエンド(SNS)を呼び出すためiam:PassRole権限の付与を行う

import json
import boto3

PROFILE_NAME = 'resources_dev_admin'
ROLE_NAME = 'ResourcesEnvMainRole'
FUNCTION_NAME = 'func_test_01'

POLICY_NAME = 'ApiGatewayPassRolePolicy'

session = boto3.Session(profile_name=PROFILE_NAME)
iam_client = session.client('iam')

sts_client = session.client('sts')
aws_account_id = sts_client.get_caller_identity()['Account']
role_arn = f"arn:aws:iam::{aws_account_id}:role/{ROLE_NAME}"

policy_dict = {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": role_arn
        }
    ]
}

try:
    response = iam_client.put_role_policy(
        RoleName=ROLE_NAME,
        PolicyName=POLICY_NAME,
        PolicyDocument=json.dumps(policy_dict)
    )
    print(f"Successfully applied policy '{POLICY_NAME}' to role '{ROLE_NAME}'.")

except Exception as e:
    print(f"Error applying role policy: {e}")

API Gatewayの作成

import boto3

PROFILE_NAME = 'resources_dev'
ROLE_NAME = 'ResourcesEnvMainRole'
FUNCTION_NAME = 'func_test_01'
API_NAME = f"{FUNCTION_NAME}-api"
TOPIC_NAME = f"{FUNCTION_NAME}-sns"

HTTP_METHOD = "POST"
STAGE_NAME = "dev"

session = boto3.Session(profile_name=PROFILE_NAME)
apigateway_client = session.client('apigateway')

aws_region = session.region_name
sts_client = session.client('sts')
aws_account_id = sts_client.get_caller_identity()['Account']

try:
    create_response = apigateway_client.create_rest_api(
        name=API_NAME
    )
    rest_api_id = create_response['id']
    print(f"REST_API_ID: {rest_api_id}")

    resources_response = apigateway_client.get_resources(
        restApiId=rest_api_id
    )
    
    root_resource_id = None
    for item in resources_response.get('items', []):
        if item['path'] == '/':
            root_resource_id = item['id']
            break

    if not root_resource_id:
        raise ValueError("ルートリソース (/) が見つかりませんでした。")

    print(f"ROOT_RESOURCE_ID: {root_resource_id}")

    method_response = apigateway_client.put_method(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        authorizationType='NONE'
    )
    print(f"Successfully created {HTTP_METHOD} method.")

    credentials_arn = f"arn:aws:iam::{aws_account_id}:role/{ROLE_NAME}"
    sns_topic_arn = f"arn:aws:sns:{aws_region}:{aws_account_id}:{TOPIC_NAME}"
    integration_uri = f"arn:aws:apigateway:{aws_region}:sns:path//"

    request_parameters = {
        "integration.request.querystring.Action": "'Publish'",
        "integration.request.querystring.TopicArn": f"'{sns_topic_arn}'",
        "integration.request.querystring.Message": "method.request.body"
    }

    response = apigateway_client.put_integration(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        type='AWS',
        integrationHttpMethod=HTTP_METHOD,
        uri=integration_uri,
        credentials=credentials_arn,
        requestParameters=request_parameters,
        passthroughBehavior='WHEN_NO_TEMPLATES'
    )
    print(f"Successfully integrated {HTTP_METHOD} method with SNS topic: {TOPIC_NAME}")

    apigateway_client.put_method_response(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        statusCode='200'
    )

    response = apigateway_client.put_integration_response(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        statusCode='200',
        selectionPattern=""
    )

    response = apigateway_client.create_deployment(
        restApiId=rest_api_id,
        stageName=STAGE_NAME
    )
    
    invoke_url = f"https://{rest_api_id}.execute-api.{aws_region}.amazonaws.com/{STAGE_NAME}/"
    print(f"Successfully deployed API Gateway to stage: {STAGE_NAME}")
    print(f"Invoke URL: {invoke_url}")

except Exception as e:
    print(f"Error managing API Gateway: {e}")

POST 送信をテストする

import json
import urllib.request

INVOKE_URL = "https://amazonaws.com"

post_data = {
    "message": "Hello from API Gateway to SNS!",
    "data1":"data1",
    "data2":2
}
encoded_data = json.dumps(post_data).encode('utf-8')

req = urllib.request.Request(
    url=INVOKE_URL,
    data=encoded_data,
    headers={'Content-Type': 'application/json'},
    method='POST'
)

try:
    with urllib.request.urlopen(req) as res:
        status_code = res.getcode()
        response_body = res.read().decode('utf-8')
        print(f"Response Status: {status_code}")
        print(f"Response Body: {response_body}")
        
except urllib.error.HTTPError as e:
    print(f"HTTP Error: {e.code}")
    print(f"Error Body: {e.read().decode('utf-8')}")
except urllib.error.URLError as e:
    print(f"URL Error: {e.reason}")

ログの確認

from datetime import datetime, timedelta, timezone
import boto3

PROFILE_NAME = 'resources_dev'
ROLE_NAME = 'ResourcesEnvMainRole'
FUNCTION_NAME = 'func_test_01'

LOG_GROUP_NAME = f"/aws/lambda/{FUNCTION_NAME}"

session = boto3.Session(profile_name=PROFILE_NAME)
client = session.client("logs")

SINCE_MINUTES = 10
JST = timezone(timedelta(hours=9))  # JST (UTC+9)

start_time = int(
    (datetime.now(JST) - timedelta(minutes=SINCE_MINUTES)).timestamp() * 1000
)

paginator = client.get_paginator("filter_log_events")

for page in paginator.paginate(
    logGroupName=LOG_GROUP_NAME, startTime=start_time
):
    for event in page.get("events", []):
        dt = datetime.fromtimestamp(event["timestamp"] / 1000, JST)
        print(f"[{dt.isoformat()}] {event['message'].rstrip()}")

API Gateway 連携の一括作成(各種権限付与/API Gateway作成/呼び出し実行)

import json
import boto3
import urllib.request
from botocore.exceptions import ClientError

PROFILE_NAME_ADMIN = 'resources_dev_admin'

PROFILE_NAME = 'resources_dev_admin'
ROLE_NAME = 'ResourcesEnvMainRole'
FUNCTION_NAME = 'func_test_01'
API_NAME = f"{FUNCTION_NAME}-api"
TOPIC_NAME = f"{FUNCTION_NAME}-sns"


session1 = boto3.Session()

sts_client = session1.client('sts')
AWS_ROOT_ACCOUNT_ID = sts_client.get_caller_identity()['Account']

session2 = boto3.Session(profile_name=PROFILE_NAME_ADMIN)
iam_client = session2.client('iam')

trust_policy = {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": f"arn:aws:iam::{AWS_ROOT_ACCOUNT_ID}:root",
                "Service": [
                    "lambda.amazonaws.com",
                    "apigateway.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

response = iam_client.update_assume_role_policy(
    RoleName=ROLE_NAME,
    PolicyDocument=json.dumps(trust_policy),
)

print(response)


POLICY_ARNS = [
    'arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator'
]

for policy_arn in POLICY_ARNS:
    try:
        iam_client.attach_role_policy(
            RoleName=ROLE_NAME,
            PolicyArn=policy_arn
        )
        print(f"Success: Attached {policy_arn.split('/')[-1]}.")
    except ClientError as e:
        print(f"Error: Failed to attach {policy_arn.split('/')[-1]}.")
        print(e.response['Error']['Message'])

print("Process completed.")


POLICY_NAME = 'ApiGatewayPassRolePolicy'

sts_client2 = session2.client('sts')
aws_account_id = sts_client2.get_caller_identity()['Account']
role_arn = f"arn:aws:iam::{aws_account_id}:role/{ROLE_NAME}"

policy_dict = {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": role_arn
        }
    ]
}

try:
    response = iam_client.put_role_policy(
        RoleName=ROLE_NAME,
        PolicyName=POLICY_NAME,
        PolicyDocument=json.dumps(policy_dict)
    )
    print(f"Successfully applied policy '{POLICY_NAME}' to role '{ROLE_NAME}'.")

except Exception as e:
    print(f"Error applying role policy: {e}")


HTTP_METHOD = "POST"
STAGE_NAME = "v1"

session = boto3.Session(profile_name=PROFILE_NAME)
apigateway_client = session.client('apigateway')

aws_region = session.region_name
sts_client = session.client('sts')
aws_account_id = sts_client.get_caller_identity()['Account']

try:
    create_response = apigateway_client.create_rest_api(
        name=API_NAME
    )
    rest_api_id = create_response['id']
    print(f"REST_API_ID: {rest_api_id}")

    resources_response = apigateway_client.get_resources(
        restApiId=rest_api_id
    )
    
    root_resource_id = None
    for item in resources_response.get('items', []):
        if item['path'] == '/':
            root_resource_id = item['id']
            break

    if not root_resource_id:
        raise ValueError("ルートリソース (/) が見つかりませんでした。")

    print(f"ROOT_RESOURCE_ID: {root_resource_id}")

    method_response = apigateway_client.put_method(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        authorizationType='NONE'
    )
    print(f"Successfully created {HTTP_METHOD} method.")

    credentials_arn = f"arn:aws:iam::{aws_account_id}:role/{ROLE_NAME}"
    sns_topic_arn = f"arn:aws:sns:{aws_region}:{aws_account_id}:{TOPIC_NAME}"
    integration_uri = f"arn:aws:apigateway:{aws_region}:sns:path//"

    request_parameters = {
        "integration.request.querystring.Action": "'Publish'",
        "integration.request.querystring.TopicArn": f"'{sns_topic_arn}'",
        "integration.request.querystring.Message": "method.request.body"
    }

    response = apigateway_client.put_integration(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        type='AWS',
        integrationHttpMethod=HTTP_METHOD,
        uri=integration_uri,
        credentials=credentials_arn,
        requestParameters=request_parameters,
        passthroughBehavior='WHEN_NO_TEMPLATES'
    )
    print(f"Successfully integrated {HTTP_METHOD} method with SNS topic: {TOPIC_NAME}")

    apigateway_client.put_method_response(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        statusCode='200'
    )

    response = apigateway_client.put_integration_response(
        restApiId=rest_api_id,
        resourceId=root_resource_id,
        httpMethod=HTTP_METHOD,
        statusCode='200',
        selectionPattern=""
    )

    response = apigateway_client.create_deployment(
        restApiId=rest_api_id,
        stageName=STAGE_NAME
    )
    
    invoke_url = f"https://{rest_api_id}.execute-api.{aws_region}.amazonaws.com/{STAGE_NAME}/"
    print(f"Successfully deployed API Gateway to stage: {STAGE_NAME}")
    print(f"Invoke URL: {invoke_url}")

except Exception as e:
    print(f"Error managing API Gateway: {e}")

print('---')
print(f"Invoke URL: {invoke_url}")
print('---')
print('')

API Gatewayの削除(構成が不要になった場合のみ)

import boto3

PROFILE_NAME = 'resources_dev'
ROLE_NAME = 'ResourcesEnvMainRole'
FUNCTION_NAME = 'func_test_01'
API_NAME = f"{FUNCTION_NAME}-api"

session = boto3.Session(profile_name=PROFILE_NAME)
apigateway_client = session.client('apigateway')

try:
    rest_api_id = None
    paginator = apigateway_client.get_paginator('get_rest_apis')
    for page in paginator.paginate():
        for api in page.get('items', []):
            if api['name'] == API_NAME:
                rest_api_id = api['id']
                break
        if rest_api_id:
            break

    if rest_api_id:
        apigateway_client.delete_rest_api(restApiId=rest_api_id)
        print(f"Successfully deleted API Gateway: {API_NAME}")
    else:
        print(f"API Gateway '{API_NAME}' does not exist. Skipped.")

except Exception as e:
    print(f"Error deleting API Gateway: {e}")

関連記事

AWS APIGateway からの SNS 連携

更新日:2026年08月15日

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?