echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【AWS】Web ACLを設定する方法&CloudFormationでの注意点

概要

前回前々回と、CloudFrontに関する記事でしたが、今回もCloudFrontです。
今回は前々回に触れられなかった、WAF Web ACL(※)の作成について。

※Web Application Firewall - Web Access Control Listの略。WAFと同じもの...という認識でOK。

やること

  • Web ACLの前提条件作成
  • 条件の作成
  • ルールの作成
  • Web ACLの作成
  • CloudFrontへの適用

Webの前提条件作成

AWS WAF and AWS Shield」から「AWS WAF」を選択し、「Configure Web ACL」を選択します。
※「Concepts overview」では、条件(Condition)・ルール(Rules)・Web ACLについての説明があるので、読んでおくとよいかも。

「Next」ボタンをクリックすると「Name web ACL」画面でWeb ACLの前提条件を設定します。

項目は以下の通り。

項目名 説明
Web ACL name(必須) Web ACL nameの名前
CloudWatch metric name(必須) Web ACLのCloudWatch上でのメトリクス名。(Web ACL Nameを設定すると自動で設定されます。任意設定も可能)
Region(必須) Web ACL nameを設定するWeb ACLのリージョン(下記参照)
AWS resource to associate Web ACLを関連付けするAWSリソース。(後から設定することも可能)

【Regionについて】

  • CloudFrontに関連付けを行う場合、「Global」を選択してください。
  • 個別リージョンは、CloudFront以外のリソース(API Gatewayなど)に関連付けする場合に設定します。

f:id:Makky12:20190812181252p:plain

条件(Condition)の作成

「Create conditions」で、Web ACLに適用する条件(=フィルタ)を設定します。
設定可能なのは、下記の通り。

項目名 説明
Cross-site scripting match conditions XSS攻撃に対応するフィルタ
Geo match conditions アクセスされた地域(国)でのフィルタ
IP match conditions アクセス元のIPアドレスに対応するフィルタ
Size constraint conditions リクエストデータのデータ量でのフィルタ
SQL injection match conditions SQLインジェクション攻撃に対応するフィルタ
String and regex match conditions リクエストデータに含まれる文字列に対応するフィルタ

個々項目の詳細設定については、公式サイトを参考。
(項目名を見ればわかるようなものばかりだけど)

とりあえず、ちょっと分かりにくそうなものだけ抜粋。

項目名 説明
Part of the request to filter on フィルタ対象の項目(ヘッダ、クエリ文字列、など)
Transformation チェック前にフィルタ対象項目の値に実施させる処理。
(「Simplify Command Line」は、コマンドラインと思われる文字列を無効化する処理。OSコマンドインジェクション対応用です)
Comparison operator データ量比較の不等号の設定(=、<=、< など)
Match type 文字列比較のパターン(部分一致、完全一致、など)

f:id:Makky12:20190812181323p:plain

ルール(Rule)の作成

「Create rules」でルールを作成します。
「条件とルールって、何が違うの?」と思うかもしれませんが、Web ACLでの「ルール」は、「1つ以上の条件をまとめて管理する入れもの」となります。

「Create Rule」ボタンをクリック後、ルールの内容、及び「Add Conditions」で関連付けする条件を設定します。

  • 「Add Conditions」は、「Geo Match」及び「IP Address」が「指定した条件と一致」、それ以外は「指定した条件と少なくとも1つが一致」です。
  • 「Rule Type」は、下記の通り。
項目名 説明
Regular Rule 「Add Conditions」で設定した内容を満たしたとき、ルールを適用する
Rate-Based Rule 「Add Conditions」で設定した内容を満たし、かつ5分間のリクエスト回数が「Rate Limit」以上のとき、ルールを適用する

「Add rules to a web ACL」で作成したルールを選んで、「Add Rule to web ACL」をクリックすると、そのルールをWeb ACLへ採用できます(複数選択可能です)。
その後、以下の2項目&その際の動作を設定します。

項目名 説明
If a request matches all of the conditions in a rule 個々のルールについて、設定した条件をすべて満たした場合の動作(上から順に評価)
If a request doesn't match any rules どのルールも、ルールに設定したどの条件も満たさなかった場合(=デフォルトの動作)

最後に「Review And Create」をクリックします。

f:id:Makky12:20190812181348p:plain

Web ACLの作成

最後の「Web ACLの作成」ですが、これは「Review and create」で「Confirm and Create」ボタンをクリックするだけです。
また各項目の「Edit」ボタンをクリックすると、その項目の再設定ができます。

f:id:Makky12:20190812181405p:plain

CloudFrontへの適用

最後にCloudFrontへの適用ですが、これはCloudFrontの[Distribution Settings] - [AWS WAF Web ACL]で、作成したWeb ACLを選択するだけです。

f:id:Makky12:20190812181419p:plain

CloudFromationでWeb ACLを作成する際の注意

ここからは、CloudFormationでWeb ACLを作成する際の注意になります。

※CloudFormationの詳細はここでは説明しないので「CloudFormation?よくわからないし、関係ないな」って人は、スルーでOKです。

WAFリソースの違い

CloudFormationのWAFリソースには、下記2種類があります。

