AWS Configの通知をSNS、Lambdaを通してSlackへ 〜実行編〜

3日目ですね。

今日は昨日の続きで実際にリソースに変更を加えて、その結果をSlackに通知してみましょう。

変更内容通知の構造を調査

まずは、昨日作ったConfigで監視対象としたリソースに変更を加えて、Lambda(というよりCloudWatch Logs)に変更内容を出力してみます。こうすることで、変更通知がどのような構造になっているかを確認することができます。

RouteTable

とあるRouteTableに10.0.1.0/24宛のトラフィックはInternetGateway経由で送るという設定を追加してみましょう。変更を通知したいだけなので、意味のない設定を追加しているということは気にしないでくださいm(_ _)m

f:id:matetsu:20151203232318p:plain

これに対するConfigからのSNS通知は、下記の様になります。

{
    "configurationItemDiff": {
        "changedProperties": {
            "Configuration.Routes.0": {
                "previousValue": null,
                "updatedValue": {
                    "destinationCidrBlock": "10.0.1.0/24",
                    "destinationPrefixListId": null,
                    "gatewayId": "igw-AAAAAAAA",
                    "instanceId": null,
                    "instanceOwnerId": null,
                    "networkInterfaceId": null,
                    "vpcPeeringConnectionId": null,
                    "state": "active",
                    "origin": "CreateRoute"
                },
                "changeType": "CREATE"
            }
        },
        "changeType": "UPDATE"
    },
    "configurationItem": {
        "configurationItemVersion": "1.1",
        "configurationItemCaptureTime": "2015-12-03T13:59:59.958Z",
        "configurationStateId": 5,
        "relatedEvents": [
            "527b06c5-3dfc-482b-84df-0f92185ed838"
        ],
        "awsAccountId": "ACCOUNT_NO",
        "configurationItemStatus": "OK",
        "resourceId": "rtb-XXXXXXXX",
        "resourceName": null,
        "ARN": "arn:aws:ec2:ap-northeast-1:ACCOUNT_NO:route-table/rtb-XXXXXXXX",
        "awsRegion": "ap-northeast-1",
        "availabilityZone": "Not Applicable",
        "configurationStateMd5Hash": "STATE_HASH",
        "resourceType": "AWS::EC2::RouteTable",
        "resourceCreationTime": null,
        "tags": {},
        "relationships": [
        (略)
        ],
        "configuration": {
            "routeTableId": "rtb-281ba241",
            "vpcId": "vpc-5b1ba232",
            "routes": [
            (略)
            ],
            "associations": [
            (略)
            ],
            "tags": [],
            "propagatingVgws": []
        }
    },
    "notificationCreationTime": "2015-12-03T14:00:00.821Z",
    "messageType": "ConfigurationItemChangeNotification",
    "recordVersion": "1.2"
}

ConfigurationDiffとConfigurationItemのresourceID、resourceTypeあたりを取っておけば、簡単な内容通知としては使えそうですね。

NetworkACL

とあるNetworkACLのInboundに0.0.0.0/0からのすべてのICMPを許可する設定を追加してみましょう。

f:id:matetsu:20151203234048p:plain

これに対する通知はこんな感じ。

{
    "configurationItemDiff": {
        "changedProperties": {
            "Configuration.Entries.0": {
                "previousValue": null,
                "updatedValue": {
                    "ruleNumber": 10,
                    "protocol": "1",
                    "ruleAction": "allow",
                    "egress": false,
                    "cidrBlock": "0.0.0.0/0",
                    "icmpTypeCode": {
                        "type": -1,
                        "code": -1
                    },
                    "portRange": null
                },
                "changeType": "CREATE"
            }
        },
        "changeType": "UPDATE"
    },
    "configurationItem": {
        "configurationItemVersion": "1.1",
        "configurationItemCaptureTime": "2015-12-03T14:09:57.945Z",
        "configurationStateId": 5,
        "relatedEvents": [
            "eabe4ad9-fb26-42d2-982a-73fe08d961a6"
        ],
        "awsAccountId": "ACCOUNT_NO",
        "configurationItemStatus": "OK",
        "resourceId": "acl-XXXXXXXX",
        "resourceName": null,
        "ARN": "arn:aws:ec2:ap-northeast-1:ACCOUNT_NO:network-acl/acl-XXXXXXXX",
        "awsRegion": "ap-northeast-1",
        "availabilityZone": "Multiple Availability Zones",
        "configurationStateMd5Hash": "c91cc8f80553b988016a6ec69fc57b16",
        "resourceType": "AWS::EC2::NetworkAcl",
        "resourceCreationTime": null,
        "tags": {},
        "relationships": [
        (略)
        ],
        "configuration": {
            "networkAclId": "acl-XXXXXXXX",
            "vpcId": "vpc-XXXXXXXX",
            "isDefault": true,
            "entries": [
            (略)
            ],
            "associations": [
            (略)
            ],
            "tags": []
        }
    },
    "notificationCreationTime": "2015-12-03T14:09:58.933Z",
    "messageType": "ConfigurationItemChangeNotification",
    "recordVersion": "1.2"
}

