[AWS][boto3][create_stack] CloudFormationスタックをシリアルに実行するサンプルスクリプト

## 概要

- [以前作ったスクリプト](https://zenn.dev/zgw426/articles/eece074c015382160a0b) の拡張版
- CloudFormationスタックを順次実行するboto3スクリプト
- 実行中のスタックが完了するまで次のスタックは実行しない
- 以下の要望に対応できそう
    - CloudFormationスタックで作成したAWSリソースに依存したAWSリソースをboto3(Pythonスクリプト)で作りたい
    - CloudFormationスタックの実行回数や実行ごとのパラメータを動的に変更したい

## 環境

Windows 10 の以下バージョンの環境にて動作を確認
VPC→サブネット→SG→EC2の順でシリアルにスタックを実行している。実行後に、作成したAWSリソースの情報(EC2のIDなど)が取得できていることがわかる。

```
PS C:\> python3 --version
Python 3.8.10
PS C:\> aws --version
aws-cli/2.2.3 Python/3.8.8 Windows/10 exe/AMD64 prompt/off
PS C:\> python3
Python 3.8.10 (tags/v3.8.10:3d8993a, May  3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import boto3
>>> import botocore
>>> print(f'boto3 version: {boto3.__version__}')
boto3 version: 1.14.43
>>> print(f'botocore version: {botocore.__version__}')
botocore version: 1.17.43
>>>
PS C:\> aws configure
AWS Access Key ID [****************XXXX]:
AWS Secret Access Key [****************XXXX]:
Default region name [ap-northeast-1]:
Default output format [json]:
```

## スクリプト

スクリプトはgithubにおきました。

https://github.com/zgw426/boto3-create_stack-SerialExecution

## 実行前の準備

- S3バケットを作成し、さらにprefix(フォルダ)を作成し以下のようにymlファイルを格納します。

```
PS C:\> aws s3 ls s3://{{S3バケット名}} --recursive
2021-05-07 18:32:58       1940 test/ec2-linux.yml
2021-05-07 17:36:00       1112 test/sg.yml
2021-05-07 17:46:45       1821 test/subnet-public.yml
2021-05-07 17:36:00       1062 test/vpc.yml
```

## 実行方法

コマンド例

```
C:\Users\usr001\tmp> python3 .\test.py -f sample.json -s3 {{S3バケット名}} -p test
```

-f に sample.json を、-s3 に実行前の準備でymlファイルを格納したS3バケット名を、-p prefix(フォルダ)名を指定します。


## 制御について

CloudFormaionスタック実行時に使用するymlファイルと指定するパラメータは、sample.json で指定します。

例１）sample.jsonが以下の場合、ymlファイルは vpc.yml でPJPrefixとVPCCIDRがパラメータになります。StackNameはスタック名です。

```json:sample.json
[
    {"StackName": "test001-vpc","Code": "vpc.yml", "PJPrefix": "Project1","VPCCIDR": "10.11.0.0/16"}
]
```

例２）sample.jsonが以下の場合、2つのスタックを実行します。vpc.ymlを使用したスタックの実行が完了した後で、subnet-public.ymlを使用したスタックが実行を開始します。

```json:sample.json
[
    {"StackName": "test001-vpc","Code": "vpc.yml", "PJPrefix": "Project1","VPCCIDR": "10.11.0.0/16"},
    {"StackName": "test001-subnet1","Code": "subnet-public.yml", "PJPrefix": "Project1","NetworkName": "Net001","PublicSubnetCIDR": "10.11.1.0/24", "AZName": "ap-northeast-1a"},
]
```

例３）本リポジトリに格納したsample.jsonは以下であり、VPC→サブネットワーク→セキュリティグループ→EC2インスタンスの順にシリアルにスタックを実行します。※EC2起動は課金の可能性があるので注意※

```json:sample.json
[
    {"StackName": "test001-vpc","Code": "vpc.yml", "PJPrefix": "Project1","VPCCIDR": "10.11.0.0/16"},
    {"StackName": "test001-subnet1","Code": "subnet-public.yml", "PJPrefix": "Project1","NetworkName": "Net001","PublicSubnetCIDR": "10.11.1.0/24", "AZName": "ap-northeast-1a"},
    {"StackName": "test001-sg","Code": "sg.yml", "PJPrefix": "Project1","ServiceName": "ServiceA","SGNo": "001"},
    {"StackName": "test001-ec2","Code": "ec2-linux.yml", "PJPrefix": "Project1","ServiceName": "ServiceA","SGNo": "001",
        "NetworkName":             "Net001",
        "KeyPairName":             "keypair_ap-northeast-1_01",
        "EC2InstanceName":         "ec2-001",
        "EC2InstanceAMI":          "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2",
        "EC2InstanceInstanceType": "t2.micro",
        "EC2InstanceVolumeType":   "gp2",
        "EC2InstanceVolumeSize":   "8"
    }
]
```


## 実行例

スクリプトを実行したときに出力される標準出力の例


```
PS C:\Users\usr001\tmp> python3 .\test.py -f sample.json -s3 {{S3バケット名}} -p test
{'StackName': 'test001-vpc', 'TemplateURL': 'https://{{S3バケット名}}.s3-ap-northeast-1.amazonaws.com/test/vpc.yml', 'Parameters': [{'ParameterKey': 'PJPrefix', 'ParameterValue': 'Project1'}, {'ParameterKey': 'VPCCIDR', 'ParameterValue': '10.11.0.0/16'}]}
[LOG] CFn Stack [test001-vpc] start.
[LOG] CFn Stack [test001-vpc] end.
スタック名 : test001-vpc
スタックID : arn:aws:cloudformation:ap-northeast-1:121212121212:stack/test001-vpc/00000000-0000-0000-0000-000000000000
パラメータ
        VPCCIDR = 10.11.0.0/16
        PJPrefix = Project1
[CloudFormation] Outputs
        VPCCIDR : 10.11.0.0/16 : Project1-vpc-cidr
        InternetGateway : igw-00000000000000000 : Project1-igw
        VPC : vpc-00000000000000000 : Project1-vpc
{'StackName': 'test001-subnet1', 'TemplateURL': 'https://{{S3バケット名}}.s3-ap-northeast-1.amazonaws.com/test/subnet-public.yml', 'Parameters': [{'ParameterKey': 'PJPrefix', 'ParameterValue': 'Project1'}, {'ParameterKey': 'NetworkName', 'ParameterValue': 'Net001'}, {'ParameterKey': 'PublicSubnetCIDR', 'ParameterValue': '10.11.1.0/24'}, {'ParameterKey': 'AZName', 'ParameterValue': 'ap-northeast-1a'}]}        
[LOG] CFn Stack [test001-subnet1] start.
[LOG] CFn Stack [test001-subnet1] end.
スタック名 : test001-subnet1
スタックID : arn:aws:cloudformation:ap-northeast-1:121212121212:stack/test001-subnet1/0000000-0000-0000-0000-000000000000
パラメータ
        AZName = ap-northeast-1a
        PublicSubnetCIDR = 10.11.1.0/24
        NetworkName = Net001
        PJPrefix = Project1
[CloudFormation] Outputs
        PublicSubnet01 : subnet-0000000000000000 : Net001-ap-northeast-1a
        PublicSubnetCIDR : 10.11.1.0/24 : Net001-ap-northeast-1a-cidr
        PublicRouteTable01 : rtb-0000000000000000 : Net001-ap-northeast-1a-routetbl
{'StackName': 'test001-sg', 'TemplateURL': 'https://{{S3バケット名}}.s3-ap-northeast-1.amazonaws.com/test/sg.yml', 'Parameters': [{'ParameterKey': 'PJPrefix', 'ParameterValue': 'Project1'}, {'ParameterKey': 'ServiceName', 'ParameterValue': 'ServiceA'}, {'ParameterKey': 'AWSResource', 'ParameterValue': 'EC2'}, {'ParameterKey': 'SGNo', 'ParameterValue': '001'}]}
[LOG] CFn Stack [test001-sg] start.
[LOG] CFn Stack [test001-sg] end.
スタック名 : test001-sg
b7a1f
パラメータ
        SGNo = 001
        ServiceName = ServiceA
        AWSResource = EC2
        PJPrefix = Project1
[CloudFormation] Outputs
        SecurityGroupEC2 : sg-00000000000000000 : sg-Project1-EC2-001
[LOG] CFn Stack [test001-ec2] start.
[LOG] CFn Stack [test001-ec2] end.
スタック名 : test001-ec2
スタックID : arn:aws:cloudformation:ap-northeast-1:121212121212:stack/test001-ec2/00000000-0000-0000-0000-000000000000
パラメータ
        KeyPairName = keypair_ap-northeast-1_01
        NetworkName = Net001
        EC2InstanceName = ec2-001
        SGNo = 001
        EC2InstanceVolumeSize = 8
        ServiceName = ServiceA
        EC2InstanceAMI = /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
        PJPrefix = Project1
        EC2InstanceInstanceType = t2.micro
        EC2InstanceVolumeType = gp2
[CloudFormation] Outputs
        EC2InstancePrivateIp : 10.11.1.45 : Project1-ec2-001-private-ip
        EC2InstanceID : i-00000000000000000 : Project1-ec2-001-id
PS C:\Users\usr001\tmp>
```

ーーー

おわりです。