---
page_title: Authenticate a Stack
description: Learn how to set up OIDC authentication or use the store block to authenticate Stacks with their providers.
---

# Authenticate a Stack

You can authenticate a Stack in two ways:  by using OIDC to dynamically authenticate with the built-in `identity_token` block or by accessing existing authentication credentials with the `store` block. We recommend authenticating your Stack with OIDC because using static credentials to authenticate providers presents a security risk, even if you rotate your credentials regularly.

## Authenticate with OIDC

[OpenID Connect](https://openid.net/connect/) (OIDC) is an identity layer on top of the OAuth 2.0 protocol. You can use HCP Terraform’s [Workload identity](/terraform/cloud-docs/workspaces/dynamic-provider-credentials/workload-identity-tokens) tokens, built on the OpenID Connect protocol, to securely connect and authenticate your Stacks with cloud providers.

-> **Hands on**: Walk through setting up OIDC for a Stack in the [Deploy a Stack with HCP Terraform](/terraform/tutorials/cloud/stacks-deploy) tutorial. 

Stacks have a built-in `identity_token` block that creates workload identity tokens, also known as JWT tokens. You can use these tokens to authenticate Stacks with Terraform providers securely.

This guide covers how to set up OIDC for Stacks using the `identity_token` block.

### Overview

The details of Stack authentication differ based on which cloud provider you are setting up, but the basic steps remain the same:

1. Set up the trust configuration between your cloud provider and HCP Terraform, which usually includes creating roles and policies for your cloud provider.  
1. Add an `identity_token` block to your Stack’s deployment file using the audience and roles you created in the previous step.   

Your deployments can reference the value of the `identity_token` block to pass the trust relationship role to your Stack’s operations.

### Configure trust configuration

In this example, you will set up an example trust policy with AWS. For examples of setting up trust policies with other providers, refer to our [repository of example configurations](https://github.com/hashicorp-guides/terraform-stacks-identity-tokens/tree/main).

We recommend using Terraform to create and configure the trust relationship and permissions for the cloud provider you want to authenticate with. Using the following Terraform configuration, [create a new workspace](/terraform/cloud-docs/workspaces/create) and configure the `aws_region`, `tf_organization`, `tf_project,` and `tf_stack` variables for your specific set up.

```hcl 
# main.tf

variable "aws_region" {
  type        = string
  description = "The AWS region to create the role in."
}

variable "tf_organization" {
  type        = string
  description = "The name of the organization that this workspace and Stack live in."
}

variable "tf_project" {
  type        = string
  description = "The name of the project that this workspace and Stack live in."
}

variable "tf_stack" {
  type        = string
  description = "The name of the Stack you will you use this token in."
}

provider "aws" {
  region = var.aws_region
}

resource "aws_iam_openid_connect_provider" "stacks_openid_provider" {
  url            = "https://app.terraform.io"
  client_id_list = ["aws.workload.identity"]

  # This is the thumbprint of https://app.terraform.io as of 2024/08/07
  # Refer to "Adjust access of trust" to learn how to update this thumbprint
  thumbprint_list = ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"]
}

resource "aws_iam_role" "stacks_role" {
  name               = "stacks-${var.tf_organization}-${var.tf_project}-${var.tf_stack}"
  assume_role_policy = data.aws_iam_policy_document.stacks_role_policy.json
}

data "aws_iam_policy_document" "stacks_role_policy" {
  statement {
    effect = "Allow"
    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.stacks_openid_provider.arn]
    }
    actions = ["sts:AssumeRoleWithWebIdentity"]
    condition {
      test     = "StringEquals"
      variable = "app.terraform.io:aud"
      values   = ["aws.workload.identity"]
    }
    condition {
      test     = "StringLike"
      variable = "app.terraform.io:sub"
      # This value dictates which HCP Terraform organizations, projects,  
      # and stacks can assume the new role you are creating.
      # 
      # You can widen access to an entire organization or project by
      # tweaking the value below. You can also restrict access to specific
      # deployments or operations. Refer to Configure trust for more information.
      values = ["organization:${var.tf_organization}:project:${var.tf_project}:stack:${var.tf_stack}:*"]
    }
  }
}

# Now, you give the new role access to things you want to manage in your Stack.
#
# The policies below are too broad for a production use case, but you set them 
# broadly for now to ensure this Stacks can do anything during development and 
# testing. In practice, only give your Stack access to what it needs to manage.

resource "aws_iam_role_policy_attachment" "iam" {
  role       = aws_iam_role.stacks_role.name
  policy_arn = "arn:aws:iam::aws:policy/IAMFullAccess"
}

resource "aws_iam_role_policy_attachment" "sudo" {
  role       = aws_iam_role.stacks_role.name
  policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}

# Your workspace returns this output role, which you use to configure your 
# deployments. 
output "role_arn" {
  value = aws_iam_role.stacks_role.arn
}
```

After setting up your workspace in HCP Terraform, perform a plan and apply operation. HCP Terraform will create your OpenID provider, policy, and role before outputting your new role’s ARN with the `role_arn` output. Copy your new AWS ARN role because this is the value you use to configure your cloud provider with your Stack and HCP Terraform.

#### Adjust access of trust

Workload identity tokens contain useful metadata in their payloads, known as claims. Once a cloud platform verifies a token using HCP Terraform’s public key, it can look at the identity token's claims to match it to the correct permissions or reject it.

You do not need to understand the full token specification or what every claim means, but you can use the claims to adjust your trust relationship.

Workload identity tokens commonly reference the following claims.

| Claim | Explanation |
| :---- | :---- |
| `jti` (JWT ID) | A unique identifier for each token, which is a UUID. |
| `iss` (issuer) | Full URL of the HCP Terraform  instance that signed the token. For example, “https://app.terraform.io”. |
| `iat` (issued at) | Unix timestamp when the token was issued. May be required by certain relying parties. |
| `nbf` (not before) | Unix Timestamp when the token can start being used. Should be the same as `iat` for your purposes and may be required by certain relying parties. |
| `aud` (audience) | Intended audience for the token. For example, “api://AzureADTokenExchange” for Azure. |
| `exp` (expiration)  | Unix timestamp based on the timeout of the operation phase that it was issued for.  |
| `sub` (subject) | Fully qualified path to a Stack deployment, followed by the operation type:<br/>`organization:<organization_name>:project:<project_name>:stack:<stack_name>:deployment:my-deployment-name:operation:<operation_type>`<br/><br/>For example:<br/>`organization:My_Org:project:My_Project:stack:My_Stack:deployment:staging:operation:apply` |

You can further customize your token’s permissions using the following custom claims.

| Claim | Explanation |
| :---- | :---- |
| `terraform_operation` ("run phase") | The Terraform operation this token was issued for. For example, “plan” or “apply”. |
| `terraform_stack_deployment_name` | Name of the deployment that the operation is for. |
| `terraform_stack_id` (stack ID) | External ID of the Stack that the operation is for.  |
| `terraform_stack_name` (stack name) | Name of the Stack that the operation is for. |
| `terraform_project_id` (project ID) | External ID of the project that the operation is taking place in. Useful if you want to use the same role across Stacks in a given project. |
| `terraform_project_name` (project name) | Name of the project that the operation is taking place in.  |
| `terraform_organization_id` (organization ID) | External ID of the HCP Terraform organization that the operation is taking place in. Useful if you are in a large company that may have more than one organization or if you want to use the same role across your entire organization. |
| `terraform_organization_name` (organization name) | Name of the HCP Terraform organization that the operation is taking place in. |
| `terraform_plan_id` (plan ID) | External ID of the plan that this token is being used for creating or applying. This might be used to track down where a token has been leaked from or to block a specific token if it has been leaked. |

You can also update your configuration to use the current thumbprint for HCP Terraform by running the following command and removing the colons from the value it returns.

```shell-session
$ echo | openssl s_client -showcerts -servername app.terraform.io -connect app.terraform.io:443 2>/dev/null | openssl x509 -fingerprint -noout -sha1
sha1 Fingerprint=FD:88:23:AE:FB:96:13:28:A1:34:70:6D:C8:57:5A:17:0F:0B:B3:7C
```

### Configure HCP Terraform

Stacks use the [`identity_token` block](/terraform/language/stacks/reference/tfdeploy#identity_token-block-configuration) to define a JSON Web Token (JWT) for a specific deployment. This token enables OIDC-based authentication, allowing your Stack deployments to securely connect to cloud providers like AWS, Azure, and GCP.

When you define the `identity_token` block, you specify its audience. For example, the example Stack deployment configuration defines an `identity_token` block specifying the AWS audience.

```hcl
identity_token "aws" {
    audience = ["aws.workload.identity"]
}
```

Once defined, you can reference an identity token in a deployment's input variables and reference that token in a provider’s configuration.

You also need to add your trust relationship, your role ARN, to configure your token with the trust relationship to authenticate AWS resources.

```hcl 
# deployments.tfdeploy.hcl

identity_token "aws" {
    audience = ["aws.workload.identity"]
}

deployment "development" {
    inputs = {
        role_arn   	= "<YOUR_ROLE_ARN>"
        identity_token = identity_token.aws.jwt
    }
}
```

Now, the deployments are authenticated with AWS and can pass the trust configuration to your Stack’s providers for authentication.

```hcl
# variables.tfstack.hcl

variable "role_arn" {
    type = string
}

variable "identity_token" {
    type      = string
    ephemeral = true
}

# providers.tfstack.hcl

required_providers {
    aws = {
        source  = "hashicorp/aws"
        version = "~> 5.7.0"
    }
}

provider "aws" "this" {
    config {
        region = var.region
        assume_role_with_web_identity {
            role_arn           = var.aws_role
            web_identity_token = var.aws_token
        }
    }
}
```

With this setup, each of your deployments pass the ARN of your trust relationship role and a JWT token generated by AWS each time you plan or apply your Stack. Each of your deployments uses their own role, configured with the specific permissions you require.

For more examples of setting up OIDC with different providers, refer to our [repository of example configurations](https://github.com/hashicorp-guides/terraform-stacks-identity-tokens/tree/main).

## Authenticate with existing credentials

You can use the `store` block to define key-value secrets in your deployment configuration and then access those values in your deployments.

You can use the `store` block to access credentials stored in an [HCP Terraform variable set](/terraform/cloud-docs/workspaces/variables/managing-variables#variable-sets). This example uses a `store` block with a variable set to access credentials stored in HCP Terraform. For more information about using `store` blocks, refer to the [Deployment configuration reference](/terraform/language/stacks/reference/tfdeploy).

Before writing any configuration, ensure your Stack can access the variable set you are targeting by allowing your project to access the set or making the set globally available.

Next, add a `store` block to your deployment configuration file to access your variable set. Once defined, your deployments can access your variable set’s values. 

```hcl
# deployments.tfdeploy.hcl

# Source environment secrets from your HCP Terraform variable set
store "varset" "tokens" {
  id       = "varset-<variables-set-id>"
  category = "env"
}

# Access your variable set's value using your store and pass them into your 
# deployment's inputs.
deployment "test" {
  inputs = {
    access_key = store.varset.tokens.AWS_ACCESS_KEY_ID    
    secret_key = store.varset.tokens.AWS_SECRET_ACCESS_KEY
    session_token = store.varset.tokens.AWS_SESSION_TOKEN
  }
}
```

For example, if your `test` deployment can use your `store` to access your variable set’s keys, and therefore, your variable set’s AWS provider credentials.  

Your `test` deployment can define your AWS credentials as `inputs`, allowing your `providers` to access those credentials. 

```hcl
# providers.tfstack.hcl

variable "access_key" {
  description = "AWS access key"
  type     = string
  ephemeral = true
}

variable "secret_key" {
  description = "AWS sensitive secret key."
  type     = string
  sensitive = true
  ephemeral = true
}

variable "session_token" {
  description = "AWS session token."
  type     = string
  sensitive = true
  ephemeral = true
}

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
}

provider "aws" "this" {
  config {
    access_key = var.access_key
    secret_key = var.secret_key
    token = var.session_token
  }
}
```

This example allows your `aws.this` provider to authenticate to AWS using the credentials from a variable set stored in HCP Terraform. For more information about using `store` blocks, refer to the [Deployment configuration reference](/terraform/language/stacks/reference/tfdeploy).