[小ネタ] CLIからCloudFormation StackSetsを作成する方法

2020.12.08

小ネタです。

ちょっとお仕事でGuardDutyを各リージョンに導入する必要があり、以下の記事を参考にCloudFormationのStackSetsを作成しようとしました。

作業自体はシンプルなものであるためマネジメントコンソールからポチポチ実行しても良いだろうと思ったのですが、いざマネジメントコンソールからStackSetsを作り各リージョンに対してスタックインスタンスを増やそうと「すべてのリージョン」ボタンをクリックしてやると、

こんな感じでアフリカ(ケープタウン)アジアパシフィック(香港)などデフォルト無効でオプトアウトしているリージョンまで候補に含まれてしまいます。

(「すべてのリージョン」で追加されるリージョンはオプトインの状況を判断してくれない...どうして...)

これだけであればオプトインしていないリージョンを手作業で「削除」すれば構いませんが、今回は複数アカウント同様の作業をする必要があったためめんどうくさいという気持ちになり、マネジメントコンソールの使用を止めCLIからStackSetsを作ることにしました。

CLIでStackSetsを作る基本

マネジメントコンソールからウィザードでStackSetsを作る場合、StackSetsの定義と対象リージョンの指定を一度に行いますが、APIとしてはそれぞれ別であり、CLIから行う場合も二段階に分けて以下のコマンドを実行してやる必要があります。

AWS Tools for PowerShellを使ってStackSetsを作る

私はメインでAWS Tools for PowerShellを使うので最初にAWS Tools for PowerShellでの手順を紹介します。

検証環境

動作確認は以下の環境で行っています。

  • 64bit版 Windows 10 Pro (Ver.20H2)
  • PowerShell 7.1.0
  • AWS Tools for PowerShell 4.1.5
    • AWS.Tools.CloudFormation, AWS.Tools.EC2 モジュールを使用

0. 前準備

前準備としてStackSetsを利用するために必要なAWSCloudFormationStackSetAdministrationRoleロールおよびAWSCloudFormationStackSetExecutionRoleロールは以下のコマンドで作成できます。

公開されているテンプレートをNew-CFNStackを使って作成しているだけです。
注意点としてはIAMロールを作るためCapabilityパラメーターにCAPABILITY_IAMCAPABILITY_NAMED_IAMを指定する必要があるくらいです。

# AWSCloudFormationStackSetAdministrationRole ロールの作成
$params = @{
    StackName = 'CFnStackSets-AdministrationRole-Stack'
    TemplateURL ='https://s3.amazonaws.com/cloudformation-stackset-templates-us-east-1/master_account_role.template'
    Capability = @('CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM')
}
New-CFNStack @params

以下の例ではGet-STSCallerIdentityを使ってMasterアカウントを自分自身のアカウントにしています。
他のアカウントにする場合は決め打ちで指定してください。

# AWSCloudFormationStackSetExecutionRole ロールの作成
$params = @{
    StackName = 'CFnStackSets-ExecutionRole-Stack'
    TemplateURL ='https://s3.amazonaws.com/cloudformation-stackset-templates-us-east-1/managed_account_role.template'
    Parameter = @(
        @{ParameterKey = 'MasterAccountId'; ParameterValue = (Get-STSCallerIdentity -Select Account)} # 自分自身のアカウントを管理
    )
    Capability = @('CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM')
}
New-CFNStack @params

1. New-CFNStack の実行

New-CFNStackSetの使い方はこんな感じです。
New-CFNStackと似てますのでそんなに困ることは無いかと思います。

$params = @{
    StackSetName = 'GuardDuty-for-All-Regions'
    Description = 'Enable GuardDuty and set SNS alert'
    Parameter = @(
        @{ParameterKey = 'MailAddress'; ParameterValue = 'your-email@example.com'}
    )
    TemplateURL = 'https://s3-ap-northeast-1.amazonaws.com/pub-codes-9nvushbw/templates/guardduty-sns.template'
}
New-CFNStackSet @params

