(アップデート)AWS Billing and Cost Managementの権限移行を一括でできるようになりました

この記事を書いたメンバー:

Daiki Handa

(アップデート)AWS Billing and Cost Managementの権限移行を一括でできるようになりました

目次

はじめに

こんにちは、半田(@handy)です。

みなさんは2023年1月に発表された以下のAWSアップデートはもう確認されたでしょうか。

このアップデートについてざっくりお伝えすると、これまでAWSの請求画面の権限制御はそこまで細かくできなかったのですが、このアップデートによりきめ細かな権限制御ができるようになった、という内容になります。

それに伴って、以下のサービスプレフィックスとアクションが廃止されるため、新しい権限への移行が必要となりました。

  • aws-portal:ViewBilling
  • aws-portal:ModifyBilling
  • aws-portal:ViewAccount
  • aws-portal:ModifyAccount
  • aws-portal:ViewPaymentMethods
  • aws-portal:ModifyPaymentMethods
  • aws-portal:ViewUsage
  • purchase-orders:ViewPurchaseOrders
  • purchase-orders:ModifyPurchaseOrders


この新しい権限というのが少し厄介で、[AWS Billing 向けのアクセスコントロールの移行]ページに新しい権限が請求画面のどの機能に紐づいているのかが記載されているのですが、廃止予定の既存権限をカスタムして実装していると新しい権限でどう実装したらよいかはわかりづらく、本当に想定通り設定されているかどうかは実際にテストをしないとわからないものとなっていました。

※完全切り替え日である7月6日を待たずともアカウント単位でテストできるツールは既に提供されています→[AWS 請求、コスト管理、アカウントコンソールの新しいアクセス許可をテストするための許可リストツール]


私も地道に検証していたのですが、そんな時にAWSから嬉しい発表が出てきました。

https://aws.amazon.com/jp/about-aws/whats-new/2023/06/scripts-bulk-updates-policies-aws-billing-cost-management-permissions/


Organizationアカウントでの操作は必要ですが、請求回りの既存の権限を読み取って、自動で新しい権限に更新(正しくは追加)してくれるスクリプトが公開されました。

詳細な手順とスクリプトは以下になります。

https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/migrate-iam-permissions.html

https://github.com/aws-samples/bulk-policy-migrator-scripts-for-account-cost-billing-consoles

今回は上記手順を元に実際にポリシーの更新を試してみたいと思います。


前提

  • Python3がインストール済みであること
  • AWS CLIがインストール済みであること
  • Organization管理アカウント(支払者アカウント)にコンソールアクセス・CLIアクセスができること


事前準備

手順にも記載がありますが、スクリプトを実行するためにはいくつか事前準備が必要になります。


ローカルでの作業

まずはスクリプトをローカルの環境にクローンしてきて、その後フォルダに移動します。

$ git clone https://github.com/aws-samples/bulk-policy-migrator-scripts-for-account-cost-billing-consoles.git
$ cd bulk-policy-migrator-scripts-for-account-cost-billing-consoles


Pythonの仮想環境を作成して、スクリプトの実行に必要なPythonパッケージをインストールします。

$ python -m venv venv
$ source venv/bin/activate
$ pip install -r requirements.txt


以下のパスに格納されているjsonファイルの一部の修正を行います。

 $ vi policy_migration_scripts/cfn_template/billing_console_policy_migrator_role.json


修正内容としては、<management_account>の部分をOrganizationのAWSアカウントIDに置換するだけです。

それ以外はデフォルトのままでOKです。

 {
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "BillingConsolePolicyMigratorRoleTemplate": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "AWS": "arn:aws:iam::<management_account>:root"
              },
              "Action": "sts:AssumeRole"
            }
          ]
        },
        "Description": "IAM role in member account for bulk policy migrator scripts",
        "Policies": [
          {
            "PolicyName": "BillingConsolePolicyMigratorRolePolicy",
            "PolicyDocument": {
              "Statement": [
                {
                  "Action": [
                    "iam:GetAccountAuthorizationDetails",
                    "iam:GetPolicy",
                    "iam:GetPolicyVersion",
                    "iam:GetUserPolicy",
                    "iam:GetGroupPolicy",
                    "iam:GetRolePolicy",
                    "iam:CreatePolicyVersion",
                    "iam:DeletePolicyVersion",
                    "iam:PutUserPolicy",
                    "iam:PutGroupPolicy",
                    "iam:PutRolePolicy",
                    "iam:SetDefaultPolicyVersion",
                    "iam:ListPolicyVersions"
                  ],
                  "Effect": "Allow",
                  "Resource": "*"
                }
              ],
              "Version": "2012-10-17"
            }
          }
        ],
        "RoleName": "BillingConsolePolicyMigratorRole"
      }
    }
  }
}


