CloudFormation IaC ジェネレーターを使ってみての所感

# はじめに

本記事は2024年2月に公開されたされたAWS CloudFormationの新規機能「IaCジェネレーター」の検証記事です。
IaCジェネレーターで生成されたテンプレートを展開した際に、どのようなエラーが発生するかや、その解決方法を纏めています。

https://aws.amazon.com/jp/about-aws/whats-new/2024/02/aws-cloudformation-templates-cdk-apps-minutes/


# IaCジェネレーターとは
アカウントに紐づくリソースをスキャンして、CloudFormationのテンプレートを自動生成してくれる機能です。
複雑な設定はなく、数ステップで簡単にテンプレートを作成することができます。
いくつか考慮事項（1回のスキャンで読み取れるリソースの最大数など）があるので、
使用の際には以下ページを参考にしてください。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/generate-IaC.html

※リージョンはCloudFormationを利用できるリージョンであればIaCジェネレーターも使用できます。

# 検証内容
今回検証に使用する構成は下記の通りです。
汎用的な構成を意識しつつ、リソース数は少なめに設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/13527d4b-f1df-10d4-75ab-0629154411bd.png)

セキュリティグループ(SG)は下記の通り作成しています。

- cfn-test-sg-001

|インバウンドルール|アウトバウンドルール|
|---|:---|
|cfn-test-sg-002(ポート指定無し)|全トラフィック許可|

- cfn-test-sg-002

|インバウンドルール|アウトバウンドルール|
|---|:---|
|cfn-test-sg-001(ポート指定無し)|全トラフィック許可|

今回は、同一アカウント同一リージョンにリソースを複製して検証します。


# 手順
IaCジェネレーターの利用手順は大まかに下記の通りです。
1. アカウントのリソースをすべてスキャンする
2. スキャンした中から必要なリソースを選択し、CloudFormation テンプレートを作成する
⇒出力されたテンプレートをもとに、リソースを作成


<br>

CloudFormationコンソールから「IaCジェネレーター」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/0a5007a7-4b02-6996-9ae1-8bb818fde0a3.png)












スキャンの項から「新しいスキャンを開始」を押下 ※ステータスが更新されるまで待機
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/9fdf0de0-2e4b-2a77-8282-6b498f07a092.png)












完了したことを確認し「テンプレートを作成」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/296a2cc5-1252-fb72-b9a7-806c594460ec.png)






















詳細指定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/581728a2-62a4-0d6e-5b93-190b83f42bb9.png)


デフォルトでは、削除ポリシーおよび置換ポリシーは「保持」であり、各リソースに次の設定を行います。
- DeletionPolicy: "Retain"
- UpdateReplacePolicy: "Retain"



##### DeletionPolicy: "Retain"
- CloudFormationスタックが削除された際にリソースを保持するか
⇒ [UpdateReplacePolicy 属性](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-updatereplacepolicy.html)

##### UpdateReplacePolicy: "Retain"
- CloudFormationスタックを更新オペレーションでリソースを置き換えるときに、リソースの既存の物理インスタンスを保持するか
⇒ [DeletionPolicy 属性](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html)



例として「保持」とした場合のVPCの定義の冒頭を抜粋します。
```
  EC2VPC00vpc067acb29ce1ed0e8200ukkdb:
    UpdateReplacePolicy: "Retain"   #置換ポリシー
    Type: "AWS::EC2::VPC"
    DeletionPolicy: "Retain"   #削除ポリシー
    Properties:
        (後略)
```

今回のスタックはすぐに削除する予定のため、これらの設定は外し「削除」としています。


<br>
スキャンしたリソースの一覧から、必要なリソースをチェック
※一応ではありますが、関連のリソースは次項である程度拾われます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/9fd34257-1ed8-c990-f966-369d7560deb1.png)
























選択したリソースに関連するリソースが表示される為、必要に応じて選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/b4904dcd-1c3f-3b46-c483-391248687607.png)

確認画面から設定を確認し、テンプレートを作成
完了後、テンプレートの詳細が開くため形式を指定しDL
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/552c6845-1479-6227-049d-7d29861801cd.png)

⇒このyamlを利用して検証していきます。

<br>
<br>

