Integrating Terraform with HashiCorp Vault to manage static secrets

One challenge that comes up when provisioning resources over a cloud using Terraform is that you will need some form of credentials to log into the cloud or to access those resources. You don’t want to store those credentials inside your code, especially if you check your code into a versioning control system (VCS) for obvious reasons.

Even if you would provide Terraform with your credentials in a secure way, like the secure variable store (terraform.tfvars), it will most likely be the case that they end up somewhere inside the state file.

We can use HashiCorp Vault to manage secrets and integrating it with Terraform is a secure way to handle sensitive information.

Store Secrets in HashiCorp Vault

  1. Sign in to Vault using Vault token.

  2. Enable a KV Secrets Engine under Secrets. Define the path and store your secrets under that path. In this example, my rds username and password are stored under "terraform/variables/RDS".

Now, to use static secrets stored in HashiCorp Vault in your Terraform code, you can use following method.

Authentication using Vault token

  1. Set up the Vault Provider: First, you need to configure the Vault provider in your Terraform code. The Vault provider allows Terraform to communicate with HashiCorp Vault.

     provider "vault" {
       address         = "https://your_vault_url.com"
     }
    
  2. Authentication: Terraform needs to authenticate with Vault to access the secrets. There are several ways to authenticate, including using a token, an AppRole, or Kubernetes authentication. Let's use the token method in this example.

    2.1. Obtain a Vault token: You will have a Vault token once you have configured Vault. You can also generate a Vault token manually.

    2.2. Set the token in Terraform: Export the Vault token as an environment variable or set it directly in your Terraform configuration.

     provider "vault" {
       address         = "https://your_vault_url.com"
       skip_tls_verify = true
       token           = "your_vault_token"
     }
    
  3. Accessing Secrets: Now that Terraform is authenticated with Vault, you can access secrets by using the vault_generic_secret data source.

    For example, let's say you have a secret named "RDS" with username and password stored in Vault.

     data "vault_generic_secret" "rds" {
       path = "terraform/variables/RDS"
     }
    
     resource "aws_db_instance" "this" {
       for_each                = local.rds_db
       identifier              = each.value.name
       engine                  = each.value.engine
       engine_version          = each.value.engine_version
       instance_class          = each.value.instance_class
       allocated_storage       = each.value.allocated_storage
       username                = data.vault_generic_secret.rds.data.rds_username
       password                = data.vault_generic_secret.rds.data.rds_password
    
       vpc_security_group_ids  = var.vpc_security_group_ids
       db_subnet_group_name    = aws_db_subnet_group.this[each.value.name].id
    
       availability_zone       = var.availability_zone
       multi_az                = var.multi_az
       publicly_accessible     = var.publicly_accessible
    
       tags = merge(
         {
           "Name" = format("%s", each.value.name)
         },
         var.tags
       )
     }
    

    In this example, we're using the Vault provider to access the secret at the path "terraform/variables/RDS" and extracting the username and password from it. These values for username and password can then be used in the AWS DB instance resource.

  4. Apply Terraform: When you run terraform apply, Terraform will read the secrets from Vault and populate them into your infrastructure code.