マネジメントコンソールでの作業

次にOrganization管理アカウントにマネジメントコンソールでログインし、CloudFormationコンソールからStackSetの作成を行います。

この時、テンプレートには先ほど修正した「billing_console_policy_migrator_role.json」を指定します。


今回は最低限の設定として、StackSet名とリージョン、最後のチェックを入れて実行します。


しばらくしてステータスがActiveになれば成功です。


確認のためにメンバーアカウントにログインして、「BillingConsolePolicyMigrator」ロールを検索すると作成されていることがわかります。

スクリプトではこのロールを使用して、ポリシーの収集と更新を行います。


念のため「BillingConsolePolicyMigrator」ロールに付いているIAMポリシーも確認すると、最初に修正したjsonファイルと同じポリシーが設定されていることがわかります。


実作業

廃止権限を利用しているIAMポリシー取得

ここからまたローカルでの作業に戻ります。


まずはスクリプト実行のため、スクリプトが格納されているフォルダに移動します。

 $ cd policy_migration_scripts/scripts


スクリプトの実行前にAWSの認証情報を設定しておきます。

今回はSSOユーザーの認証情報を使用するので、SSOコンソールから以下認証情報をコピペして設定します。

export AWS_ACCESS_KEY_ID="[アクセスキー]"
$ export AWS_SECRET_ACCESS_KEY="[シークレットアクセスキー]"
$ export AWS_SESSION_TOKEN="[セッショントークン]"


以下のコマンドを実行して、Organizationに紐づいているメンバーアカウントすべてに対してスキャンを実施して、更新対象のIAMポリシーを収集します。

 $ python identify_affected_policies.py --all


実行すると以下のようなログが出力されます。

ログにはスキャンしたメンバーアカウントのアカウントIDとIAMリソースの数が表示され、更新対象のIAMポリシーを見つけると「affected_policies_and_suggestions.json」と「detailed_affected_policies.json」にその内容が書き込まれます。

2023-06-09 19:54:55,886 - INFO - Caller account: [OrganizationアカウントID]
2023-06-09 19:54:55,886 - INFO - Using default action mapping config file
2023-06-09 19:54:55,887 - INFO - Loading default old to new action mapping file from [ホームDIR]/bulk-policy-migrator-scripts-for-account-cost-billing-consoles/policy_migration_scripts/config/action_mapping_config.json
2023-06-09 19:54:55,887 - INFO - Running in ORG mode for payer account: [OrganizationアカウントID]
2023-06-09 19:54:56,164 - INFO - Running with account: [OrganizationアカウントID]
2023-06-09 19:54:56,164 - INFO - Identifying affected policies...
2023-06-09 19:54:57,929 - INFO - Scanning 0 customer managed policies
2023-06-09 19:54:59,501 - INFO - Scanning 0 users
2023-06-09 19:55:01,119 - INFO - Scanning 0 groups
2023-06-09 19:55:03,199 - INFO - Scanning 10 roles
2023-06-09 19:55:03,418 - INFO - Scanning 1 roles
2023-06-09 19:55:03,418 - INFO - Running with account: [メンバーアカウントID-1]
2023-06-09 19:55:03,419 - INFO - Identifying affected policies...
2023-06-09 19:55:04,224 - INFO - Scanning 2 customer managed policies
2023-06-09 19:55:04,225 - INFO - Loading default old to new action mapping file from [ホームDIR]/bulk-policy-migrator-scripts-for-account-cost-billing-consoles/policy_migration_scripts/config/action_mapping_config.json
2023-06-09 19:55:04,225 - INFO - Loading default old to new action mapping file from [ホームDIR]/bulk-policy-migrator-scripts-for-account-cost-billing-consoles/policy_migration_scripts/config/action_mapping_config.json
2023-06-09 19:55:05,065 - INFO - Scanning 1 users
2023-06-09 19:55:05,903 - INFO - Scanning 0 groups
2023-06-09 19:55:07,003 - INFO - Scanning 10 roles
2023-06-09 19:55:07,986 - INFO - Scanning 1 SCPs
2023-06-09 19:55:08,259 - INFO - Running with account: [メンバーアカウントID-2]
2023-06-09 19:55:08,260 - INFO - Identifying affected policies...
2023-06-09 19:55:10,335 - INFO - Scanning 6 customer managed policies
2023-06-09 19:55:12,007 - INFO - Scanning 1 users
2023-06-09 19:55:14,011 - INFO - Scanning 1 groups
2023-06-09 19:55:15,960 - INFO - Scanning 10 roles
2023-06-09 19:55:16,427 - INFO - Scanning 7 roles
2023-06-09 19:55:16,430 - INFO - Affected policy report written to Affected_Policies_20230609-19-55-16-428968/affected_policies_and_suggestions.json
2023-06-09 19:55:16,430 - INFO - Detailed policy report written to Affected_Policies_20230609-19-55-16-428968/detailed_affected_policies.json
2023-06-09 19:55:16,430 - INFO - Done