これの違いですが、WAFのリージョンになります。(下表参照)

リソース名 リージョン
AWS::WAF Global(CloudFront)
AWS::WAFRegional Regional WAF Resource(リージョン別WAF)
  • CloudFrontに適用するWAFは「AWS::WAF」で定義しなければなりません。
  • AWS::WAFRegionalで定義すると、デプロイした際のCloudFrontとの紐づけ時に「指定されたリソースにアクセスできない(「アクセス権限がない」だったかも)」というエラーが発生します。
「Geo match conditions」設定時の注意

条件の「Geo match conditions」(=地域(国)でのフィルタ条件)ですが、CloudFromationの場合、下記の事項があるので注意しましょう。

  • AWS::WAFRegionalでのみ設定可能。(AWS::WAFでは設定不可)
    • AWS::WAFで作成すると、ルールとの紐づけ時に「指定されたIDのリソースがありません」というエラーが発生します。
  • 公式の「AWS WAF Regional リソースタイプのリファレンス」ページを表示しても、表示言語を「日本語」にしていると「AWS::WAFRegional::GeoMatchSet」へのリンクが表示されない。
    • 表示言語を「English」にすれば表示されます。
  • 今のところ、CloudFrontに「Geo match conditions」を適用する場合、コンソールで行うしかない?

まとめ

と、またちょっと長くなりましたが、今回はここまで。

最近、Cognito&AWS Amplifyを使った認証コードをガッツリ書いてたので、次のネタはそれにしようかな?

てか、Azureは...

【AWS】API Gatewayのエンドポイントに独自ドメインでアクセスする

概要  

前回CloudFrontを使った独自ドメインによるSPAの公開を記事にしましたが、今回はその第二弾で、API Gateway独自ドメインでアクセス方法に関する記事です。  

触れること

  • CloudFrontとAPI Gatewayの連携方法(てか、これしかない)  

触れないこと  

  • API Gatewayの細かい手順(作成方法・設定・Lambdaなどとの連携など)  
  • 前回触れた手順  

やること一覧  

API Gatewayの各種設定  

API Gatewayの作成や各種などについては、今回は触れません。
※公式サイトなどを参考に作成&デプロイしてください。そこまで難しくはないと思います)

とりあえず、動作確認用のLambda関数の正常動作が「テスト」で確認できればOKです。(「サンプルから作成」でOKです。)

重要なのは、「ステージ」で表示される「エンドポイントURL」で、これをコピーしておいて下さい。   f:id:Makky12:20190802185133p:plain

SSL証明書の作成  

これは前回の記事そのままなので、そちらを参考に独自ドメインの証明書を作成してください。(リージョン:北バージニアじゃないとダメな点に注意!)  

※本当によく忘れるんです、この設定... 

CloudFrontの設定  

で、一番重要なCloudFrontの設定です。
ただこれも前回との共通部分が多いので、違う部分だけ説明します。
(下記に記載していない項目は、前回と同じでOKです。)

項目名 設定値 説明 備考
Original Domain Name API Gatewayの各種設定」でコピーしたエンドポイントURL CloudFrontで制御を行うAWSリソース ※1
Origin Protocol Policy HTTPS Only CloudFrontから各種リソース(今回はAPI Gateway)に接続する際のプロトコル API Gatewayは、HTTPSのみ許可しているので
Origin Response Timeout 4~60のいずれか レスポンスを待つ時間(秒)。これを過ぎたらレスポンスタイムアウトと判断
Origin Keep-alive Timeout 1~60のいずれか API Gatewayとの接続がアイドル状態になったときに待つ待ち時間(秒)
HTTPS Port 任意 API Gatewayへの接続ポート デフォルトは443
Origin Custom Headers 任意 API Gatewayに送信したいヘッダ 任意にヘッダ情報を送信したい場合に設定する。(x-api-keyなど)
Query String Forwarding and Caching 下記説明に従い、適切な値を クエリ文字列の転送とキャッシュ ※2
CNAME API Gateway接続用の独自ドメイン CloudFront→API Gatewayに接続するURLの別名

※1:Original Domain NameにURLをコピペすると、ステージ部分(dev/stgなど)が自動で「Origin Path」にカット&ペーストされます。

※2:文字通り「クエリ文字列の転送とキャッシュ」なのですが、非常に重要な部分です。

項目名 説明 クエリ文字列のオリジン(=API Gateway)への転送
None クエリ文字列はすべて無視し、キャッシュは実行しない 一切転送されない
Forward all, cache based on whitelistl Policy ホワイトリストに指定したパラメータは、値が同値ならキャッシュから使用。違っていれば取得してキャッシュに格納する。
※順番もチェックするので注意。
すべて転送される
Forward all, cache based on all クエリ文字列部分のすべてをチェック。順番、値をすべて含め、1文字でも違ったら値を取得してキャッシュに格納する。
完全一致の場合のみキャッシュから使用
すべて転送される(クエリ文字列として)

つまり「None」だと、クエリパラメータが一切API Gatewayに渡りませんAPI Gatewayへのパラメータ転送はクエリ文字列で行うことも多いと思うので、この点は要注意です。(てか、実際ハマった...)

【参考】

Route53の設定  