リソースの作成時は「スタック」>「スタックの作成」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/f14fb105-9dc0-9f65-27de-093f1dfbf0dc.png)

手元のyamlファイルを指定してリソースを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/37ebbcc0-4b88-8f35-720b-adeebb2880cd.png)


# 検証結果
IaCジェネレーターによる出力をそのまま実行しても、リソース作成時にエラーとなる箇所がありました。引っかかった点と、エラー解消方法の一例をリソースごとに列挙していきます。



## Parameters
CloudFormationのテンプレートファイルは、以下のセクションに分けられます。
- AWSTemplateFormatVersion
- Description
- Metadata
- Parameters
- Rules
- Mappings
- Conditions
- Transform
- Resources
- Outputs
参考：[テンプレートの構造分析](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-anatomy.html#template-anatomy-sections)

リソース定義に用いる「Resources」以外は任意です。
「Parameters」や「Outputs」などは、値の受け渡し等でよく用いられます。


その上で、出力されたyamlの冒頭を見てみます。
```
Metadata:
  TemplateId: "arn:aws:cloudformation:ap-northeast-1:000000000000:generatedTemplate/b06456-9d87-4e92-8e94-dadb3ccc1dac"
  
Parameters:
  RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYtSubnetIdsaakhY:
    NoEcho: "true"
    Type: "CommaDelimitedList"
    Description: "The EC2 Subnet IDs for the DB subnet group."

Resources:
    (後略)
```
「Metadata」はリソースの作成には関係ないので今回は触れません。

「Parameters」は、以下のようにスタック作成時に任意の値を引き渡すことが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/144da6f5-888c-5e3a-a90c-6936c57eeeea.png)


今回のリソース作成に当たってはこの「Parameters」の存在が問題となります。
パラメータ(≒変数)「RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYtSubnetIdsaakhY」をどこで使用しているか確認します。
```
  RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYt:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties:
      SubnetIds:
        Ref: "RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYtSubnetIdsaakhY"
      DBSubnetGroupDescription: "Created from the RDS Management Console"
      DBSubnetGroupName: "default-vpc-067acb29ce1ed0e82"
```
RDSのサブネットグループにおける、サブネットIDのリストとして使用されています。
(出力値そのままなので、Descriptionの文言が実際と異なる点については今回は黙認します…)
参考：[AWS::RDS::DBSubnetGroup](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsubnetgroup.html)



サブネットIDは、外部から値を渡すのではなく、本テンプレート内で作成したものを参照させたいので、「AWS::RDS::DBSubnetGroup」における「SubnetIds」を以下の通り書き換えました。
```
# 変更後
  RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYt:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties:
      SubnetIds: 
      - !Ref EC2Subnet00subnet059cebf0ece9a9fb3009CEK0 #変更
      - !Ref EC2Subnet00subnet03298d9a5026eac7100wpYhs #変更
      DBSubnetGroupDescription: "Created from the RDS Management Console"
      DBSubnetGroupName: "default-vpc-067acb29ce1ed0e82"
```
配列なので「-」が必要です。
また、「!Ref」と「Ref:」は 同義です。  ⇒　参考：[組み込み関数 Ref](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html)
参照先であるサブネット定義は下記の通りです。

```
# 参考
  EC2Subnet00subnet059cebf0ece9a9fb3009CEK0:
    Type: "AWS::EC2::Subnet"
    Properties:
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      MapPublicIpOnLaunch: false
      EnableDns64: false
      AvailabilityZoneId: "apne1-az4"
      PrivateDnsNameOptionsOnLaunch:
        EnableResourceNameDnsARecord: false
        HostnameType: "ip-name"
        EnableResourceNameDnsAAAARecord: false
      CidrBlock: "10.0.128.0/20"
      Ipv6Native: false
      Tags:
      - Value: "cfn-test-subnet-private1-ap-northeast-1a"
        Key: "Name"
  EC2Subnet00subnet03298d9a5026eac7100wpYhs:
    Type: "AWS::EC2::Subnet"
    Properties:
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      MapPublicIpOnLaunch: false
      EnableDns64: false
      AvailabilityZoneId: "apne1-az1"
      PrivateDnsNameOptionsOnLaunch:
        EnableResourceNameDnsARecord: false
        HostnameType: "ip-name"
        EnableResourceNameDnsAAAARecord: false
      CidrBlock: "10.0.16.0/20"
      Ipv6Native: false
      Tags:
      - Value: "cfn-test-subnet-public2-ap-northeast-1c"
        Key: "Name"
```