こちらも同様にconfigurationItemDiffとconfigurationItemのresourceId、resourceTypeを使えば良さそうですね。

SecurityGroup

とあるSecurityGroupのInboundに0.0.0.0/0から8443/TCPへのアクセスを許可してみます。

f:id:matetsu:20151203235115p:plain

これに対する通知内容は、

{
    "configurationItemDiff": {
        "changedProperties": {
            "Configuration.IpPermissions.0": {
                "previousValue": null,
                "updatedValue": {
                    "ipProtocol": "tcp",
                    "fromPort": 8443,
                    "toPort": 8443,
                    "userIdGroupPairs": [],
                    "ipRanges": [
                        "0.0.0.0/0"
                    ],
                    "prefixListIds": []
                },
                "changeType": "CREATE"
            }
        },
        "changeType": "UPDATE"
    },
    "configurationItem": {
        "configurationItemVersion": "1.1",
        "configurationItemCaptureTime": "2015-12-02T22:16:50.445Z",
        "configurationStateId": 2,
        "relatedEvents": [],
        "awsAccountId": "ACCOUNT_NO",
        "configurationItemStatus": "OK",
        "resourceId": "sg-XXXXXXXX",
        "resourceName": null,
        "ARN": "arn:aws:ec2:ap-northeast-1:ACCOUNT_NO:security-group/sg-XXXXXXXX",
        "awsRegion": "ap-northeast-1",
        "availabilityZone": "Not Applicable",
        "configurationStateMd5Hash": "c25b5d90c889ae1ef76851b707046790",
        "resourceType": "AWS::EC2::SecurityGroup",
        "resourceCreationTime": null,
        "tags": {},
        "relationships": [
        (略)
        ],
        "configuration": {
            "ownerId": "ACCOUNT_NO",
            "groupName": "GROUP_NAME",
            "groupId": "sg-XXXXXXXX",
            "description": "DESCRIPTION",
            "ipPermissions": [
            (略)
            ],
            "ipPermissionsEgress": [
            (略)
            ],
            "vpcId": "vpc-XXXXXXXX",
            "tags": []
        }
    },
    "notificationCreationTime": "2015-12-02T22:16:53.032Z",
    "messageType": "ConfigurationItemChangeNotification",
    "recordVersion": "1.2"
}

これまた同様(ry

Lambda Functionの修正

SNSから通知されるJSONの構造と必要な要素がわかったところで、とりあえずで作っておいたLambda Functionを修正してSlackにPostできるようにします。SlackへのPostには外部ライブラリがあったほうがらくなので、一旦ローカルでの開発に移ります。

コードはこんな感じになります。(Diff部分が手抜きですみません。いろいろと対応するよりはいいかなと思って・・・)

# coding: utf-8
import json
import slackweb

def lambda_handler(event, context):
  message = json.loads(event['Records'][0]['Sns']['Message'])
  configuration_item_diff = message['configurationItemDiff']
  resource_id = message['configurationItem']['resourceId']
  resource_type = message['configurationItem']['resourceType']

  text = u"リソースの変更を検知しました。\n\n変更されたリソース: %s %s\n変更内容: \n```\n%s\n```\n" % (resource_type, resource_id, json.dumps(configuration_item_diff,  indent=2))

  hook_url = "https://hooks.slack.com/services/xxxxxxxxx/yyyyyyyyy/zzzzzzzzzzzzzzzzzzzzzzzz"
  username = "config_monitor_bot"
  channel = "#config_monitor"
  icon_emoji = ":cop:"

  slack = slackweb.Slack(url=hook_url)
  slack.notify(text=text,
               channel=channel,
               username=username,
               icon_emoji=icon_emoji)

billing_bot の時と同じように、下記のようにzipファイルを作成します。

$mkdir config_bot
$ vi lambda_function.py
(上のPythonコード)
$ vi requirements.txt
slackweb
$ pip -r requirements.txt -t ./
$ zip -r ~/func.zip * -x *.pyc event.json
(python-lambda-localでのテストで作成した/されたuploadには不要なファイルは除いておく)

LambdaのConsoleから今度はUpload .ZIP file を選んでsave。テストをするには、先ほどのCloudWatch Logsに出力されたJSONSNSが付与している部分も付け足して、下記のようなeventを登録する。これを登録したうえTestを実行すればよい。

{
  "Records": [
    {
      "Sns": {
        "Message": "(Logに出力したjsonを文字列にしたもの)"
    }
  ]
}

リソースを変更してみる

さあ、準備ができましたので、実際にリソースの変更をしてみましょう。とりあえずはRouteTableへの変更を実施してみましょう。(SecurityGroupへの変更が通知されるのにはなぜが時間がかかる模様)

しばらくすると、Slackにこんな感じで出力されます。

f:id:matetsu:20151204010008p:plain

CloudTrailでも操作のログを取ることができますが、Configを使うことでより視覚的に捉えることができます。また、サーバレスで通知の仕組みも簡単に作れるというところがいいですね。

これにて3日目終了です。実際の動作があるから昨日よりは実用的な感じに見えますかねw