最後にRoute53ですが、これは「SSL証明書の作成」同様、前回記事そのままなので、そちらを参考にホストゾーン&レコードセット(エイリアスあり)を設定してください。

あとは、ブラウザのURLなりcURLなりで動作確認してみて、正しいレスポンスが返ってこればOKです。

結構ざっくりな感じになってしまいましたが、今回はここまで。

 

【AWS】CloudFrontを用いてサーバーレスSPAを独自ドメインで公開する

概要

Vue.jsによるサーバーレスSPAをCloudFront(AWSCDNサービス)を用いて、独自ドメインで公開する方法です。

※必要最低限しか書いてないので、後日追加するかもしれません。
※2019/7/29 画像&一部説明を追加しました。

前提条件

  • Vue.jsのサービスをデプロイできる状態にしておく。
  • ドメインの取得を行っておく

※ここではこれらには(あまり)触れません

参考サイト

やること

最低限、下記のことが必要になります(多分)。

※下記事項は、別記事で触れる予定

S3バケットのWebホスティング 

Vue.jsサービスのデプロイ用S3バケットをパブリックに公開して、Webホスティングする必要があります。
手順は下記のとおりです。

  • Vue.jsサービスデプロイ用のS3バケットを作成する
  • [プロパティ]タブ - [Static Website Hosting]で、
    • 「このバケットを使用してウェブサイトをホストする」を選択する。
    • 「インデックスドキュメント」に「index.html」を指定する。
  • [アクセス権限]タブ - [アクセスコントロールリスト]で、
    • 「Everyone」の「オブジェクトの一覧」を許可する。

f:id:Makky12:20190729173019p:plain
f:id:Makky12:20190729173046p:plain
f:id:Makky12:20190729173059p:plain

SSL証明書の発行

独自ドメイン(≒CNAME)で公開するには、先にそのドメインSSL証明書の発行が必要です。
手順は以下の通りです。

  • 「Certificate Manager」にて、「証明書のプロビショニング」の「今すぐ始める」をクリック。
  • 「証明書のリクエスト」で、「パブリック証明書のリクエスト」を選択。
  • ドメイン名の追加」で、ドメイン名を入力。
    • ワイルドカード指定も可能なので、例えば「*.makky12.com」みたいな指定もできる。
  • 「検証方法の選択」では「DNS検証」と「Eメール検証」、いずれかを選択。
    • 画面に記載があるが、「DNS 設定を変更するアクセス許可」があるならDNS検証、ないならEメール認証を選択。
    • AWS的には、権限があるならDNS検証を推奨している。
  • 「証明書」画面で「状況」が「認証完了」になればOK。

※なお、SSL証明書は必ず「リージョン:北バージニア(us-east-1)」で作成しないとCloudFront設定できないので注意!!!

f:id:Makky12:20190729174113p:plain

CloudFrontの設定

で、やってCloudFrontの設定になります。やり方ですが、まず「CloudFront」で「Create Distribution」-「[Web]のGet Started」をクリックします。

設定画面での設定値は、下表を参照。

項目名 設定値 説明 備考
Original Domain Name S3バケットのWebホスティング」でWebホスティングをしたバケット CloudFrontで制御を行うAWSリソース これを選ぶと「Origin ID」が自動で設定される
Viewer Protocol Policy Redirect HTTP to HTTPS ブラウザなどからアクセスされた際に適用するプロトコル 厳密には何でもよいが、セキュリティを考えると、HTTPは避けたほうが。
Object Caching Customoize キャッシュの設定 個別設定を適用するならCustomize
TTL関連の3つ(Maximum, Minimum,Default) すべて0 キャッシュの保存期間(秒) 1以上だと、デプロイしたコンテンツの反映に時間がかかる(らしい)
Alternate Domain Names(CNAMES) 公開するドメイン ここで設定したデストリビューションの別名 証明書で証明を受けないと、設定できない
SSL Certificate 「Custom SSL Certificate」を選択し、「SSL証明書の発行」で作成したSSL証明書を選択 使用するSSL証明書 証明書を「北バージニア」で作成しないと、ここでリストに表示されない。
Default Root Object index.html デフォルトで表示するコンテンツ名 Vue.jsの場合、ルートのindex.htmlを表示する(はず)

で、「Create Distribution」をクリックし、該当のデストリビューションの「Status」が「Deployed」になればOK。
ただし5~15分程度かかるので、気長に待ちましょう。(In Progressの場合、デプロイ処理中です。)

また、以下の設定もデプロイ処理中に行っておきます。

  • 該当のデストリビューションの「ID」をクリック。
  • 「Error Pages」タブの「Create Custom Error Response」をクリック。
  • 「HTTP Error Code」の403、404について、下記設定で「Create」を行う。
項目名 設定値 説明 備考
Customize Error Response Yes レスポンスのカスタマイズを行うか
Response Page Path /index.html エラー発生時に表示するコンテンツ 詳細は下記参照
HTTP Response Code 200 OK 設定したレスポンスに設定するステータスコード

※Vue.jsだと、本来どのURLを参照してもルートのindex.html(/index.html)が表示されないといけませんが、例えば「/hoge」というパスを指定した場合も、
/hoge/index.htmlを参照します。

