[アップデート]AWS CloudFormationが変更セット作成時にカスタム名を指定するリソースを自動でインポートしてくれるようになりました
初めに
先日のアップデートでCloudFormationが既に存在するカスタム名を指定しているリソースがテンプレートに含まれている場合、変更セット作成時に自動で取り込んでくれるようになりました。
これまでスタックに既存のリソースをインポートする場合、リソースのインポート操作を通常のスタックの作成とは別枠で行う必要があり(通常の変更と一緒にできない)、かつインポート時にはテンプレート自体とは別に取り込むリソースの識別子を入力する必要がありました。
今後はカスタム名が指定されているリソースについては、ImportExistingResources
パラメータをTrueにすることで上記事のような個別の指定は不要となりCloudFormation側が自動で判別して取り込んでくれるようになります。
なおカスタム名は一部のリソースに存在する個別の属性として名称が指定可能なものとなり対応リソースは以下ページをご参照ください。
試してみる
以前試したCloudFront+S3+WAFのテンプレートあったので、事前にS3バケットを作成した上で以下のテンプレートで変更セットを作成します。ハイライト部分が今回取り込み対象とするリソースです。
AWSTemplateFormatVersion: 2010-09-09 Parameters: Prefix: Type: String Mappings: CloudFront: ManagedCachePolicyId: CachingDisabled: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad ManagedOriginRequestPolicy: AllViewerAndCloudFrontHeaders202206: 33f36d7e-f396-46d9-90e0-52428a34d9dc Resources: #---------------------------------- #----- WAF #---------------------------------- WAF: Type: AWS::WAFv2::WebACL Properties: Name: !Sub ${Prefix}-web-acl Description: !Sub WebACL for ${Prefix} Scope: CLOUDFRONT DefaultAction: Allow: {} VisibilityConfig: CloudWatchMetricsEnabled: False MetricName: !Sub ${Prefix}-web-acl SampledRequestsEnabled: False #---------------------------------- #----- CloudFront #---------------------------------- CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Enabled: True Comment: !Sub Distribution for ${Prefix} WebACLId: !Ref WAF DefaultCacheBehavior: AllowedMethods: - GET - HEAD - OPTIONS - PUT - PATCH - POST - DELETE CachePolicyId: !FindInMap [CloudFront, ManagedCachePolicyId, CachingDisabled] OriginRequestPolicyId: !FindInMap [CloudFront, ManagedOriginRequestPolicy, AllViewerAndCloudFrontHeaders202206] TargetOriginId: site-bucket ViewerProtocolPolicy: redirect-to-https DefaultRootObject: index.html HttpVersion: http2 IPV6Enabled: False Origins: - Id: site-bucket DomainName: !GetAtt SiteBucket.DomainName OriginAccessControlId: !Ref CommonOAC S3OriginConfig: OriginAccessIdentity: '' PriceClass: PriceClass_All CommonOAC: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Name: !Sub ${Prefix}-bucket-access-control Description: !Sub bucket access control for ${Prefix} OriginAccessControlOriginType: s3 SigningBehavior: never SigningProtocol: sigv4 StaticContentsDefaultCachePolicy: Type: AWS::CloudFront::CachePolicy Properties: CachePolicyConfig: Name: !Sub ${Prefix}-static-content-cache-policy Comment: !Sub cloudfront accesslog in ${Prefix} ParametersInCacheKeyAndForwardedToOrigin: EnableAcceptEncodingBrotli: False EnableAcceptEncodingGzip: True CookiesConfig: CookieBehavior: none HeadersConfig: HeaderBehavior: none QueryStringsConfig: QueryStringBehavior: all DefaultTTL: 300 MinTTL: 300 MaxTTL: 300 SiteBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: BucketName: !Sub ${Prefix}-site-contents-{AWS::AccountId} PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True OwnershipControls: Rules: - ObjectOwnership: BucketOwnerEnforced BucketEncryption: ServerSideEncryptionConfiguration: - BucketKeyEnabled: True ServerSideEncryptionByDefault: SSEAlgorithm: AES256 SiteBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref SiteBucket PolicyDocument: Version: "2012-10-17" Statement: - Sid: AccessFromCloudFront Effect: Allow Principal: Service: cloudfront.amazonaws.com Action: s3:GetObject Resource: !Sub arn:aws:s3:::${SiteBucket}/* Condition: StringEquals: AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
バケットは事前に手動で作成しておきます。
今回のオプションはまだマネジメントコンソール上で指定が見当たらなかったのでAWS CLIで操作を行います。
なお現時点ではAWS CLIでもv1系のみが対応しており、v2系はUnknown options
扱いとなったためv1側で操作を行っています。
## v2は未対応 $ aws --version aws-cli/2.13.37 Python/3.11.6 Darwin/22.5.0 exe/x86_64 prompt/off $ aws create-change-set --region us-east-1 \ --stack-name new-custom-name-import-test \ --template-body auto-import.yml \ --parameters ParameterKey=Prefix,ParameterValue=auto-import \ --change-set-name import-`date +%Y%m%d-%H%M%S` \ --import-existing-resources --change-set-type CREATE ... Unknown options: --import-existing-resource # v1は対応済み $ /tmp/aws-cli-latest/bin/aws --version aws-cli/1.30.3 Python/3.11.6 Darwin/22.5.0 botocore/1.32.3 $ /tmp/aws-cli-latest/bin/aws create-change-set --region us-east-1 \ --stack-name new-custom-name-import-test \ --template-body file://auto-import.yml \ --parameters ParameterKey=Prefix,ParameterValue=auto-import \ --change-set-name import-`date +%Y%m%d-%H%M%S` \ --import-existing-resources --change-set-type CREATE { "Id": "arn:aws:cloudformation:us-east-1:xxxxx:changeSet/import-20231120-153127/xxxxx", "StackId": "arn:aws:cloudformation:us-east-1:xxxxx:stack/new-custom-name-import-test/xxxxx" }
マネジメントコンソール側で変更セットを確認してみるとS3がImportとなっていることが確認できます。
実行してみると取り込みイベントとしてS3バケットが含まれていることが確認できます。
DeletionPolicyの指定は必要
CloudFormationでインポートするリソースにはDeletionPolicy
の指定が必須という制約がありますが、これは今回の方法を使った場合でも同様です。
未指定の場合はAdd扱いではなく変更セットの作成自体がエラーとなります。
一部のリソースはうまく取り込めなかった
検証していた中でWAFやセキュリティグループについてはうまくImportとならずAddのままとなってしまいました。
この2つのリソースについてはカスタム名に対応してはいますが取り込みリソース指定の際ににID等の別の属性が必要となるのでこの辺りの関係でしょうか?
今回はここまで追い切ることができなかったため別途確認が必要な点とはなります。
終わりに
これまでリソースのインポートする形で新たにスタックを作成する場合は一度対象のリソースをコメントアウトしてデプロイし別途インポート処理が必要、既存の追加でも変更処理が入らないように構成する必要があり非常に手間でした。
現時点では全てのリソースが対応しているわけではないですが今回のアップデートによりそういった前作業を気にする必要がなく1デプロイで通せるようになったので非常に有難いアップデートです。
付録: RustでのCreateChangeSet
WAFの件でハマってる時にCloudTrail上でImportExistingResources
のパラメータが見えなくて本当に渡ってるか不安だった時に別口でrustのsdkも既に対応してそうだったのでそちらで行うようにもしていました。
結果的にはCloudTrailには表示されないパラメータでdescribe-change-set
で確認するとTrue
になったいたので不要となりましたが今後何かで使うこともあると思いますので個人的なメモとして残しておきます。
use std::{fs::File, io::Read}; use aws_config::BehaviorVersion; use aws_sdk_cloudformation::{Client, Error, types::Parameter}; #[tokio::main] async fn main() -> Result<(), Error> { let mut contents = String::new(); let mut f = File::open("auto-import.yml").unwrap(); f.read_to_string(&mut contents).unwrap(); let shared_config = aws_config::load_defaults(BehaviorVersion::v2023_11_09()).await; let client = Client::new(&shared_config); #当初は新規作成の変更セットではなく既存の変更(追加)で考えていたため少し指定が異なります let params = Parameter::builder() .set_parameter_key(Some("Prefix".to_string())) .use_previous_value(true) .build(); let req = client.create_change_set() .stack_name("custom-name-import-test") .change_set_name("import1") .template_body(contents) .import_existing_resources(true) .parameters(params); let resp = req.send().await?; println!("{:?}", resp); Ok(())