🐙

Stacksetsでデプロイ先を制御する。(ConditionsとIf関数合わせ技)

2023/12/26に公開

Stacksets活用術

前提

  • Stacksetsを利用すると複数のアカウントに対して一斉に同一のリソースをデプロイすることができます。
  • その際に、一部のアカウントのみに特定のリソースをデプロイしたい時があります。
  • 一般的にはアカウント単位での制御はルールセクションでConditionsを利用して実現します。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html

  • 例えばアカウント1,2に対して作成するロールを分ける際は以下のようになります。
リソース Account1 Account2
RoleA o o
RoleB - o
Conditions
AWSTemplateFormatVersion: 2010-09-09
Description: sample

# ------------------------------------------------------------#
# Conditions
# ------------------------------------------------------------#
Conditions:
  IsAccount2: !Equals [!Ref AWS::AccountId, <Account2 ID>]

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  # IAM Role
  IAMRoleA:
    Type: AWS::IAM::Role
    Properties:
      Path : /
      AssumeRolePolicyDocument: 
          # 略
      RoleName: IAMRoleA 
  

  IAMRoleB:   
    Type: AWS::IAM::Role
    # ★★★★★★★★
    # Account2 のみにデプロイする
    Condition: IsAudit
    # ★★★★★★★★
    Properties:
      Path : /
      AssumeRolePolicyDocument: 
        # 略
      RoleName: IAMRoleB

課題

  • 今回IAMロールを作って共通的なポリシーを全ロールにアタッチする構成にする際に詰まりました。
  • 具体的にはポリシーのアタッチ先のロールが存在するかどうかがConditions次第(今回はアカウント次第)となっており安易に!Refでメンションするとスタック構築の際に存在しないリソースをメンションしたことによるエラーが発生します。

解決策

  • 条件ルールのConditions、条件関数のIf関数を合わせて利用することにより実現可能です。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html

Conditions+If
AWSTemplateFormatVersion: 2010-09-09
Description: sample

# ------------------------------------------------------------#
# Conditions
# ------------------------------------------------------------#
Conditions:
  IsAccount2: !Equals [!Ref AWS::AccountId, <Account2 ID>]

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  # IAM Role
  IAMRoleA:
    Type: AWS::IAM::Role
    Properties:
      Path : /
      AssumeRolePolicyDocument: 
          # 略
      RoleName: IAMRoleA 
  

  IAMRoleB:   
    Type: AWS::IAM::Role
    # Deploy only audit account
    Condition: IsAudit
    Properties:
      Path : /
      AssumeRolePolicyDocument: 
        # 略
      RoleName: IAMRoleB

  # IAM Policy
  CommonIAMPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: IAMPolicy-common
      Roles: 
        - !Ref IAMRoleA
        # ★★★★★★★★★★★★★★★★★
        # アカウント2であればIAMロールBにもポリシーをアタッチする。
        - !If [IsAccount2, !Ref IAMRoleB, !Ref AWS::NoValue]    
        # ★★★★★★★★★★★★★★★★★
        PolicyDocument: 
            # 略


  • なお、IF関数の文法としてはこんな構成になっている

まとめ

  • Conditionsルールだけ利用している記事が多く、If関数との組み合わせの紹介記事を見つけられなかったので今回執筆しました。
  • ご参考になればうれしいです。

2024/1/16追記

  • 条件分岐の中でyamlの構成ごと挿入する場合の記述方法はこちら
  • IAMポリシーなどなら正直ポリシーを分けてしまえばよいが、KMSのリソースベースポリシーなどはこのような強制分岐が有効
  PolicySmaple:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: xxx
      PolicyDocument: 
        Version: 2012-10-17
        Statement:
        - 
          Sid: xxxx
          Effect: xxxx

        # 特定の条件でのみ追加するポリシー 
        - !If
          - IsAccount2
          - 
            Sid: xxx
            Effect: Allow
            Principal:
              AWS: 
                - !Sub IAMRoleB
                  Action: 
              - xxxxxxxx
            Resource: '*'
          - !Ref AWS::NoValue

Discussion