当然そんなページはないので404 Not Foundが返されますが、その際に代わりに表示するページです。(Vue.jsのRouter側で適切な処理を実施する必要があります)

f:id:Makky12:20190729181635p:plain
f:id:Makky12:20190729181649p:plain
f:id:Makky12:20190729181808p:plain

Route53によるエイリアス設定

最後はRoute53によるエイリアス設定です。
下記手順で行います。

  • Route53の「DNS管理」を選択。
  • 「ホストゾーンの作成」で、サブドメイン名を含まないドメイン名(「makky12.com」など)を入力。
    • 「タイプ」は「パブリックゾーン」
  • 作成したホストゾーンの「ドメイン名」をクリックし、「レコードセットの作成」をクリックする。
  • 「名前」に、サブドメイン名を入力する。(「hogehohe.(makky12.com)」など)
  • エイリアス」を「はい」に設定し、「エイリアス先」に「CloudFrontの設定」で作成したデストリビューションの「Domain Name」を入力する。

f:id:Makky12:20190729182150p:plain

長々と記載してしまいましたが、あとは「S3バケットのWebホスティング」で作成したバケットにVue.jsをデプロイすれば、これで独自ドメインでSPAを起動できるはずです。

【Vue.js】子コンポーネントの埋め込み方法+各種プロパティ設定方法

経緯

最近、仕事でVue.jsを触っており(AWSによるサーバーレスSPA)、何とか一週間で他のメンバが開発をできる土台(フレームワーク的なもの)をVue.jsで作成する...程度はできるようになりました。

その中で、コンポーネント(*.vue)の扱いでよく使いそうな、子コンポーネントの扱いについて便利そうな事項をまとめました。

参考ページ

そもそも、子コンポーネントの埋め込み方は?

単に親コンポーネント内に子コンポーネントを埋め込む場合は、下記でOKです。

/* これが子コンポーネント */
<template>
  <div id="body_header">
    <router-link to="/">page1</router-link>
    <router-link to="/page2">page2</router-link>
  </div>
</template>  
  
<script>
export default {
  name:"body_header",
  components: {},
  data: function () {
    return {}
  },
}
</script>  
  
<style>
#body_header {
  height: 40px;
  background: white;
  /* box-shadow: 0px 3px 3px rgba(0,0,0,0.1); */
  display: flex;
  justify-content: center;
  align-items: center;
  border-bottom: 1px #000000 solid;
}
#body_header a {
  text-decoration: none;
  color: #2c3e50;
  margin: 0 10px;
  padding: 3px 10px;
  background: #5ccebf;
}
</style>  
/* これは親コンポーネント */  
<template>
  <div id="app">  
    <!-- ここで埋め込む -->
    <bodyHeader></bodyHeader>
    <router-view></router-view>
  </div>
</template>  
  
<script>  
// 上記の子コンポーネントを読み込む
import bodyHeader from './components/common/header'
export default {
  name: 'App',
  
  components: {
    // ここで子コンポーネント&HTMLタグ名を登録
    bodyHeader: bodyHeader,
  },
  data: function () {
    return {}
  }
}
</script>  
  
<style>
body {
  margin: 0;
}  
  
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
</style>  

コンポーネントの変数を変えたい

コンポーネントの使い方でよくあるのが、下記の例だと思います。

その場合、子コンポーネントの変数やスタイルシートなどを変更する必要があると思いますが、そのやり方です。

コンポーネントの変数の変更方法

コンポーネントの変数を親コンポーネントから変更する場合、下記でOKです。

/* これが子コンポーネント
(無関係な部分は先述のソースと同じなので省略) 
*/
<template>
  <div id="body_header">
    <router-link to="/page1">{{ $myLink }}</router-link>
  </div>
</template>  
  
<script>
export default {
  name:"body_header",
  components: {},
  
  data: function () {
    return {
      myLink : ''
    }
  },
  
  methods:  {
    setter(linkValue) {
      this.myLink = linkValue;
    }
  }
}
</script>  
/* これは親コンポーネント */  
<template>
  <div id="app">  
    <bodyHeader ref="myBodyHeader"></bodyHeader>
  </div>
</template>  
  
<script>  
// 上記の子コンポーネントを読み込む  
import bodyHeader from './components/common/header'  
export default {
  mounted: function() {
    // 子コンポーネントのpage1へのリンクに「page1へ飛びます」と表示される。
    this.$refs.myBodyHeader.setter('page1へ飛びます');
  }
}
</script>  
  
<style scoped>
</style>  

コンポーネントのスタイルの変更方法

コンポーネントのスタイルを変更する場合は、ちょっと厄介ですが、下記の方法でOKです。

※なお、親コンポーネントのstyleタグにscopeを付けない方法もありますが、本来の使い方ではないですし、Vue.jsのスタイルルールA(必須)に反するので、お勧めしません。

  • 子クラスのスタイルを採用したいHTMLタグに、何かクラスを定義する。
  • 親クラスで、子クラス埋め込みタグの親要素として、何かクラス付きのタグを埋め込む
  • 親クラスのstyle内で、「(子クラス埋め込みタグの親要素クラス) (子クラス埋め込みタグクラス) >>> (子クラスのスタイルを採用したい要素のクラス)」という定義を用意する。
