個人的にCloudFormationでよく使用する記載、コマンド集

## 概要
CloudFormation　を使用する際に、よく使用する表記やコマンド、操作などの
情報共有および備忘録用としてメモしておきます。

## よく使用するパラメータ、表記など

### !Sub + !FindInMap の組み合わせ

`!Sub` と `!FindInMap` を組み合わせることで使い回ししやすCFnを作成することができる。
リージョン毎に値を変更したい場合に、よく使用する。

```yaml
Mappings:
  Prefix:
    ap-northeast-1:
      value: Tokyo
    ap-northeast-3:
      value: Osaka
# ========== <省略> ================
        BackupPlanName: !Sub
          - ${Key}-backup-plan 
          - Key: !FindInMap [Prefix, !Ref "AWS::Region", value]
          # 東京リージョンの場合 Tokyo-backup-plan
          # 大阪リージョンの場合 Osaka-backup-plan
```

### アカウントID

**`AWS::AccountId` を使用することで、アカウントIDの値を返すことができる。**
ユニークな値を生成する際などに使用する。

```yaml
Outputs:
  MyAccountIdk:
    Value: !Ref "AWS::AccountId"
    Value: !Sub "${AWS::AccountId}"
```

### リージョンの表記

`AWS::Region` を使用することで、デプロイ先のリージョンを表現することができる。

```yaml
Outputs:
  MyStacksRegion:
    Value: !Ref "AWS::Region"
    Value: !Sub "${AWS::Region}"
```

### AZ  (アベイラリティゾーン)

`!GetAZs` を使用することで、AZの一覧を表示することができる。この関数に `!Select` を組み合わせることで、 簡単にAZを表現することができる。以下は例

```yaml
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""] # ap-northeast-1a
```

:::note warn
デフォルト サブネットがあるアベイラビリティーゾーンがない場合を除き、`Fn::GetAZs` 関数はデフォルト サブネットがあるアベイラビリティーゾーンのみを返します。その場合、すべてのアベイラビリティーゾーンが返されます。
:::

### 条件

`Conditions` や `Condition` を使用することで、パラメータの条件によってリソースを作成したりすることができる。
また `AWS::NoValue` を使用することで属性を削除することができる。

```yaml
Parameters:
  EnvType:
    Description: Environment type.
    Default: test
    Type: String
    AllowedValues:
      - prod
      - test
Conditions:
  CreateProdResources: !Equals # EnvTypeがprodの場合、true
    - !Ref EnvType
    - prod

Resources:
  InstanceProfile:
    Condition: CreateProdResources # CreateProdResourcesがtrueのみ作成される
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref EC2Role
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref ImageId
      InstanceType: t3.micro
      IamInstanceProfile: !If
        - CreateProdResources
        - !Ref InstanceProfile
        - !Ref AWS::NoValue # CreateProdResourcesがfalseの場合、この属性を削除する
      SubnetId: !Ref PrivateSubnetId
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-EC2
```

### 循環依存

**1つのリソースがそれ自体に依存していること**、あるいは**2つのリソースが互いに依存していることによって、リソースの作成に失敗する場合がある。**
セキュリティグループを作成する際に、よく発生する。
※セキュリティグループAはセキュリティグループAの通信のみ許可する場合など、

以下に循環依存のNGな例と、OKの例を記載する。
```yaml
# NGな例
  InstanceSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH from EIC
      VpcId: !Ref Vpc
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref InstanceSG
```

```yaml
# OKな例 SGの箱を作成し、Igressを別途設定することで循環依存を解消できる
  InstanceSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH from EIC
      VpcId: !Ref Vpc
  InstancSgIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref InstanceSG
      IpProtocol: tcp
      FromPort: 22
      ToPort: 22
      SourceSecurityGroupId: !Ref InstanceSG
```

### 最新のAMI取得

パラメータストアの「パブリックパラメータ」に最新のAMIのIDが記載されている。
そこから値を取得することによって、最新のAMI IDを取得することができる。

```yaml
# AL2023の場合
Parameters:
  Al2023EC2ImageId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
    # /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64

Resources:
  MyEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageId
      InstanceType: t3.micro
```

:::note warn
最新の AMI ID を指定できる反⾯、AMI ID が更新された場合は EC2 の再作成が⾏われます
スタック更新の際には、変更セットで意図しない EC2 の再作成が⾏われないことをご確認ください。
:::

