AWS Lambda から Incoming Webhooks を使って Slack に投稿する

概要

slack では webhook 用のURL を通し、メッセージを投稿することができます。
ここでは、AWS Lambda を invoke -> 内部でシェルスクリプトを実行 -> Incoming Webhooks に POST という手順を試してみたので、その方法を書いておきます。

Incoming Webhooks

Slack Apps の Incoming Webhooks のページから、web hook 用の URL を取得します。

https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX
↑こんな感じのURL

この URL にメッセージをPOSTすることで、設定したチャンネルにそのメッセージが投稿されます。
メッセージだけでなく、投稿者の名前、アイコンなども設定できます。

例えば、

curl -X POST https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX -d '{"text": "TEST!!!!", "username": "dango", "icon_emoji": ":dango:"}'

こんな感じのPOSTをすると、

20170222-183306.png

こんな感じの投稿になります。

引数にテキスト、ユーザー名、アイコンを受け取ってポストできるような適当スクリプトを作っておきます。

post.sh
#!/bin/sh

TEXT=$1
USERNAME=$2
ICON=$3

BODY="{'text': '${TEXT}', 'username': '${USERNAME}', 'icon_emoji': '${ICON}'}"

curl -X POST https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXX -d "${BODY}" 

Lambda

次は上記を Lambda を通してやってみます。
Lambda 関数の作成時に、ランタイムは node.js を選択、設計図には node-exec を選択します。

このLambda 関数から先ほど作ったシェルスクリプトを実行し、Slack への投稿を行います。

Lambda 関数は実行時に json 形式のペイロードを受け取り、その内容を関数に渡すことができます。

event.json
{
    "text": "Post from lambda!!",
    "username": "test",
    "icon": ":golfer:"
}

↑こんなペイロードを作っておきます。
これを渡してスクリプトを実行できるような関数を作成します。

index.js
var exec = require('child_process').exec;

exports.handler = function(event, context) {
  command = '/bin/bash ./post.sh ' + event['text'] + ' ' + event['username'] + ' ' + event['icon'];
  child = exec(command, function(error) {
    // Resolve with result of process
    context.done(error, 'Process complete!');
  });

  // Log process stdout and stderr
  child.stdout.on('data', console.log);
  child.stderr.on('data', console.error);
};

この index.js、post.sh を zip 形式にまとめてlambda へとアップロードし、Lambda 関数を作成します。
後の設定は適当に。。。

作成した関数に、アクション>テストイベントの設定から、先ほど作った json を設定し、テストを実行します。

20170218-124510.png

20170222-183909.png

成功のログとともに、Slack へと投稿されていることが確認できました!

感想

Lambda を使うのはこれが初めてなので、細かい設定はよくわからず作ってますが、なんとか成功しました。
(基本的に参考に記したリンク先のやり方を真似させていただきました。)
Lambda を使ってみたかっただけなので Lambda である必要はないと思いますが、割と簡単に設定できました。
Incoming Webhooks ではいろんな形式の投稿ができるっぽいので時間があれば試してみたい。。。

参考

続きを読む

サーバーレスシステムでユーザー情報をアクセストークンを使って取得する

やりたいこと

サーバレス認証.png

サーバーレスシステムでの個人情報保護について考えてみます。

システムで管理するべき個人情報は、できるだけクラウド内にとどめ、クライアントとやりとりしないということにできれば、通信路上での漏えいを防ぐことができます。
また、個人情報の管理を、限定することができれば、システムの大部分は、個人情報の漏洩について意識することなくシステム開発を行うことができます。

このような要求のシステムの場合、AWSでは、次のようになります。

  1. Cognito Identity Poolでユーザー管理をする。
  2. クライアント⇔AWS間の通信路上はアクセストークンと処理結果のみの受け渡しする。
  3. AWS内ではLambdaがアクセストークンを受け取りCognitoのユーザー情報をもとに処理を行う。