そして、不要となった下記「Parameters」の項は削除します。
```
# 削除
Parameters:
  RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYtSubnetIdsaakhY:
    NoEcho: "true"
    Type: "CommaDelimitedList"
    Description: "The EC2 Subnet IDs for the DB subnet group."
```




## SecurityGroup
AWS::EC2::SecurityGroupにおいては幾つかのエラーが出ました。

##### ①名称
```
#エラー
Resource handler returned message:
"Security Group with default already exists" 
```

これは、同一アカウント・同一リージョンでリソースを複製しているための名称重複です。
今回は一旦、-duplicateを付与しておきます。他のSGも同様とします。


##### ②SG間の相互参照
```
#エラー
Resource handler returned message:
"You have specified two resources that belong to different networks.
```

該当箇所のソースを見てみると、
SourceSecurityGroupIdが"sg-0cf1f83c2254ef9a5"を直接指定しています。

```
# 変更前
  EC2SecurityGroup00sg00c281f5bdf8c761100su49B:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "cfn-test-sg-002"
      GroupName: "cfn-test-sg-002-duplicate"
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      SecurityGroupIngress:
      - IpProtocol: "tcp"
        FromPort: 0
        SourceSecurityGroupId: "sg-0cf1f83c2254ef9a5" #直接指定となっている箇所
        ToPort: 0
        SourceSecurityGroupOwnerId: "000000000000"
      SecurityGroupEgress:
      - CidrIp: "0.0.0.0/0"
        IpProtocol: "-1"
        FromPort: -1
        ToPort: -1
      Tags:
      - Value: "cfn-test"
        Key: "test"
```
この部分はSecurityGroupIngress(SGのインバウンドルール)を指定している部分であり、テンプレート内で定義されたSG「cfn-test-sg-001」を参照させたい箇所です。

- cfn-test-sg-001

|インバウンドルール|アウトバウンドルール|
|---|:---|
|cfn-test-sg-002(ポート指定無し)|全トラフィック許可|

- cfn-test-sg-002

|インバウンドルール|アウトバウンドルール|
|---|:---|
|cfn-test-sg-001(ポート指定無し)|全トラフィック許可|




但し、SG間で相互に許可をさせながらリソースを定義することは出来ません。
相互残照の場合は、ルールを後から追加するように書き換える必要があります。

参考：
[AWS::EC2::SecurityGroupIngress](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-securitygroupingress.html)
[AWS::EC2::SecurityGroupEgress](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-securitygroupegress.html)



SecurityGroupIngressは削り、「AWS::EC2::SecurityGroupIngress」によって定義します。
名称が解りやすいよう、テンプレート内の識別子を置換しています。
- セキュリティグループ
　：EC2SecurityGroupCfnTestSg001 , EC2SecurityGroupCfnTestSg002
- ルール追記部分
　：EC2SecurityGroupCfnTestSg001Ingress01 , EC2SecurityGroupCfnTestSg002Ingress01



IpProtocolにおいて全てのプロトコルを指定する場合、"-1"を指定します。
 ```
 # 変更後
 # Sg001
   EC2SecurityGroupCfnTestSg001:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupName: "cfn-test-sg-001-duplicate"
      GroupDescription: "cfn-test-sg-001"
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      SecurityGroupEgress:
      - CidrIp: "0.0.0.0/0"
        IpProtocol: "-1"
        FromPort: -1
        ToPort: -1
      Tags:
      - Value: "cfn-test"
        Key: "test"
 # 追記 Sg001インバウンドルール
  EC2SecurityGroupCfnTestSg001Ingress01:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
      Description: "allow-cfn-test-sg-001"
      IpProtocol: "-1"
      SourceSecurityGroupId: !Ref EC2SecurityGroupCfnTestSg002
      GroupId: !Ref EC2SecurityGroupCfnTestSg001
 # Sg002
   EC2SecurityGroupCfnTestSg002:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "cfn-test-sg-002"
      GroupName: "cfn-test-sg-002-duplicate"
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      SecurityGroupEgress:
      - CidrIp: "0.0.0.0/0"
        IpProtocol: "-1"
        FromPort: -1
        ToPort: -1
      Tags:
      - Value: "cfn-test"
        Key: "test"
# 追記 Sg002インバウンドルール
  EC2SecurityGroupCfnTestSg002Ingress01:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
      Description: "allow-cfn-test-sg-001"
      IpProtocol: "-1"
      SourceSecurityGroupId: !Ref EC2SecurityGroupCfnTestSg001
      GroupId: !Ref EC2SecurityGroupCfnTestSg002
 ```
