Required providers
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
vault = {
source = "hashicorp/vault"
}
}
}Providers
provider "aws" {
region = "ap-south-1"
}
provider "vault" {
address = "http://127.0.0.1:8200"
skip_child_token = true
auth_login {
path = "auth/approle/login"
parameters = {
role_id = "987f09d1-7b77-ae4c-9877-0e4eb03584b0"
secret_id = "10c82687-ed0a-82e3-792e-1c42bc5bb10b"
}
}
}Replace role_id and secret_id with values from your Vault setup. Prefer environment variables or a secrets CI store over committing them to Git.
Read the secret
data "vault_kv_secret_v2" "ec2" {
mount = "secret"
name = "ec2"
}Terraform reads secret/ec2 → key_name → terraform-key.
Launch EC2 with the secret
resource "aws_instance" "web" {
ami = "ami-0199ac7c9fbf9ed83"
instance_type = "t3.micro"
key_name = data.vault_kv_secret_v2.ec2.data["key_name"]
tags = {
Name = "Vault-Demo"
}
}No secret values appear as literals in the resource block.
Deploy
terraform init
terraform plan
terraform applyComplete flow
terraform apply
↓
Vault provider (AppRole login)
↓
Vault issues token
↓
Read secret/ec2 → terraform-key
↓
AWS provider launches EC2 (Vault-Demo)
Production practices
| Do | Don't |
|---|---|
| Use AppRole or cloud IAM auth | Commit root tokens |
| Prefer short-lived credentials | Hardcode passwords in .tf |
| Restrict Vault policies tightly | Give Terraform unrestricted Vault access |
| Audit secret reads | Store prod secrets in -dev Vault |
Vault vs hardcoded secrets
| Hardcoded | Vault |
|---|---|
| Visible in Git | Centralized, access-controlled |
| Hard to rotate | Rotatable / dynamic secrets |
| No audit trail | Auditable access |
Treat Role ID / Secret ID like passwords. Inject them via CI secrets or a secure local env — never push them to a public repository.