アクセストークンを受け取ったLambdaにアクセストークンに対応するユーザー情報へのアクセス権限が委譲され、LambdaはCognitoのユーザー情報を取得し処理できるようになります。よって、Lambdaは、アクセストークンを受け取れるようにする必要はありますが、ユーザー情報にアクセスするためのロールを付与する必要がなくなります。

参考までに、LambdaからCognitoにHTTPS通信する際に、どのような情報をやりとりするとアクセストークンと引き換えにユーザー情報を取得できるのか、cURL, Postman, AWS CLIを使って調べたのでまとめておきます。

結論

必要な情報は以下でした。
– Cognito Identity Service Providerが発行したアクセストークン
– Cognito Identity Poolのリージョン
– 各種おきまりのヘッダー(Content-Type、X-Amz-Target)

※ 各種おきまりのヘッダーをつけないと以下のエラーが返ってきます。

{"code":"BadRequest","message":"The server did not understand the operation that was requested.","type":"client"}

cURL

$ curl 'https://cognito-idp.<リージョン>.amazonaws.com/' -H 'Content-Type: application/x-amz-json-1.1' -H 'X-Amz-Target: AWSCognitoIdentityProviderService.GetUser' --data-binary '{"AccessToken":"<アクセストークン>"}'

Postman

AWS CLI

$ aws cognito-idp get-user --access-token <アクセストークン>

(備考)

  • <リージョン>は、東京だったら「ap-northeast-1」です。
  • このシステムでは、HTTPS通信が必須です。
  • AWSのサービス間のセキュリティ(ex. Lambda ⇔ Cognito)の信頼性は、別途、考慮が必要です。

続きを読む

AWS re:Invent 2016 Security Follow Up に行ってきた

こんにちは、ひろかずです。
AWS re:Invent 2016 Security Follow Up に行ってきたので、一筆書きます。

  • 例によって、リアルタイム執筆ですので、誤字、脱字、表記ゆれはご容赦ください。

スタートが19:00のせいか、半分くらいの人出からのスタートです。
最終的には8割位埋まっていました。

[AWSセッション]

「セキュリティ新サービス紹介① AWS Organizations」

桐山 隼人
アマゾン ウェブ サービス ジャパン株式会社
技術本部 レディネスソリューション部
セキュリティソリューションアーキテクト

AWS Organizations は…

  • 現在プレビュー申し込みできます。

サービス概要

複数のAWSアカウントの一元管理

  • アプリケーションや環境、チームのような単位でAWSアカウントをグループ管理
  • グループポリシーを適用

AWSアカウント管理の自動化

  • コンソール、SDK、CLIでの管理操作
  • 管理操作をCloudTrailでロギング

請求の簡素化

  • 複数アカウントの一括請求(Consolidated Billing)
  • Consolidated Billing ファミリーの自動移行

主要なコンセプト(用語)

組織

  • 一元管理可能な複数のAWSアカウントのセット

AWSアカウント

  • 管理される最小単位
  • IAMを用いてAWSリソースへアクセス

マスターアカウント

  • 組織内の他アカウントの作成招待削除ができる特別なアカウント
  • 組織内コントロールポリシーの適用
  • 組織における支払いアカウント

組織単位(OU)

  • 後述

管理ルート

  • 後述

利用の流れ

1. 組織の作成

  • マスターアカウントは支払いアカウントになるので、慎重に決めましょう

2. AWSアカウントの自動作成

  • マスターアカウントからの自動作成

入力要素

  • メアド(必須)
  • アカウント名(必須)
  • IAMロール名(必須)
  • BillingへのIAMユーザアクセス(任意設定)

モードは二つ

  • FullControlモード
  • Billingモード

FullControlモード

  • Billingモードを含む
  • Billingからの昇格には、全てのAWSのアカウントからの同意が必要

Billingモード
– 課金状況を見れる

3. 既存AWSのアカウントの組織への招待

  • 招待されたアカウントは、了解/拒否を選べる。(デフォルト拒否)
  • 参加した瞬間にポリシーが適用される

4. AWSアカウントのグループ化