SourceSecurityGroupIdとGroupIdの差異が少し解りづらいので補足します。
- SourceSecurityGroupId：疎通を許可する対象となるSG
- GroupId：ルールの追加元SG(追記対象)


※AWS::EC2::SecurityGroupIngress を用いて後から追記する項目が複数ある場合、
紐づけことにリソース定義が必要となるようです。



## ENI
ENIにおいてもエラーを確認しました。
```
Resource handler returned message: 
"Only one primary private IP address can be specified. 
```


原因は下記において、
PrivateIpAddressとPrivateIpAddressesの双方で指定が行われているためです。
但し、そもそもこのNATgatewayに紐づくENIは自動で生成されるため、
テンプレート上で定義する必要がありません。
このため下記は削除します。

```
# 削除対象
  EC2NetworkInterface00eni0001547e122f5540f00VZniZ:
    Type: "AWS::EC2::NetworkInterface"
    Properties:
      Description: "Interface for NAT Gateway nat-0018e64d199708761"
      PrivateIpAddress: "10.0.13.43"   #重複
      PrivateIpAddresses:
      - PrivateIpAddress: "10.0.13.43"   #重複
        Primary: true
```

参考：[AWS::EC2::NetworkInterface](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-networkinterface.html)

## RouteTable
ルートテーブルは、下記の2要素から成ります。
- AWS::EC2::RouteTable ルートテーブル自体の定義

- AWS::EC2::Route ルーティング定義


#### ①ルーティング定義
「AWS::EC2::Route」側のエラーです。

```
Resource handler returned message: 
"The request must contain exactly one of gatewayId, natGatewayId, networkInterfaceId,
vpcPeeringConnectionId, egressOnlyInternetGatewayId, transitGatewayId, localGatewayId,
carrierGatewayId, vpcEndpointId, coreNetworkArn or instanceId
```

これは、「GatewayId」「VpcEndpointId」が重複していることに起因します。
```
# 変更前
# Route
  EC2Route00rtb006e3fc895adfda8c00Zuif6:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId:
        Ref: "EC2RouteTable00rtb006e3fc895adfda8c00YFsu2"
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId:
        Ref: "EC2InternetGateway00igw08f9bad99562b7c9e00rSQZZ"   #重複
      VpcEndpointId: "igw-08f9bad99562b7c9e"   #重複(かつ直指定)

```
VpcEndpointIdはIgw(igw-08f9bad99562b7c9e)を直接指定しているため、テンプレート内のIgwの識別子を参照(Ref)しているGatewayIdのみを残します。
```
# 変更後
# Igw
  EC2InternetGateway00igw08f9bad99562b7c9e00rSQZZ:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
      - Value: "cfn-test-igw"
        Key: "Name"

# Route
  EC2Route00rtb006e3fc895adfda8c00Zuif6:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId:
        Ref: "EC2RouteTable00rtb006e3fc895adfda8c00YFsu2"
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId:
        Ref: "EC2InternetGateway00igw08f9bad99562b7c9e00rSQZZ"   #修正
```
同様に、「GatewayId」「VpcEndpointId」「DestinationPrefixListId」は重複しています。
さらに、それぞれ固定値を指定しているため、本来はテンプレート内で定義しているリソース(S3エンドポイント)を指定する必要があります。
下記はS3のGWエンドポイントです。
```
# 変更前(最終的には削除)
# Route
  EC2Route00rtb02f19754d1894e29c004gbWg:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId:
        Ref: "EC2RouteTable00rtb02f19754d1894e29c005D6sg"
      DestinationPrefixListId: "pl-61a54008"    #重複
      GatewayId: "vpce-015ebc91268ccfcfa"       #重複
      VpcEndpointId: "vpce-015ebc91268ccfcfa"   #重複
# Route
  EC2Route00rtb01d114d76e0f6e9270007nfg:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId:
        Ref: "EC2RouteTable00rtb01d114d76e0f6e92700cDp0Q"
      DestinationPrefixListId: "pl-61a54008"    #重複
      GatewayId: "vpce-015ebc91268ccfcfa"       #重複
      VpcEndpointId: "vpce-015ebc91268ccfcfa"   #重複
```
しかし、S3のGWエンドポイントを指定している「AWS::EC2::VPCEndpoint」を見ると、
RouteTableIds によって、紐づけが既に行われているようです。
```
# 変更前
# S3エンドポイント
  EC2VPCEndpoint00vpce015ebc91268ccfcfa00WAiAZ:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      PrivateDnsEnabled: false
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      RouteTableIds:
      - Ref: "EC2RouteTable00rtb02f19754d1894e29c005D6sg"   #ルートテーブル
      - Ref: "EC2RouteTable00rtb01d114d76e0f6e92700cDp0Q"   #ルートテーブル
      ServiceName: "com.amazonaws.ap-northeast-1.s3"
      PolicyDocument:
        Version: "2008-10-17"
        Statement:
        - Resource: "*"
          Action: "*"
          Effect: "Allow"
          Principal: "*"
      VpcEndpointType: "Gateway"
      SecurityGroupIds: []
      SubnetIds: []
```
上記により、S3エンドポイント作成時にルートテーブルへの追記が行われます。
このため、RouteにおけるS3エンドポイント関連の記述は削除してしまいます。