/* これが子コンポーネント
(無関係な部分は先述のソースと同じなので省略) 
*/
<template>
  <div id="body_header">
    <div class="my_item">{{ $myItem }}</div>
  </div>
</template>  
/* これは親コンポーネント */  
<template>
  <div id="app">  
    <div class="parent">
      <bodyHeader class="bh"></bodyHeader>
    </div>
  </div>
</template>  
  
<script>  
// 上記の子コンポーネントを読み込む  
import bodyHeader from './components/common/header'  
export default {
  mounted: function() {
    // 子コンポーネントのpage1へのリンクに「page1へ飛びます」と表示される。
    this.$refs.myBodyHeader.setter('page1へ飛びます');
  }
}
</script>  
  
<style scoped>  
.parent .bh >>> .my_item {
  /* 子コンポーネントのmy_itemクラスの背景色が青色になる */
  background-color: #0000FF;
}
</style>  

ちなみに

もちろん、1つの親コンポーネント内で同じ子コンポーネントを使いまわすことも可能です。

/* これが子コンポーネント
(無関係な部分は先述のソースと同じなので省略) 
*/
<template>
  <div id="body_header">
    <div class="my_item">{{ $myItem }}</div>
  </div>
</template>  
  
<script>
export default {
  name:"body_header",
  components: {},
  
  data: function () {
    return {
      myItem : ''
    }
  },
  
  methods:  {
    setter(itemValue) {
      this.myItem = itemValue;
    }
  }
}
</script>  
  
<style scoped>  
</style>
/* これは親コンポーネント */  
<template>
  <div id="app">  
    <div class="parent">
      <bodyHeader ref="myBodyHeader" class="bh1"></bodyHeader>
    </div>  
    <div class="parent">
      <bodyHeader2 ref="myBodyHeader2" class="bh2"></bodyHeader>
    </div>
  </div>
</template>  
  
<script>  
// 上記の子コンポーネントを読み込む  
import bodyHeader from './components/common/header'  
import bodyHeader2 from './components/common/header'  
  
export default { 
    components: {
      bodyHeader : bodyHeader ,
      bodyHeader2: bodyHeader2
  },
  
  mounted: function() {
    this.$refs.myBodyHeader.setter('bodyHeader1');  
    this.$refs.myBodyHeader2.setter('bodyHeader2')
  }
}
</script>  
  
<style scoped>  
.parent .bh1 >>> .my_item {
  background-color: #0000FF;
}  
  
.parent .bh2 >>> .my_item {
  background-color: #FF0000;
}
</style>  

ちょっと急ぎ足&文字数が多くなりましたが、今回はここまで。

...てかはてなブログ、Vue.jsに対応してくれないかなあ。(HTML/CSS/javascriptうまく表示してくれるとか)

【Serverless Framework】簡単デプロイとserverless.ymlの記載について その2

※2019/7/1 デプロイ先バケット名を任意の名前にする方法を追記しました。

概要

前回Serverless Frameworkについて書きましたが、書ききれなかった点があったので、今回はその点についての内容になります。

serverless.ymlで使用できる変数について

Serverless.ymlでは、下記の変数を使用できます。

  • 変数は${self;;xxx}のように、「${}」形式で参照します。
  • 「custom」項目内に変数名と値のペアを設定することで、独自の変数&値を設定することができます。
  • 外部ファイルに定義した変数を使用することもできます。(下記ソース参照)
  • 下記変数は、カンマの後に値を指定することで、初期値を設定可能。
記載方法 説明 備考
${self:xxx} そのserverless.ymlファイル自身の項目(xxx)を参照する
${env:YYY} 使用しているPCの環境変数(YYY)を参照する
${opt:zzz} deployコマンド実施時に、引数(zzz)に指定された値を参照する
${file(aaa):bbb} 外部ファイル(aaa)に定義した変数(bbb)を参照する bbbは外部ファイルの変数名。省略した場合、外部ファイルの変数すべてが読み込まれる。
${cf:リージョン名.ccc} 別のCloudFormationの「出力」cccに設定されている値を参照する 「出力」にはServerless.ymlの「Outputs」で登録可能。
リージョン名は省略可能。その場合、デフォルトのリージョン名が適用される。
service:
  name: makky12-service
  
# 独自の変数を定義する場合、こんな感じでcustom内に定義する。
custom:
  author: makky12

  # 外部ファイルの変数を参照する例。  
  # exportは外部ファイルの内容をすべて読み込み、export_partnerは外部ファイルの  
  # 特定の変数(partner)の値のみを読み込む。  
  # またどちらも実際に読み込むファイルは、ルート直下の「--env xxx」で  
  # 指定されたxxxフォルダ内のserverless.ymlファイル。  
  # 例えば、 deploy --env dev なら、ルート直下の「dev」フォルダ内の  
  # serverless.ymlファイルを参照する。
  export: ${(./${opt:env, 'dev'}/serverless.yml)}  
  export_partner: ${(./${opt:env, 'dev'}/serverless.yml):partner}  
  