AWSアカウントは組織単位(OU)に所属できる

  • アプリケーションとか環境とかチームとか
  • 階層になるので、最上位が 管理ルート になる。

5. 組織Controlポリシー

  • 適用対象は、組織全体、OU、AWSアカウント
  • ポリシーは、階層構造に基いて継承される。
  • ローカル管理者からは上書きできない
  • IAMと記述方式は一緒
  • IAMユーザ、IAMロールの権限は、 組織ControlポリシーとIAMポリシーの最大公約数

どんなことができるの?

例:サービスControlポリシー

  • 呼び出しを許可するAPIを定義(ホワイト、ブラックリスト)
  • 業界で認可されたサービスのみを許可する(本番環境)
  • 開発環境はちょっと緩くすることもアリ

「セキュリティ新サービス紹介② AWS Shield」

荒木 靖宏
アマゾン ウェブ サービス ジャパン株式会社
技術本部 レディネスソリューション部 部長

AWS Shieldとは?

  • AWSが管理(マネージ)するDDoSプロテクションサービス

攻撃の種別

  • ボリューム攻撃(UDP反射型:全体の6割を占める:SSDP反射型が一番多い、その他NTPやDNS、SNMPも)
  • ステート管理攻撃(SYNフラッドとか:2割くらい)
  • アプリケーションレイヤー(F5攻撃とか、slowles:15%くらい)

DDoSを緩和する取り組み

難しさはどこに?

  • 複雑な前準備(オペレータやISPとの調整、スクレイピングサービスへのトラフィック誘導)
  • 事前の帯域確保(工事時間がかかる)
  • アプリの見直し(仕様書起こすとか大変)

難しいのはマニュアルでの対応
AWSでスクレイピングしても、ユーザーからはレイテンシ向上にしか見えない。お金は?
AWSの解

  • ありきたり攻撃の自動保護(サービス差別化につながらない部分を持つ)
  • 可用性の確保

DDoS防御はAWSにあらかじめ組み込まれている

  • 各リージョン
  • 常時オン(外部Routingなし)
  • SYB/ACK、UDPフラッド反射型
  • 追加費用無し

お客様の声

  • 大きなDDoS対策
  • 見える化
  • アプリレイヤー守ってよ

AWSの解

  • 無料サービス(Standard)
  • 有料サービス(Advanced)

Standard

BLACK Watch

  • 決定論的フィルタリング
  • 不正な形式のTCPパケットはドロップする
  • インライン検査のスコアリングに基づくトラフィック優先順位付け(優先順位の低い攻撃トラフィックを優先的に破棄)

キャパ追加

  • Routingポリシーで内部的に分散

検知と緩和を継続(ヒューリスティック異常検出:通常のベースラインから異常を知る)

  • リクエスト数/s
  • 送信元IP
  • URL
  • UserAgent

Advanced

AWS内で完結する
常時オン
レイテンシは最小限
手頃な価格
対応しているのは以下サービス(EC2は入っていない。TCPのみ。UDPは使えない。)

  • ALB
  • CLB
  • CF
  • Route53

L7は、AWS WAFの利用

  • Standard:セルフサービス(自分でカスタムシグネチャを書く)
  • Standard/Advanced:DDoSエキスパートチームからのアドバイス
  • Advanced:積極的DRT(DDoS Response Team)が関与する

DRT(DDoS Response チーム)はなにやるの?

  • 常時モニタがDRTを呼び出す
  • トリアージ
  • DRTチームがルールを書いて提供

レポート

  • ニアリアルタイムで、パケット情報見れる(サンプル取得:全部じゃない)

DRTへのコンタクト

  • 第一報はサポート経由でしてね(エンタープライズサポート)

費用

  • Standard:アドバイスに基づくスケール費用は 有料 :月額課金なし
  • Advance:アドバイスに基づくスケール費用は無料:$3000/月:一年継続コミット

[パートナーセッション]

「スポンサー側から見た AWS re:Invent の醍醐味と感じたセキュリティの流れ」

