AWS CloudFormationを利用してIAMユーザを作成してみた

## 【はじめに】

個人でAWSアカウントを複数持つことはあまりないと思いますが、会社組織では部署や役割ごとに複数のアカウントを所有することは当たり前のようにあります。

会社組織でインフラ構築をする際に、ルートユーザに代わりAdmin権限を持つIAMユーザを作成することでアカウントを管理することが推奨されています。

そこで今回は、AWS CloudFormationを利用して複数のアカウントに同じAdmin権限を持った初期IAMユーザを作成する方法を解説していきます。

AWS無料枠内で利用できますので、今後のキャリアを見据えて、是非操作してみましょう。

### 本記事の内容

- AWS CloudFormationを利用するメリットは？
- 実際にAWS CloudFormationを利用してIAMユーザを作成してみる
- 作成したリソースを確認する
- 作成したリソースを削除する（スタックとIAMユーザの削除）


### 【AWS CloudFormationを利用するメリットは？】

AWS CloudFormationはリソースの設定やプロビジョニングをコード化して実行できるシステム環境構築サービスです。

テンプレートからスタックを作成して、インフラの一括管理を行うため、開発者がそれぞれのリソースを個別に管理するよりもミスが発生しにくくなります。

また、人的ミスを防ぐだけではなく、環境の構築に時間や手間がかからないため、より簡単にリソースを管理・運用することができます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html

## 【実際にAWS CloudFormationを利用してIAMユーザを作成してみる】

実際に、AWS CloudFormationを利用して複数のアカウントに同じAdmin権限を持った初期IAMユーザを作成していきます。

まずは要件を確認していきましょう。

**要件**

Admin権限を持つ初期 IAM ユーザ（管理者用）を作成する

- グループ名：Admin-group-{アカウントID}
- ユーザー名：Admin-user-{アカウントID}
- グループに「AdministratorAccess」ポリシーアタッチ
- パスワードの設定自体は各IAMユーザで設定する想定
- ログインURLとパスワード設定画面のURLとユーザ名をOutputに出力
- オプション：対象スタックを削除してもリソースを保持する

以上の要件でAWS CloudFormationのテンプレートを作成してきます。

### AWS CloudFormationテンプレート

今回はJSONでテンプレートを作成しました。

```json:templete-create-IAM-admin-user
{
    "AWSTemplateFormatVersion" : "2010-09-09",

    "Description" : "Templete for creating Initial Administrator IAM",

    "Resources": {
        "IAMGroupInitialAdministrator":{
            "Type": "AWS::IAM::Group",
            "DeletionPolicy": "Retain",
            "Properties":{
                "GroupName":{"Fn::Join" : [
                    "",
                    ["Admin-group-", {"Ref": "AWS::AccountId"}]
                  ]},
                "ManagedPolicyArns" : [
                    "arn:aws:iam::aws:policy/AdministratorAccess"
                ]
            }
        },
        "IAMUserInitialAdministrator":{
            "Type": "AWS::IAM::User",
            "DeletionPolicy": "Retain",
            "Properties":{
                "UserName": {"Fn::Join" : [
                    "",
                    ["Admin-user-", {"Ref": "AWS::AccountId"}]
                    ]},
                "Groups": [{"Fn::Join": [
                    "",
                    ["Admin-group-", {"Ref": "AWS::AccountId"}]
                    ]}
                ]
            }
        }
    },
    "Outputs": {
        "UserName": {
            "Description": "Info about User name",
            "Value": {"Ref" : "IAMUserInitialAdministrator"}
        },
        "PasswordSettingURL": {
            "Description": "Please create a password at the URL",
            "Value": {"Fn::Join": [
                "",
                ["https://","console.aws.amazon.com/iam/home?region=",{"Ref": "AWS::Region"},"#/users/",{"Ref": "IAMUserInitialDeveloper"},"?section=security_credentials"]
            ]}
        },
        "LoginURL": {
            "Description": "Info about console login URL",
            "Value": {"Fn::Join" : [
                "",
                ["https://",{"Ref": "AWS::AccountId"}, ".signin.aws.amazon.com/console"]
            ]}
        }
    }
}
```