#### ②デフォルトルーティング
また、出力にはルーティングにおけるデフォルトの設定(送信先 10.0.0.0/16、ターゲット local)が含まれています。
これらに起因し"AlreadyExists"エラーとなったため、デフォルト項目を削除しています。
```
# 削除対象例
  EC2Route00rtb02f19754d1894e29c005HQs7:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId:
        Ref: "EC2RouteTable00rtb02f19754d1894e29c005D6sg"
      DestinationCidrBlock:
        Fn::GetAtt:
        - "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
        - "CidrBlock"
      GatewayId: "local"
      VpcEndpointId: "local"
```

#### ③ルートテーブル名称
「AWS::EC2::RouteTable」側では
名称の重複エラーが発生したため、Tags(Name)の値に手を加えています。
```
# 変更後
  EC2RouteTable00rtb006e3fc895adfda8c00YFsu2:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      Tags:
      - Value: "cfn-test-rtb-public-duplicate" #-duplicate付与
        Key: "Name"
```
## InternetGateway
下記のように、ルートテーブルとIgwのネットワークが異なるエラーも見受けられました。
```
Resource handler returned message: 
"route table rtb-02061b8f32330fc10 and network gateway igw-0b1b31defadd4e6d6
belong to different networks
(Service: Ec2, Status Code: 400, Request ID: 386f6561-018c-4345-85b5-facd6cee3665)" 
(RequestToken: b9776b2a-f2d8-180a-9953-5d2e38e95eb1, HandlerErrorCode: GeneralServiceException)
```

こちらは、IgwとVPCとの紐づけが行われていないことが原因のようです。
「AWS::EC2::VPCGatewayAttachment」を追加し、作成したVPCとIgwの紐づけを行う必要があります。

```
# 変更後(追記)
  EC2igwVPCAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref EC2InternetGateway00igw08f9bad99562b7c9e00rSQZZ
      VpcId: !Ref EC2VPC00vpc067acb29ce1ed0e8200ukkdb
```

参考：[AWS::EC2::VPCGatewayAttachment](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpcgatewayattachment.html)






## KMS(RDS)
KMSを利用しているのはRDSです。
CloudFormationのスタック作成時、このようなエラーとなります。
```
Resource handler returned message: 
"The new key policy will not allow you to update the key policy in the future.
```
AWS管理キーの設定をそのまま出力しているため、キーポリシーが不当であるとされているようです。


解決方法は大きく2つあります。
- 方法①リソース「AWS::KMS::Key」において、KeyPolicyプロパティをキー管理者・ユーザー用の内容に変更
- 方法②リソース「AWS::RDS::DBInstance」において、KmsKeyIdプロパティを指定しない(出力元と同様の設定はこちら)