南原 正樹様
トレンドマイクロ株式会社 パートナービジネス推進本部
アライアンスパートナーグループ 担当課長代理

Trend MicroとAWS re:invent

ダイヤモンドスポンサー
並び順は一番最後(アルファベット順)
出展したのは、DeepSecurityとDSaaS
スポンサーアクティビティ(セッションは各1時間:youtubeに上ってるよ)

  • AWSに特化したセッション
  • CISOに聞いてみよう(コンプライアンス、レギュレーションの話)
  • ブースは、DevSecOpsを安全な空の旅をモチーフに表現

スポンサーで感じたこと(日本 vs 北米)

  • 日本は、捕まると長いので敬遠される。アンケート欠かされる。製品売ろう。
  • 北米は、コンタクト獲得と製品が分業。スキャンしたらノベルティたくさんくれる。コンプライアンス準拠。ビジネス視点。

来場者

  • 北米はカジュアルに自分のアイデアを語ってくる。Tシャツ大好き。

re:inventで見た気になるセキュリティの流れ

Security Automation

セキュリティオペレーションの自動化を目指すことでセキュリティ運用コストの低減と対応の迅速化を目指す。
セキュリティはインフラ構築と分離しない。併せて考える。

  • AWS WAFとLambdaとの連携(ブラックIPとか)
  • CloudTrailのIPを検査とか
  • GitHubに公開されてるよ!

Security for ServerLess

サーバレスを主体として組み上げた環境でのセキュリティのポイント

  • IAMを中心とした権限管理
  • 許可されたトラフィックの精査(サニタイズからログの精査まで)
  • 例えば、CF/WAF前、API Gateway中、Lambda後ろ、最後はDB

最後に

AWSはエコシステムとマーケットチャンス
セキュリティもニューノーマルからスーパーパワー
積極的に新しい取り組みに挑戦しよう(日本の方がセキュリティに深く真面目に考えている)

「攻撃傾向と企業が取るべきセキュリティ施策 in Las Vegas」

佐藤 裕貴様
三井物産セキュアディレクション株式会社
Alert Logic事業部 マネージャー

大橋 和正様
三井物産セキュアディレクション株式会社
プロフェッショナルサービス事業部
アカウントマネージャー

昨今の攻撃傾向

海外(Verizonレポート)

  • WebAPに対する攻撃が増えてきている。(前年度4倍)

日本(IPAレポート)

  • インターネットバンキング、標的型、ランサム、Webサービス不正ログイン、サイト改ざん

事前対策

セキュアコーディング
脆弱性診断
アクセス分離(SG、NACL、ELB)、2要素認証(IAMロール、MFA)

  • 監視ポイントが限定できて運用負荷を軽減

ログの収集

  • 平時のログ傾向を把握する
  • ログは不変であること(削除/改ざんの防止)
  • 以下の状態であること(アクセス性,検索性,継続モニタ)

セキュリティ運用の最適化

  • L1:SG,IAM
  • L2:IDS/WAF,脆弱性スキャン
  • L3:CloudTrail,SIEM

[エンドユーザー様セッション]

「ユーザーからみたre:Invent のこれまでと今後」

宮崎 幸恵 様
株式会社リクルートテクノロジーズ
ITソリューション統括部 インフラソリューション2部 RAFTEL2G

  • 2012年からre:invent皆勤賞
  • 一年おきにセキュリティ系サービスのビッグウェーブが来る傾向
  • Organizationsキター
  • 現地は熱い!体感!感じろ!

バッテリー切れでライブでかけませんでした!すみません!

「AWS re:Invent の衝撃 ~Large Scale Violence & Security Culture Shock~」

大橋 衛様
KDDI株式会社
技術統括本部 プラットフォーム開発本部
アジャイル開発センター フレームワークG 課長補佐

  • 現地は熱い!体感!感じろ!飯はマズイ!
  • 予防、検知、回復の話
  • 日本は、予防に重点を置いている制約型.検知が最小限で、回復は手動と尻すぼみ
  • 海外は、予防はsoso、検知にかなり力を入れて、回復は自動化とパワーのかけ方が日本と逆