テンプレートの各セクションに沿って解説していきます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-anatomy.html

### AWSTempleteFormatVersion（必須）

2022年7月時点では、公式サイトに以下のように記載されています。

>AWSTemplateFormatVersion セクション (任意) は、テンプレートの機能を識別します。最新のテンプレートの形式バージョンは 2010-09-09 であり、現時点で唯一の有効な値です。

```json
"AWSTemplateFormatVersion" : "2010-09-09",
```

### Description（オプション）

作成するテンプレートの説明を書きます。**管理者用のIAM ユーザの作成**ということで以下のように追記します。

```json
"Description" : "Templete for creating Initial Administrator IAM",
```

### Resources（必須）

スタックに含めるAWS リソースを宣言します。

```json
"Resources" : {
    "Logical ID" : {
        "Type" : "Resource type",
        "Properties" : {
            Set of properties
        }
    }
}
```

リソースには、3つのフィールドで構成されています。

- **論理ID（Logical ID）**

>テンプレートの他の部分のリソースを参照するために使用します。--中略--たとえば、EC2 インスタンスリソースに MyEC2Instance の論理 ID を指定します。AWS CloudFormation がインスタンスを作成すると、AWS CloudFormation は自動的に物理 ID (i-28f9ba55) を生成して、インスタンスに割り当てます。この物理 ID を使用して、Amazon EC2 コンソールでインスタンスを特定したり、そのプロパティ (DNS 名など) を表示したりすることができます。カスタム名をサポートしているリソースの場合は、リソースをすばやく識別するために、独自の名前 (物理 ID) を割り当てることができます。

今回では、作成するIAMグループとIAMユーザに対して以下のように論理IDを指定します。

```json
"Resources" : {
	"IAMGroupInitialAdministrator" : {

	},
	"IAMUserInitialAdministrator" : {

	},

}
```

- **リソースタイプ**

>リソースタイプは、宣言しているリソースのタイプを識別します。たとえば、AWS::EC2::Instance は EC2 インスタンスを宣言します。

今回はIAMの中でもグループとユーザに関わるリソースタイプを指定します。

```json
"Resources" : {
	"IAMGroupInitialAdministrator" : {
		"Type": "AWS::IAM::Group",

	},
	"IAMUserInitialAdministrator" : {
		"Type": "AWS::IAM::User",

	},

}
```

- **リソースプロパティ**

>リソースプロパティは、リソースに対して指定できる追加オプションです。

今回、グループには「グループ名」と「ポリシー」、ユーザには「ユーザ名」と「グループ名」をプロパティとして指定します。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_IAM.html

```json
"Resources": {
        "IAMGroupInitialAdministrator":{
        	"Type": "AWS::IAM::Group",
            	"DeletionPolicy": "Retain",
            	"Properties":{
                	"GroupName":{"Fn::Join" : [
                    	"",
                    	["Admin-group-", {"Ref": "AWS::AccountId"}]
                  	]},
                	"ManagedPolicyArns" : [
                    		"arn:aws:iam::aws:policy/AdministratorAccess"
                	]
            	}
        },
        "IAMUserInitialAdministrator":{
            	"Type": "AWS::IAM::User",
            	"DeletionPolicy": "Retain",
            	"Properties":{
                	"UserName": {"Fn::Join" : [
                    		"",
                    		["Admin-user-", {"Ref": "AWS::AccountId"}]
                    		]},
                	"Groups": [{"Fn::Join": [
                    		"",
                    		["Admin-group-", {"Ref": "AWS::AccountId"}]
                    		]}
                	]
            	}
        }
},

```

**ポイント**

