TerraformでIAM系リソースの一括インポート

TerraformでIAM系リソースの一括インポート

TerraformでI AM系リソースの一括インポート
2025.09.11

Terraformで、多数のIAM系リソースのインポートを行う機会がありましたので、その際の手順などを記載します。

経緯

あるAWSアカウントで、全てWeb Consoleで設定作業がされていたため、IaC化を進めていくこととなりました。今回は、IAM系のリソースをTerraformでIaC化を行うこととしましたが、多数の設定が存在し、それらのimport定義を作成するのはとても大変なため、スクリプトを生成AIで準備して行いました。

(以下のブログで紹介したRoute53ホストゾーンのTerraformでの一括インポートと、手順は共通する部分があります)
Terraformで多数のDNSレコードを含むRoute53ホストゾーンを一括インポート

対象リソース

今回の具体的な対象リソースは以下の通りです。(IAM Userは運用上の課題がまだあり、今回は対象外です。)
IAM Role, IAM Policy, IAM Group, インスタンスプロファイル

但し、一部除外するものなどがあり、条件は以下の通りです。

  • IAM Roleの中で、AWSが自動作成するサービスリンクロールは対象外
  • IAM Policyは、カスタマー管理ポリシーのみが対象
  • IAM Roleの、IAM Policyアタッチ設定においては、カスタマー管理ポリシー以外がアタッチされていた場合でも、そのアタッチ情報は管理対象とする

基本的には、AWSが自動作成するような部分は除外して、ユーザ側で管理する部分のみをIaC化するため、上記のような条件としました。

Terraform側で定義するリソースは、以下の通りとしました。

リソース名 説明
aws_iam_role IAM Roleの定義
aws_iam_policy IAM Policyの定義
aws_iam_role_policy_attachment IAM RoleにIAM Policyをアタッチする定義
aws_iam_role_policy IAM Roleにインラインポリシーを設定する定義
aws_iam_instance_profile インスタンスプロファイルの定義
aws_iam_group IAM Groupの定義
aws_iam_group_policy_attachment IAM GroupにIAM Policyをアタッチする定義
aws_iam_group_policy IAM Groupにインラインポリシーを設定する定義

手順の概要

以下のような手順としました。

  1. 生成AIで、importブロックを作成するスクリプトを生成
  2. 生成したスクリプトで、importブロックのtfファイルを作成
  3. 「terraform plan -generate-config-out=xxx.tf」で、import定義を基に、resource定義のtfファイルを作成
  4. resource定義のtfファイルを修正
  5. 「terraform plan」で確認
  6. 「terraform apply」でインポート

それぞれの手順の詳細を以下に記載します

生成AIで、importブロックを作成するスクリプトを生成

以下のようなプロンプトでスクリプトを生成しました。

			
			terraformとAWSに関連するpythonスクリプトの作成依頼です。
あるAWSアカウントで、全ての設定がWeb Consoleで作成されています。ここで、そのアカウントに存在している以下のIAM 関連リソースの設定をTerraformでコード化したいです。

<Terraform化の対象リソース>
IAM Role
IAM Policy
IAM Group
インスタンスプロファイル

このため、上記リソースを、terraformのimportブロックを作成して、importしたいです。
importブロックを作成するterraformの対象リソースは以下の通りです。

<terraformの対象リソース>
aws_iam_role
aws_iam_policy
aws_iam_role_policy_attachment
aws_iam_role_policy
aws_iam_group
aws_iam_group_policy_attachment
aws_iam_group_policy
aws_iam_instance_profile

ただし、対象リソースは、以下のようにしてください。
・aws_iam_role は、AWS自動作成のサービスリンクロールは対象外とします。
・aws_iam_policy は、カスタマー管理ポリシーのみを対象とします。
・aws_iam_policy は、カスタマー管理ポリシーのみを対象です。ただし、"aws_iam_role_policy_attachment"では、I AM Role (サービスリンクロールは除外されている)に対して、もしもカスタマー管理ポリシー以外がアタッチされていた場合、"aws_iam_role_policy_attachment"にそのアタッチの定義を含めて下さい。

<importブロックの記載について>
それぞれのimportブロックの例は以下の通りです。toの右側のコメントは、リソース名の指定方法です。idの右側のコメントは、idの指定方法です。