provider:  
  # 環境変数を使用する例。  
  # 例えば事前にser MY_REGION=ap-northeast-1としていた場合、  
  # regionにはap-northeast-1が設定される。  
  region: ${env:MY_REGION}  
  
  apiName: HogehogeGateway  
  
  # 例えば、事前にOutputsに下記定義をしたserverless.ymlをデプロイした場合...
  # resources:
  #   Outputs:
  #     Value:
  #       "Ref": myRestApi
  #     Export:
  #       name: myRestApiId  
  #  
  # restApiIdには、スタック名が「myStack」というCloudFormationの、  
  # 事前にmyRestApiで定義したAPI GatewayのRestAPI IDが代入される。
  apiGateway:  
    restApiId: ${cf:myStack.myRestApiId}  
  
# serverless.ymlの変数を使用する方法
functions:
  myFunction:  
    # この場合、参照先serverless.ymlで下記定義をしていた場合、  
    # partner: abc  
    # thanksto: xyz
    #   
    # 実際のdescriptionの出力内容は以下の通りになる。  
    # author=makky12_partner=abc_thanksto=xyz
    description: author=${self:custom.author}_partner=${self:custom.export_partner}_thanksto=${self:custom.export.thanksto}
  myFunction2:
    # もちろん、serverless framework既定の項目の値を参照することも可能。
    description: ApiGateway=${self.provider.apiName}

deploymentBucket記載時の注意

前回「deploymentBucketには、パッケージ化したファイルが格納されるバケット情報を指定する」と記載しましたが、これには一つ問題があり、

name(バケット名)を定義すると、その名前のバケットが存在しない場合、エラーになる

という現象があります。(nameを指定しない場合、バケットがデフォルトの名前で新規作成されます。またnameのバケットが既に存在する場合は問題ありません)

これだとバケット名を任意の名前で新規作成できないのですが、下記方法で回避できます。

  • npm経由で「serverless-deployment-bucket」モジュールをインストールする
  • serverless.ymlの「plugins」に、このモジュールを追加する。(配列で定義する)
  • deploymentBucket.nameに、デプロイ先バケットの名前を設定する。
  • serverless-deployment-bucketの設定を「custom」に定義する。(下表参照。なおすべて任意項目で、値はすべてbooleanで指定。)
項目名 説明 初期値
versioning デプロイ先バケットで、バージョニングを可能にする false
enabled serverless-deployment-bucketを有効にするか true
policy バケットのポリシーをJSONで出力するか(?) なし(設定しない)
> npm install serverless-deployment-bucket --save-dev
# オプション項目を設定する場合。(基本、デフォルトで問題ないと思う)
custom:  
  enabled: true  
  versioning : true  
  policy: true  
    
plugins:
  - serverless-deployment-bucket  
  
provider:  
  deploymentBucket:  
    name: self-custom-bucket-name  

【参考】:serverless-deployment-bucket

デプロイのやり方

といっても、デプロイ自体はとても簡単で、サービスのルートフォルダ(serverless.ymlがあるフォルダ)で、「sls deploy」コマンドを実施するだけです。
ただ、いくつか便利なオプションがあります。

オプション名 説明 備考
--packege(-P) <格納先フォルダのパス> 関連ファイルのパッケージのみ行い、デプロイはしない フォルダのパスを省略した場合、serverlessというフォルダを作成し、そこにパッケージ化したファイルを格納する
--verbose(-v) デプロイ状況をコンソール上で確認できる CloudFormationのログに近い。よく使うと思う。
--function(-f) <functions:内の定義名> 定義名のLambda関数のみデプロイし、全体のデプロイは行わない 一回デプロイ後、Lambda関数のみ修正した場合に便利。通常デプロイに比べて、短時間で済む

もちろん、まだまだフォローしきれてない部分もありますので、そこは適宜公式サイトで確認すると、いろいろ理解が深まると思います。

それでは、今回はこの辺で。

【Serverless Framework】簡単デプロイとserverless.ymlの記載について その1

※2019/6/27 Outputs項目に関する記載を追加しました。
※2019/6/27 さらにapiKeys/usagePlan/private項目に関する記載を追加しました。

概要

近日、仕事でAWSを触っていますが、その関係でServerless Frameworkを触ることになりました。

公式サイト:Serverless Framework

Serverless FrameworkはSAM同様、サーバーレスアプリ開発フレームワーク(正確にはテンプレートモデルに近い。Webアプリなどのフレームワークとはちょっと違う)CLIで、以下のことを行うことができます。

  • 環境(言語)ごとのテンプレートモデルの適用
  • 使用するリソース(Lambda、S3、DymnamoDBなど)の定義
  • コマンドライン上からの一括デプロイ(CloudFormationへの展開)

また、主に下記の点がSAMと異なります。

  • マルチクラウドプラットフォームである。(AWSだけでなく、AzureやGCPなどでも使える)
  • (ローカル環境での)デバッグ実行に、コンテナ(Dockerなど)が不要

があります。

ただ、公式ドキュメントが全編英語で、解説してるサイトもあまり多くなかったので、自分なりに「とりあえずデプロイ」できるところまでをまとめました。

インストール &プロジェクト作成

インストールは公式サイトのトップにもある通り、npm installをするだけです。(「-g」は必要に応じて置き換えてください。)

npm install -g serverless

