[アップデート] AWS CloudFormation の API を使って、既存リソースからテンプレートを生成出来るようになったようなので使ってみた

2024.02.02

いわさです。

今朝、AWS API と AWS CLI のアップデートで興味深いものがアナウンスされていました。

CloudFormation IaC generator allows you to scan existing resources in your account and select resources to generate a template for a new or existing CloudFormation stack.

CloudFormation IaC Generator って初めて聞いたなと思いつつ、もしかして Former2 的なやつではという予感が。

Former2 というのは AWS 上にデプロイされている既存リソースから CloudFromation テンプレートを生成してくれる Web サービスです。
お世話になっている方も多いのではないでしょうか。

情報が全然ないのですが API リファレンスを基に実際に使ってみましたので紹介します。
What's New のアナウンスもそのうち出そうな気がしますが。

基本的な流れの確認

AWS CLI のバージョン 1.32.32 以上からコマンドが追加されているのでローカルの AWS CLI をアップデートしました。

% aws-v1 --version
aws-cli/1.32.33 Python/3.9.8 Darwin/23.2.0 botocore/1.34.33

VPC と EC2 をマネジメントコンソールから作成しておきます。

流れとしてはリソースを指定してcreate-generated-templateコマンドを実行します。
そうすると非同期でテンプレート作成タスクが実行されているようで、describe-generated-templateコマンドで生成状況を確認することが出来ます。
生成ステータスが完了になったら、get-generated-templateでテンプレート内容を取得するという流れのようです。

試しにリソース指定なしで試したところ次のようになりました。

テンプレート生成はリソース指定なしでも可能ですね。

% aws-v1 cloudformation create-generated-template --generated-template-name hoge0202template
{
    "GeneratedTemplateId": "arn:aws:cloudformation:ap-northeast-1:123456789012:generatedTemplate/08fd9978-d6e0-4aee-be70-0ba1dc8dcf3b"
}

すぐにステータスが完了になりました。

% aws-v1 cloudformation describe-generated-template --generated-template-name hoge0202template
{
    "GeneratedTemplateId": "arn:aws:cloudformation:ap-northeast-1:123456789012:generatedTemplate/08fd9978-d6e0-4aee-be70-0ba1dc8dcf3b",
    "GeneratedTemplateName": "hoge0202template",
    "Resources": [],
    "Status": "COMPLETE",
    "StatusReason": "Template complete",
    "CreationTime": "2024-02-01T22:03:17.318Z",
    "LastUpdatedTime": "2024-02-01T22:03:17.544Z",
    "Progress": {
        "ResourcesSucceeded": 0,
        "ResourcesFailed": 0,
        "ResourcesProcessing": 0,
        "ResourcesPending": 0
    },
    "TemplateConfiguration": {
        "DeletionPolicy": "RETAIN",
        "UpdateReplacePolicy": "RETAIN"
    },
    "TotalWarnings": 0
}

ただ、リソースを指定していなかったせいでテンプレートの取得は出来ませんでした。

% aws-v1 cloudformation get-generated-template --generated-template-name hoge0202template         

An error occurred (GeneratedTemplateNotFound) when calling the GetGeneratedTemplate operation: Generated template does not contain any resources. (Service: CfnTemplateGenerationServiceLambda; Status Code: 400; Error Code: GeneratedTemplateNotFoundException; Request ID: a797bdf9-6e9e-4fd5-bf5d-14b6869b1bf4; Proxy: null)

実際にリソースを指定してやってみた

まぁ基本的な流れがわかったので、次は実際のリソースを指定してやってみましょう。

テンプレート生成

リソースの指定方法がちょっと厄介だなと思っていて、リソースごとにリソースタイプを指定しつつ、リソースごとに違う識別子をキーバリューで指定する必要があります。
CFN-CLI を使っている人には馴染みがあると思いますがprimaryIdentityfierというやつです。

今回は EC2 と VPC なので、次のように指定しました。

% cat hoge1.json
{
    "Resources": [
        {
            "ResourceType": "AWS::EC2::Instance",
            "LogicalResourceId": "hoge0202templateEc2",
            "ResourceIdentifier": {
                "InstanceId": "i-03e0683b381e5f030"
            }
        },
        {
            "ResourceType": "AWS::EC2::VPC",
            "LogicalResourceId": "hoge0202templateVpc",
            "ResourceIdentifier": {
                "VpcId": "vpc-08643a6137cb66618"
            }
        }
    ],
    "GeneratedTemplateName": "hoge0202template-hoge"
}
% aws-v1 cloudformation create-generated-template --cli-input-json file://hoge1.json
{
    "GeneratedTemplateId": "arn:aws:cloudformation:ap-northeast-1:123456789012:generatedTemplate/000838b8-6fb9-422a-90e1-a3ab4e8e4131"
}