import {
to = aws_iam_role.developer_name     #I AM Role名と同じにする
id = "developer_name"                #I AM Role名
}

import {
to = aws_iam_policy.UsersManageOwnCredentials                         # I AM Policy名と同じとする
id = "arn:aws:iam::123456789012:policy/UsersManageOwnCredentials"     # I AM PolicyのARN
}

import {
to = aws_iam_role_policy_attachment.test_role_test_policy          # I AM Role名_I AM Policy名 の形式とする
id = "test-role/arn:aws:iam::xxxxxxxxxxxx:policy/test-policy"      # ロール名とポリシーARNを / で区切る。
}

import {
to = aws_iam_role_policy.role_of_mypolicy_name_mypolicy_name              # I AM Role名_I AM Policy名 の形式とする
id = "role_of_mypolicy_name:mypolicy_name"                                # ロール名とポリシー名を : で区切る。
}

import {
  to = aws_iam_instance_profile.app_instance_profile_1   # インスタンスプロファイル名と同じとする
  id = "app-instance-profile-1"                          # インスタンスプロファイル名
}

import {
  to = aws_iam_group.developers            # I AM Group名と同じにする
  id = "developers"                        # I AM Group名
}

import {
  to = aws_iam_group_policy_attachment.test_group_test_policy         # I AM Group名_I AM Policy名 の形式とする
  id = "test-group/arn:aws:iam::xxxxxxxxxxxx:policy/test-policy"    # グループ名とポリシーARNを / で区切る
}

import {
  to = aws_iam_group_policy.group_of_mypolicy_name_mypolicy_name         # I AM Group名_I AM Policy名 の形式とする
  id = "group_of_mypolicy_name:mypolicy_name"                            # グループ名とポリシー名を : で区切る。
}

ただし、上記の to で指定するリソース名は、全て以下の共通ルールに従うものとします。
・大文字は小文字にする
・ -(ハイフン)は_(アンダーバー)にする。
・先頭が数字だった場合は、その前に_(アンダーバー)を付与する。
・ドット(.)は、(アンダーバー)に変える
・ /(スラッシュ)は(アンダーバー)に変える

上記のようなimportブロックのファイルを作りたいですが、Web Consoleで多数作成されているため、手動での作成は手間がかかります。importブロックのファイル作成を自動化するPythonスクリプトを作成して下さい。
スクリプト内には、適宜日本語のコメントをつけて下さい。

		

その結果、生成されたスクリプトは以下のようなものです。
(今回、スクリプトの詳細までは検証していませんが、今回の目的は問題なく達成できました)

			
			#!/usr/bin/env python3
"""
AWS IAMリソースをTerraformのimportブロックに自動変換するスクリプト

このスクリプトは以下のIAMリソースを対象とします:
- IAM Role (サービスリンクロールは除外)
- IAM Policy (カスタマー管理ポリシーのみ)
- IAM Group
- インスタンスプロファイル
- ロール・グループとポリシーのアタッチメント関係
"""

import boto3
import json
import re
from typing import List, Dict, Any
import argparse
import sys