>IAMリソースに名前を付けると、複数のRegionで同じテンプレートを再利用した場合、回復不能なエラーが発生することがあります。これを防ぐには、以下の例のようにFn::JoinとAWS::Regionを使ってリージョン固有の名前を作成することをお勧めします。{"Fn::Join": ["", [{"Ref": "AWS::Region"}, {"Ref": "MyResourceName"}]]}.

公式サイトに記載のあるように、一意のIAMグループとユーザ名を作成するために`"Fn:Join": ["", ["Admin-group(user)-",{"Ref": "AWS::AccountId"}]]` と指定します。


### Outputs（オプション）

出力内容の記述をします。今回は、ユーザ名とパスワード設定画面のURLとログインURLを出力します。

```json
"Outputs" : {
  	"Logical ID" : {
   		"Description" : "Information about the value",
    		"Value" : "Value to return",
    		"Export" : {
      			"Name" : "Value to export"
    		}
  	}
}
```

Outputsには、4つのフィールドで構成されています。

- **論理ID（Logical ID）**

>現在の出力の識別子。論理 ID は英数字 (a–z、A–Z、0–9) とし、テンプレート内で一意である必要があります。

今回では、「ユーザ名」、「パスワード設定画面のURL」、「ログイン画面のURL」を出力します。

```json
"Outputs" : {
	"UserName" : {
    
  	},
	"PasswordSettingURL": {
	
	},
	"LoginURL": {

	}
}
```

- **Description（オプション）**

>出力値について説明する String 型。

以下のように、各出力項目に対して説明を記述します。

```json
"Outputs" : {
	"UserName" : {
 		"Description": "Info about User name",

  	},
	"PasswordSettingURL": {
		"Description": "Please create a password at the URL",
	
	},
	"LoginURL": {
		"Description": "Info about console login URL",

	}
}
```

- **Value（必須）**

>aws cloudformation describe-stacks コマンドから返されるプロパティの値。

実際に出力される値を以下のように指定します。

```json
"Outputs" : {
	"UserName" : {
 		"Description": "Info about User name",
		"Value": {"Ref" : "IAMUserInitialAdministrator"}
  	},
	"PasswordSettingURL": {
		"Description": "Please create a password at the URL",
		"Value": {"Fn::Join": [
                "",
                ["https://","console.aws.amazon.com/iam/home?region=",{"Ref": "AWS::Region"},"#/users/",{"Ref": "IAMUserInitialAdministrator"},"?section=security_credentials"]
            ]}
	},
	"LoginURL": {
		"Description": "Info about console login URL",
		"Value": {"Fn::Join" : [
                "",
                ["https://",{"Ref": "AWS::AccountId"}, ".signin.aws.amazon.com/console"]
            ]}
	}
}
```
**ポイント**

"Ref" 関数を使用して文字列を結合する場合は、前後の文字列に対して「,（カンマ）」で繋ぎます。


- **エクスポート（オプション）**

>エクスポートされるリソース出力の名前。

今回は省略します。


### CloudFormationでスタックを作成する

- ルートユーザでログインします。
- 管理コンソールの検索バーで「CloudFormation」と入力して選択します。
- CloudFormationのダッシュボードを開きます。
- 画面左の「スタック」をクリックし、画面右上の[スタックの作成]をクリックします。

