Terraform CloudからAWSを管理する

Terraform CloudからAWSに接続し管理する方法を記述します。

# 参考
https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/aws-configuration#optional-environment-variables

https://dev.classmethod.jp/articles/terraform-cloud-dynamic-provider-credentials/

# 前提
1. Terraform Cloudにプロジェクト、ワークスペースは作成済み
1. プログラムはVSCで管理しています。
# 手順
1. OIDC Identity Providerを作成します。
1. IAMロールを作成します。
1. ワークスペースのVariableに作成したIAMロールの情報を登録します。

## OIDC Identity Provider作成
AWSコンソールのIAM > アクセス管理 > IDプロバイダ > プロバイダを追加 から作成します。

OpenID Connect を選択
プロバイダのURLに`https://app.terraform.io`を設定して「サムプリントを取得」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/53618178-b69c-f560-477f-925410fa174e.png)
対象者に`aws.workload.identity`を設定して「プロバイダを追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/7e58e406-4246-dc58-76cc-63707d619d1d.png)
作成されました。「ロールの割り当て」をクリックし、ロールを作成していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/a751e3a3-88b9-5155-2c46-456ba74ae84a.png)

## IAMロール作成
新しいロールを作成を選択し「次へ」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/fddd63d5-00b6-5bb1-69f2-a491a6c902d9.png)
ウェブID、IDプロバイダー`app.terraform.io:eud`、Audience`aws.workload.identity`が選択されていると思います。「次のステップ：アクセス権限」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/a4cdaa3e-67e8-3174-5bad-d7e3d89a4374.png)
Terraform Cloudで実行したい権限を持つポリシーを適宜選んで「次のステップ：タグ」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/b5686556-1993-5a49-3680-0a8c845ca2d1.png)
「次のステップ：確認」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/74c2d446-3374-c822-40c8-7ab1219d577a.png)
ロール名、ロールの説明を適宜設定して「ロールの作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/d1680450-dbb1-aada-d4be-0554159fd2f6.png)
ロールが作成されました。Terraformの設定にARNが必要なのでコピーしておきます。
![shuusei.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/7295cd01-7176-059e-687d-453fabb8a80a.png)
作成したロールの詳細画面の信頼関係のタブから「信頼ポリシーを編集」をクリックします。
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/dfca2a41-6cdb-e58c-7d7f-0bf8600f0a13.png)
`app.terraform.io:sub`のkeyを追加します。valueは `organization:ORG_NAME:project:PROJECT_NAME:workspace:WORKSPACE_NAME:run_phase:RUN_PHASE`でTerraformの組織、プロジェクト、ワークスペース、runコマンドを置き換えて、セットします。
```diff_json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<アカウントID>:oidc-provider/app.terraform.io"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "app.terraform.io:aud": "aws.workload.identity",
+                    "app.terraform.io:sub": "organization:urushibata-org:project:urushibata-aws-management:workspace:aws-managed-terraform:run_phase:*"
                }
            }
        }
    ]
}
```

## Variable登録
Terraform CloudのWorkspace Variablesに環境変数で`TFC_AWS_PROVIDER_AUTH:true`と`TFC_AWS_RUN_ROLE_ARN:ロールのARN`を作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/9d2e2646-c038-c726-c975-f3706b90ffaf.png)

# 実行
Terraform Cloudから`plan & apply`実行できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/f3e2372a-a565-506c-ff23-ae277a7cf6ae.png)


# ローカル開発
stateファイルはTerraform Cloud内で管理されます。ローカルで開発をしているときにクラウドのstateファイルを参照して`plan`でテストしたいと思うので、その方法も記述します。

以下のようにterraformの中にorganization、warkspaceの名前を追加します。
```terraform
terraform {
  cloud {
    organization = "urushibata-org"
    workspaces {
      name = "aws-managed-terraform"
    }
  }
}
```
ログインして`plan`を実行してみます。
```console
$ terraform login
Terraform will request an API token for app.terraform.io using your browser.

If login is successful, Terraform will store the token in plain text in
the following file for use by subsequent commands:
    /home/s-urushibata/.terraform.d/credentials.tfrc.json

Do you want to proceed?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes


---------------------------------------------------------------------------------

Open the following URL to access the tokens page for app.terraform.io:
    https://app.terraform.io/app/settings/tokens?source=terraform-login


---------------------------------------------------------------------------------

Generate a token using your browser, and copy-paste it into this prompt.

Terraform will store the token in plain text in the following file
for use by subsequent commands:
    /home/s-urushibata/.terraform.d/credentials.tfrc.json

Token for app.terraform.io:
  Enter a value:


Retrieved token for user urush1batalala


---------------------------------------------------------------------------------

                                          -
                                          -----                           -
                                          ---------                      --
                                          ---------  -                -----
                                           ---------  ------        -------
                                             -------  ---------  ----------
                                                ----  ---------- ----------
                                                  --  ---------- ----------
   Welcome to Terraform Cloud!                     -  ---------- -------
                                                      ---  ----- ---
   Documentation: terraform.io/docs/cloud             --------   -
                                                      ----------
                                                      ----------
                                                       ---------
                                                           -----
                                                               -


   New to TFC? Follow these steps to instantly apply an example configuration:

   $ git clone https://github.com/hashicorp/tfc-getting-started.git
   $ cd tfc-getting-started
   $ scripts/setup.sh

$ terraform plan
Running plan in Terraform Cloud. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.

Preparing the remote plan...

The remote workspace is configured to work with configuration at
./environment/mng relative to the target repository.

Terraform will upload the contents of the following directory,
excluding files or directories as defined by a .terraformignore file
at /home/s-urushibata/work/terraform/aws-managed-terraform/.terraformignore (if it is present),
in order to capture the filesystem context the remote workspace expects:
    /home/s-urushibata/work/terraform/aws-managed-terraform

To view this run in a browser, visit:
https://app.terraform.io/app/urushibata-org/aws-managed-terraform/runs/run-AXSufPxmKGuezL13

Waiting for the plan to start...

Terraform v1.5.3
on linux_amd64
Initializing plugins and modules...
module.organization.data.aws_iam_policy_document.region_restriction: Refreshing...
module.organization.data.aws_iam_policy_document.region_restriction: Refresh complete after 0s [id=1127618634]
module.organization.aws_organizations_policy.region_restriction_policy: Refreshing state... [id=p-4kuo19vh]
module.organization.aws_organizations_policy.region_restriction_policy: Drift detected (update)

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are
needed.

------------------------------------------------------------------------

Cost Estimation:

Resources: 0 of 1 estimated
           $0.0/mo +$0.0
```
実行された`plan`はクラウド側に履歴が残ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/e524bfcb-23eb-00b5-09d7-6bf0229d4d34.png)

ソースをVCSで管理していれば、もし誤ってローカルから`apply`してしまってもエラーとなるので安心です。

```console
$ terraform apply
╷
│ Error: Apply not allowed for workspaces with a VCS connection
│
│ A workspace that is connected to a VCS requires the VCS-driven workflow to ensure that the VCS remains the single
│ source of truth.
╵
```