class TerraformImportBlockGenerator:
    def __init__(self, profile_name: str = None, region: str = 'ap-northeast-1'):
        """
        初期化

        Args:
            profile_name: AWSプロファイル名(指定なしの場合はデフォルトプロファイルを使用)
            region: AWSリージョン
        """
        try:
            if profile_name:
                session = boto3.Session(profile_name=profile_name)
            else:
                session = boto3.Session()

            self.iam_client = session.client('iam', region_name=region)
            self.account_id = session.client('sts').get_caller_identity()['Account']
            print(f"アカウントID: {self.account_id}")

        except Exception as e:
            print(f"AWSセッションの初期化に失敗しました: {e}")
            sys.exit(1)

    def normalize_resource_name(self, name: str) -> str:
        """
        リソース名をTerraformの命名規則に合わせて正規化

        Args:
            name: 元のリソース名

        Returns:
            正規化されたリソース名
        """
        # 大文字を小文字に変換
        normalized = name.lower()

        # ハイフンをアンダースコアに変換
        normalized = normalized.replace('-', '_')

        # ドットをアンダースコアに変換
        normalized = normalized.replace('.', '_')

        # スラッシュをアンダースコアに変換
        normalized = normalized.replace('/', '_')

        # 先頭が数字の場合はアンダースコアを付与
        if normalized[0].isdigit():
            normalized = '_' + normalized

        return normalized

    def get_customer_managed_policies(self) -> List[Dict[str, Any]]:
        """
        カスタマー管理ポリシーの一覧を取得

        Returns:
            カスタマー管理ポリシーのリスト
        """
        print("カスタマー管理ポリシーを取得中...")
        policies = []

        try:
            paginator = self.iam_client.get_paginator('list_policies')

            for page in paginator.paginate(Scope='Local'):
                for policy in page['Policies']:
                    policies.append({
                        'PolicyName': policy['PolicyName'],
                        'Arn': policy['Arn']
                    })
        except Exception as e:
            print(f"カスタマー管理ポリシーの取得に失敗: {e}")

        print(f"カスタマー管理ポリシー {len(policies)}個を取得しました")
        return policies

    def get_iam_roles(self) -> List[Dict[str, Any]]:
        """
        IAMロール一覧を取得(サービスリンクロールは除外)

        Returns:
            IAMロールのリスト
        """
        print("IAMロールを取得中...")
        roles = []

        try:
            paginator = self.iam_client.get_paginator('list_roles')

            for page in paginator.paginate():
                for role in page['Roles']:
                    # サービスリンクロールを除外
                    if '/aws-service-role/' not in role['Path']:
                        roles.append({
                            'RoleName': role['RoleName'],
                            'Arn': role['Arn']
                        })
        except Exception as e:
            print(f"IAMロールの取得に失敗: {e}")

        print(f"IAMロール {len(roles)}個を取得しました(サービスリンクロール除く)")
        return roles

    def get_iam_groups(self) -> List[Dict[str, Any]]:
        """
        IAMグループ一覧を取得

        Returns:
            IAMグループのリスト
        """
        print("IAMグループを取得中...")
        groups = []

        try:
            paginator = self.iam_client.get_paginator('list_groups')

            for page in paginator.paginate():
                for group in page['Groups']:
                    groups.append({
                        'GroupName': group['GroupName'],
                        'Arn': group['Arn']
                    })
        except Exception as e:
            print(f"IAMグループの取得に失敗: {e}")

        print(f"IAMグループ {len(groups)}個を取得しました")
        return groups

    def get_instance_profiles(self) -> List[Dict[str, Any]]:
        """
        インスタンスプロファイル一覧を取得

        Returns:
            インスタンスプロファイルのリスト
        """
        print("インスタンスプロファイルを取得中...")
        profiles = []

        try:
            paginator = self.iam_client.get_paginator('list_instance_profiles')

            for page in paginator.paginate():
                for profile in page['InstanceProfiles']:
                    profiles.append({
                        'InstanceProfileName': profile['InstanceProfileName'],
                        'Arn': profile['Arn']
                    })
        except Exception as e:
            print(f"インスタンスプロファイルの取得に失敗: {e}")

        print(f"インスタンスプロファイル {len(profiles)}個を取得しました")
        return profiles

    def get_role_policies(self, role_name: str) -> List[Dict[str, Any]]:
        """
        特定のロールにアタッチされたポリシーを取得

        Args:
            role_name: ロール名

        Returns:
            アタッチされたポリシーのリスト(管理ポリシーとインラインポリシー)
        """
        attached_policies = []
        inline_policies = []

        try:
            # 管理ポリシーを取得
            paginator = self.iam_client.get_paginator('list_attached_role_policies')
            for page in paginator.paginate(RoleName=role_name):
                for policy in page['AttachedPolicies']:
                    attached_policies.append({
                        'PolicyName': policy['PolicyName'],
                        'PolicyArn': policy['PolicyArn'],
                        'Type': 'attached'
                    })

            # インラインポリシーを取得
            paginator = self.iam_client.get_paginator('list_role_policies')
            for page in paginator.paginate(RoleName=role_name):
                for policy_name in page['PolicyNames']:
                    inline_policies.append({
                        'PolicyName': policy_name,
                        'PolicyArn': None,
                        'Type': 'inline'
                    })

        except Exception as e:
            print(f"ロール {role_name} のポリシー取得に失敗: {e}")

        return attached_policies + inline_policies

    def get_group_policies(self, group_name: str) -> List[Dict[str, Any]]:
        """
        特定のグループにアタッチされたポリシーを取得

        Args:
            group_name: グループ名

        Returns:
            アタッチされたポリシーのリスト(管理ポリシーとインラインポリシー)
        """
        attached_policies = []
        inline_policies = []

        try:
            # 管理ポリシーを取得
            paginator = self.iam_client.get_paginator('list_attached_group_policies')
            for page in paginator.paginate(GroupName=group_name):
                for policy in page['AttachedPolicies']:
                    attached_policies.append({
                        'PolicyName': policy['PolicyName'],
                        'PolicyArn': policy['PolicyArn'],
                        'Type': 'attached'
                    })

            # インラインポリシーを取得
            paginator = self.iam_client.get_paginator('list_group_policies')
            for page in paginator.paginate(GroupName=group_name):
                for policy_name in page['PolicyNames']:
                    inline_policies.append({
                        'PolicyName': policy_name,
                        'PolicyArn': None,
                        'Type': 'inline'
                    })

        except Exception as e:
            print(f"グループ {group_name} のポリシー取得に失敗: {e}")

        return attached_policies + inline_policies

    def generate_import_blocks(self, output_file: str = 'import_blocks.tf'):
        """
        すべてのリソースのimportブロックを生成

        Args:
            output_file: 出力ファイル名
        """
        print("Terraformのimportブロックを生成中...")

        import_blocks = []
        import_blocks.append("# Terraformインポートブロック")
        import_blocks.append("# このファイルはgenerate_import_blocks.pyにより自動生成されました\n")

        # カスタマー管理ポリシー
        policies = self.get_customer_managed_policies()
        if policies:
            import_blocks.append("# カスタマー管理ポリシー")
            for policy in policies:
                resource_name = self.normalize_resource_name(policy['PolicyName'])
                import_blocks.append(f"import {{")
                import_blocks.append(f"  to = aws_iam_policy.{resource_name}")
                import_blocks.append(f"  id = \"{policy['Arn']}\"")
                import_blocks.append(f"}}\n")

        # IAMロール
        roles = self.get_iam_roles()
        if roles:
            import_blocks.append("# IAMロール")
            for role in roles:
                resource_name = self.normalize_resource_name(role['RoleName'])
                import_blocks.append(f"import {{")
                import_blocks.append(f"  to = aws_iam_role.{resource_name}")
                import_blocks.append(f"  id = \"{role['RoleName']}\"")
                import_blocks.append(f"}}\n")

        # IAMグループ
        groups = self.get_iam_groups()
        if groups:
            import_blocks.append("# IAMグループ")
            for group in groups:
                resource_name = self.normalize_resource_name(group['GroupName'])
                import_blocks.append(f"import {{")
                import_blocks.append(f"  to = aws_iam_group.{resource_name}")
                import_blocks.append(f"  id = \"{group['GroupName']}\"")
                import_blocks.append(f"}}\n")

        # インスタンスプロファイル
        instance_profiles = self.get_instance_profiles()
        if instance_profiles:
            import_blocks.append("# インスタンスプロファイル")
            for profile in instance_profiles:
                resource_name = self.normalize_resource_name(profile['InstanceProfileName'])
                import_blocks.append(f"import {{")
                import_blocks.append(f"  to = aws_iam_instance_profile.{resource_name}")
                import_blocks.append(f"  id = \"{profile['InstanceProfileName']}\"")
                import_blocks.append(f"}}\n")

        # ロールポリシーアタッチメント
        if roles:
            import_blocks.append("# ロールポリシーアタッチメント")
            for role in roles:
                role_policies = self.get_role_policies(role['RoleName'])

                for policy in role_policies:
                    if policy['Type'] == 'attached':
                        # 管理ポリシーのアタッチメント
                        role_name_normalized = self.normalize_resource_name(role['RoleName'])
                        policy_name_normalized = self.normalize_resource_name(policy['PolicyName'])
                        resource_name = f"{role_name_normalized}_{policy_name_normalized}"

                        import_blocks.append(f"import {{")
                        import_blocks.append(f"  to = aws_iam_role_policy_attachment.{resource_name}")
                        import_blocks.append(f"  id = \"{role['RoleName']}/{policy['PolicyArn']}\"")
                        import_blocks.append(f"}}\n")

                    elif policy['Type'] == 'inline':
                        # インラインポリシー
                        role_name_normalized = self.normalize_resource_name(role['RoleName'])
                        policy_name_normalized = self.normalize_resource_name(policy['PolicyName'])
                        resource_name = f"{role_name_normalized}_{policy_name_normalized}"

                        import_blocks.append(f"import {{")
                        import_blocks.append(f"  to = aws_iam_role_policy.{resource_name}")
                        import_blocks.append(f"  id = \"{role['RoleName']}:{policy['PolicyName']}\"")
                        import_blocks.append(f"}}\n")

        # グループポリシーアタッチメント
        if groups:
            import_blocks.append("# グループポリシーアタッチメント")
            for group in groups:
                group_policies = self.get_group_policies(group['GroupName'])

                for policy in group_policies:
                    if policy['Type'] == 'attached':
                        # 管理ポリシーのアタッチメント
                        group_name_normalized = self.normalize_resource_name(group['GroupName'])
                        policy_name_normalized = self.normalize_resource_name(policy['PolicyName'])
                        resource_name = f"{group_name_normalized}_{policy_name_normalized}"

                        import_blocks.append(f"import {{")
                        import_blocks.append(f"  to = aws_iam_group_policy_attachment.{resource_name}")
                        import_blocks.append(f"  id = \"{group['GroupName']}/{policy['PolicyArn']}\"")
                        import_blocks.append(f"}}\n")

                    elif policy['Type'] == 'inline':
                        # インラインポリシー
                        group_name_normalized = self.normalize_resource_name(group['GroupName'])
                        policy_name_normalized = self.normalize_resource_name(policy['PolicyName'])
                        resource_name = f"{group_name_normalized}_{policy_name_normalized}"

                        import_blocks.append(f"import {{")
                        import_blocks.append(f"  to = aws_iam_group_policy.{resource_name}")
                        import_blocks.append(f"  id = \"{group['GroupName']}:{policy['PolicyName']}\"")
                        import_blocks.append(f"}}\n")

        # ファイルに出力
        try:
            with open(output_file, 'w', encoding='utf-8') as f:
                f.write('\n'.join(import_blocks))
            print(f"importブロックを {output_file} に出力しました")

        except Exception as e:
            print(f"ファイル出力に失敗: {e}")
            sys.exit(1)