##### 方法①ユーザー管理キーとして、キー管理者用のポリシーに変更
キーポリシーの箇所を抜粋すると下記の通りです。
```
  KMSKey00dc3a37bd94a2431d90fcdaebbf1ceb7f00XhFIS:
    Type: "AWS::KMS::Key"
(中略)
      KeyPolicy:
        Version: "2012-10-17"
        Statement:
        - Condition:
            StringEquals:
              kms:CallerAccount: "000000000000"
              kms:ViaService: "rds.ap-northeast-1.amazonaws.com"
          Resource: "*"
          Action:
          - "kms:Encrypt"
          - "kms:Decrypt"
          - "kms:ReEncrypt*"
          - "kms:GenerateDataKey*"
          - "kms:CreateGrant"
          - "kms:ListGrants"
          - "kms:DescribeKey"
          Effect: "Allow"
          Principal:
            AWS: "*"
          Sid: "Allow access through RDS for all principals in the account that are\
            \ authorized to use RDS"
        - Resource: "*"
          Action:
          - "kms:Describe*"
          - "kms:Get*"
          - "kms:List*"
          - "kms:RevokeGrant"
          Effect: "Allow"
          Principal:
            AWS: "arn:aws:iam::000000000000:root"
          Sid: "Allow direct access to key metadata to the account"
(後略)
```
上記の箇所を、キー管理者やキーユーザー用に書き換える事で解消できます。

参考：
[KMS キーの管理をキー管理者に許可する](https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-administrators)
[KMS キーの使用をキーユーザーに許可する](https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-users)



##### 方法②リソース「AWS::RDS::DBInstance」において、KmsKeyIdプロパティを指定しない。

IaCジェネレーターからの出力ではこのようになっています。(関連箇所のみ抜粋)
```
RDSDBInstance00cfntestdatabase100NiLUw:
    Type: "AWS::RDS::DBInstance"
    Properties:
      StorageEncrypted: true
      
    (中略)

      KmsKeyId:
        Fn::GetAtt:
        - "KMSKey00dc3a37bd94a2431d90fcdaebbf1ceb7f0XhFIS"
        - "Arn"

    (後略)
```
「AWS::RDS::DBInstance」においてKmsKeyIdプロパティを指定しない場合、作成されるRDSはデフォルトのKMS キーを使用します。
また、暗号化の設定有無は「AWS::RDS::DBInstance」のStorageEncrypted プロパティに依存します。
つまり、以下のように変更することで、出力元と同様に、AWS管理のKMSキーを利用することができます。
- StorageEncrypted：true
- KmsKeyIdプロパティを指定しない

今回はこちらの方法で修正します。

```
# 修正後
RDSDBInstance00cfntestdatabase100NiLUw:
    Type: "AWS::RDS::DBInstance"
    Properties:
      StorageEncrypted: true

    (後略)
```
参考：[AWS::RDS::DBInstance](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbinstance.html)








## RDS

#### ①パスワード

RDSのパスワードは2項目の設定で制御されます。
- ManageMasterUserPassword
マスターユーザーのパスワードを AWS Secrets Manager で管理する場合にTlueを指定。
後述の「MasterUserPassword」が設定されている場合は Secrets Manager 使用不可。

