AWS CloudFormation StackSetsを利用してPermissions boundary用IAMポリシーを複数アカウントで作成してみる

2022.09.08

マルチアカウント環境で Permissions boundary (アクセス許可の境界) を利用する場合において、事前に Permissions boundary としてアタッチするための IAM ポリシーを各 AWS アカウントで作成しておきたい場合があります。

本ブログでは、AWS CloudFormation StackSets を利用して複数の AWS アカウントに IAM ポリシーを作成する方法を試してみます。

Permissions boundary の利用例は次のブログで紹介しています。

AWS CloudFormation StackSets については次のブログが参考になります。


作成する IAM ポリシー

AWS ナレッジセンターで紹介されている Permissions boundary 用の IAM ポリシーを作成してみたいと思います。

作成するポリシーです。<AccountID>には IAM ポリシーを作成する AWS アカウントのアカウント ID を指定する必要があります。

test-permissions-boundary-policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAdminAccess",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    },
    {
      "Sid": "DenyAccessToCostAndBilling",
      "Effect": "Deny",
      "Action": [
        "account:*",
        "aws-portal:*",
        "savingsplans:*",
        "cur:*",
        "ce:*"
    ],
    "Resource": "*"
   },
   {
     "Sid": "DenyPermBoundaryIAMPolicyAlteration",
     "Effect": "Deny",
     "Action": [
       "iam:DeletePolicy",
       "iam:DeletePolicyVersion",
       "iam:CreatePolicyVersion",
       "iam:SetDefaultPolicyVersion"
   ],
   "Resource": [
     "arn:aws:iam::<AccountID>:policy/test-permissions-boundary-policy"
   ]
  },
  {
    "Sid": "DenyRemovalOfPermBoundaryFromAnyUserOrRole",
    "Effect": "Deny",
    "Action": [
      "iam:DeleteUserPermissionsBoundary",
      "iam:DeleteRolePermissionsBoundary"
   ],
   "Resource": [
     "arn:aws:iam::<AccountID>:user/*",
     "arn:aws:iam::<AccountID>:role/*"
   ],
   "Condition": {
     "StringEquals": {
       "iam:PermissionsBoundary": "arn:aws:iam::<AccountID>:policy/test-permissions-boundary-policy"
     }
    }
   },
   {
     "Sid": "DenyAccessIfRequiredPermBoundaryIsNotBeingApplied",
     "Effect": "Deny",
     "Action": [
     "iam:PutUserPermissionsBoundary",
     "iam:PutRolePermissionsBoundary"
   ],
   "Resource": [
     "arn:aws:iam::<AccountID>:user/*",
     "arn:aws:iam::<AccountID>:role/*"
   ],
   "Condition": {
     "StringNotEquals": {
       "iam:PermissionsBoundary": "arn:aws:iam::<AccountID>:policy/test-permissions-boundary-policy"
     }
    }
  },
  {
    "Sid": "DenyUserAndRoleCreationWithOutPermBoundary",
    "Effect": "Deny",
    "Action": [
      "iam:CreateUser",
      "iam:CreateRole"
  ],
  "Resource": [
    "arn:aws:iam::<AccountID>:user/*",
    "arn:aws:iam::<AccountID>:role/*"
  ],
  "Condition": {
    "StringNotEquals": {
      "iam:PermissionsBoundary": "arn:aws:iam::<AccountID>:policy/test-permissions-boundary-policy"
    }
   }
 },
 {
   "Sid": "DenyIAMActions",
   "Effect": "Deny",
   "Action": "iam:PassRole",
   "Resource": "arn:aws:iam::<AccountID>:role/*"
  }
 ]
}


CloudFormation テンプレート

作成するポリシーにおいて、ポイントになる部分は<AccountID>です。CloudFormation テンプレートでリソースを作成するアカウント毎に値を変える必要があります。その解決策として疑似パラメータ参照が利用できます。

擬似パラメータ参照 - AWS CloudFormation

疑似パラメータ参照を利用して、上述の IAM ポリシーを作成する CloudFormation テンプレートの例を示します。

AWSTemplateFormatVersion: 2010-09-09
Description: Create IAM Policy for Permissions boundary

Parameters:
  ManagedPolicyName:
    Description: Managed policy name for permissions boundary
    Type: String