「affected_policies_and_suggestions.json」には影響がある既存ポリシーの名前とポリシードキュメント、提案された新ポリシードキュメントが書き込まれます。

 [
    {
        "AccountsScanned": [
            "[OrganizationアカウントID]",
            "[メンバーアカウントID-1]",
            "[メンバーアカウントID-2]"
        ],
        "TotalAffectedAccounts": 1,
        "TotalAffectedPolicies": 1,
        "TotalSimilarPolicyGroups": 1
    },
    {
        "GroupName": "Group1",
        "ImpactedPolicies": [
            {
                "Account": "[メンバーアカウントID-1]",
                "PolicyType": "CustomerManagedPolicy",
                "PolicyName": "AdministratorDenyPolicy",
                "PolicyIdentifier": "arn:aws:iam::[メンバーアカウントID-1]:policy/AdministratorDenyPolicy"
            }
        ],
        "ImpactedPolicyStatements": [
            {
                "Effect": "Deny",
                "Action": [
                    "aws-portal:*Account",
                    "aws-portal:*PaymentMethods"
                ],
                "Resource": "*"
            }
        ],
        "SuggestedPolicyStatementsToAppend": [
            {
                "Action": [
                    "account:CloseAccount",
                    "account:DeleteAlternateContact",
                    "account:GetAccountInformation",
                    "account:GetAlternateContact",
                    "account:GetChallengeQuestions",
                    "account:GetContactInformation",
                    "account:PutAlternateContact",
                    "account:PutChallengeQuestions",
                    "account:PutContactInformation",
                    "billing:GetContractInformation",
                    "billing:GetIAMAccessPreference",
                    "billing:GetSellerOfRecord",
                    "billing:PutContractInformation",
                    "billing:UpdateIAMAccessPreference",
                    "invoicing:GetInvoicePDF",
                    "payments:CreatePaymentInstrument",
                    "payments:DeletePaymentInstrument",
                    "payments:GetPaymentInstrument",
                    "payments:GetPaymentStatus",
                    "payments:ListPaymentPreferences",
                    "payments:MakePayment",
                    "payments:UpdatePaymentPreferences"
                ],
                "Resource": "*",
                "Effect": "Deny",
                "Sid": "BillingConsolePolicyMigrator0"
            }
        ]
    }


既存では「AdministratorDenyPolicy」ポリシーで"aws-portal:*Account"と"aws-portal:*PaymentMethods"を拒否しており、新ポリシードキュメントではそれらの操作に紐づく複数のサービスプレフィックスとアクションが定義されていました。

実際に同じ操作となっているかどうかは確認が必要ですが、これだけでも調査・検証にかかる時間はかなり削減できそうです。


※ログの途中で出力されていた「action_mapping_config.json」ファイルを見ると廃止対象の権限と紐づく新しい権限のマッピングが見つかったので、恐らくこの内容を元に新ポリシードキュメントの提案を行っているのではないかと思います。

 $ cat ./bulk-policy-migrator-scripts-for-account-cost-billing-consoles/policy_migration_scripts/config/action_mapping_config.json
{
    "aws-portal:ViewAccount": [
        "account:GetAccountInformation",
        "account:GetAlternateContact",
        "account:GetChallengeQuestions",
        "account:GetContactInformation",
        "billing:GetContractInformation",
        "billing:GetIAMAccessPreference",
        "billing:GetSellerOfRecord",
        "payments:ListPaymentPreferences"
    ],
    "aws-portal:ModifyAccount": [
        "account:CloseAccount",
        "account:DeleteAlternateContact",
        "account:PutAlternateContact",
        "account:PutChallengeQuestions",
        "account:PutContactInformation",
        "billing:PutContractInformation",
        "billing:UpdateIAMAccessPreference",
        "payments:UpdatePaymentPreferences"
    ],
    "aws-portal:ViewPaymentMethods": [
        "account:GetAccountInformation",
        "payments:GetPaymentStatus",
        "payments:ListPaymentPreferences",
        "payments:GetPaymentInstrument",
        "invoicing:GetInvoicePDF"
    ],
    "aws-portal:ModifyPaymentMethods": [
        "payments:DeletePaymentInstrument",
        "payments:CreatePaymentInstrument",
        "payments:UpdatePaymentPreferences",
        "payments:MakePayment",
        "account:GetAccountInformation"
    ],
    "aws-portal:ViewUsage": [
        "cur:GetUsageReport"
    ],
    "aws-portal:ViewBilling": [
        "account:GetAccountInformation",
        "billing:GetBillingData",
        "billing:GetBillingDetails",
        "billing:GetBillingNotifications",
        "billing:GetBillingPreferences",
        "billing:GetContractInformation",
        "billing:GetCredits",
        "billing:GetIAMAccessPreference",
        "billing:GetSellerOfRecord",
        "billing:ListBillingViews",
        "ce:DescribeNotificationSubscription",
        "ce:DescribeReport",
        "ce:GetAnomalies",
        "ce:GetAnomalyMonitors",
        "ce:GetAnomalySubscriptions",
        "ce:GetCostAndUsage",
        "ce:GetCostAndUsageWithResources",
        "ce:GetCostCategories",
        "ce:GetCostForecast",
        "ce:GetDimensionValues",
        "ce:GetPreferences",
        "ce:GetReservationCoverage",
        "ce:GetReservationPurchaseRecommendation",
        "ce:GetReservationUtilization",
        "ce:GetRightsizingRecommendation",
        "ce:GetSavingsPlansCoverage",
        "ce:GetSavingsPlansPurchaseRecommendation",
        "ce:GetSavingsPlansUtilization",
        "ce:GetSavingsPlansUtilizationDetails",
        "ce:GetTags",
        "ce:GetUsageForecast",
        "ce:ListCostAllocationTags",
        "ce:ListSavingsPlansPurchaseRecommendationGeneration",
        "consolidatedbilling:GetAccountBillingRole",
        "consolidatedbilling:ListLinkedAccounts",
        "cur:GetClassicReport",
        "cur:GetClassicReportPreferences",
        "cur:ValidateReportDestination",
        "freetier:GetFreeTierAlertPreference",
        "freetier:GetFreeTierUsage",
        "invoicing:GetInvoiceEmailDeliveryPreferences",
        "invoicing:GetInvoicePDF",
        "invoicing:ListInvoiceSummaries",
        "payments:GetPaymentInstrument",
        "payments:GetPaymentStatus",
        "payments:ListPaymentPreferences",
        "tax:GetTaxInheritance",
        "tax:GetTaxRegistrationDocument",
        "tax:ListTaxRegistrations"
    ],
    "aws-portal:ModifyBilling": [
        "billing:PutContractInformation",
        "billing:RedeemCredits",
        "billing:UpdateBillingPreferences",
        "ce:CreateAnomalyMonitor",
        "ce:CreateAnomalySubscription",
        "ce:CreateNotificationSubscription",
        "ce:CreateReport",
        "ce:DeleteAnomalyMonitor",
        "ce:DeleteAnomalySubscription",
        "ce:DeleteNotificationSubscription",
        "ce:DeleteReport",
        "ce:ProvideAnomalyFeedback",
        "ce:StartSavingsPlansPurchaseRecommendationGeneration",
        "ce:UpdateAnomalyMonitor",
        "ce:UpdateAnomalySubscription",
        "ce:UpdateCostAllocationTagsStatus",
        "ce:UpdateNotificationSubscription",
        "ce:UpdatePreferences",
        "cur:PutClassicReportPreferences",
        "freetier:PutFreeTierAlertPreference",
        "invoicing:PutInvoiceEmailDeliveryPreferences",
        "payments:CreatePaymentInstrument",
        "payments:DeletePaymentInstrument",
        "payments:MakePayment",
        "payments:UpdatePaymentPreferences",
        "tax:BatchPutTaxRegistration",
        "tax:DeleteTaxRegistration",
        "tax:PutTaxInheritance"
    ],
    "purchase-orders:ViewPurchaseOrders": [
        "purchase-orders:GetPurchaseOrder",
        "purchase-orders:ListPurchaseOrderInvoices",
        "purchase-orders:ListPurchaseOrders",
        "invoicing:GetInvoicePDF",
        "payments:ListPaymentPreferences"
    ],
    "purchase-orders:ModifyPurchaseOrders": [
        "purchase-orders:AddPurchaseOrder",
        "purchase-orders:DeletePurchaseOrder",
        "purchase-orders:UpdatePurchaseOrder",
        "invoicing:UpdatePurchaseOrderStatus"
    ]


「detailed_affected_policies.json」には影響がある既存ポリシーの定義が書き込まれます。

このファイルを見ることで、どのアカウントのどのようなポリシーが影響を受けるのかがすぐにわかるようになっています。

 [
    {
        "Account": "[メンバーアカウントID-1]",
        "PolicyType": "CustomerManagedPolicy",
        "PolicyName": "AdministratorDenyPolicy",
        "PolicyIdentifier": "arn:aws:iam::[メンバーアカウントID-1]:policy/AdministratorDenyPolicy",
        "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": [
                        "aws-portal:*Account",
                        "aws-portal:*PaymentMethods"
                    ],
                    "Resource": "*",
                    "Effect": "Deny"
                }
            ]
        }
    }