上記がおそらく最低限の指定方法で、別途オプションで既存スタックを指定したり、UpdateReplacePolicy などを指定したり出来ます。

テンプレート生成状況の確認

作成指示をしたテンプレート名を指定して状況を確認してみましょう。

% aws-v1 cloudformation describe-generated-template --generated-template-name hoge0202template-hoge
{
    "GeneratedTemplateId": "arn:aws:cloudformation:ap-northeast-1:123456789012:generatedTemplate/000838b8-6fb9-422a-90e1-a3ab4e8e4131",
    "GeneratedTemplateName": "hoge0202template-hoge",
    "Resources": [
        {
            "ResourceType": "AWS::EC2::Instance",
            "LogicalResourceId": "hoge0202templateEc2",
            "ResourceIdentifier": {
                "InstanceId": "i-03e0683b381e5f030"
            },
            "ResourceStatus": "COMPLETE",
            "ResourceStatusReason": "Resource Template complete",
            "Warnings": [
                {
                    "Type": "UNSUPPORTED_PROPERTIES",
                    "Properties": [
                        {
                            "PropertyPath": "AdditionalInfo",
                            "Required": false,
                            "Description": "This property is reserved for internal use. If you use it, the stack fails with this error: Bad property set: [Testing this property] (Service: AmazonEC2; Status Code: 400; Error Code: InvalidParameterCombination; Request ID: 0XXXXXX-49c7-4b40-8bcc-76885dcXXXXX)."
                        },
                        {
                            "PropertyPath": "PropagateTagsToVolumeOnCreation",
                            "Required": false,
                            "Description": "Indicates whether to assign the tags from the instance to all of the volumes attached to the instance at launch. If you specify true and you assign tags to the instance, those tags are automatically assigned to all of the volumes that you attach to the instance at launch. If you specify false, those tags are not assigned to the attached volumes."
                        }
                    ]
                }
            ]
        },
        {
            "ResourceType": "AWS::EC2::VPC",
            "LogicalResourceId": "hoge0202templateVpc",
            "ResourceIdentifier": {
                "VpcId": "vpc-08643a6137cb66618"
            },
            "ResourceStatus": "COMPLETE",
            "ResourceStatusReason": "Resource Template complete",
            "Warnings": []
        }
    ],
    "Status": "COMPLETE",
    "StatusReason": "All resources complete",
    "CreationTime": "2024-02-01T22:23:36.995Z",
    "LastUpdatedTime": "2024-02-01T22:23:47.301Z",
    "Progress": {
        "ResourcesSucceeded": 2,
        "ResourcesFailed": 0,
        "ResourcesProcessing": 0,
        "ResourcesPending": 0
    },
    "TemplateConfiguration": {
        "DeletionPolicy": "RETAIN",
        "UpdateReplacePolicy": "RETAIN"
    },
    "TotalWarnings": 2
}

お、なんかもう完了してますね。数秒でした。
VPC は問題なくて、EC2 で少し警告が出ています。
AdditionalInfoPropagateTagsToVolumeOnCreationがサポートされていないと。ただテンプレート自体の作成には成功していそうです。

生成されたテンプレートを取得

では最後にテンプレートを取得してみます。
先程はリソースが指定されてなかったため失敗していた部分ですね。