## セキュリティやポリシー、インポート関連

### スタックポリシー

CFnはデフォルトで全てのリソースが更新されるようになっている。
だが、EC2、RDSなど更新したくないリソースがでてくるケースがある。
スタックポリシーを使用することで、意図しない更新を防ぐことができる。
スタックポリシーはJSON形式で作成する。以下は、EC2とRDSの更新を防ぐスタックポリシーである。

```json
{
  "Statement" : [
  {
    "Effect" : "Deny",
    "Principal" : "*",
    "Action" : "Update:*",
    "Resource" : "*",
    "Condition" : {
      "StringEquals" : {
        "ResourceType" : ["AWS::EC2::Instance", "AWS::RDS::DBInstance"]
      }
    }
  },
  {
    "Effect" : "Allow",
    "Principal" : "*",
    "Action" : "Update:*",
    "Resource" : "*"
  }
  ]
}
```

既存のリソースに設定する場合のコマンド例

```yaml
aws cloudformation set-stack-policy \
--stack-name sample-stack \
--stack-policy-body=file://stack-policy.json 
```

### 既存リソースのインポート （ざっくり手順）

1. インポートしたいリソースをCFnに記載し、`DeletionPolicy: Retain` を追記する。
2. CFnのマネジメントコンソールより、インポートを実行する。この際、リソース識別子を入力する必要がある。
3. ドリフト検出を実行し、テンプレートとリソースが一致していることを確認する。

:::note warn
EC2、RDSの依存リリースが多いものは、インポートが難しいので注意すること。
:::

### CloudFormation Guard

Ruleを記述した `.guard` ファイルを作成し、CFnのテンプレートに対してルールの検証をすることができる。
この機能を利用することで、テンプレートが要件に沿ったものかを判断することができる。
CI/CDに活用できる。

例　EBS暗号化が有効になっていることを確認する `guard` ルール

```yaml
# EC2のEBSが暗号化されているかの確認
let aws_ec2_instance_resources = Resources.*[ Type == 'AWS::EC2::Instance' ]
rule aws_ec2_ebs when %aws_ec2_instance_resources !empty {
  %aws_ec2_instance_resources.Properties.BlockDeviceMappings[*].Ebs.Encrypted == true
}

# EBSが暗号化されているかの確認
let aws_ec2_volume_resources = Resources.*[ Type == 'AWS::EC2::Volume' ]
rule aws_ebs when %aws_ec2_volume_resources !empty {
  %aws_ec2_volume_resources.Properties.Encrypted == true
}
```

テンプレートの検証方法

ルールに沿っていれば何も結果が出力されず、ルールに違反している場合はメッセージが出力される。

```yaml
cfn-guard validate --data <テンプレート名>.yaml --rules <guardファイル>.guard
```
## コマンド集

### デプロイ

```bash
aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --profile sample
```


### 検証コマンド

以下のコマンドを実行することで、テンプレートを検証することができる。
問題がなければ、パラメータの構文情報が返却され、
問題があれば、エラーメッセージが返却される。

```bash
aws cloudformation validate-template \
--template-body file:./sampletemplate.json \
--profile sample
```

### 削除コマンド

```bash
aws cloudformation delete-stack \
--stack-name myteststack \
--profile sample
```

### 変更セットの作成

新しいパラメータ値、修正済みのテンプレート、または両方を指定できる。たとえば、次のコマンドで  `Sample` という名前の変更セットが作成される

```yaml
aws cloudformation create-change-set \
--stack-name sample-stack \
--change-set-name Sample \
--template-body=file://<CFnファイル名>.yaml 
```

### 変更セットの確認

```yaml
aws cloudformation describe-change-set --change-set-name <変更セットのID>
```

### 変更セットの実行

```yaml
aws cloudformation execute-change-set --change-set-name <変更セットのID>
```

## その他

### Amazon CodeWhisperer

CodeWhisperer を利⽤すると、コメント⾏に記載した構成を実現するためのコードサジェストやコードの補完をしてくれます。
こちらを使用すると、記述スピードが20割近く速くなるので、可能であれば導入することをおすすめします。
https://docs.aws.amazon.com/ja_jp/codewhisperer/latest/userguide/what-is-cwspr.html