この記事はモチベーションクラウドシリーズ Advent Calendar 2022 9日目の投稿です。
はじめに
AWSにはAppConfigというアプリケーションの設定などを管理するサービスがあり、さらに今年の3月に機能フラグとしての利用に特化した機能もリリースされました。
機能フラグ(Feature Flag)についてはこちらを参考にしてください。
弊社サービスにおいてもAppConfigを機能フラグとして活用しているのですが、設定内容が変更された際に変更内容をSlackに通知したかったため仕組みを作りました。
やっていることはシンプルですがググってもあまり似たようなケースがヒットしなかったため、参考になれば幸いです。
構成
ざっくり上記の構成になります。
Lambdaの代わりにAWS Chatbotを使用してSlackへメッセージ投稿するパターンもありますが、その場合リソース名とイベント名しか取得できず具体の設定内容を通知できなかったため、間にLambdaを挟んでPythonのプログラムからSlackへ投稿しています。
投稿されるメッセージは下記のようなイメージです。
※ アイコンは事前にSlack側でカスタム絵文字を登録しています。
実装
AppConfig
まず公式手順に則ってアプリケーション、プロファイル/機能フラグ、環境を構築します。
構築した後にAppConfigの拡張機能を設定します。
AppConfigの拡張機能タブから"AppConfig deployment events to Amazon EventBridge"を選択します。
右上の"リソースに追加"から通知したいリソースを選択します。
リソースはアプリケーション/プロファイル/環境から選択することができ、また複数選択も可能です。
ただしアプリケーションとプロファイルの両方に紐づけてしまうと通知も2回来てしまうのでお気をつけください。
Lambda
次にLambdaの設定をします。
一から作成を選択し、ランタイムはPython3.9にして関数作成します。
実行ロールにAppConfigを参照するための権限を付与します。
AppConfig関連についてはなぜかAWS管理のポリシーがないため下記のポリシーを作成して付与しました。
おそらく全ては不要ですがlist/Getにプラスして StartConfigurationSession
が必要になります。
Policy設定
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"appconfig:ListEnvironments",
"appconfig:ListTagsForResource",
"appconfig:GetExtensionAssociation",
"appconfig:GetLatestConfiguration",
"appconfig:ListDeployments",
"appconfig:GetExtension",
"appconfig:GetDeployment",
"appconfig:ListExtensions",
"appconfig:GetEnvironment",
"appconfig:GetDeploymentStrategy",
"appconfig:GetHostedConfigurationVersion",
"appconfig:ListConfigurationProfiles",
"appconfig:ListApplications",
"appconfig:ListExtensionAssociations",
"appconfig:ListDeploymentStrategies",
"appconfig:GetConfiguration",
"appconfig:GetApplication",
"appconfig:GetConfigurationProfile",
"appconfig:ListHostedConfigurationVersions",
"appconfig:StartConfigurationSession"
],
"Resource": "*"
}
]
}
実際の使用するコードは下記です。
なおコードについては下記のブログを参考にしました。
サンプルコード
SlackのWebHookやチャンネル名、投稿する文面などは適宜変更してください。
import json
import boto3
import os
import urllib.request
SLACK_URL = os.environ['SLACK_URL']
SLACK_CHANNEL = os.environ['SLACK_CHANNEL']
def post_slack(msg):
data = {
'text': msg,
'username': 'AppConfig',
'icon_emoji': ':appconfig:',
'channel': SLACK_CHANNEL
}
method = 'POST'
request_headers = {'Content-Type': 'application/json; charset=utf-8'}
body = json.dumps(data).encode("utf-8")
request = urllib.request.Request(
url=SLACK_URL,
data=body,
method=method,
headers=request_headers
)
urllib.request.urlopen(request)
print("Slackにメッセージ投稿しました")
print(f"Message: {msg}")
def get_environment_name(application_id, environment_id):
client = boto3.client("appconfig")
environment = client.get_environment(
ApplicationId=application_id,
EnvironmentId=environment_id
)
print(f"環境名: {environment['Name']}")
return environment['Name']
def get_appconfig_configuration(application_id, environment_id, profile_id):
client = boto3.client("appconfigdata")
session = client.start_configuration_session(
ApplicationIdentifier=application_id,
EnvironmentIdentifier=environment_id,
ConfigurationProfileIdentifier=profile_id
)
config_token = session['InitialConfigurationToken']
configuration = client.get_latest_configuration(
ConfigurationToken=config_token
)['Configuration'].read()
print(f"config: {configuration.decode('utf-8')}")
return configuration.decode('utf-8')
def lambda_handler(event, context):
application_id = event['detail']['Application']['Id']
environment_id = event['detail']['Environment']['Id']
profile_id = event['detail']['ConfigurationProfile']['Id']
profile_name = event['detail']['ConfigurationProfile']['Name']
environment_name = get_environment_name(
application_id,
environment_id
)
configuration = get_appconfig_configuration(
application_id,
environment_id,
profile_id
)
msg = f"env: {environment_name}にprofile: {profile_name}がデプロイされました。\n"
msg += f"``` {configuration} ```"
post_slack(msg)
AppConfigのeventのサンプルだとApplication/Environment/ConfigurationProfileのそれぞれIdとNameを通知してくれるみたいですが、動作確認した際何故かApplicationとEnvironmentはNameがeventに含まれていなかったです。
そのためEnvironmentについてはコード内でIDを元に取得するようにしました。
公式Docのeventサンプル
{
"version":"0",
"id":"c53dbd72-c1a0-2302-9ed6-c076e9128277",
"detail-type":"On Deployment Complete",
"source":"aws.appconfig",
"account":"111122223333",
"time":"2022-07-09T01:44:15Z",
"region":"us-east-1",
"resources":[
"arn:aws:appconfig:us-east-1:111122223333:extensionassociation/z763ff5"
],
"detail":{
"InvocationId":"5tfjcig",
"Parameters":{
},
"Type":"OnDeploymentComplete",
"Application":{
"Id":"ba8toh7",
"Name":"MyApp"
},
"Environment":{
"Id":"pgil2o7",
"Name":"MyEnv"
},
"ConfigurationProfile":{
"Id":"ga3tqep",
"Name":"MyConfigProfile"
},
"DeploymentNumber":1,
"ConfigurationVersion":"1"
}
}
EventBridge
最後にEventBridge側の設定をします。
公式の手順に則ってルールの作成をします。
イベントパターンの箇所については下記のように設定します。
AWSのサービス: AppConfig
イベントタイプ: AppConfig Extensions Event Notification
特定のイベントを選択すると下記の3つから通知の対象を選択することができるので適宜変更してください。
任意のイベントを選択すると全てのイベントが対象になります。
- On Deployment Rollback
- On Deployment Complete
- On Deployment Start
その後にターゲットに先ほど作成したLambdaを指定して完了です。
まとめ
簡単なコードを書く必要はありましたが、全体含めても数時間で設定しきれたので良かったと思います。
AppConfigの拡張機能は自作することも可能なようなので機会があれば試してみたいです。