CloudFormaitonで作成したStackを手動で変更した時のメモ

投稿内容は私個人の意見であり、所属企業・部門見解を代表するものではありません。

##

手動で変更した場合の対応は、以下に記述があるとおりテンプレートに合わせて環境を手動でイジる必要があります。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/troubleshooting.html#troubleshooting-errors-update-rollback-failed
>
リソースに対する変更が AWS CloudFormation 外で加えられた
元のスタックテンプレートと一致するようにリソースを手動で同期した後、更新のロールバックを続けます。たとえば、AWS CloudFormation がロールバックしようとしているリソースを手動で削除した場合、元のスタックで使用していたのと同じ名前およびプロパティを使用してそのリソースを手動で作成する必要があります。


セキュリティグループを例にどのような対応が必要になるか記載したいと思います。

基本なるテンプレートは以下です。

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '443'
        ToPort: '443'
        CidrIp: 0.0.0.0/0
```

##ユースケースごとに挙動の違いを確認する

以下の様なユースケースを考えてみました。

ケース1)セキュリティグループを削除
ケース2)セキュリティグループのルール内容を変更
ケース3)セキュリティグループのルールを追加
ケース4)複数リソースを定義してあり、一つを削除して、片方の値を更新した場合
ケース5)手動で変更後にテンプレートを変更しない場合


###ケース1)セキュリティグループを削除
```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '443'
        ToPort: '443'
        CidrIp: 0.0.0.0/0
```
セキュリティグループを手動で削除してみるとDriftは以下になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/b171050e-6cb7-d9d2-975f-339b4a0dae86.png)

HTTPSからHTTPに変更してupdate stackしてみます。

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/838a891d-df6a-62c8-53e8-81269496223d.png)

リソースが存在しないためUpdateに失敗し、UpdateのRollbackも失敗します。
このようなケースは、スキップしたいリソースを指定して更新のロールバックを続ける
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-continueupdaterollback.html

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/16088f5f-84fa-8308-4463-a3c98ec442b7.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/f7e16734-565f-af5c-fce4-d51d74de3c7c.png)

ロールバックが完了しました。新規にルールを作成するためにリソース名を変更します。

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0
```
update stack実行します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/4189844d-ac8e-d160-ac1f-1b896eceea25.png)

無事新しいリソースが作成できた。

Driftもない状況です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/ab6ae2c9-ea83-70a0-4df3-66356af121bd.png)

###ケース2)セキュリティグループのルール内容を変更

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0
```

HTTPからLDAPにしてみる
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/968ceb6d-89e0-d81d-b436-89c48908290d.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/2f6bf3aa-3a5c-de0c-e665-ce73b48efba6.png)

CidrIpを変更する

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 192.168.0.7/32
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/00852067-f4ec-c9f0-2949-c38622f30c35.png)

Updateは成功し、新規にルールが作成される

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/e5a55ed1-8590-9574-f7da-c3096bd78470.png)

Driftあり
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/700d2253-9468-0860-14d4-321cfa4df7fe.png)

LDAPを手動で削除
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/4e1da552-22b8-978f-a560-a65cb7132bd9.png)

DrifrがなくなりIN_SYNCになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/79d734e8-b26e-1aff-eff2-d18801ae900f.png)

###ケース3)セキュリティグループのルールを追加

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 192.168.0.7/32
```

手動でSMTPを追加
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/2a582ed9-8aed-51ae-ec83-7af9bc955697.png)

HTTPからHTTPSに変更する

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '443'
        ToPort: 443'
        CidrIp: 192.168.0.7/32
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/d7967beb-05f8-05f6-6d41-d07cc4396cac.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/9326bb0d-3b4f-d653-96ee-57762336a21f.png)

手動で追加したルールがDriftとなる
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/cd9026e9-f7a6-e5c1-1da2-8253fb050ff4.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/b8c230b4-5d16-0cfc-2832-709df6c55087.png)

手動で削除するとDriftは解消できる

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/ba5e25fa-0e9b-42d6-70a6-459eaedb68c0.png)

###ケース4)複数リソースを定義してあり、一つを削除して、片方の値を更新した場合

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup1:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup1
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 192.168.0.7/32
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup2
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '443'
        ToPort: '443'
        CidrIp: 192.168.0.7/32
```

SecurityGroup1を手動で削除し、SecurityGroup2を443から22に手動で変更します。
Driftを確認すると、DeletedとModifiedとなっています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/288e663d-ebe8-f0c2-59f8-a0e08917625a.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/d00ca08d-a53e-0709-c883-234b2772bd18.png)

SecurityGroup1を削除して、SecurityGroup2のポートをHTTPSからHTTPSに変更してUpdateStackします。

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup2
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 192.168.0.7/32
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/7803b508-cfdb-f89d-b2fa-c15b69972360.png)

Deletedは解消
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/26d6f1fc-f903-e0ba-13fb-59d78b23fcd9.png)

ルールが2つになっている
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/a49cd887-9eec-b1a9-d39f-c57afe811c02.png)

ルールを削除してIN_SYNCにする
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/238d5e9e-b0d3-2a89-4e54-9a248d42403e.png)

###ケース5)手動で変更後にテンプレートを変更しない場合

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: vpc-26505643
      GroupDescription: SecurityGroup2
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 192.168.0.7/32
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/34326ad8-10e0-2b35-bf61-fa739b97acf4.png)

テンプレートに変更がないと、そもそもcFnは実行されません。

```
aws cloudformation update-stack --template-body file://test3.yaml --stack-name teststack

An error occurred (ValidationError) when calling the UpdateStack operation: No updates are to be performed.
```

##まとめ

Driftで差分を把握することが容易になりましたが、手動で変更した場合は、リソースごとに対応方法がことなるため、うやはり運用者にはリソースを変更するIAM権限を付与せず、変更はcFnから実施するようにした方が混乱は少なくなるかと思います。やはり前提として、cFnでの変更作業を実施する場合、cFnからしか変更できないようにすべきだと考えています。

cFnでの管理が向いているもの
大前提としてステートを持たないもの→DBなどは障害時のリストアがあるためcFnでの管理に向かない
初期構築のみでcFnで一気に構築し、運用は手動or別のツールを使用する
Blue/Green的に切り替える場合の環境構築
テンプレートを細かく分けて、update stackの変更の範囲を局所化するといった設計が必要

cFnでの運用に向かいないもの
ネストしたStack構成。割り切ってImport/Exportなしでべた書きで運用した方が結合度は低くなり運用負荷が下がる場合もある