😽

Terreform CloudでDynamic Provider Credentialsを試す

2024/01/20に公開

今までだと、TerraformでAWSを使うときに認証情報としてAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYを設定したてかと思います。

こんな感じで
https://developer.hashicorp.com/terraform/tutorials/cloud-get-started/cloud-create-variable-set#create-a-variable-set

ただよりセキュアなやり方としてDynamic Provider Credentialsという方法があるので、こちらを試してみたいと思います。

https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials

Using static credentials in your workspaces to authenticate providers presents a security risk, even if you rotate your credentials regularly. Dynamic provider credentials improve your security posture by letting you provision new, temporary credentials for each run.

キーを定期的に変更してたとてセキュリティリスクあるよと、Dynamic provider credentials を使うとテンポラリの認証情報だからセキュリティ改善できるよ。(適当な脳内翻訳

シナリオ

今回はTerraform CloudでAWS上にEC2インスタンスを起動する、というシナリオで試したいと思います。

Dynamic provider credentialsをAWSで使う方法はこちら
https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/aws-configuration

手順

ℹ️基本的にAWS Management Consoleから設定しています。

1. AWS側の設定

1-1. IAMからOIDC Identity Providerを作成

IAM > Identity Providers > Add provider

以下の内容で設定

項目 設定値
Provider type OpenID Connect
Provider URL https://app.terraform.io
Audience aws.workload.identity

1-2. Role、Trust Policyの設定

Identity Providerを作成したら、Assign roleでRoleの設定をします。
Identity Provider一覧から作成したProvider(app.terraform.io)をクリックすると以下の詳細画面になるので、右上のAssign roleからRoleを設定していきます。

とりあえず今回は新しくRoleを作成。

最初にSelect trusted entityを設定。
以下の内容で設定

項目 設定値
Trusted entity type Web identity
Web identity/Identity provider app.terraform.io
Web identity/Audience aws.workload.identity

次にpermission設定。
今回はEC2インスタンス起動なので、AmazonEC2FullAccess を設定。

最後にRole名や説明を設定。

2. Terraform Cloud側の設定

Terraform Cloud側はVariableを設定するだけです。今回はVariable setsから設定しました。

項目 設定
TFC_AWS_PROVIDER_AUTH true
TFC_AWS_RUN_ROLE_ARN 作成したRoleのARM

3. 実際に実行してみる

以下のサンプルを作成。

terraform.tf
terraform {
  cloud {
    organization = "tsaeki"

    workspaces {
      name = "20240119_aws" # 事前に20240119_awsというworkspaceを作成しています
    }
  }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

    required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "ap-northeast-1"
}

resource "aws_instance" "app_server" {
  ami           = "ami-0506f0f56e3a057a4"
  instance_type = "t2.micro"

  tags = {
    Name = "ExampleAppServerInstance"
  }
}

実行

-> % terraform init

Initializing Terraform Cloud...
Terraform Cloud configuration has changed.

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v4.67.0

Terraform Cloud has been successfully initialized!

You may now begin working with Terraform Cloud. Try running "terraform plan" to
see any changes that are required for your infrastructure.

If you ever set or change modules or Terraform Settings, run "terraform init"
again to reinitialize your working directory.
-> % terraform apply
Running apply in Terraform Cloud. Output will stream here. Pressing Ctrl-C
will cancel the remote apply if it's still pending. If the apply started it
will stop streaming the logs, but will not stop the apply running remotely.

Preparing the remote apply...

To view this run in a browser, visit:
https://app.terraform.io/app/tsaeki/20240119_aws/runs/run-Ygm8JPcNkdzBabkJ

Waiting for the plan to start...

Terraform v1.7.0
on linux_amd64
Initializing plugins and modules...

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.app_server will be created
  + resource "aws_instance" "app_server" {
      + ami                                  = "ami-0506f0f56e3a057a4"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "ExampleAppServerInstance"
        }
      + tags_all                             = {
          + "Name" = "ExampleAppServerInstance"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

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

Cost Estimation:

Resources: 1 of 1 estimated
           $8.6304/mo +$8.6304

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

Do you want to perform these actions in workspace "20240119_aws"?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.app_server: Creating...
aws_instance.app_server: Still creating... [10s elapsed]
aws_instance.app_server: Still creating... [20s elapsed]
aws_instance.app_server: Still creating... [30s elapsed]
aws_instance.app_server: Creation complete after 35s [id=i-0c1dc55764b207ac4]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

無事AWSにEC2インスタンスを起動することができました。

参考

https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials
https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/aws-configuration
https://kakakakakku.hatenablog.com/entry/2023/05/24/090806

Discussion