バッテリー切れでライブでかけませんでした!すみません!

雑感

モバイルバッテリーがそろそろ必要なようです。

続きを読む

CloudWatch専用の通知bot、marbotを使ってみた

CloudWatchのslack連携というとLambdaでpostが鉄板ですが、より良くしようとするとLambdaをメンテしていく必要があります。

メッセージを視覚的に良くするならslackのattachmentsなどで工夫が必要だし、用途別に通知を分けるとかなると、Lambdaに分岐書いたりとかLambdaをコピペしたりなど。。。

たまたま、marbotというCloudWatchの専用botを見つけ、試しに導入してみたら結構イイ感じだったのでその紹介です。

marbotについて

公式サイト – marbot

CloudWatch専用の通知botで、アラートのslack通知や簡易的なエスカレーションがあります。
今のところは有料プランはなく、費用ゼロで使用できます。

AWSのChatbotコンテストで入賞してたみたいです。
https://aws.amazon.com/jp/blogs/news/congratulations-to-the-winners-of-the-serverless-chatbot-competition/

marbotの機能

marbotのFeaturesに説明がありますが、ザックリまとめると以下になります。

  • Slackへのアラート通知
  • アラートのエスカレーション
  • アラートの管理(チェック、パス、クローズ)
  • Dailyのアラートサマリ

導入方法

ここではCloudWatchの設定(SNSのtopicも登録済み)は済んでいるという前提で進めます。

  1. marbot# Add to Slackがあるので、認可してSlackに追加
  2. slackのchannelにmarbotを追加
    • 追加するとSNSで設定するEndpointが表示されます
    • 指定したchannelに参加しているメンバーがmarbot通知の対象になり、これについては後述
  3. CloudWatchのnotificationで指定しているSNSにsubscriptionを追加
    • HTTPS形式を選び、2.のEndpointを設定
  4. slack上でYou completed the SNS topic subscriptionとなっていれば、SNS連携は完了

マスクしてますが、ここにEndpointが表示されます。

invite-image

あとはCloudWatchのイベントが発火すると、marbotから通知がslackへ送信されます。

marbotのアラート通知

marbotからのアラート通知ですが、基本はchannelメンバーへのダイレクトメッセージです。
ここではアラート発生の流れと、アラートへのアクション、エスカレーションについて説明します。

アラートへのアクション

marbotからのアラート通知には、Acknowledge,Pass,Closeいずれかのアクションが出来るようになっています。
実際のインシデント想定をした場合、こんな感じでしょう。

  • Acknowledge : アラートの認知、障害内容のチェック開始など
  • Pass : 誰かにパス
  • Close : 終了

アラート発生のフロー

実際にやってみたところ、以下の流れになりました。

  1. アラート発生 🔔
  2. channelのオンラインメンバーにmarbotからアラート通知のダイレクトメッセージ
    • オンラインメンバーが複数の場合、誰か一人に通知するっぽい
  3. アラートへのアクション
    • Acknowledge => marbotがAcknowledgedと認知
    • Pass => marbotが次のオンラインメンバーへ通知
    • Close => marbotがClosedと認知

marbotのアラート通知はこんな感じになります。

alert-image

エスカレーション

marbotは特定の条件で、エスカレーションとしてchannelへ全体通知します。

  • オンラインメンバーが誰もいなかった
  • 通知を受けたメンバーがアクションを起こさなかった(5分固定っぽい)
  • 全員がPassした

つまり、誰も気づかなかった、誰も行動を起こさなかったらchannelへの全体通知されることになります。

escalate-image

Dailyのアラートサマリ

marbotは一日のアラートを集計し、日々channelへこんな感じのまとめを投稿してくれます。
(これはテストで適当に発生させたアラート)

summary-image

marbotの運用を考えてみる

