「え！？ CloudFormationを使ってCloudFrontFunctionsの中身も作成できるんですか！？！？」（CloudFrontFunctionsのIP制限の例）

#1. はじめに
　「Cloud Cloud うるせータイトルだな」と思った方もいらっしゃると思いますが、僕もそう思います。以前[CloudFrontFunctionsについて紹介した記事](https://qiita.com/N_H_tennis/items/d43994cb4e64dac2eab1)を書きましたが、CloudFormationを用いてもCloudFrontFunctionsをデプロイできるという情報を入手したため、今回記事にしてみました。ちなみにCloudFormationって何？ AWS-SAM-CLIって何？って人は[こちらの記事](https://qiita.com/N_H_tennis/items/c0e49be8e04c2a201b6f)が参考になるかもしれません。
　とりあえず手頃な例として、CloudFrontFunctionを用いたIP制限の例をご紹介したいと思います。

#2. テンプレートの書き方
　CloudFormationのテンプレートに慣れている方はなんとなくイメージがつくと思いますが、以下のような流れでcloudfrontのテンプレートに追記していきます。

１、ResourcesにCloudFrontFunctionsの記述を追加
２、CloudFrontDistributionのプロパティの中に、CloudFrontFunctionsの設定を追加

###ResourcesにCloudFrontFunctionsの記述を追加
　CloudFrontFunctionsを追加する場合はResourcesの欄に以下のように記述します。

```yaml
TestFunctions:
  Type: AWS::CloudFront::Function
  Properties:
    Name: <関数名>
    AutoPublish: true 
    FunctionConfig:
      Comment: <コメント>
      Runtime: cloudfront-js-1.0
    FunctionCode: |
      <任意のコード>
```

####Name
　CloudFrontFcuntionの名前。注意として、CloudFormationではストック名、リソース名、関数名の三つでCloudFrontFunctionが管理されます。そのため、すでにCloudFormationでデプロイ済の関数名を変更しようとした時、Nameの値をいきなり変更すると「TestFunctionsと紐づく関数名が違います。」みたいなエラーが吐き出されます。ほんとなんでこんな仕様なんでしょうかね。。。？
　そういうエラーに悩まされたら、一旦ストックごとリソースと関数を消してしまいましょう。

####AutoPublish
　デプロイ時に関数を自動で発行するかどうかのパラメーターになります。こちらをtrueにしておかないと、CloudFrontを同じテンプレート上で作成し紐づけた際に「TestFunctionsがデプロイされていないため、関数を設定できません。」みたいなエラーが吐き出されます。CloudFrontを同じテンプレートで管理する方はtrueで問題ないと思います。

####FunctionConfig
　コメントとランタイムを指定できます。ちなみに、こちらの情報は公式HPでは必須となっていませんが、記述しておかないとエラーになります。何か書いておきましょう。現状ランタイムには「cloudfront-js-1.0」しか値が入りません。

####FunctionCode
　ここに関数の中身を書きます。これに関しては「3.IP制限のやり方」にて記述します。

###CloudFrontDistributionのプロパティの中に、CloudFrontFunctionsの設定を追加
 こちらは大した手間ではありません。リソースで記述したディストリビューションの中で、
Properties → DistributionConfig → DefaultCacheBehavior(もしくは CacheBehaviors) → FunctionAssociations
の中で、イベントタイプとCloudFrontFcuntionのARNを記述します。

```yaml
TestDistribution:
  Type: "AWS::CloudFront::Distribution"
  Properties:
    DistributionConfig:
      DefaultCacheBehavior:
        - FunctionAssociations: 
            - EventType: viewer-request
              FunctionARN: !GetAtt TestFunctions.FunctionMetadata.FunctionARN
```

　どこのパスに関数を設定するかでDefaultCacheBehaviorとするかCacheBehaviors決定しましょう。今回は簡単なIP制限になりますのでDefaultCacheBehaviorに設定します。
　イベントタイプは任意の値を入力してください。viewer-request か viewer-response の値を入力可能です。

#3.IP制限のやり方
　FunctionCodeの中身を利用してIP制限を行っていきます。
　CloudFrontFcuntionでは、関数を実行する際にeventとして様々な情報を受け取ります。IPアドレスだけでなくヘッダー、クエリ、クッキーの情報も含まれます。受け取ったeventの中で、event.viewer.ipという箇所にIPアドレスが含まれているため、今回はそれを用いて制限をします。

以下コードです。

```yaml
      FunctionCode: |
        function handler(event) {
          var request = event.request;
          var viewerIP = event.viewer.ip;
          if(viewerIP != '1.1.1.1'){
            var indexPath = `/error/index.html`;
            request.uri = indexPath;
          }
          return request
        }
```

　これにて任意のIPアドレス以外が入ってきたときにerror/index.htmlに飛ばすことができるようになりました。'1.1.1.1'には任意のIPアドレスを入力してください。

#4.終わりに
　IP制限等の簡単な処理の場合、WAFやLambdaEdge等が選択肢に入ってくると思いますが、CloudFrontFcuntionがリリースされたことで、気軽にIP制限を行えるようになりました。ただ、[署名付きクッキーによるカスタムエラーレスポンス](https://qiita.com/N_H_tennis/items/0d4696b9c0d14e749edd)を同時に利用すると（なぜかはわかっていませんが）エラーが発生してしまうので注意が必要です。なぜ、エラーが発生するのかわかったらまた加筆したいと思います。
ご精読ありがとうございました。