AWS CLIのcloudformation deployコマンドのオプションを改めて確認してみる

AWS CLIでCloudFormationのスタックのデプロイを行う際に使用可能なオプションについて整理しました。 ユースケース別でそれぞれのオプションの解説をしていきます。
2023.04.29

CloudFormationとは

CloudFormation(略称: CFn)はAWSから提供されているIaCツールの一つで、これを使用することでインフラストラクチャーをコードとして管理できるようになります。

YAMLまたはJSON形式のファイルでインフラの定義を行い、これをアップロードすることでファイルの状態になるようにインフラの作成・変更が可能です。

CLIでの操作

CloudFormationはマネージメントコンソールでのデプロイの他、AWS CLIからでもデプロイ可能です。 今回はAWS CLIを用いたCloudFormationのデプロイのオプションをいくつかのユースケースに分けて説明していきます。

オプションの一覧

aws cloudformation deployコマンドには以下のオプションがあります。(他のコマンドと共通のオプションを除く)

オプション 説明 必須
--template-file スタックで使用するテンプレートファイルを指定します。 o
--stack-name (value) スタックの名前を指定します。 o
--s3-bucket (value) テンプレートファイルをアップロードするS3バケットを指定します。 x
--force-upload S3上のテンプレートファイルを強制的に更新します x
--s3-prefix (value) テンプレートファイルをアップロードするS3バケットのプレフィックス(フォルダ)を指定します。 x
--kms-key-id (value) テンプレートファイルを暗号化するために使用するKMSキーのIDを指定します。 x
--parameter-overrides (value) [(value)...] スタックのパラメータを上書きします。 x
--capabilities (value) [(value)...] スタックに必要なIAM権限を指定します。 x
--no-execute-changeset 変更セットを自動で実行しないようにします。 x
--disable-rollback or --no-disable-rollback スタックのロールバックを無効にします。(デフォルト: --no-disable-rollback) x
--role-arn (value) スタックのデプロイに使用するIAMロールを指定します。 x
--notification-arns (value) [(value)...] スタックのデプロイ状況を通知するSNSトピックのARNを指します。 x
--fail-on-empty-changeset or --no-fail-on-empty-changeset 変更がない場合に、スタックの更新に失敗するかどうかを指定します。(デフォルト--no-fail-on-empty-changeset) x
--tags [(value)...] スタック内のリソースにタグを追加します。 x

シンプルなデプロイ

必須のオプション2つのみでの実行です。 今回使用しているテンプレートファイルは付録に書いておきます。

シンプルな例

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack

S3バケットの指定

以下ではアップロードするテンプレートのS3バケットを指定しています。 デフォルトではcf-templatesから始まるバケットが作成され、そこにテンプレートファイルが配置されます。

テンプレートをアップロードするS3バケットの指定

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --s3-bucket sample-bucket \
    --s3-prefix cfn

これを実行するとテンプレートファイルはS3のsample-bucketの中のcfn/{ファイルMD5ハッシュ}.templateに配置されます。

強制的に上書きしたい場合は--force-uploadで上書きできます。 SAMを使ってるとこのオプションを使うこともあるようです、

--kms-key-idを使用するとS3上で暗号化されて保存されます。引数はKMSのIDです。(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxのような形式) これを指定することで、KMSへのアクセス権限を持たないユーザー・ロールからはS3バケットからはテンプレートがダウンロードできなくなります。万が一S3の設定を誤ってテンプレートがパブリックに公開されていてもひとまずは安心です。 ただ、KSMの権限を持たなくてもマネージメントコンソールのCloudFomationのページからは普通に生のテンプレートが見えます。

テンプレート中のパラメータの上書き

今回使用しているテンプレートファイルではVPCのIDをパラメータとしています。 デフォルト値が設定されているので今までは指定せずにデプロイできていました。 ここでは別のVPCを指定してみます。

テンプレート中のパラメータを上書き

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --parameter-overrides VPCId=vpc-yyyyy

複数パラメータがある場合はスペースで区切って指定可能です。

テンプレート中のパラメータを上書き(複数)

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --parameter-overrides VPCId=vpc-yyyyy AppPort=3000

複数個パラメータがある場合は以下のようなファイルを作ってパラメータを渡して上げると便利です。

sample.txt

VPCId=vpc-yyyyy
AppPort=3000

ファイルからパラメータを読み込み

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --parameter-overrides $(cat sample.props)

IAMリソースが含まれるスタック

IAMリソースが含まれるスタックを作成する場合は--capabilitiesの指定が必要になります。 これを指定しないとInsufficientCapabilitiesというエラーが発生します。

指定できる値は以下の2つです。

説明 ユースケース
CAPABILITY_IAM IAMリソースの名前はCFnが自動で作成する IAMリソース名にCFnが生成するプレフィクスが含まれても良い場合
CAPABILITY_NAMED_IAM IAMリソース名を自分で指定する IAMリソース名をテンプレート中の値で指定したい場合

AWS CLIのドキュメントには書かれていませんが、実はもう2つあります。CAPABILITY_RESOURCE_POLICYCAPABILITY_AUTO_EXPANDの2つです。 ただ、ここで説明するとやや長くなるので、また別の記事で書こうかと思います。

IAMリソースを含むようなスタック

aws cloudformation deploy \
    --template-file sample-role.yaml \
    --stack-name sample-role-stack \
    --capabilities CAPABILITY_NAMED_IAM

CFnテンプレートにおいてRoleName等のリソース名は実は必須ではなく、指定しない場合は以下の命名規則の名前のリソースが作成されます

  • {スタック名}-{リソース名}-{ランダムな値}