プロジェクト作成は「serverless create」コマンドで作成します。

  • テンプレート名は言語別のテンプレート名。「aws-nodejs」「aws-python3」「aws-csharp」など。(詳細は公式ページを参照)
  • サービス名はアプリのプロジェクト名(≒アプリ名)。Serverless Frameworkでは「サービス」という扱いになる。
  • プロジェクトフォルダのパスはオプション。このフォルダがない場合、プロジェクト作成時に自動で作成して、そこをプロジェクトのルートにしてくれる。
# 「sls」は「serverless」の省略系。どちらでも実行可。
sls create --template (テンプレート名) --name (プロジェクト名)  --path (プロジェクトフォルダのパス)  
# e.g.  
sls create --template aws-nodejs --name makky12-serverless-fw-app --path makky12-dir

テンプレートファイル(serverless.yml) の定義

※これ以降は、AWSをベースに説明します。(それ以外の場合は、公式ドキュメントを参照)

serverless.ymlは、デプロイされるサービスの定義を行うファイルです。
(SAMのtemplate.ymlに該当)

つまりこの内容が、CloudFormationやLambdaなどの各種機能に設定されます。

ただこの公式ドキュメントのボリュームが大きいので、補足説明がいるなという項目だけ説明します。
(基本的なことは、公式の下記ページに書いてあります。全編英語ですが)

※プロジェクトに最初から入ってるhandler.jsを動かすだけなら、下記を設定してデプロイを実施するだけで、API Gateway経由でブラウザ実行できます。
(項目数は多いですが、ほとんどにデフォルト値が設定されてます。)

  • providerに「region: ap-northeast-1」を設定する。
  • 66 ~ 69行目(「events」~「method:get」)までのコメントを外す

ルート項目(インデントがない項目)

項目名 説明 備考
provider 複数項目で共通で使用する項目の値を設定する 項目別に上書き可能
custom serverless.yml内で使用する変数と値のペアを設定する 変数については後述
package プロジェクトをパッケージする際の設定をする
plugins 外部プラグインを使用する場合、プラグイン名を記入する 公式に書いてないんだけど...
functions Lambda関数の設定をする
resources プロジェクトで使用するリソース(S3/DynamoDBなど)の設定をする

provider項目

項目名 説明 備考
stage dev(開発)/stg(ステージング)など、環境が分かる設定をする 初期値はdev
〇〇name CloudFormation/API Gatewayなどで使用される名前 未指定の場合、デフォルト名がつけられる(プロジェクト名+αなど)
apiKeys 作成するAPIKeyの名前を指定する 配列で指定する
usagePlan 使用量プランの定義を指定する apiKeysに指定したAPIKeyに紐づく使用量プランとして登録される。
(なぜか)usagePlanName(使用量プラン名)は指定できない。(resourcesだと指定可能)
profile credentialを定義済の場合、そのプロファイル名を指定する デプロイの際、設定したプロファイルの認証情報を使用する
deploymentBucket パッケージされたファイルを格納するS3のバケット名を指定する 注意が必要。(後述)
role 全Lambda関数に適用される共通のIAMロールを設定する
iamRoleStatements Lambda関数以外のリソース(S3/DynamoDBなど)のIAMロールの設定をする
environment Lambda関数の環境変数と値のペアを設定する

package項目

項目名 説明 備考
include/exclude パッケージされるファイルとして含めたい/除きたいファイルを指定 ワイルドカードによる再帰的指定可能。tsconfig.jsonの同盟項目と同じ
excludeDevDependencies excludeDevDependenciesのモジュールを自動で除外するか デフォルトはtrue
artifact 自分でパッケージをする際のパッケージファイルの置き場? include/exclude設定関係なく、deploy時にパッケージファイルから除外される
individually Lambda関数単位でのパッケージを行うかどうか

functions項目

※functionsの子要素には、デプロイされるLambda関数のfunction名を記載する。下記はその子要素(functionsの孫要素)

項目名 説明 備考
handler 実行する関数。[ファイル名.exports名]形式で記載
events Lambda関数を実行するトリガーとなるイベントの種類 http(API Gateway)/s3/Alexa Skillなど
reservedConcurrency この関数を同時並行で実行できる数?
environment この関数に定義する環境変数と値のペア

resources項目

※resources要素には「Resources」-「リソース定義名」までは決め打ちになる。(「serverless.ymlのサンプル」参照)
下記は「リソース定義名」の子要素になる。(=resourcesのひ孫要素)
※Propertiesの子要素は、Typeごとに異なる。CloudFormationのテンプレートを参照。

項目名 説明 備考
Type リソースのタイプ。DynamoDB/S3など [ファイル名.exports名]形式で記載 AWS::S3::Bucketのように、CloudFormation形式で記載
Properties リソースタイプ別の詳細設定 S3ならバケット名など。(設定項目はリソースにより異なる)

Outputs項目

※Outputsの子要素には、Output項目の定義名を指定する。下記はその子要素になる。(=Outputsの孫要素)
※Outputsに定義した項目はCloudformationの「出力」に登録され、別のCloudFormationテンプレイートファイルから参照することができる。
※もちろん別のserverless.ymlファイルからも参照することが可能。(詳細はその2で記載予定)