Resources:
  IamManagedPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: Managed policy for permissions boundary
      ManagedPolicyName: !Sub ${ManagedPolicyName}
      Path: /
      PolicyDocument:
        Version: 2012-10-17
        Statement:
        - Sid: AllowAdminAccess
          Effect: Allow
          Action: '*'
          Resource: '*'
        - Sid: DenyAccessToCostAndBilling
          Effect: Deny
          Action:
          - 'account:*'
          - 'aws-portal:*'
          - 'savingsplans:*'
          - 'cur:*'
          - 'ce:*'
          Resource: '*'
        - Sid: DenyPermBoundaryIAMPolicyAlteration
          Effect: Deny
          Action:
          - 'iam:DeletePolicy'
          - 'iam:DeletePolicyVersion'
          - 'iam:CreatePolicyVersion'
          - 'iam:SetDefaultPolicyVersion'
          Resource: !Sub arn:aws:iam::${AWS::AccountId}:policy/${ManagedPolicyName}
        - Sid: DenyRemovalOfPermBoundaryFromAnyUserOrRole
          Effect: Deny
          Action:
          - 'iam:DeleteUserPermissionsBoundary'
          - 'iam:DeleteRolePermissionsBoundary'
          Resource:
          - !Sub arn:aws:iam::${AWS::AccountId}:user/*
          - !Sub arn:aws:iam::${AWS::AccountId}:role/*
          Condition:
            StringEquals:
              iam:PermissionsBoundary: !Sub arn:aws:iam::${AWS::AccountId}:policy/${ManagedPolicyName}
        - Sid: DenyAccessIfRequiredPermBoundaryIsNotBeingApplied
          Effect: Deny
          Action:
          - 'iam:PutUserPermissionsBoundary'
          - 'iam:PutRolePermissionsBoundary'
          Resource:
          - !Sub arn:aws:iam::${AWS::AccountId}:user/*
          - !Sub arn:aws:iam::${AWS::AccountId}:role/*
          Condition:
            StringNotEquals:
              iam:PermissionsBoundary: !Sub arn:aws:iam::${AWS::AccountId}:policy/${ManagedPolicyName}
        - Sid: DenyUserAndRoleCreationWithOutPermBoundary
          Effect: Deny
          Action:
          - 'iam:CreateUser'
          - 'iam:CreateRole'
          Resource:
          - !Sub arn:aws:iam::${AWS::AccountId}:user/*
          - !Sub arn:aws:iam::${AWS::AccountId}:role/*
          Condition:
            StringNotEquals:
              iam:PermissionsBoundary: !Sub arn:aws:iam::${AWS::AccountId}:policy/${ManagedPolicyName}
        - Sid: DenyIAMActions
          Effect: Deny
          Action: 'iam:PassRole'
          Resource: !Sub arn:aws:iam::${AWS::AccountId}:role/*

テンプレートでは IAM ポリシー名をパラメータとして指定できるようにしています。

ポリシーを定義する PolicyDocument では疑似パラメータ参照である${AWS::AccountId}を利用してアカウント ID を記載しています。


CloudFormation StackSets でリソース作成

StackSets で複数のアカウントにリソースを作成してみます。

StackSets を利用するためには事前準備が必要です。

AWS Organizations を利用している環境の場合は、次のブログの「(準備) アクセスの有効化」を参考に、AWS CloudFormation StackSets の「アクセスの有効化」を行います。

AWS Organizations を利用していない環境の場合は、次のブログの「事前準備」を参考に StackStes を実行する管理アカウントと実行されるアカウントのそれぞれに対して事前に IAM ロールを作成します。

本ブログでは AWS Organizations を利用している環境で StackSets を展開してみます。

CloudFormation の StackSets メニューにおける「StackSets の作成」から設定を行います。AWS Organizations でアクセスの有効化をしているため「サービスマネージドアクセス許可」を選択し、今回はテンプレートファイルをアップロードします。

StackSets 名とパラメータを入力します。パラメータは作成する IAM ポリシー名を入力します。

今回は、オプションはデフォルト設定のまま進めます。

デプロイは 2 つのアカウントが存在する OU を指定しています。「同時アカウントの最大数」は OU に存在するアカウント数に合わせて 2 としています。

最後にレビューを行い、実行します。

2 つのアカウントに展開されました。

2 つのアカウントで作成されたポリシーを確認してみます。

アカウント ID は例示の値に置換していますが、アカウント毎にアカウント ID が異なるポリシーの作成を確認できました。表示が長いので折りたたんでいます。

アカウント 111122223333 の test-permissions-boundary-policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "*",
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "AllowAdminAccess"
        },
        {
            "Action": [
                "account:*",
                "aws-portal:*",
                "savingsplans:*",
                "cur:*",
                "ce:*"
            ],
            "Resource": "*",
            "Effect": "Deny",
            "Sid": "DenyAccessToCostAndBilling"
        },
        {
            "Action": [
                "iam:DeletePolicy",
                "iam:DeletePolicyVersion",
                "iam:CreatePolicyVersion",
                "iam:SetDefaultPolicyVersion"
            ],
            "Resource": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy",
            "Effect": "Deny",
            "Sid": "DenyPermBoundaryIAMPolicyAlteration"
        },
        {
            "Condition": {
                "StringEquals": {
                    "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy"
                }
            },
            "Action": [
                "iam:DeleteUserPermissionsBoundary",
                "iam:DeleteRolePermissionsBoundary"
            ],
            "Resource": [
                "arn:aws:iam::111122223333:user/*",
                "arn:aws:iam::111122223333:role/*"
            ],
            "Effect": "Deny",
            "Sid": "DenyRemovalOfPermBoundaryFromAnyUserOrRole"
        },
        {
            "Condition": {
                "StringNotEquals": {
                    "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy"
                }
            },
            "Action": [
                "iam:PutUserPermissionsBoundary",
                "iam:PutRolePermissionsBoundary"
            ],
            "Resource": [
                "arn:aws:iam::111122223333:user/*",
                "arn:aws:iam::111122223333:role/*"
            ],
            "Effect": "Deny",
            "Sid": "DenyAccessIfRequiredPermBoundaryIsNotBeingApplied"
        },
        {
            "Condition": {
                "StringNotEquals": {
                    "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy"
                }
            },
            "Action": [
                "iam:CreateUser",
                "iam:CreateRole"
            ],
            "Resource": [
                "arn:aws:iam::111122223333:user/*",
                "arn:aws:iam::111122223333:role/*"
            ],
            "Effect": "Deny",
            "Sid": "DenyUserAndRoleCreationWithOutPermBoundary"
        },
        {
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::111122223333:role/*",
            "Effect": "Deny",
            "Sid": "DenyIAMActions"
        }
    ]
}
アカウント 444455556666 の test-permissions-boundary-policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "*",
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "AllowAdminAccess"
        },
        {
            "Action": [
                "account:*",
                "aws-portal:*",
                "savingsplans:*",
                "cur:*",
                "ce:*"
            ],
            "Resource": "*",
            "Effect": "Deny",
            "Sid": "DenyAccessToCostAndBilling"
        },
        {
            "Action": [
                "iam:DeletePolicy",
                "iam:DeletePolicyVersion",
                "iam:CreatePolicyVersion",
                "iam:SetDefaultPolicyVersion"
            ],
            "Resource": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy",
            "Effect": "Deny",
            "Sid": "DenyPermBoundaryIAMPolicyAlteration"
        },
        {
            "Condition": {
                "StringEquals": {
                    "iam:PermissionsBoundary": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy"
                }
            },
            "Action": [
                "iam:DeleteUserPermissionsBoundary",
                "iam:DeleteRolePermissionsBoundary"
            ],
            "Resource": [
                "arn:aws:iam::444455556666:user/*",
                "arn:aws:iam::444455556666:role/*"
            ],
            "Effect": "Deny",
            "Sid": "DenyRemovalOfPermBoundaryFromAnyUserOrRole"
        },
        {
            "Condition": {
                "StringNotEquals": {
                    "iam:PermissionsBoundary": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy"
                }
            },
            "Action": [
                "iam:PutUserPermissionsBoundary",
                "iam:PutRolePermissionsBoundary"
            ],
            "Resource": [
                "arn:aws:iam::444455556666:user/*",
                "arn:aws:iam::444455556666:role/*"
            ],
            "Effect": "Deny",
            "Sid": "DenyAccessIfRequiredPermBoundaryIsNotBeingApplied"
        },
        {
            "Condition": {
                "StringNotEquals": {
                    "iam:PermissionsBoundary": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy"
                }
            },
            "Action": [
                "iam:CreateUser",
                "iam:CreateRole"
            ],
            "Resource": [
                "arn:aws:iam::444455556666:user/*",
                "arn:aws:iam::444455556666:role/*"
            ],
            "Effect": "Deny",
            "Sid": "DenyUserAndRoleCreationWithOutPermBoundary"
        },
        {
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::444455556666:role/*",
            "Effect": "Deny",
            "Sid": "DenyIAMActions"
        }
    ]
}


diffコマンドで 2 つのポリシーを比較してみるとアカウント ID に違いがあることが分かります。

% diff 111122223333.json 444455556666.json
29c29
<             "Resource": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy",
---
>             "Resource": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy",
36c36
<                     "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy"
---
>                     "iam:PermissionsBoundary": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy"
44,45c44,45
<                 "arn:aws:iam::111122223333:user/*",
<                 "arn:aws:iam::111122223333:role/*"
---
>                 "arn:aws:iam::444455556666:user/*",
>                 "arn:aws:iam::444455556666:role/*"
53c53
<                     "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy"
---
>                     "iam:PermissionsBoundary": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy"
61,62c61,62
<                 "arn:aws:iam::111122223333:user/*",
<                 "arn:aws:iam::111122223333:role/*"
---
>                 "arn:aws:iam::444455556666:user/*",
>                 "arn:aws:iam::444455556666:role/*"
70c70
<                     "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/test-permissions-boundary-policy"
---
>                     "iam:PermissionsBoundary": "arn:aws:iam::444455556666:policy/test-permissions-boundary-policy"
78,79c78,79
<                 "arn:aws:iam::111122223333:user/*",
<                 "arn:aws:iam::111122223333:role/*"
---
>                 "arn:aws:iam::444455556666:user/*",
>                 "arn:aws:iam::444455556666:role/*"
86c86
<             "Resource": "arn:aws:iam::111122223333:role/*",
---
>             "Resource": "arn:aws:iam::444455556666:role/*",


さいごに

Permissions boundary としてアタッチするための IAM ポリシーを AWS CloudFormation StackSets を利用して複数の AWS アカウントに作成する方法を試してみました。

他の用途として、AWS IAM Identity Center でアタッチする IAM ポリシーを各アカウントで作成する場合にも利用できるかもしれません。

以上、このブログがどなたかのご参考になれば幸いです。