marbotはslackのchannelをグループ管理としてみなしているようです。
なので、アラートを受けるべきメンバーを集めたchannelにmarbotを入れれば、アラートのコントロールもしやすくなるかと思います。

例えばこんな感じにわけてみるとか。

  • myservice : サービス関係者全員
  • myservice-dev : サービスのエンジニアAll
  • myservice-incident : 通知に必ず対応すべき関係者
    • marbot参加

ちなみにインテグレーション数ですが、channel個別にmarbotを招待できるので登録は1つで済みます。

Architectureについて

この記事に、アーキテクチャの概要が説明されていました。
https://cloudonaut.io/marbot-aws-serverless-chatbot-competition/

  • AWS Lambda and Amazon API Gateway
  • Amazon DynamoDB
  • Amazon Kinesis Streams and Amazon Kinesis Analytics
  • Amazon SQS
  • Amazon SNS and AWS CloudWatch

LambdaとAPI Gateway、DynamoDBはサーバレスの鉄板なので、すぐに想像できましたが、目を引いたのはSQSとKinesisです。

記事を読んで理解できましたが、アラート通知でn分スルーされたらchannelへ通知などを実装するために、SQSをタイマーとして使っているようです。なるほど。

KinesisはDailyサマリーの算出用途ですね。

まとめ

CloudWatchの通知をSlackに連携していない、またはslackに連携しているけどアラートを放置しがちなケースでは、marbotのエスカレーションが効果的になるかも。

続きを読む

API Gateway+Lambda+Serverless+TravisでAPI開発のCI/CDパイプラインを構築する

概要

スクリーンショット 2017-02-21 10.15.16.png

API GatewayとLambdaを使ったAPI開発時のCI/CDについての記事です。
僕がnode.jsを普段から使用しているため、解説はnodeがベースになっています。

https://github.com/horike37/serverless-api-integration-test-sample
ソースはすべてGitHubに上がってますのでそちらもご確認ください。

CIで実施する内容

以下の内容をCIとして行うことを考えます。

  • ESLintによる構文チェック
  • Mocha, Chaiを使用したユニットテスト
  • APIをAWSへデプロイしてテストを行うインテグレーションテスト

CDで実施する内容

以下のようなルールでデプロイのサイクルを回します。

  • Gitのdevelopmentブランチへのpushをテスト環境へのデプロイと想定。

    • 構文チェックとユニットテストを実施。
    • ビルドが通れば、ServerlessのdevelopmentステージにAPIをデプロイ。
  • Gitのmasterを最新ソースの集約場所として想定して、pushを実施。
    • 構文チェックとユニットテストとインテグレーションテストを実施。
    • ビルドが通ってもデプロイは実施しない。
  • Gitのtagへのpushを本番環境へのデプロイと想定。
    • 構文チェックとユニットテストとインテグレーションテストを実施。
    • ビルドが通れば、ServerlessのproductionステージにAPIをデプロイ。

Continuous Integration

ESLintによる構文チェック

http://eslint.org/
JavaScript界隈ではもっともメジャーな構文チェックツールだと思います。
構文ルールを定義して、構文エラーや記法を統一させることでソースの可読性や品質を向上させます。

https://github.com/horike37/serverless-api-integration-test-sample/blob/master/.eslintrc.js#L2
今回はルールとしてArbnbを採用しました。どの構文ルールを採用するかはチームの好みで決めれば良いと思います。

Mocha, Chaiを使用したユニットテスト

単体テストを実施します。メソッドや関数毎に入力値と出力値をチェックして関数単位での品質チェックを行います。

https://github.com/horike37/serverless-api-integration-test-sample/tree/master/lib
このようにLambdaがメインで実行する部分から、ビジネスロジックをclassに切り出してあげるとテストが書きやすくなるのでそうすることが多いです。

APIをAWSへデプロイしたテストを行うインテグレーションテスト

インテグレーションテストはサーバレスアーキテクチャの特徴とも言えるテストです。