既存のポリシーを更新する

「affected_policies_and_suggestions.json」を確認して、対象のIAMリソースと新しい権限が問題なければアップデートスクリプトを実行します。

アップデートスクリプトは引数として「affected_policies_and_suggestions.json」が含まれたフォルダパスを受け取り、スクリプトは「affected_policies_and_suggestions.json」をインプットとして更新処理を実行します。

$ ls Affected_Policies_20230609-19-55-16-428968 
affected_policies_and_suggestions.json  detailed_affected_policies.json
$ python update_affected_policies.py --affected-policies-directory Affected_Policies_20230609-19-55-16-428968


アップデートスクリプトが正常に完了すると、以下のようにポリシーが更新されます。

  • アップデートスクリプト実行前

  • アップデートスクリプト実行後


※手順にも以下のように記載がありますが、スクリプトはあくまで新しい権限の追加のみを実施し、既存の廃止対象権限の削除などは行わないため注意が必要です


更新内容を元に戻したい場合

もし既存ポリシーを更新した後に何か問題があって元のポリシー設定に戻したい場合、すべてのアカウントにログインして手動で変更するのはとても非効率です。

そのため、スクリプトには変更内容を元に戻すオプションも準備されています。


手順としては簡単で、以下のスクリプトを実行するだけで元の設定に戻すことができます。

 $ python rollBack_affected_polices.py --all


