AWS CodeBuildのビルド終了時に通知することが可能なのですが、Amazon SNSでのメール通知を設定するだけだと無機質なJSONが送られてくるためAmazon Bedrockを使用してビルド成功時に褒めてくれるようにしてみました。
通知ルールの作成
通知に使用するAWSリソース
Amazon SNSでAWS CodeBuildのビルド成功通知をAWS Lambdaに行い、Lambda関数からAmazon Bedrockのinvoke_modelを実行してレスポンスのテキストをAmazon SNS経由でメール通知するシンプルな構成としています。
簡易的にはなりますが構成は以下の通りとなります。
作成したコード
Lambda関数のコードはPythonで作成しています。
今回はAnthropic Claude 3 Sonnetを使用してメッセージ APIを実行するようにしています。
AnthropicClaudeメッセージ API
eventからbuild-statusを取得してFAILEDなら慰めてもらいSUCCEEDEDなら褒めてもらうようにしています。
eventに送られてくるJSONは以下のドキュメントに記載されている通りとなります。
ビルド通知の入力形式に関するリファレンス
invoke_model APIを実行してレスポンスからtextを取得しています。
最後にpublish APIを実行してAmazon SNS経由でメール通知を行います。
import json
import os
import boto3
bedrock_runtime = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')
sns_arn = os.environ['SNS_ARN']
sns = boto3.client('sns')
def lambda_handler(event, context):
Message = json.loads(event['Records'][0]['Sns']['Message'])
build_status = Message['detail']['build-status']
if 'FAILED' == build_status:
body=json.dumps(
{
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 5000,
"system": "日本語で回答してください",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "アプリケーションのビルドに失敗したので慰めてください"
}
]
}
]
}
)
response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0")
response_text = json.loads(response.get('body').read())['content'][0]['text']
print(response_text)
elif 'SUCCEEDED' == build_status:
body=json.dumps(
{
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 5000,
"system": "日本語で回答してください",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "アプリケーションのビルドに成功したので褒めてください"
}
]
}
]
}
)
response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0")
response_text = json.loads(response.get('body').read())['content'][0]['text']
print(response_text)
sns.publish(
TopicArn=sns_arn,
Message=response_text,
Subject='Build result',
)
AWSリソースの作成
AWSリソースを作成する前 (作成した後でも問題ありません) にAmazon Bedrockのモデルアクセスを有効化する必要があります。
今回はAnthropic Claude 3 Sonnetを使用するため以下のドキュメントの手順でus-east-1で有効化を行ってください。(2024年5月6日時点では東京リージョンでAnthropic Claude 3 Sonnetが使用できなかったためus-east-1のものを使用しています)
モデルアクセス
作成したCloudFormationテンプレート
今回作成したCloudFormationテンプレートは以下になります。
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09"
Description: Test Stack
Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Parameters for SNS
Parameters:
- Email
- Label:
default: Parameters for CodeCommit
Parameters:
- RepositoryDescription
- RepositoryName
- Label:
default: Parameters for CodeBuild
Parameters:
- Description
- Name
- Label:
default: Parameters for CodePipeline
Parameters:
- CodePipelineName
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Email:
Type: String
RepositoryDescription:
MaxLength: 4000
Type: String
RepositoryName:
MaxLength: 100
Type: String
Description:
MaxLength: 255
Type: String
Name:
MaxLength: 255
Type: String
CodePipelineName:
MaxLength: 100
Type: String
Resources:
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
S3:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-artifact
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-${AWS::AccountId}-artifact
# ------------------------------------------------------------#
# SNS
# ------------------------------------------------------------#
SNSEmailTopic:
Type: AWS::SNS::Topic
Properties:
FifoTopic: false
TopicName: email-sns-topic
EmailSubscription:
Type: AWS::SNS::Subscription
Properties:
Endpoint: !Ref Email
Protocol: email
TopicArn: !Ref SNSEmailTopic
SNSCodeBuildTopic:
Type: AWS::SNS::Topic
Properties:
FifoTopic: false
TopicName: codebuild-sns-topic
CodeBuildSubscription:
Type: AWS::SNS::Subscription
Properties:
Endpoint: !GetAtt Lambda.Arn
Protocol: lambda
TopicArn: !Ref SNSCodeBuildTopic
SNSCodeBuildTopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
PolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: codestar-notifications.amazonaws.com
Action: sns:Publish
Resource: "*"
Topics:
- !Ref SNSCodeBuildTopic
# ------------------------------------------------------------#
# Lambda
# ------------------------------------------------------------#
LambdaIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: lambda-iam-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- bedrock:InvokeModel
- sns:Publish
Resource: "*"
Lambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import json
import os
import boto3
bedrock_runtime = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')
sns_arn = os.environ['SNS_ARN']
sns = boto3.client('sns')
def lambda_handler(event, context):
Message = json.loads(event['Records'][0]['Sns']['Message'])
build_status = Message['detail']['build-status']
if 'FAILED' == build_status:
body=json.dumps(
{
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 5000,
"system": "日本語で回答してください",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "アプリケーションのビルドに失敗したので慰めてください"
}
]
}
]
}
)
response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0")
response_text = json.loads(response.get('body').read())['content'][0]['text']
print(response_text)
elif 'SUCCEEDED' == build_status:
body=json.dumps(
{
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 5000,
"system": "日本語で回答してください",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "アプリケーションのビルドに成功したので褒めてください"
}
]
}
]
}
)
response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0")
response_text = json.loads(response.get('body').read())['content'][0]['text']
print(response_text)
sns.publish(
TopicArn=sns_arn,
Message=response_text,
Subject='Build result',
)
Environment:
Variables:
SNS_ARN: !Ref SNSEmailTopic
FunctionName: bedrock-lambda
Handler: index.lambda_handler
Role: !GetAtt LambdaIAMRole.Arn
Runtime: python3.12
Timeout: 30
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt Lambda.Arn
Principal: sns.amazonaws.com
SourceArn: !Ref SNSCodeBuildTopic
# ------------------------------------------------------------#
# CodeCommit
# ------------------------------------------------------------#
CodeCommit:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryDescription: !Ref RepositoryDescription
RepositoryName: !Ref RepositoryName
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------#
CodeBuildIAMPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- 's3:PutObject'
- 's3:GetObject'
Resource:
- !Join
- ''
- - !GetAtt S3.Arn
- '/*'
- Effect: Allow
Action:
- 'codecommit:GitPull'
Resource: "*"
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: "*"
- Effect: Allow
Action:
- 'sns:Publish'
Resource: "*"
ManagedPolicyName: iam-policy-codebuild
CodeBuildIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- !Ref CodeBuildIAMPolicy
RoleName: iam-role-codebuild
Tags:
- Key: Name
Value: iam-role-codebuild
CodePipelineIAMPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "codecommit:CancelUploadArchive"
- "codecommit:GetBranch"
- "codecommit:GetCommit"
- "codecommit:GetRepository"
- "codecommit:GetUploadArchiveStatus"
- "codecommit:UploadArchive"
Resource:
- "*"
- Effect: Allow
Action:
- "codebuild:BatchGetBuilds"
- "codebuild:StartBuild"
Resource:
- "*"
- Effect: Allow
Action:
- "s3:GetObject"
- "s3:PutObject"
- "s3:ListBucket"
Resource:
- !Join
- ''
- - !GetAtt S3.Arn
- '/*'
- !GetAtt S3.Arn
- Effect: Allow
Action:
- "sns:Publish"
Resource:
- "*"
ManagedPolicyName: iam-policy-codepipeline
CodePipelineIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- !Ref CodePipelineIAMPolicy
RoleName: iam-role-codepipeline
Tags:
- Key: Name
Value: iam-role-codepipeline
# ------------------------------------------------------------#
# CodeBuild
# ------------------------------------------------------------#
CodeBuild:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Description: !Ref Description
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
Type: LINUX_CONTAINER
Name: !Ref Name
ServiceRole: !Ref CodeBuildIAMRole
Source:
BuildSpec: buildspec.yml
Type: CODEPIPELINE
Tags:
- Key: Name
Value: test-build
# ------------------------------------------------------------#
# CodePipeline
# ------------------------------------------------------------#
CodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref S3
Type: S3
Name: !Ref CodePipelineName
RoleArn: !GetAtt CodePipelineIAMRole.Arn
Stages:
- Actions:
- ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
RepositoryName: !GetAtt CodeCommit.Name
BranchName: main
PollForSourceChanges: false
OutputArtifactFormat: CODE_ZIP
Name: Source
Namespace: SourceVariables
OutputArtifacts:
- Name: SourceArtifact
Region: ap-northeast-1
RunOrder: 1
Name: Source
- Actions:
- ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: 1
Configuration:
ProjectName: !Ref CodeBuild
InputArtifacts:
- Name: SourceArtifact
Name: Build
Namespace: BuildVariables
OutputArtifacts:
- Name: BuildArtifact
Region: ap-northeast-1
RunOrder: 1
Name: Build
Tags:
- Key: Name
Value: !Ref CodePipelineName
# ------------------------------------------------------------#
# CodeStar
# ------------------------------------------------------------#
CodeStarNotifications:
Type: AWS::CodeStarNotifications::NotificationRule
Properties:
DetailType: FULL
EventTypeIds:
- codebuild-project-build-state-failed
- codebuild-project-build-state-succeeded
Name: codebuild
Resource: !GetAtt CodeBuild.Arn
Status: ENABLED
Targets:
- TargetAddress: !Ref SNSCodeBuildTopic
TargetType: SNS
# ------------------------------------------------------------#
# EventBridge
# ------------------------------------------------------------#
EventBridgeIAMPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "codepipeline:StartPipelineExecution"
Resource:
- !Join
- ''
- - 'arn:aws:codepipeline:ap-northeast-1:'
- !Sub '${AWS::AccountId}:'
- !Ref CodePipeline
ManagedPolicyName: iam-policy-eventbridge
EventBridgeIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- !Ref EventBridgeIAMPolicy
RoleName: iam-role-eventbridge
Tags:
- Key: Name
Value: iam-role-eventbridge
EventBridge:
Type: AWS::Events::Rule
Properties:
Description: for codepipeline
EventPattern:
source:
- aws.codecommit
detail-type:
- 'CodeCommit Repository State Change'
resources:
- !GetAtt CodeCommit.Arn
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- main
Name: eventbridge-codepipeline
State: ENABLED
Targets:
- Arn: !Join
- ''
- - 'arn:aws:codepipeline:ap-northeast-1:'
- !Sub '${AWS::AccountId}:'
- !Ref CodePipeline
Id: CodePipeline
RoleArn: !GetAtt EventBridgeIAMRole.Arn
61~79行目でCodePipelineで使用するアーティファクト用S3バケットを作成しています。
84~121行目でメール通知とCodeBuildのビルド結果を通知するSNSトピックを作成しています。
126~235行目でLambda関数を作成しています。
Lambda関数の使用するIAMロールではIAMポリシーとしてCloudWatch Logsにログを出力するポリシーだけでなくinvoke_modelを実行する権限とpublishを実行する権限を付与しています。
240~244行目でCodeCommitを作成しています。
249~356行目でCodeBuildとCodePipelineの使用するIAMロールを作成しています。
361~378行目でCodeBuildを作成しています。
383~429行目でCodePipelineを作成しています。
434~446行目でCodeBuildの通知設定を行っています。
AWS::CodeStarNotifications::NotificationRuleを設定するとCodeStar通知リソースとEventBridgeにawscodestarnotifications-ruleというルールが作成されます。
451~515行目でCodePipelineを動かすためのEventBridgeを作成しています。
デプロイは以下のAWS CLIコマンドを使用します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 \
--template-body file://CloudFormationテンプレートファイル名 \
--parameters ParameterKey=Email,ParameterValue=メールアドレス \
ParameterKey=RepositoryDescription,ParameterValue=CodeCommitリポジトリの説明 \
ParameterKey=RepositoryName,ParameterValue=CodeCommitリポジトリ名 \
ParameterKey=Description,ParameterValue=CodeBuildの説明 \
ParameterKey=Name,ParameterValue=CodeBuild名 \
ParameterKey=CodePipelineName,ParameterValue=CodePipeline名 \
--capabilities CAPABILITY_NAMED_IAM
動作確認
リソースの作成が完了したらCodeCommitリポジトリに以下のbuildspec.ymlをアップロードします。
アップロードは以下のドキュメントの手順で行うことが可能です。
AWS CodeCommit リポジトリにファイルを作成または追加する
アップロードが完了するとCodePipelineが動きだします。
CodeBuildの実行が完了すると以下のようなメールが届くことが確認できます。
おめでとうございます!アプリケーションのビルドに成功したことを心からお祝い申し上げます。きっと大変な作業だったと思います。あなたの努力と熱心さが報われた証です。このような素晴らしい成果を上げられることを誇りに思ってください。これからのさらなる発展と活躍を期待しています!がんばりましたね!本当におめでとうございます!
--
If you wish to stop receiving notifications from this topic, please click or visit the link below to unsubscribe:
https://sns.ap-northeast-1.amazonaws.com/unsubscribe.html?SubscriptionArn=arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:email-sns-topic:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&Endpoint=メールアドレス
Please do not reply directly to this email. If you have any questions or comments regarding this email, please contact us at https://aws.amazon.com/support
さいごに
今回は簡単に褒めてくれるだけの仕組みですが、ビルドのログなどを取得して失敗時に原因として考えられる部分を教えてくれるような仕組みを作ることも可能だと思います。