サーバレスアーキテクチャは複数のサービスで構成されているケースが多く、ユニットテストだけではアーキテクチャ全体のテストを網羅できません。実際のクラウド環境へリソースをデプロイして結合テストを行い、すべて成功すれば、それを破棄します。

このデプロイ管理にはServerlessを使用しています。

https://github.com/horike37/serverless-api-integration-test-sample/blob/master/integration-test/test.js#L18
https://github.com/horike37/serverless-api-integration-test-sample/blob/master/integration-test/test.js#L41
テストの最初と最後にアーキテクチャのデプロイと削除を行っています。このメソッドの実体は、sls deploysls removeです。

it('should return correct values from all apis', () => {
     const testEndpoint = `${endpoint}/hello`;

     return fetch(testEndpoint, { method: 'GET' })
       .then(response => response.json())
       .then((json) => expect(json.message).to.equal('Go Serverless v1.0! Your function executed successfully!'));
  });

https://github.com/horike37/serverless-api-integration-test-sample/blob/master/integration-test/test.js#L35-L37
そしてここがテストのメインの部分です。デプロイされたAPIへリクエストを送り、その返り値をchaiでチェックを行います。
こうすることで何本APIを作ったとしても自動テストが可能になるというメリットがあります。

Continuous Delivery

デプロイスクリプト

以下の様なスクリプトでデプロイを実施します。Gitのtagにpushされた際にproduction環境へ。Gitのdevelopmentブランチにpushされた際にdeployment環境へ。それぞれビルドが正しく通ればデプロイされるようになっています。

bin/deploy.sh
#!/bin/bash
set -e
BRANCH=${TRAVIS_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}
if [[ $TRAVIS_TAG ]]; then
  STAGE="production"
elif [[ $BRANCH == 'development' ]]; then
  STAGE="development"
fi

if [ -z ${STAGE+x} ]; then
  echo "Not deploying changes";
  exit 0;
fi

echo "Deploying from branch $BRANCH to stage $STAGE"
npm prune --production  #remove devDependencies
sls deploy --stage $STAGE --region $AWS_REGION

ServerlessのIAM権限