[![Image from Gyazo](https://i.gyazo.com/59863d09da3191d4449becb41126a619.png)](https://gyazo.com/59863d09da3191d4449becb41126a619)

[![Image from Gyazo](https://i.gyazo.com/969b75c54bf3a428e7ae58b31946d2ac.png)](https://gyazo.com/969b75c54bf3a428e7ae58b31946d2ac)

- 「スタックの作成」画面の「テンプレートの準備」で「テンプレートの準備完了」を選択します。
- 「テンプレートの指定」で「テンプレートファイルのアップロード」を選択し、作成したjsonをアップロードします。
- アップロードしたら[次へ]をクリックします。

[![Image from Gyazo](https://i.gyazo.com/a61712fed9eff27252834bc191787c67.png)](https://gyazo.com/a61712fed9eff27252834bc191787c67)

- 「スタックの詳細を指定」画面の「スタックの名前」を任意で指定し、[次へ]をクリックします。

[![Image from Gyazo](https://i.gyazo.com/c3ba1fd05174afdebab2a009f33627f1.png)](https://gyazo.com/c3ba1fd05174afdebab2a009f33627f1)

- 「スタックオプションの設定」画面では、今回何も指定せずに[次へ]をクリックします。

[![Image from Gyazo](https://i.gyazo.com/e023e9845c0237464fe476bf1f7a8c2f.png)](https://gyazo.com/e023e9845c0237464fe476bf1f7a8c2f)

[![Image from Gyazo](https://i.gyazo.com/d24478d3ba2e3aa76673c327157b64a2.png)](https://gyazo.com/d24478d3ba2e3aa76673c327157b64a2)


- 「レビュー」画面で設定した内容を確認し、問題がなければ一番下の「AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。」にチェックを入れて[スタックの作成]をクリックします。

[![Image from Gyazo](https://i.gyazo.com/4878ca6b3cafb972f8b6d1a19b8bd93c.png)](https://gyazo.com/4878ca6b3cafb972f8b6d1a19b8bd93c)

## 【作成したリソースを確認する】

スタックの作成を開始すると、以下のように「ステータス」が「CREATE_IN_PROGRESS」となっていますが、作成が完了すると「CREATE _COMPLETE」になります。

[![Image from Gyazo](https://i.gyazo.com/84da6fb27dfe04ce4cf340d6329baa00.png)](https://gyazo.com/84da6fb27dfe04ce4cf340d6329baa00)

[![Image from Gyazo](https://i.gyazo.com/b041e917e0e168f465cdbcb2e8e5ba8b.png)](https://gyazo.com/b041e917e0e168f465cdbcb2e8e5ba8b)

また、「出力」タブに「Outputs」で指定した値が出力されていることが確認できます。

[![Image from Gyazo](https://i.gyazo.com/f3bea68a03ee5a3753b792f13fd7fad3.png)](https://gyazo.com/f3bea68a03ee5a3753b792f13fd7fad3)

IAMのダッシュボードでもユーザとグループが作成されていることが確認できます。

[![Image from Gyazo](https://i.gyazo.com/3cbc78bfbfea27b02c63a26b90483835.png)](https://gyazo.com/3cbc78bfbfea27b02c63a26b90483835)

## 【作成したリソースを削除する（スタックとIAMユーザの削除）】

- CloudFormationのダッシュボードを開きます。
- 画面左の「スタック」をクリックし、対象のスタックを選択して[削除]をクリックします。
-  ポップアップで[スタックの削除]をクリックします。

[![Image from Gyazo](https://i.gyazo.com/6ab34a19d492babee325dfc2742f7eab.png)](https://gyazo.com/6ab34a19d492babee325dfc2742f7eab)

[![Image from Gyazo](https://i.gyazo.com/5bf130495223028b8390a2ab57e5046d.png)](https://gyazo.com/5bf130495223028b8390a2ab57e5046d)

[![Image from Gyazo](https://i.gyazo.com/cdc08cb22c2869651519bdfd4641041c.png)](https://gyazo.com/cdc08cb22c2869651519bdfd4641041c)

**ポイント**

通常スタックを削除すると、スタックによって作成されたリソースも削除されます。

今回は、テンプレートで各リソースに対して` "DeletionPolicy": "Retain"`と指定しているため、スタックが削除されてもリソースは削除されないようになっています。

IAMユーザとグループを削除するには、IAMのダッシュボードから削除を行います。（今回は省略します。）


## 【さいごに】

今回は、AWS CloudFormationを利用して複数のアカウントに同じAdmin権限を持った初期IAMユーザを作成する方法を解説しました。

テンプレートの作成は公式リファレンスを見ながら、試行錯誤して作成して慣れていくしかありません。
（私も実際にテンプレートを作成したのは、今回が初めてです。）

自分のキャリアアップのために、是非一度は操作してみると良いと思います。