% aws-v1 cloudformation get-generated-template --generated-template-name hoge0202template-hoge     
{
    "Status": "COMPLETE",
    "TemplateBody": "{\"Metadata\":{\"TemplateId\":\"arn:aws:cloudformation:ap-northeast-1:123456789012:generatedTemplate/000838b8-6fb9-422a-90e1-a3ab4e8e4131\"},\"Resources\":{\"hoge0202templateEc2\":{\"UpdateReplacePolicy\":\"Retain\",\"Type\":\"AWS::EC2::Instance\",\"DeletionPolicy\":\"Retain\",\"Properties\":{\"Tenancy\":\"default\",\"SecurityGroups\":[\"hoge0202vm\"],\"PrivateIpAddress\":\"10.0.20.222\",\"InstanceInitiatedShutdownBehavior\":\"stop\",\"CpuOptions\":{\"ThreadsPerCore\":1,\"CoreCount\":1},\"BlockDeviceMappings\":[{\"Ebs\":{\"SnapshotId\":\"snap-09d9ea33312bc8e69\",\"VolumeType\":\"gp3\",\"Iops\":3000,\"VolumeSize\":8,\"Encrypted\":false,\"DeleteOnTermination\":true},\"DeviceName\":\"/dev/xvda\"}],\"AvailabilityZone\":\"ap-northeast-1c\",\"PrivateDnsNameOptions\":{\"EnableResourceNameDnsARecord\":false,\"HostnameType\":\"ip-name\",\"EnableResourceNameDnsAAAARecord\":false},\"SubnetId\":\"subnet-03e5dc3bbc504707e\",\"SecurityGroupIds\":[\"sg-024069bd29d7b073c\"],\"EbsOptimized\":false,\"DisableApiTermination\":false,\"KeyName\":\"hoge\",\"SourceDestCheck\":true,\"PlacementGroupName\":\"\",\"NetworkInterfaces\":[{\"PrivateIpAddresses\":[{\"PrivateIpAddress\":\"10.0.20.222\",\"Primary\":true}],\"SecondaryPrivateIpAddressCount\":0,\"DeviceIndex\":\"0\",\"GroupSet\":[\"sg-024069bd29d7b073c\"],\"Ipv6Addresses\":[],\"SubnetId\":\"subnet-03e5dc3bbc504707e\",\"AssociatePublicIpAddress\":true,\"NetworkInterfaceId\":\"eni-0f9aece8cf8ca6e20\",\"DeleteOnTermination\":true}],\"ImageId\":\"ami-0b193da66bc27147b\",\"InstanceType\":\"t2.micro\",\"Monitoring\":false,\"Tags\":[{\"Value\":\"hoge0202vm\",\"Key\":\"Name\"}],\"CreditSpecification\":{\"CPUCredits\":\"standard\"}}},\"hoge0202templateVpc\":{\"UpdateReplacePolicy\":\"Retain\",\"Type\":\"AWS::EC2::VPC\",\"DeletionPolicy\":\"Retain\",\"Properties\":{\"CidrBlock\":\"10.0.0.0/16\",\"EnableDnsSupport\":true,\"InstanceTenancy\":\"default\",\"EnableDnsHostnames\":true,\"Tags\":[{\"Value\":\"hoge0202-vpc\",\"Key\":\"Name\"}]}}}}"
}

なるほど、こういう感じか。
ステータスとテンプレートの本文が取得されていて、TemplateBody に JSON 文字列で出力されていますね。

jq か何か整えてみます。

% aws-v1 cloudformation get-generated-template --generated-template-name hoge0202template-hoge | jq -r '.TemplateBody' | jq
{
  "Metadata": {
    "TemplateId": "arn:aws:cloudformation:ap-northeast-1:123456789012:generatedTemplate/000838b8-6fb9-422a-90e1-a3ab4e8e4131"
  },
  "Resources": {
    "hoge0202templateEc2": {
      "UpdateReplacePolicy": "Retain",
      "Type": "AWS::EC2::Instance",
      "DeletionPolicy": "Retain",
      "Properties": {
        "Tenancy": "default",
        "SecurityGroups": [
          "hoge0202vm"
        ],
        "PrivateIpAddress": "10.0.20.222",
        "InstanceInitiatedShutdownBehavior": "stop",
        "CpuOptions": {
          "ThreadsPerCore": 1,
          "CoreCount": 1
        },
        "BlockDeviceMappings": [
          {
            "Ebs": {
              "SnapshotId": "snap-09d9ea33312bc8e69",
              "VolumeType": "gp3",
              "Iops": 3000,
              "VolumeSize": 8,
              "Encrypted": false,
              "DeleteOnTermination": true
            },
            "DeviceName": "/dev/xvda"
          }
        ],
        "AvailabilityZone": "ap-northeast-1c",
        "PrivateDnsNameOptions": {
          "EnableResourceNameDnsARecord": false,
          "HostnameType": "ip-name",
          "EnableResourceNameDnsAAAARecord": false
        },
        "SubnetId": "subnet-03e5dc3bbc504707e",
        "SecurityGroupIds": [
          "sg-024069bd29d7b073c"
        ],
        "EbsOptimized": false,
        "DisableApiTermination": false,
        "KeyName": "hoge",
        "SourceDestCheck": true,
        "PlacementGroupName": "",
        "NetworkInterfaces": [
          {
            "PrivateIpAddresses": [
              {
                "PrivateIpAddress": "10.0.20.222",
                "Primary": true
              }
            ],
            "SecondaryPrivateIpAddressCount": 0,
            "DeviceIndex": "0",
            "GroupSet": [
              "sg-024069bd29d7b073c"
            ],
            "Ipv6Addresses": [],
            "SubnetId": "subnet-03e5dc3bbc504707e",
            "AssociatePublicIpAddress": true,
            "NetworkInterfaceId": "eni-0f9aece8cf8ca6e20",
            "DeleteOnTermination": true
          }
        ],
        "ImageId": "ami-0b193da66bc27147b",
        "InstanceType": "t2.micro",
        "Monitoring": false,
        "Tags": [
          {
            "Value": "hoge0202vm",
            "Key": "Name"
          }
        ],
        "CreditSpecification": {
          "CPUCredits": "standard"
        }
      }
    },
    "hoge0202templateVpc": {
      "UpdateReplacePolicy": "Retain",
      "Type": "AWS::EC2::VPC",
      "DeletionPolicy": "Retain",
      "Properties": {
        "CidrBlock": "10.0.0.0/16",
        "EnableDnsSupport": true,
        "InstanceTenancy": "default",
        "EnableDnsHostnames": true,
        "Tags": [
          {
            "Value": "hoge0202-vpc",
            "Key": "Name"
          }
        ]
      }
    }
  }
}

