Cloud FormationのNestを利用してDynamoDBのAuto Scaling Policyの設定を共通化しつつ設定する方法

# Cloud FormationのNestを利用してDynamoDBのAuto Scaling Policyの設定を共通化しつつ設定する方法

AWS IoT Analytics（Analyticsと略す）の設定をCloud Formationで行う際にいろいろ躓いたポイントがあった為、備忘録として書いています。（2019/04/21現在）

この記事にたどり着いた方であれば各サービスの詳細は理解されていると思いますので、サービスの説明は行いません。

## 結論

この記事を必要としている人はすぐにでも結論が欲しいと思いますので、先に結論のYAMLデータを記載します。

1. CloudFormationNest-DynamoDB.ymal
2. DynamoAutoScalingPolicy.yaml

`CloudFormationNest-DynamoDB`を利用する際は`DynamoAutoScalingPolicy`のS3のオブジェクトURLが必要になりますので、予め`DynamoAutoScalingPolicy`をS3に配置しておいてください。


``` CloudFormationNest-DynamoDB.ymal
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  DynamoAutoScalingPolicyArn:
    Type: String
    Description: ARN of DynamoAutoScalingPolicy located in S3
Resources:
  MyTable:
    Type: 'AWS::DynamoDB::Table'
    Properties:
      AttributeDefinitions:
        - AttributeName: myId
          AttributeType: S
      BillingMode: PROVISIONED
      KeySchema:
        - AttributeName: myId
          KeyType: HASH
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: false
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5
      SSESpecification:
        SSEEnabled: false
      TableName: DeviceDetail
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 3015a06e-6c21-4f73-989c-c64dc5d12838
  DeviceDetailTableStack:
    Type: 'AWS::CloudFormation::Stack'
    Properties:
      Parameters:
        TableArn: !GetAtt 
          - MyTable
          - Arn
      TemplateURL: !Ref DynamoAutoScalingPolicyArn
    Metadata:
      'AWS::CloudFormation::Designer':
        id: fa5b0b9f-a8cc-40b7-8006-a2da962f8247
Metadata:
  'AWS::CloudFormation::Designer':
    3015a06e-6c21-4f73-989c-c64dc5d12838:
      size:
        width: 60
        height: 60
      position:
        x: 60
        'y': 90
      z: 1
      embeds: []
    fa5b0b9f-a8cc-40b7-8006-a2da962f8247:
      size:
        width: 60
        height: 60
      position:
        x: 180
        'y': 90
      z: 1
      embeds: []
```

``` DynamoAutoScalingPolicy.yaml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  WriteScalableTarget:
    Type: 'AWS::ApplicationAutoScaling::ScalableTarget'
    Properties:
      MaxCapacity: !Ref MaxCapacityUnit
      MinCapacity: !Ref MinCapacityUnit
      ResourceId: !Select 
        - '5'
        - !Split 
          - ':'
          - !Ref TableArn
      RoleARN: !Join 
        - ':'
        - - 'arn:aws:iam:'
          - !Ref 'AWS::AccountId'
          - >-
            role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable
      ScalableDimension: 'dynamodb:table:WriteCapacityUnits'
      ServiceNamespace: dynamodb
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 8b392778-4c47-48cc-aac1-8f13e12adff1
  WriteScalingPolicy:
    Type: 'AWS::ApplicationAutoScaling::ScalingPolicy'
    Properties:
      PolicyName: !Join 
        - ':'
        - - DynamoDBWriteCapacityUtilization
          - !Select 
            - '5'
            - !Split 
              - ':'
              - !Ref TableArn
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref WriteScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: !Ref TargetValue
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBWriteCapacityUtilization
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 69304c5c-aee0-43bd-9ed7-ebca83b836f8
  ReadScalableTarget:
    Type: 'AWS::ApplicationAutoScaling::ScalableTarget'
    Properties:
      MaxCapacity: !Ref MaxCapacityUnit
      MinCapacity: !Ref MinCapacityUnit
      ResourceId: !Select 
        - '5'
        - !Split 
          - ':'
          - !Ref TableArn
      RoleARN: !Join 
        - ':'
        - - 'arn:aws:iam:'
          - !Ref 'AWS::AccountId'
          - >-
            role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable
      ScalableDimension: 'dynamodb:table:ReadCapacityUnits'
      ServiceNamespace: dynamodb
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 41d783a2-011a-424c-b8da-a4b61296ef53
  ReadScalingPolicy:
    Type: 'AWS::ApplicationAutoScaling::ScalingPolicy'
    Properties:
      PolicyName: !Join 
        - ':'
        - - DynamoDBReadCapacityUtilization
          - !Select 
            - '5'
            - !Split 
              - ':'
              - !Ref TableArn
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ReadScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: !Ref TargetValue
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBReadCapacityUtilization
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 2ffe994e-2f5e-44a0-b591-ccb6222630e2
Parameters:
  TableArn:
    Type: String
    Description: ARN of the table
  MaxCapacityUnit:
    Type: Number
    Description: Table autoscaling maximum capacity unit
    Default: 40000
  MinCapacityUnit:
    Type: Number
    Description: Table autoscaling minimum capacity unit
    Default: 5
  TargetValue:
    Type: Number
    Description: ''
    Default: 70
Outputs: {}
Metadata:
  'AWS::CloudFormation::Designer':
    41d783a2-011a-424c-b8da-a4b61296ef53:
      size:
        width: 60
        height: 60
      position:
        x: 60
        'y': 90
      z: 1
      embeds: []
    2ffe994e-2f5e-44a0-b591-ccb6222630e2:
      size:
        width: 60
        height: 60
      position:
        x: 180
        'y': 90
      z: 1
      embeds: []
      isassociatedwith:
        - 41d783a2-011a-424c-b8da-a4b61296ef53
    8b392778-4c47-48cc-aac1-8f13e12adff1:
      size:
        width: 60
        height: 60
      position:
        x: 60
        'y': 210
      z: 1
      embeds: []
    69304c5c-aee0-43bd-9ed7-ebca83b836f8:
      size:
        width: 60
        height: 60
      position:
        x: 180
        'y': 210
      z: 1
      embeds: []
      isassociatedwith:
        - 8b392778-4c47-48cc-aac1-8f13e12adff1
```