- MasterUserPassword
マスターユーザーのパスワード。(「/」「"」「@」を除く ASCII 文字)


一方で、IaCジェネレーターからの出力では下記の通りでした。
```
ManageMasterUserPassword: false
```
今回はSecrets Manager を使用する形に書き換えます。
パスワード設定が適切に行われていれば問題ありません。

```
# 変更後
ManageMasterUserPassword: True
```


#### ②DB名称
DB名が重複してしまうため、名称(DBInstanceIdentifier)のみ変更しています。

```
# 変更後
  RDSDBInstance00cfntestdatabase100NiLUw:
    Type: "AWS::RDS::DBInstance"
    Properties:
      StorageEncrypted: true
      AssociatedRoles: []
      CertificateDetails: {}
      ProcessorFeatures: []
      StorageThroughput: 0
      PreferredBackupWindow: "16:24-16:54"
      MonitoringInterval: 0
      DBParameterGroupName: "default.postgres16"
      Endpoint: {}
      NetworkType: "IPV4"
      DedicatedLogVolume: false
      CopyTagsToSnapshot: true
      MultiAZ: false
      Engine: "postgres"
      Tags:
      - Value: "cfn-test"
        Key: "test"
      LicenseModel: "postgresql-license"
      EngineVersion: "16.1"
      StorageType: "gp2"
      DBInstanceClass: "db.t3.micro"
      AvailabilityZone: "ap-northeast-1c"
      OptionGroupName: "default:postgres-16"
      PreferredMaintenanceWindow: "sat:18:53-sat:19:23"
      EnablePerformanceInsights: false
      AutoMinorVersionUpgrade: true
      DBSubnetGroupName:
        Ref: "RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYt"
      DeletionProtection: false
      DBInstanceIdentifier: "cfn-test-database-2"
      AllocatedStorage: "20"
      CACertificateIdentifier: "rds-ca-rsa2048-g1"
      ManageMasterUserPassword: True
      MasterUserSecret: {}
      VPCSecurityGroups:
      - Fn::GetAtt:
        - "EC2SecurityGroupCfnTestSg001"
        - "GroupId"
      DBSecurityGroups: []
      MasterUsername: "postgres"
      EnableIAMDatabaseAuthentication: false
      MaxAllocatedStorage: 1000
      BackupRetentionPeriod: 1
      PubliclyAccessible: false
      EnableCloudwatchLogsExports: []
```


#### ③DBサブネットグループ名称
DBSubnetGroupNameが重複してしまうため、名称を変更しています。
```
# 変更後
  RDSDBSubnetGroup00defaultvpc067acb29ce1ed0e8200IoUYt:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties:
      SubnetIds: 
      - !Ref EC2Subnet00subnet059cebf0ece9a9fb3009CEK0
      - !Ref EC2Subnet00subnet03298d9a5026eac7100wpYhs
      DBSubnetGroupDescription: "Created from the RDS Management Console"
      DBSubnetGroupName: "cfn-test-DBSubnetGroup"
```





## 補足：AvailabilityZoneについて

元のリソースと別のリージョンや、別のAZで展開しようとしており、リソース内で「AvailabilityZoneId」が定義されている場合、注意が必要です。
```
  EC2Subnet00subnet059cebf0ece9a9fb3009CEK0:
    Type: "AWS::EC2::Subnet"
    Properties:
      VpcId:
        Ref: "EC2VPC00vpc067acb29ce1ed0e8200ukkdb"
      MapPublicIpOnLaunch: false
      EnableDns64: false
      AvailabilityZoneId: "apne1-az4"
      PrivateDnsNameOptionsOnLaunch:
        EnableResourceNameDnsARecord: false
        HostnameType: "ip-name"
        EnableResourceNameDnsAAAARecord: false
      CidrBlock: "10.0.128.0/20"
      Ipv6Native: false
      Tags:
      - Value: "cfn-test-subnet-private1-ap-northeast-1a"
        Key: "Name"
```
上記では、AvailabilityZoneId: "apne1-az4"となっていますが、これはAZ名で言うと「ap-northeast-1a」に該当します。
これは、コンソール > EC2 > ダッシュボード から確認可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743306/4eccec31-21b0-6e44-b720-182723f8b569.png)
ゾーン名とゾーンIDの対応が微妙に判りづらいのと、使用できないゾーン(例：ap-northeast-1b)があること注意が必要です。
 


## 所感
AWS内で完結する、リソースからのテンプレート書き起こしツールというのはやはり便利だと思います。
関連リソースの洗い出しは特に嬉しい機能です。
所々気になる所もありますが、0から書くことを考えれば格段に効率的であり初動のハードルも低く感じます。
但し、丸投げしても綺麗に出力されるツールというよりは、後から手を加える事を前提に活用するツールなので、Cloudformationとリソース理解は必要だと感じられました。


## 
株式会社ジールでは、初期費用が不要で運用・保守の手間もかからず、ノーコード・ローコードですぐに手元データを分析可能なオールインワン型データ活用プラットフォーム「ZEUSCloud」を提供しております。
ご興味がある方は是非下記のリンクをご覧ください：
https://www.zdh.co.jp/products-services/cloud-data/zeuscloud/