def main():
    """
    メイン関数
    """
    parser = argparse.ArgumentParser(
        description='AWS IAMリソースのTerraformインポートブロック生成ツール',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用例:
  python generate_import_blocks.py                           # デフォルトプロファイルを使用
  python generate_import_blocks.py --profile myprofile       # 指定プロファイルを使用
  python generate_import_blocks.py --output my_imports.tf    # 出力ファイル名を指定
        """
    )

    parser.add_argument(
        '--profile',
        help='使用するAWSプロファイル名',
        default=None
    )

    parser.add_argument(
        '--output',
        help='出力ファイル名(デフォルト: import_blocks.tf)',
        default='import_blocks.tf'
    )

    parser.add_argument(
        '--region',
        help='AWSリージョン(デフォルト: ap-northeast-1)',
        default='ap-northeast-1'
    )

    args = parser.parse_args()

    # インポートブロック生成器を初期化
    generator = TerraformImportBlockGenerator(
        profile_name=args.profile,
        region=args.region
    )

    # インポートブロックを生成
    generator.generate_import_blocks(output_file=args.output)

    print("処理が完了しました!")
    print(f"生成されたファイル: {args.output}")
    print("\n次の手順:")
    print("1. terraform init を実行")
    print("2. terraform plan を実行してインポート対象を確認")
    print("3. terraform import コマンドを実行してリソースをインポート")

if __name__ == '__main__':
    main()

		

生成したスクリプトで、importブロックのtfファイルを作成

上記のスクリプトを利用して、Terraformのimportブロックのtfファイルを作成します。
以下のようにして実行できます。スクリプトのファイル名を「generate_import_blocks.py」とした場合、カレントディレクトリに「import_blocks.tf」というファイル名で、接続先のAWSアカウントの、今回対象のIAMリソースのimportブロックが出力されます。

			
			python generate_import_blocks.py

		

出力された結果は以下例のようになります。

			
			# IAMロール
import {
  to = aws_iam_role.hogerole
  id = "hogerole"
}

# カスタマー管理ポリシー
import {
  to = aws_iam_policy.hogepolicy
  id = "arn:aws:iam::************:policy/hogepolicy"
}

# ロールポリシーアタッチメント
import {
  to = aws_iam_role_policy_attachment.test_role_test_policy
  id = "test-role/arn:aws:iam::aws:policy/test-policy"
}

		

「terraform plan -generate-config-out=xxx.tf」で、import定義を基に、resource定義のtfファイルを作成

次に以下のコマンドを実行します。(terraform initは完了している前提です)xxx.tfのファイル名は任意です。

			
			terraform plan -generate-config-out=xxx.tf

		

xxx.tfに、importブロックで指定していたI AMのリソース定義が出力されます。

resource定義のtfファイルを修正

上記で出力したtfファイルでは、デフォルト値のパラメータなども明示的に出力されます。今回は、視認性の観点より、運用上不要そうなパラメータの削除を以下のように行いました。(削除するパラメータは、運用により検討下さい)

			
			# aws_iam_role で削除したパラメータ
force_detach_policies = false
name_prefix          = null
permissions_boundary = null
tags                 = {}
tags_all             = {}
description          = null

# aws_iam_role で残したパラメータ
assume_role_policy
max_session_duration
name
path
tags           # 設定値が存在する場合のみ残した
tags_all       # 設定値が存在する場合のみ残した
description    # 設定値が存在する場合のみ残した

		
			
			# aws_iam_policy で削除したパラメータ
name_prefix          = null
tags                 = {}
tags_all             = {}
description          = null

# aws_iam_policy で残したパラメータ
name
path
policy
tags           # 設定値が存在する場合のみ残した
tags_all       # 設定値が存在する場合のみ残した
description    # 設定値が存在する場合のみ残した

		
			
			# aws_iam_group で削除したパラメータ
# 無し

# aws_iam_group で残したパラメータ
name
path

		
			
			# aws_iam_role_policy_attachment で削除したパラメータ
# 無し

# aws_iam_role_policy_attachment で残したパラメータ
policy_arn
role

		
			
			# aws_iam_role_policy で削除したパラメータ
name_prefix          = null

# aws_iam_role_policy で残したパラメータ
name
policy
role

		
			
			# aws_iam_instance_profile で削除したパラメータ
name_prefix          = null
tags                 = {}
tags_all             = {}

# aws_iam_instance_profile で残したパラメータ
name
path
role

		
			
			# aws_iam_group_policy_attachment で削除したパラメータ
# 無し

# aws_iam_group_policy_attachment で残したパラメータ
group
policy_arn

		
			
			# aws_iam_group_policy で削除したパラメータ
name_prefix          = null

# aws_iam_group_policy で残したパラメータ
group
name
policy

		

「terraform plan」で確認

terraform planを行い、インポート対象リソースに間違いないことを確認しました。最後に出力されるPlan結果のimportの数が、importブロックで指定したリソース数と一致して、add, change , destroyの数が全て 0 であることを確認しました。

			
			terraform plan

(実行結果例)
Plan: 123 to import, 0 to add, 0 to change, 0 to destroy

		

「terraform apply」でインポート

terraform applyを行い、インポートしました。実行結果は、直前のplanと同じになったことを確認しました。また、tfstateファイルが所定の保存場所(S3バケット)に作成・更新されたことを確認しました。

			
			terraform apply

(実行結果例)
Apply complete! Resources: 123 imported, 0 added, 0 changed, 0 destroyed.

		

おわりに

Terraformで、多数のI AM系リソースのインポートを行う作業について記載しました。この記事が皆様のお役に立てば幸いです。

アノテーション株式会社について

アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。

サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。

当社は様々な職種でメンバーを募集しています。

「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。

Share this article

FacebookHatena blogX

Related articles

TerraformでIAM系リソースの一括インポート | DevelopersIO