Storing access keys in HCP Terraform poses a security risk. While HCP Terraform secures sensitive credentials as write-only variables, you must audit the usage of long-lived access keys to detect if they are compromised. Not only is leaking the access key a risk, but many organizations have a policy to block the creation of such access keys.
Fortunately, in many cases, you can authenticate with more secure alternatives to access keys. One such alternative is AWS IAM OIDC federation, which uses identity and access management (IAM) to grant external identities (such as HCP Terraform) the ability to assume an IAM role.
HCP Terraform’s dynamic provider credentials allow Terraform runs to assume an IAM role through native OpenID Connect (OIDC) integration and obtain temporary security credentials for each run. These AWS credentials allow you to call AWS APIs that the IAM role has access to at runtime. These credentials are usable for only one hour by default, so their usefulness to an attacker is limited.
This brief tutorial will show you how to set up an OIDC provider and access AWS from HCP Terraform using dynamic provider credentials and OIDC federation.
»
For this tutorial, you will use HCP Terraform to provision an OIDC provider that establishes a trust relationship between HCP Terraform and your AWS account. This setup allows HCP Terraform to assume an IAM role at runtime and pass the obtained temporary security credentials to the AWS Terraform provider to run terraform plan
or apply
.
To set up an OIDC provider, the below steps assume that you already have a method available to authenticate to your AWS account.
»
To set up the HCP Terraform OIDC provider for OIDC federation in AWS, use the following example configuration:
data "tls_certificate" "provider" {
url = "
}
Â
resource "aws_iam_openid_connect_provider" "hcp_terraform" {
url = "
Â
client_id_list = [
"aws.workload.identity", # Default audience in HCP Terraform for AWS.
]
Â
thumbprint_list = [
data.tls_certificate.provider.certificates[0].sha1_fingerprint,
]
}
Once the HCP Terraform OIDC provider is created, create an ‘example’ IAM role that HCP Terraform will assume at runtime:
data "aws_iam_policy_document" "example_oidc_assume_role_policy" {
statement {
effect = "Allow"
Â
actions = ["sts:AssumeRoleWithWebIdentity"]
Â
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.hcp_terraform.arn]
}
Â
condition {
test = "StringEquals"
variable = "app.terraform.io:aud"
values = ["aws.workload.identity"]
}
Â
condition {
test = "StringLike"
variable = "app.terraform.io:sub"
values = ["organization:ORG_NAME:project:PROJECT_NAME:workspace:WORKSPACE_NAME:run_phase:*"]
}
}
}
Â
resource "aws_iam_role" "example" {
name = "example"
assume_role_policy = data.aws_iam_policy_document.example_oidc_assume_role_policy.json
}
The IAM role defined above currently includes only an assume_role_policy
and lacks additional permissions. Depending on your requirements, you may need to add more permissions to the role to allow it to create and manage resources, such as S3 buckets, or EC2 instances.
In the aws_iam_policy_document
, define a condition that evaluates the OIDC subject claim for HCP Terraform organization, project, workspace, and run phase. The subject claim in the example searches for specific organization, project, and workspace. However, you can make the claim more flexible by using wildcards (*), such as organization:ORG_NAME:project:PROJECT_NAME:workspace:*:run_phase:*
.
This claim allows for matching of all workspaces and run phases within a specific HCP Terraform project and organization, which can be helpful in scenarios like using HCP Terraform’s no-code modules to provide self-service infrastructure, where workspace names may not be known in advance.
Note that wildcards in OIDC subject claims can simplify access policies but introduce potential security risks. To balance flexibility and security, use wildcards carefully. While you can scope claims down to a specific HCP Terraform workspace or run phase for maximum security, wildcards can be used selectively to replace certain values, offering a compromise between granularity and convenience.
You can add additional permissions to an IAM role by using the aws_iam_policy_document
data source and the aws_iam_policy
resource. See the example below:
data "aws_iam_policy" "s3_full_access" {
arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}
Â
resource "aws_iam_role_policy_attachment" "example_s3_full_access" {
policy_arn = data.aws_iam_policy.s3_full_access.arn
role = aws_iam_role.example.name
}
»
When using OIDC federation, apart from the region argument, you don’t need to include any authentication configuration within the provider block. As long as you set up the correct environment variables in your workspace—specifically, set TFC_AWS_PROVIDER_AUTH
to true
and TFC_AWS_RUN_ROLE_ARN
to the IAM role ARN that HCP Terraform should assume at runtime.
resource "tfe_variable" "tfc_aws_provider_auth" {
key = "TFC_AWS_PROVIDER_AUTH"
value = "true"
category = "env"
workspace_id = tfe_workspace.example.id
}
Â
resource "tfe_variable" "tfc_example_role_arn" {
sensitive = true
key = "TFC_AWS_RUN_ROLE_ARN"
value = aws_iam_role.example.arn
category = "env"
workspace_id = tfe_workspace.example.id
}
HCP Terraform will automatically assume the IAM role and inject the temporary credentials for you, using the workspace environment variables, allowing you to focus on creating infrastructure.
»
For improved security and scalability, we recommend implementing a pattern where one or more HCP Terraform workspaces inject the IAM role and OIDC provider ARNs into other workspaces using an HCP Terraform variable set. This enables the platform/cloud team to create HCP Terraform workspaces with pre-configured AWS authentication, scoped to a specific IAM role and permissions.
Whether you create an OIDC provider per AWS account, per environment, or use a single OIDC provider, providing pre-configured AWS authentication for teams’ HCP Terraform workspace is a win-win for both the platform/cloud team and the teams they enable to work autonomously.
Below is an example configuration that creates a variable set for a specific IAM role and sets two environment variables. HCP Terraform uses these environment variables to assume the IAM role and obtain temporary security credentials at runtime, injecting them into the provider to enable access to any AWS API allowed by the IAM role’s policies.
First, create the variable set:
resource "tfe_variable_set" "example" {
name = aws_iam_role.example.name
description = "OIDC federation configuration for ${aws_iam_role.example.arn}"
organization = "XXXXXXXXXXXXXXX"
}
Next, set up the required environment variables and link them to the variable set:
resource "tfe_variable" "tfc_aws_provider_auth" {
key = "TFC_AWS_PROVIDER_AUTH"
value = "true"
category = "env"
variable_set_id = tfe_variable_set.example.id
}
Â
resource "tfe_variable" "tfc_example_role_arn" {
sensitive = true
key = "TFC_AWS_RUN_ROLE_ARN"
value = aws_iam_role.example.arn
category = "env"
variable_set_id = tfe_variable_set.example.id
}
Finally, share the variable set with another HCP Terraform workspace. This ensures that the targeted workspace receives and uses the environment variables, allowing HCP Terraform to automatically assume the IAM role and inject the temporary security credentials:
resource "tfe_workspace_variable_set" "example" {
variable_set_id = tfe_variable_set.example.id
workspace_id = "ws-XXXXXXXXXXXXXXX"
}
»
Using the IAM role created earlier in this tutorial, which has been assigned S3 permissions, you can create a bucket right away within the workspace you’ve delegated access to without needing any additional configuration:
provider "aws" {
region = "us-west-2"
}
Â
resource "aws_s3_bucket" "example" {
bucket = "example"
}
»
For more on how to securely access AWS from HCP Terraform with OIDC federation, check out the Dynamic Credentials with the AWS Provider and OIDC federation documentation. Find a more complete example of configuring the AWS IAM OIDC identity provider on GitHub.