pict3の日記

AWSネタを中心に

AWSのIPアドレスレンジ変更をBacklogに通知する(SNS + Lambda + S3)【cloudpack大阪BLOG】

やりたいこと

AWSのリソースが使うIPアドレスレンジは、以下に公開されています。

https://ip-ranges.amazonaws.com/ip-ranges.json

ちょくちょく更新されますが、FireWallやWAFの設定に反映させる必要があるシステムをお持ちのプロジェクト担当者へ、正確かつ迅速に通知することを目標に自動化システムを構築しました。

実現

AWSのリソースが使うIPアドレスレンジの変更は、SNSで受信することができるようです。 また、担当者への連絡はBacklogを使うこととします。

以下のような構成で実現します。 f:id:pict3:20151216134539p:plain

S3

S3に通知を受ける直前のIPアドレスレンジを保管しておきます。 バケット名は適当に。

Lambdaファンクション

IAMロールには、S3の読み書き権限を付けておきます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import httplib
import json
import difflib

import boto3

s3 = boto3.resource('s3')

import pprint
pp = pprint.PrettyPrinter(indent=4)



API_KEY               = "<Backlogより発行>"
BACKLOG_HOST          = "<Backlogスペース + ドメイン>"

S3_BACKET_NAME        = "<バケット名>"
S3_LAST_IP_RANGE_OBJ  = "last-ip-range.json"


def get_project_id(project_code):

    uri = "/api/v2/projects/%s?apiKey=%s" % (project_code, API_KEY)
    
    connect = httplib.HTTPSConnection(host = BACKLOG_HOST, port = 443)
    connect.request("GET", uri)
    response = connect.getresponse()

    body = response.read()

    connect.close()

    data = json.loads(body)

    project_id = data['id']

    return project_id


def post_issue(project_id, summary, issue_type_id, priority_id, description):

    data = {
        "projectId"   : project_id,
        "summary"     : summary,
        "issueTypeId" : issue_type_id,
        "priorityId"  : priority_id,
        "description" : description
    }

    params = json.dumps(data)
    headers = {
        "Accept":"application/json",
        "Content-Type":"application/json",
    }
    uri = "/api/v2/issues?apiKey=%s" % (API_KEY)

    connect = httplib.HTTPSConnection(host = BACKLOG_HOST, port=443)
    connect.request("POST", uri , params, headers)
    response = connect.getresponse()

    connect.close()

def download_ip_range(file):
    uri = "/ip-ranges.json"
    
    connect = httplib.HTTPSConnection(host = "ip-ranges.amazonaws.com", port = 443)
    connect.request("GET", uri)
    response = connect.getresponse()

    body = response.read()
    
    f = open(file, "w")
    f.write(body)
    f.close()

    connect.close()

    return

def construct_description(diff):
    description =  u"AWSの使用するIPアドレスレンジに更新がありました。"
    description += u"変更内容は以下です。"
    description += '\n'
    description += "{code}\n"
    for buf in diff:
        description += buf
    description += "{/code}\n"
    
    print(description)

    return description

def lambda_handler(event, context):

    last_ip_range_file    = '/tmp/' + S3_LAST_IP_RANGE_OBJ + '_last'
    current_ip_range_file = '/tmp/' + S3_LAST_IP_RANGE_OBJ + '_current'

    download_ip_range(current_ip_range_file)
    s3.meta.client.download_file(S3_BACKET_NAME, S3_LAST_IP_RANGE_OBJ, last_ip_range_file)

    last_ip_range    = open(last_ip_range_file, "r")
    current_ip_range = open(current_ip_range_file, "r")

    diff = difflib.context_diff(last_ip_range.readlines(), 
                                current_ip_range.readlines(), 
                                fromfile='before', 
                                tofile='after')

    description = construct_description(diff)

    summary = "[連絡] AWSリソースのIPアドレスレンジに更新がありました"
    issue_type_id = 3
    priority_id   = 3

    # 通知したいプロジェクトを追加
    project_ids = []
    project_ids.append(get_project_id("HOGE"))
    project_ids.append(get_project_id("FUGA"))

    for project_id in project_ids:
        post_issue(project_id, summary, issue_type_id, priority_id, description)

    response = s3.meta.client.upload_file(current_ip_range_file, S3_BACKET_NAME, S3_LAST_IP_RANGE_OBJ)

SNS

こちらのサイトを参考にTopic ARNを設定します。 ProtocolはLambda、Endpointは先ほど作ったファンクションを選択します。 f:id:pict3:20151216152842p:plain

Lambdaのイベントソースに反映されます。 f:id:pict3:20151216153044p:plain

結果

ちゃんとBacklogに投稿されました! Lambdaの恩恵、デカすぎです!! f:id:pict3:20151216160000p:plain