## Cloud Formation Stackの説明

最初に公式リファレンスの【[Cloud Formation Stack](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html)】を見てみましょう。
重要な項目は次の2つです。

```
Parameters
    このネストされたスタックが作成されたときに AWS CloudFormation に渡される一連のパラメーター。
    必須: 条件による (ネストされたスタックで入力パラメータが要求される場合は必須)
    タイプ: 整数
    更新に伴う要件: 更新に中断が伴うかどうかは、更新対象のリソースによって異なります。更新が原因で、ネストされたスタックが置き換えられることはありません。

TemplateURL
    リソースとして作成するスタックを指定するテンプレートの URL。テンプレートファイルには、.json、.yaml、.template、.txt など、任意の拡張子を使用できます。テンプレートは、Amazon S3 バケットに保存されている必要があります。
      つまり、URL は、https://s3.amazonaws.com/.../TemplateName.extension の形式になっている必要があります。
    必須: Yes
    タイプ: String
    更新に伴う要件: 更新に中断が伴うかどうかは、更新対象のリソースによって異なります。更新が原因で、ネストされたスタックが置き換えられることはありません。
```

`TemplateURL`は`DynamoAutoScalingPolicy.ymal`のS3のオブジェクトURLになります。
`Parameters`は`DynamoAutoScalingPolicy.ymal`に設定されている`Parameters`に依存しますので、`DynamoAutoScalingPolicy.ymal`のパラメータを見てみましょう。

```
Parameters:
  TableArn:
    Type: String
    Description: ARN of the table
  MaxCapacityUnit:
    Type: Number
    Description: Table autoscaling maximum capacity unit
    Default: 40000
  MinCapacityUnit:
    Type: Number
    Description: Table autoscaling minimum capacity unit
    Default: 5
  TargetValue:
    Type: Number
    Description: ''
    Default: 70
```

現在は4つのパラメータについて設定がされています。

・`TableArn`はAutoScalingの設定を行いたいテーブルのARN
・`MaxCapacityUnit`はAutoScalingの最大キャパシティーユニット（公式のデフォルト値を設定）
・`MinCapacityUnit`はAutoScalingの最少キャパシティーユニット（公式のデフォルト値を設定）
・`TargetValue`はAutoScalingのターゲット使用率（公式のデフォルト値を設定）

基本的には、上記の4つのパラメータを設定すればいいですが、`TableArn`を除くパラメータはデフォルト値を設定しているので、デフォルトでいい場合は特に設定を行う必要はありません。

次に`CloudFormationNest-DynamoDB.yaml`のCloud Formation Stackを見てみましょう。

```
  DeviceDetailTableStack:
    Type: 'AWS::CloudFormation::Stack'
    Properties:
      Parameters:
        TableArn: !GetAtt 
          - MyTable
          - Arn
      TemplateURL: !Ref DynamoAutoScalingPolicyArn
```

今回は`TableArn`のみを設定しているので、他3つのパラメータはデフォルト値で設定されます。
これで、数行のCloud Formation Stackを記述するだけで特定のDynamoDBにAuto Scalingの設定を行うことができます。

## 最後に

説明が足りなかったり、誤りがありましたらコメントにてご指摘ください。
調べてみるとCloud FormationのNestは意外と簡単でした。

今回のNestにAuto Scalingの設定を寄せたことによって、テーブルごとに４つの項目で140行程度を毎回書かなくてよくなりました。
私がやっていたときは7つのテーブルがあったので、24項目840行程度を削減することができました。
構成次第では積極的に利用していきたいと思います。