これでスタックインスタンスが無い状態のStackSetsを作成できます。

2. New-CFNStackInstance の実行

次にこのStackSetsに対してNew-CFNStackInstanceを使い各リージョン毎のスタックインスタンスを増やしてやります。
先に手順を示しておくと、以下の様にすると自身のアカウントのオプトインされた全リージョンに対してスタックインスタンスを作成します。

$params = @{
    StackSetName = 'GuardDuty-for-All-Regions'
    Account = (Get-STSCallerIdentity -Select Account) # 今回は自身のアカウントのみ
    StackInstanceRegion = (Get-EC2Region -Select Regions.RegionName) # オプトインされたリージョンのみを取得
    OperationPreference = [Amazon.CloudFormation.Model.StackSetOperationPreferences]@{
        MaxConcurrentCount = 1
        FailureToleranceCount = 0
    }
}
New-CFNStackInstance @params

これでオプトインしているリージョンだけに対してインスタンスを作りますので後は完了を待つだけになります。

オプトインされたリージョンのみを取得する方法

New-CFNStackInstanceでは-StackInstanceRegionパラメーターにインスタンスを作るリージョン名の配列を渡してやります。

オプトインの状態を含めたリージョン情報はGet-EC2Regionで取得可能であり、引数なしで実行した場合オプトインしているリージョンのみ取得できます。
オプトアウトしているリージョンも合わせて欲しい場合は-AllRegionパラメーターを指定してやります。

このため、

Get-EC2Region -Select Regions.RegionName

とすることでオプトインしているリージョン名の一覧を取得できます。

AWS CLIを使ってStackSetsを作る

続けてAWS CLIを使った手順を紹介します。

検証環境

動作確認は以下の環境で行っています。

  • Ubuntu 18.04 on WSL2
  • AWS CLI 2.1.7 + Bash 4.4.20

0. 前準備

AWSCloudFormationStackSetAdministrationRoleロールおよびAWSCloudFormationStackSetExecutionRoleロールは以下のコマンドで作成できます。

# AWSCloudFormationStackSetAdministrationRole ロールの作成
aws cloudformation create-stack \
    --stack-name CFnStackSets-AdministrationRole-Stack \
    --template-url https://s3.amazonaws.com/cloudformation-stackset-templates-us-east-1/master_account_role.template \
    --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

# AWSCloudFormationStackSetExecutionRole ロールの作成
aws cloudformation create-stack \
    --stack-name CFnStackSets-ExecutionRole-Stack \
    --template-url https://s3.amazonaws.com/cloudformation-stackset-templates-us-east-1/managed_account_role.template \
    --parameters ParameterKey=MasterAccountId,ParameterValue=$(aws sts get-caller-identity --query Account --output text) \
    --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

1. aws cloudformation create-stack-set の実行

aws cloudformation create-stack-setの使い方はこんな感じです。

aws cloudformation create-stack-set \
    --stack-set-name GuardDuty-for-All-Regions \
    --description 'Enable GuardDuty and set SNS alert' \
    --parameters ParameterKey=MailAddress,ParameterValue=your-email@example.com \
    --template-url https://s3-ap-northeast-1.amazonaws.com/pub-codes-9nvushbw/templates/guardduty-sns.template

2. aws cloudformation create-stack-instances の実行

aws cloudformation create-stack-instancesの使い方はこんな感じです。

aws cloudformation create-stack-instances \
    --stack-set-name GuardDuty-for-All-Regions \
    --accounts $(aws sts get-caller-identity --query Account --output text) \
    --regions $(aws ec2 describe-regions --query Regions[*].RegionName --output text | tr '\t' ' ') \
    --operation-preferences MaxConcurrentCount=1,FailureToleranceCount=0

やっていることはPowerShellの場合と同様です。
aws ec2 describe-regionsでオプトインしているリージョンの一覧を取得しています。

最後に

ざっとこんな感じです。
知ってしまえば大した内容ではありませんがそれなりに役に立つことがあるかと思います。