項目名 説明 備考
Value 他の定義で参照させたいリソース、プロパティなどの情報。 Ref, Fn::GetAttなど、参照を使用して設定することが可能
Export 他の定義から参照する際に使用する名前(≒エイリアス) 「Name」プロパティに設定する
Description この定義についての説明

serverless.ymlのサンプル

app: makky12-serverless-app

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: ">=1.0.0"

custom:  
  author : makky12  

provider:
  name: aws
  runtime: nodejs10.x  
  stage: dev
  region: ap-northeast-1
  stackName: makky12-serverless-app
  apiName: makky12-serverless-app-dev
  deploymentBucket:
    name: makky12-serverless-app-dev-bucket
    serverSideEncryption: AES256
  deploymentPrefix: makky12
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "s3:ListBucket"
      Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
    - Effect: "Allow"
      Action:
        - "s3:PutObject"
      Resource:
        Fn::Join:
          - ""
          - - "arn:aws:s3:::"
            - "Ref" : "ServerlessDeploymentBucket"
            - "/*"

package:
  exclude:
    - exclude-dir/readmme.txt

functions:
  greeting:
    handler: handler.greeting
    events:
      # httpはAPI Gatewayがトリガになるイベント。  
      # corsはクロスオリジン制約に関する設定。
      # privateにtrueを設定すると、APIキーの指定が必須になる。(x-api-key)
      - http:
          path: users/create
          method: get
          cors: true
          private: true

  environment:
    NENGO: Reiwa

resources:
  Resources:
    makky12Bucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: makky12-bucket
    makky12Table:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: makky12Table
        AttributeDefinitions:
          - AttributeName: e-mail
            AttributeType: S
        KeySchema:
          - AttributeName: e-mail
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

Outputs:
  MyWebHookUrl:
    Description: The Url of the Custom WebHook
    Value:
      "Fn::GetAtt": [ myWebHook, Url ]
    Export:
      Name: myWebHookUrlVal

と、なんか書いてたらものすごく長くなってしまいましたので、本当はデプロイやserverless.ymlの変数仕様についても書く予定だったのですが、それはその2に書きます。

【Microsoft】de:code2019体験記その2 面白かったセッション紹介

概要

前回、de:code2019の「C# ドキドキ・ライブコーディング対決」を自分でも実施しましたが、今回は純粋に面白かったセッションの紹介をしようと思います。

Xamarin.Forms アプリケーション設計パターン

マイクロソフトの大田 一希氏(かずきさん)のセッション。
Xamarin.Formsの新機能や、設計&作成時のパターンなどについて、詳しく解説していただきました。

また、デモもあり、動作しているところを実演していただける...
はずだったんですよね。

マイクロソフトのこういう大きいイベントでは、どなたか一名は必ず本番で悲劇に見舞われるのですが...今回はかずきさんだったようです。(意味深)

とはいえ、楽しませて頂きましたし、僕もその気持ちは痛いほどわかりますからね。(てか何で、起こって欲しくないときに限って、確実に起こるんでしょうね、フリーズって。)

Windows 10 対応のデスクトップアプリを作る技術

上に続き、再びかずきさんのセッション。
UWPとかWPFとか、その他についての情報でした。

てか、UWPって敬遠気味だったんですが、すごい進化してるんですね。
デモを見てて、すごくおもしろそうでした。

何事も、食わず嫌いはいけませんね。ホントに

エンジニアの人生設計 ~どのようにキャリアを描いていけばよいのか~

MSのテクノロジーセンター長(要はめちゃくちゃ偉い人)、澤円さんのセッション。
僕がde:code2019で一番聞きたかったのがこのセッション。

いや、事前に一番人気と聞いていましたが(しかも澤さん本人から!)、想像以上でした。 (さすがに参加者が多かったとはいえ、前のセッション開始時から行列ができてたのは、このセッションだけです)

しまいには部屋に収まりきらず、急遽別会場を用意して、それでも収まりきらない...という大盛況ぶり。

さすが「プレゼンの神」の異名は伊達じゃないなあ、と思いました。

さて内容は、技術とは一切関係はないのですが、いわば「人生を豊かに」そして「幸せに生きるには」という、非常に重要なテーマです。

そして重要なのは、

  • 自己分析&自分に合ったことをする
  • キャリアの定期的な見直し&ポートフォリオの公開
  • ビジネスのつながりを積極的に構築する(Twitter&LinkedInなどで)
  • 睡眠だけは、絶対おろそかにしてはダメ!

という、分かっているんだけど、なかなか出来ないことの重要性を改めて教えてくれる、ありがたい内容でした。

...僕も幸せになりたいなあ。

結論

個人的は、やはり勉強会に限らず、たとえ東京の勉強会・イベントであっても、出来る限りこういうイベントは参加しておくべきだなあ、と感じました。

確かに出費は痛いんですが、色々な人と出会えたり、新しい技術を知ることが出来るのは大きいですし、何よりモチベーションの上がり方が半端ないです。

来年も、ぜひde:codeに参加したいなあ、と思いました。

...てなことを、現在(2019/6/13 21時ちょうど)AWS Summitに向かう新幹線の中で書いてます。