なんかそれっぽいものが出力されましたね。

ちなみに、get-generated-templateコマンドはフォーマットを指定するオプションがあって、省略時は JSON のようですが、YAML も指定出来ます。

% aws-v1 cloudformation get-generated-template --generated-template-name hoge0202template-hoge --format YAML | jq -r '.TemplateBody'
---
Metadata:
  TemplateId: "arn:aws:cloudformation:ap-northeast-1:123456789012:generatedTemplate/000838b8-6fb9-422a-90e1-a3ab4e8e4131"
Resources:
  hoge0202templateEc2:
    UpdateReplacePolicy: "Retain"
    Type: "AWS::EC2::Instance"
    DeletionPolicy: "Retain"
    Properties:
      Tenancy: "default"
      SecurityGroups:
      - "hoge0202vm"
      PrivateIpAddress: "10.0.20.222"
      InstanceInitiatedShutdownBehavior: "stop"
      CpuOptions:
        ThreadsPerCore: 1
        CoreCount: 1
      BlockDeviceMappings:
      - Ebs:
          SnapshotId: "snap-09d9ea33312bc8e69"
          VolumeType: "gp3"
          Iops: 3000
          VolumeSize: 8
          Encrypted: false
          DeleteOnTermination: true
        DeviceName: "/dev/xvda"
      AvailabilityZone: "ap-northeast-1c"
      PrivateDnsNameOptions:
        EnableResourceNameDnsARecord: false
        HostnameType: "ip-name"
        EnableResourceNameDnsAAAARecord: false
      SubnetId: "subnet-03e5dc3bbc504707e"
      SecurityGroupIds:
      - "sg-024069bd29d7b073c"
      EbsOptimized: false
      DisableApiTermination: false
      KeyName: "hoge"
      SourceDestCheck: true
      PlacementGroupName: ""
      NetworkInterfaces:
      - PrivateIpAddresses:
        - PrivateIpAddress: "10.0.20.222"
          Primary: true
        SecondaryPrivateIpAddressCount: 0
        DeviceIndex: "0"
        GroupSet:
        - "sg-024069bd29d7b073c"
        Ipv6Addresses: []
        SubnetId: "subnet-03e5dc3bbc504707e"
        AssociatePublicIpAddress: true
        NetworkInterfaceId: "eni-0f9aece8cf8ca6e20"
        DeleteOnTermination: true
      ImageId: "ami-0b193da66bc27147b"
      InstanceType: "t2.micro"
      Monitoring: false
      Tags:
      - Value: "hoge0202vm"
        Key: "Name"
      CreditSpecification:
        CPUCredits: "standard"
  hoge0202templateVpc:
    UpdateReplacePolicy: "Retain"
    Type: "AWS::EC2::VPC"
    DeletionPolicy: "Retain"
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsSupport: true
      InstanceTenancy: "default"
      EnableDnsHostnames: true
      Tags:
      - Value: "hoge0202-vpc"
        Key: "Name"

なるほど。
create-generated-templateで指定したリソースだけ取得される感じですね。
例えば VPC だとサブネットとか、EC2 であればインスタンスプロファイルとかセキュリティグループとか、そのあたりは全部個別に指定が必要ですね。

さいごに

本日は AWS CloudFormation の API を使って、既存リソースからテンプレートを生成出来るようになったようなので使ってみました。

ついに来たかガタッという機能ですね。

使って見た感じだと、もっと使い勝手が向上されるような追加のアップデートや関連する機能があると嬉しいと思いました。
例えば Former2 のようにリソースを列挙してくれて指定出来るとか、リソースごとの識別子を良い感じに引っ張ってきてくれるとか、そのあたりが欲しくなりますね。

と思っていたのですが、今回のアップデートではテンプレートの生成以外に、リソーススキャン機能も追加されてますね。これらからリソース一覧を列挙し、そこからリソースタイプと識別子のキーバリュー、関連リソースまで列挙出来るみたいです。これすごいな。

このあたりはまた別途試してみたいと思います。
Former2 相当の機能がマネジメントコンソール上に追加される日が近い気がしてきました。