TravisにてServerlessが動作するように専用のユーザを発行します。今回は最低限で以下の様なIAMポリシーを与えています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt543534534",
            "Effect": "Allow",
            "Action": [
                "iam:GetRole",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:PutRolePolicy",
                "iam:DeleteRolePolicy",
                "iam:PassRole",
                "logs:*",
                "s3:*",
                "lambda:*",
                "cloudformation:*",
                "apigateway:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

そして、TravisからAWSリソースへアクセスするためにcredentialをTravisへ設定します。
もちろん生のcredentialを.travis.ymlへ追記することはご法度です。

Travisのコマンドにより暗号化し、credentialがTravisの環境上でしか使えないようにします。

$ travis encrypt AWS_ACCESS_KEY_ID=xxxxxxxxxxx --add
$ travis encrypt AWS_SECRET_ACCESS_KEY=xxxxxxxxxxx --add

このコマンドにより。.travis.ymlに暗号化した状態でcredentialが設定されます。
これはTravis上でビルドを実行した際に環境変数として設定されます。

Travisの設定ファイル

最終的にはこんな感じに.travis.ymlが仕上がりました。

travis.yml
language: node_js
node_js:
  - '4.3'
env:
  global:
    - AWS_REGION=us-east-1
    - SLS_DEBUG=true
    - secure: lSxO7tZ0c/FA8VL72042dqQZ+tRjsS93iVYxMr1ghP/0tBxdmrhhYdAD9UrSv/Kk+Y1jRlkpQ2uaARHoy+6ZqmhchX32HpIYBQVJ/ntMSgv37gFbrNTOfSFoATMTRy6RT2UKaIWAa4xnmDxaQNFPx4X5l9Y25RdivoR+WXrEPd4eCCTVL/23bABSIySSTs+VGqQIppE4Jw5ibbcSoTLsuj00nK+VrmYHNlTSiEuKIxgFC1Ix0hqayJ/kely0DqYW/CY/vCCf0V4yazJo9fG1EFfrHsSIAKKeGRY7WMnLPJ7hJGwRiVV1/atMx/5kRPKOADcRTfoh3noXS3/sd1hbGjTwnJVRVrYiUocHwuNbo1TpW1On85jXEdvnKY9JYelFEnXnWn6A2bRMhgL/zul/WuSPCGq7HpsGMRhXrEiEYJ9YhnVNiUTaV2amoOClMOpHFnStMfTJVg7NJ8mBF4XOzODvhAzyPFDWdJ94Ejl1LAnGOAp/wBQVbFswKPdwdosFU6LyirQkA4k0q7C4zXYywyQtrY7H9w7FtKo+U4596GQAQtvzQz6GS42c1WBX0fIrMu1VXc+KmwCUBEVmvBxLS7c0DJUI61atDFGq7788K7IMWw83lIFjJULdwv1qU4uBi3MvPm2OHCdRAzBGEYIC87zfcYI/gi41rh3bj/C0wiI=
    - secure: LIh0lkl/t72EbMd47WgEXqnoG3REp+oPhIfDR5Cs8SMO4sacvo2j4pRkRKIwwpdKozxdgLEMl1rwDoHyYPH77FzvDnwiufpaYgQs278wmi+6ZvoC9nhgdn2sT7cFnYuYAO8dC7G/NHzXogAVmiObf3I+hzNLjDqWwWVjqPm41p4P4c2EJUVo0nVlcaUOf8elS1j6zp+ZL1EQo4Fm4IumDgNpZUP4bSq8CcVPvF0ynMlslI8XNMnBOiYmG+644qILScyPK1Q2SPdMLqL5YXHuYfE0aCpFcWOcNZIalBmaxPqFNW+QHQvaYiwoENx/i91KS3U2mqfcNYY4o9viih47PFsaddvtBeB83Wfls7GIZ/XmvBKREuS5Gwhz930DbAvUNQT1ylS9Y6TTIIWIbe3Qmv6ngd9TrDHlnbVhQYgar9ur+TgvLyhs5YLAeLn85c3Z3GYN5JUuCq5bclzh4I+myagaWJPoujS1nT+vKLUW8hu5MkxApn+uFUo/OIW1WG4qho/ddh7RoJbI9oTebWjpXhLwd1pSET1yBVSEetORluh0pW7r5A425Rm80B58Mg/x0NNmM1x6DBrCZ5R8d+Bam6C7P4WxxrRex7vFcqRWeKlJNINO+rtPW3Uuo/s9yC9980uQ00eY0kqhouR8ol6xs/4Xye1AHovPR2unzEe37fM=
before_install:
  - chmod +x ./bin/deploy.sh
  - npm i -g serverless@1.6.1
install:
  - travis_retry npm install
script:
  - npm run lint
  - npm run test
  - if [[ $TRAVIS_BRANCH == "master" ]] || [[ $TRAVIS_TAG ]]; then npm run integration-test; fi
after_success:
  - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage
  - ./bin/deploy.sh

実行結果

developmentブランチへのpush

https://travis-ci.org/horike37/serverless-api-integration-test-sample/builds/203491545
ESLintとユニットテストのビルドが成功し、developmentステージへデプロイされています。

masterブランチへのpush

https://travis-ci.org/horike37/serverless-api-integration-test-sample/builds/203489525
ESLintとユニットテストとインテグレーションテストが成功しましたが、デプロイはされていません。

tagへのpush

https://travis-ci.org/horike37/serverless-api-integration-test-sample/builds/203484838

0.1というタグにpushをしています。
ESLintとユニットテストとインテグレーションテストが成功し、puroductionステージにデプロイされています。

如何でしたでしょうか。こんな感じでCI/CDパイプラインを構築することですべてが自動化され品質も担保されるようになりました。よろしければ是非参考にしてみてください!

では、良いパイプラインライフを!

参考文献

CodePipelineでServerless Frameworkのデプロイを管理する

続きを読む