もし一部のアカウントだけロールバックしたい、または一部のアカウント以外をロールバックしたい場合はスクリプトオプションを指定することも可能になっています。

※最初のスキャンに使用した「identify_affected_policies.py」でもこれらのオプションは利用可能です

# 特定アカウントのみをロールバック
 $ python rollBack_affected_polices.py --accounts 111122223333,5555555555,6666666666

# 特定アカウント以外をロールバック
$ python rollBack_affected_polices.py --all--exclude-accounts 7777777777,8888888888,9999999999


まとめ

今回は更新対象のポリシーが1つのみにしていましたが、通常AWS Organizationを使用されている環境であればその比ではない数のポリシーが更新対象となっているかと思いますので、必要に応じてこのスクリプトの利用を検討されるのが良いかと思います。

ただし、新権限が旧権限とまったく同じ操作ができるかどうかは実際にテストしてみないとわからないため、そのあたりは冒頭でご説明したテストツールも併用して検証されるのが良いかと思います。


おまけ

検証用のAWS環境を保有している会社であれば、AWS Organizationを使用しているケースは多いかと思いますが、1個人がOrganizationの管理アカウントに対してアクセスする権限を与えてもらえるかというと、難しい場合もあるかと思います。


そこで個人的に推したいのは、個人Organizationの利用です。

私自身過去に以下の記事を見て実際に利用していますが、OUやSCPの作成・設定、AWS IAM Identity Centerの利用など、会社の環境では気軽に使うには少しハードルが高い検証でもすぐに試せるので、かなり役に立っています。

料金の発生するAWSサービスを利用したいときは注意が必要ですが、デフォルトで無料枠が付いているサービスを使うなどやり方次第でできることは多いので、是非お勧めです。


最後に

この記事がどなたかの参考になれば幸いです。


カテゴリー
タグ

この記事を書いたメンバー

SAPシステムや基幹システムのクラウド移行・構築・保守、
DXに関して
お気軽にご相談ください

03-6260-6240 (受付時間 平日9:30〜18:00)