AWS CloudFormationのデプロイ済みスタックのテンプレートを取得


CloudFormationのデプロイ済みスタックのテンプレートをawscliでYAMLやJSONのフォーマットで取得する方法です。

aws cloudformation get-templateコマンドでテンプレートを取得できるのですが、デプロイ時YAMLとJSONのどちらを使ったかによって返ってくるフォーマットが違い、毎回試行錯誤してしまうので、メモしておきます。

テンプレートを取得するワンライナー

YAMLでデプロイしたスタックをYAMLで取得

$ aws cloudformation get-template --stack-name $STACKNAME | jq .TemplateBody -r

JSONでデプロイしたスタックをJSONで取得

$ aws cloudformation get-template --stack-name $STACKNAME | jq .TemplateBody

YAMLでデプロイしたスタックをJSONで取得

$ aws cloudformation get-template --stack-name $STACKNAME | jq .TemplateBody -r | yq

$ aws cloudformation get-template --stack-name $STACKNAME | ruby -rjson -ryaml -e 'puts JSON.pretty_generate(YAML.load(JSON.load(ARGF)["TemplateBody"]))'

$ aws cloudformation get-template --stack-name $STACKNAME | jq .TemplateBody -r | python -c 'import sys; import json; from awscli.customizations.cloudformation.yamlhelper import yaml_parse; print(json.dumps(yaml_parse(sys.stdin.read()), indent=4))'

1つ目と2つ目は !GetAtt みたいなのには非対応です。Pythonのboto3がある環境では、3つ目のようにがんばれば !GetAtt みたいなのに対応できます。

JSONでデプロイしたスタックをYAMLで取得

$ aws cloudformation get-template --stack-name $STACKNAME | yq .TemplateBody -y

$ aws cloudformation get-template --stack-name $STACKNAME | ruby -ryaml -e 'puts YAML.dump(YAML.load(ARGF)["TemplateBody"])'

試してみる

テンプレートファイル

RDSのインスタンスを作るシンプルなテンプレートで試します。

AWSTemplateFormatVersion: '2010-09-09'

Resources:
  SampleRDS:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: postgres
      EngineVersion: 12.5
      DBInstanceIdentifier: samplerdsinstance1
      DBInstanceClass: db.t3.micro
      StorageType: gp2
      AllocatedStorage: 20 # GiB
      DBName: mydb
      MasterUsername: myuser
      MasterUserPassword: mypassword

Outputs:
  SampleRDSEndpoint:
      Value: !GetAtt SampleRDS.Endpoint.Address
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "SampleRDS": {
      "Type": "AWS::RDS::DBInstance",
      "Properties": {
        "Engine": "postgres",
        "EngineVersion": 12.5,
        "DBInstanceIdentifier": "samplerdsinstance2",
        "DBInstanceClass": "db.t3.micro",
        "StorageType": "gp2",
        "AllocatedStorage": 20,
        "DBName": "mydb",
        "MasterUsername": "myuser",
        "MasterUserPassword": "mypassword"
      }
    }
  },
  "Outputs": {
    "SampleRDSEndpoint": {
      "Value": {
        "Fn::GetAtt": ["SampleRDS", "Endpoint.Address"]
      }
    }
  }
}

デプロイ

$ aws cloudformation deploy --template-file template.yaml --stack-name sample-yaml
$ aws cloudformation deploy --template-file template.json --stack-name sample-json

aws cloudformation get-templateコマンド

デプロイ時のYAML/JSONでコマンドの出力がだいぶ違うんですよね。。。

YAMLの場合は、YAMLのコードがそのまま文字列になってJSONに埋め込まれている。JSONの場合は、そのままJSON。

$ aws cloudformation get-template --stack-name sample-yaml
{
    "TemplateBody": "AWSTemplateFormatVersion: '2010-09-09'\n\nResources:\n  SampleRDS:\n    Type: AWS::RDS::DBInstance\n    Properties:\n      Engine: postgres\n      EngineVersion: 12.5\n      DBInstanceIdentifier: samplerdsinstance1\n      DBInstanceClass: db.t3.micro\n      StorageType: gp2\n      AllocatedStorage: 20 # GiB\n      DBName: mydb\n      MasterUsername: myuser\n      MasterUserPassword: mypassword\n\nOutputs:\n  SampleRDSEndpoint:\n      Value: !GetAtt SampleRDS.Endpoint.Address\n",
    "StagesAvailable": [
        "Original",
        "Processed"
    ]
}
$ aws cloudformation get-template --stack-name sample-json
{
    "TemplateBody": {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Resources": {
            "SampleRDS": {
                "Type": "AWS::RDS::DBInstance",
                "Properties": {
                    "Engine": "postgres",
                    "EngineVersion": 12.5,
                    "DBInstanceIdentifier": "samplerdsinstance2",
                    "DBInstanceClass": "db.t3.micro",
                    "StorageType": "gp2",
                    "AllocatedStorage": 20,
                    "DBName": "mydb",
                    "MasterUsername": "myuser",
                    "MasterUserPassword": "mypassword"
                }
            }
        },
        "Outputs": {
            "SampleRDSEndpoint": {
                "Value": {
                    "Fn::GetAtt": [
                        "SampleRDS",
                        "Endpoint.Address"
                    ]
                }
            }
        }
    },
    "StagesAvailable": [
        "Original",
        "Processed"
    ]
}

テンプレート取得

YAMLデプロイのスタックをYAMLで取得。YAML自体がJSONの文字列になっているのでjqコマンドに -r オプションを付ける。

$ aws cloudformation get-template --stack-name sample-yaml | jq .TemplateBody -r
AWSTemplateFormatVersion: '2010-09-09'

Resources:
  SampleRDS:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: postgres
      EngineVersion: 12.5
      DBInstanceIdentifier: samplerdsinstance1
      DBInstanceClass: db.t3.micro
      StorageType: gp2
      AllocatedStorage: 20 # GiB
      DBName: mydb
      MasterUsername: myuser
      MasterUserPassword: mypassword

Outputs:
  SampleRDSEndpoint:
      Value: !GetAtt SampleRDS.Endpoint.Address

JSONデプロイのスタックをJSONで取得。

$ aws cloudformation get-template --stack-name sample-json | jq .TemplateBody
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "SampleRDS": {
      "Type": "AWS::RDS::DBInstance",
      "Properties": {
        "Engine": "postgres",
        "EngineVersion": 12.5,
        "DBInstanceIdentifier": "samplerdsinstance2",
        "DBInstanceClass": "db.t3.micro",
        "StorageType": "gp2",
        "AllocatedStorage": 20,
        "DBName": "mydb",
        "MasterUsername": "myuser",
        "MasterUserPassword": "mypassword"
      }
    }
  },
  "Outputs": {
    "SampleRDSEndpoint": {
      "Value": {
        "Fn::GetAtt": [
          "SampleRDS",
          "Endpoint.Address"
        ]
      }
    }
  }
}

YAMLとJSONを変換したければ、冒頭に書いた通りyqコマンドやワンライナーで変換できます。

コマンドのインストール

jqコマンド

jq のインストールはUbuntuならば

$ sudo apt install -y jq

CentOS 8ならば

$ sudo yum install -y jq

yqコマンド

yqはPythonで書かれており、pipでインストールできます。

$ pip install yq

内部でjqを呼び出しているようで、jqがないと次のようなエラーになります。

yq: Error starting jq: FileNotFoundError: [Errno 2] No such file or directory: 'jq'. Is jq installed and available on PATH?