例で使っているテンプレートのRoleNameを指定しないと以下のようなRoleが作成されます。

  • sample-role-stack-SampleRole-{ランダムな値}

スタックの更新

一度作成したスタックを再度更新したいケースがあると思います。 更新に際して、変更セットと呼ばれるものが作成されるのですが、この実行を制御することも可能です。

--no-execute-changesetを指定した場合は、自動的に変更セットが実行されません。 デフォルトでは変更が自動で実行されます。 後で変更セットを確認し実行したい場合などに有益なオプションです。

変更セットを自動的に実行しない

aws cloudformation deploy \
    --template-file sample-update.yaml \
    --stack-name sample-stack \
    --no-execute-changeset

作成された変更セットはマネージメントコンソールまたはCLIから実行可能です。

--fail-on-empty-changesetは変更セットがない場合にコマンド自体エラーコードで終わらせるオプションです。デフォルトでは失敗しません。

ロールバックの制御

CloudFormationではスタックの作成・変更に失敗した際に直前の正常な状態に戻すようにロールバックを行うことができます。

[アップデート]CloudFormationにロールバック前にプロビジョニングエラーをトラブルシューティングするオプションが導入されました | DevelopersIO

--disable-rollbackオプションを使用することで、作成に成功したリソースを保持できるようになります。 デフォルトでは自動でロールバックが行われます。

ロールバックを無効化

aws cloudformation deploy \
    --template-file sample-invalid.yaml \
    --stack-name sample-stack \
    --disable-rollback

サービスロールを指定

CloudFormationではスタック作成に際して使用するIAMロールを指定することができます。 サービスロールを指定することでCloudFormationにデプロイする人の権限を分離し、さらにスタックの作成に際して必要な権限を管理しやすくなります。 デフォルトではAWS CLIを実行するのに使用しているロールが使用されます。

スタックの作成・変更を行うIAMロールを指定

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --role-arn arn:aws:iam::{アカウントID}:role/sample-cfn-service-role

リソースにタグを一括して付与する

CloudFormationでは作成するリソースに対して一括でタグを付与できるので便利です。 これもパラメータの上書きと同様スペースで区切って複数指定可能です。

タグの一括付与

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --tag Env=stg Project=sample-prj

SNS経由での通知

CloudFormationではデプロイの結果をSNSで通知することが可能です。 SNSを使えばメールやSlack(別途Lambdaなどで連携が必要)などで通知を受け取ることが可能なので便利かと思います。 こちらもパラメータやタグと同様にスペースで区切って複数設定する事が可能です。

SNSによる通知

aws cloudformation deploy \
    --template-file sample.yaml \
    --stack-name sample-stack \
    --notification-arns arn:aws:sns:ap-northeast-1:{アカウントID}:sample-sns

終わりに

AWS CLIのCloudFormationのオプションを確認することができました。 マネージメントコンソールとの違いは変更セットあたりでしょうか?(マネージメントコンソールでは変更セットの作成の操作が必要) 同一のコマンドでスタックの更新ができるのは便利だと思いました。

付録

シンプルなテンプレート

sample.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: SecurityGroup Template Fix
Parameters:
  VPCId:
    Type: String
    Default: vpc-xxxxx
  AppPort:
    Type: Number
    Default: 80

Resources:
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
        GroupDescription: "Sample Security Group"
        GroupName: sample-sg
        VpcId: !Ref VPCId
        Tags:
          - Key: Name
            Value: sample-sg
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: !Ref AppPort
            ToPort: !Ref AppPort
            CidrIp: 172.16.0.10/32

更新用のテンプレート

sample-update.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: SecurityGroup Template
Parameters:
  VPCId:
    Type: String
    Default: vpc-xxxxx
  AppPort:
    Type: Number
    Default: 80

Resources:
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
        GroupDescription: "Sample Security Group"
        GroupName: sample-sg
        VpcId: !Ref VPCId
        Tags:
          - Key: Name
            Value: sample-sg
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: !Ref AppPort
            ToPort: !Ref AppPort
            CidrIp: 0.0.0.0/32 # ここを更新

デプロイが途中で失敗するテンプレート

sample-invalid.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: SecurityGroup Template
Parameters:
  VPCId:
    Type: String
    Default: vpc-xxxxx
  AppPort:
    Type: Number
    Default: 80

Resources:
  SecurityGroup: #こっちは作成に成功して残る
    Type: "AWS::EC2::SecurityGroup"
    Properties:
        GroupDescription: "Sample Security Group"
        GroupName: sample-sg
        VpcId: !Ref VPCId
        Tags:
          - Key: Name
            Value: sample-sg
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: !Ref AppPort
            ToPort: !Ref AppPort
            CidrIp: 172.16.0.10/32

  InvalidSecurityGroup: # こっちは作成できない
    Type: "AWS::EC2::SecurityGroup"
    Properties:
        GroupDescription: "Sample Invalid Security Group"
        GroupName: sample-invalid-sg
        VpcId: vpc-aaaabbbbbcccc # 無意味なVPC ID
        Tags:
          - Key: Name
            Value: sample-invalid-sg
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: !Ref AppPort
            ToPort: !Ref AppPort
            CidrIp: 172.16.0.10/32

IAMリソースを作成するテンプレート

sample-role.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM Role Defenition

Resources:
  SampleRole:
    Type: 'AWS::IAM::Role'
    Properties:
      # RoleName: sample-role # これを指定する場合はCAPABILITY_NAMED_IAMが必要
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ec2.amazonaws.com"
            Action: "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore