KCS Carrot logoKCS キャロットBlog

AWS 勉強会 – 実践編②

書いた人: @Naoya-Kishinami

はじめに

システム開発第一グループの kishinami です。

この記事は、ケーシーエスキャロット Advent Calendar 2025の22日目の記事です。

目的

前回は、AWS マネジメントコンソール上で各サービスを手動で構築しましたが、実際の現場では CloudFormation や CDK などの IaC(Infrastructure as Code) ツールを使ってリソースをコード管理し、デプロイすることが一般的です。

そこで今回は、Lambda 関数で意図的にエラーを発生させて CloudWatch Alarm で検知し、SNS トピックを通じて Slack へ通知する構成を CloudFormation テンプレートで構築します。

下図は今回の構成イメージです。

実現想定図

※ SNS トピックおよび Chatbot は、前回のハンズオンで作成したリソースを流用します。

構築準備

CloudFormation での構築に伴い、以下のファイル・ディレクトリ構成を用意します。

├─cfn.yml # CloudFormation テンプレート
├─lambda
│  └─error
│      └─lambda_function.py # Lambda Function 用コード
└─setup.sh # デプロイ用スクリプト

なお、ターミナルで作業を実施する際は、前回同様 Windows - WSL2 (Ubuntu 22.04) 環境で VSCode を使用しています。

CloudFormation テンプレート

CloudFormation でデプロイするための YAML ファイルを以下に示します。

cfn.yml

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Parameters:
  Name:
    Type: String

Globals:
  Function:
    Handler: lambda_function.lambda_handler
    MemorySize: 512
    Runtime: python3.11
    Timeout: 300

Resources:
  RoleError:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchLogsReadOnlyAccess
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                Resource:
                  - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/error-${Name}:*
      RoleName: !Sub ${Name}-error-role

  FuncError:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./lambda/error
      Role: !GetAtt RoleError.Arn
      FunctionName: !Sub error-${Name}
      Policies:
        - AWSLambdaExecute

  FuncErrorLogGroup:
    Type: AWS::Logs::LogGroup
    DeletionPolicy: Delete
    Properties:
      LogGroupName: !Sub /aws/lambda/error-${Name}
      RetentionInDays: 60

  FunctionParameterInitializeErr:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub error-${Name}
      AlarmActions:
        - !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:topic-test
      MetricName: Errors
      Namespace: AWS/Lambda
      Dimensions:
        - Name: FunctionName
          Value: !Sub error-${Name}
      Statistic: Maximum
      Period: 60
      EvaluationPeriods: 1
      DatapointsToAlarm: 1
      Threshold: 1
      ComparisonOperator: GreaterThanOrEqualToThreshold
      TreatMissingData: notBreaching

本テンプレートは Lambda 関数、実行用の IAM ロール、CloudWatch Logs、CloudWatch Alarm を作成します。 Lambda 関数でエラーが発生すると、 CloudWatch Alarm が検知し、指定した SNS トピックへ通知が送信されます。

Lambda 関数用コード

続いて、Lambda 関数で実行するコードを示します。 テスト用に ZeroDivisionError を発生させる内容となっています。

lambda_function.py

def divide_number(a:int, b:int):
    result = a / b
    print(f"result: {result}")

def lambda_handler(event, context):
    divide_number(10, 2)
    divide_number(0, 2)
    divide_number(10, 0) # ZeroDivisionError が発生

デプロイスクリプト

最後は CloudFormation テンプレートをパッケージングしてデプロイするためのスクリプトです。 実行前に <user name> <bucket name> を適宜置き換えてください。

setup.sh

export NAME="<user name>" # 実施ユーザー名を指定
export UPLOAD_BUCKET_NAME="<bucket name>" # アップロード先バケット名を指定
export REGION="ap-northeast-1"
export STACK_ERROR="error-${NAME}"
export TEMPLATE_FILE="cfn.yml"
export OUTPUT_FILE="cfn_out.yml"

aws cloudformation package \
  --region ${REGION} \
  --s3-bucket ${UPLOAD_BUCKET_NAME} \
  --s3-prefix serverless \
  --template-file ${TEMPLATE_FILE} \
  --output-template-file ${OUTPUT_FILE}

aws cloudformation deploy \
  --no-fail-on-empty-changeset \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --region ${REGION} \
  --stack-name ${STACK_ERROR} \
  --template-file ${OUTPUT_FILE} \
  --parameter-overrides \
    Name=${NAME}

以上で準備が整いました!

デプロイ確認

先に作成したファイル群を、指定のフォルダ配下に置いたうえで、Admin 権限を持つプロファイルを環境変数へ指定した上で、デプロイ用スクリプトを実施します。

$ export AWS_PROFILE=AdminRole
$ tree
.
├── cfn.yml
├── lambda
│   └── error
│       └── lambda_function.py
└── setup.sh
$ $ ./setup.sh 

Successfully packaged artifacts and wrote output template to file cfn_out.yml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /home/kishinami/blog/cfn_out.yml --stack-name <YOUR STACK NAME>

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - error-kishinami

以上を実施することで、下図のように各リソースがデプロイされました!

リソース群

動作検証

デプロイした lambda 関数を実行してみましょう。

  1. AWS コンソールで Lambda の画面を表示し、左側ペインから 関数 を選択。
  2. 先の CloudFormation で作成した Lambda 関数をクリックし、テストタブを選択して テスト をクリック。

以上を実施すると、下図のようにエラーが発生します。

Lambda エラー

さらに少し経つと Slack にも通知が届きました。

Slack エラー

最後に

今回は CloudFormation を使って、監視・通知の自動化構成をコードで管理する方法を紹介しました。

コンソール上で手動作成する場合、手順を別途管理する必要があり、設定変更時の周知も困難です。

一方、CloudFormation などで IaC として管理すると、コード自体を GitHub などのバージョン管理システムで管理でき、変更履歴もコミットとして残せます。

このように、手動構築と比べて IaC には再現性や変更管理の面で大きなメリットがあることを実感できると思います。