SSM Parameter Store: Standard and SecureString Parameters
Scenario
Your application across multiple services needs to share configuration values — database hostnames, feature flags, and API keys. You want a centralised, version-controlled store where sensitive values are encrypted. AWS SSM Parameter Store provides both plain-text (String) and encrypted (SecureString) parameters and integrates natively with EC2, ECS, Lambda, and CodeBuild.
- Create a String parameter for configuration like /app/prod/db_host.
- Create a StringList parameter for /app/prod/allowed_origins.
- Create a SecureString parameter encrypted with a KMS key for /app/prod/api_key.
- Use a data "aws_ssm_parameter" source to read a parameter back and demonstrate cross-resource consumption.
- Attach an IAM policy granting ssm:GetParameter* for the path hierarchy /app/prod/*.
Additional Context
SSM Parameter Store supports a hierarchical path structure separated by /. This allows you to write IAM policies using path prefixes (e.g., /app/prod/*) to grant access to all parameters in a path, making it easy to manage permissions by environment.
When reading a SecureString parameter via the data source, set with_decryption = true. This requires the caller to have both ssm:GetParameter and kms:Decrypt permissions. The decrypted value is stored in Terraform state — treat it as sensitive.
versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}variables.tf
variable "aws_region" { type = string; default = "us-east-1" }
variable "app_path" { type = string; default = "/app/prod" }
variable "db_host" { type = string; default = "mydb.xxxxx.us-east-1.rds.amazonaws.com" }
variable "api_key" { type = string; sensitive = true; description = "API key to store as SecureString." }
variable "environment" { type = string; default = "prod" }main.tf
# main.tf
provider "aws" {
region = var.aws_region
}
data "aws_caller_identity" "current" {}
# KMS key for SecureString encryption.
resource "aws_kms_key" "ssm" {
description = "KMS key for SSM Parameter Store — ${var.environment}"
enable_key_rotation = true
tags = { ManagedBy = "terraform", Environment = var.environment }
}
resource "aws_kms_alias" "ssm" {
name = "alias/ssm-params-${var.environment}"
target_key_id = aws_kms_key.ssm.key_id
}
# Standard String parameter — plaintext configuration (non-sensitive).
resource "aws_ssm_parameter" "db_host" {
name = "${var.app_path}/db_host"
type = "String"
value = var.db_host
description = "Application database hostname"
tags = { Environment = var.environment, ManagedBy = "terraform" }
}
# StringList parameter — comma-separated list of allowed CORS origins.
resource "aws_ssm_parameter" "allowed_origins" {
name = "${var.app_path}/allowed_origins"
type = "StringList"
value = "https://app.example.com,https://admin.example.com"
description = "Allowed CORS origins for the API"
tags = { Environment = var.environment, ManagedBy = "terraform" }
}
# SecureString parameter — encrypted with KMS CMK.
resource "aws_ssm_parameter" "api_key" {
name = "${var.app_path}/api_key"
type = "SecureString"
value = var.api_key
key_id = aws_kms_key.ssm.arn
description = "Third-party API key (encrypted)"
tags = { Environment = var.environment, ManagedBy = "terraform" }
}
# Read the db_host parameter back via a data source.
# This demonstrates how other Terraform configs can consume parameters.
data "aws_ssm_parameter" "db_host_lookup" {
name = aws_ssm_parameter.db_host.name
with_decryption = false # not encrypted, no KMS needed
}
# IAM policy granting access to all parameters in the /app/prod/ hierarchy.
data "aws_iam_policy_document" "ssm_read" {
statement {
sid = "ReadAppParams"
effect = "Allow"
actions = [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
]
resources = [
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter${var.app_path}/*"
]
}
statement {
sid = "DecryptSecureStrings"
effect = "Allow"
actions = ["kms:Decrypt"]
resources = [aws_kms_key.ssm.arn]
}
}
resource "aws_iam_policy" "ssm_read" {
name = "ssm-read-app-params-${var.environment}"
policy = data.aws_iam_policy_document.ssm_read.json
}outputs.tf
output "db_host_param_name" { value = aws_ssm_parameter.db_host.name }
output "api_key_param_name" { value = aws_ssm_parameter.api_key.name }
output "ssm_policy_arn" { value = aws_iam_policy.ssm_read.arn }
output "db_host_value" { value = data.aws_ssm_parameter.db_host_lookup.value }Workflow Commands
terraform init
terraform apply -var="api_key=supersecret123"
# Retrieve parameters from CLI (as an application would)
aws ssm get-parameter --name /app/prod/db_host --query Parameter.Value --output text
aws ssm get-parameter --name /app/prod/api_key --with-decryption --query Parameter.Value
# Get all parameters in the path at once
aws ssm get-parameters-by-path --path /app/prod --with-decryption
terraform destroy -var="api_key=supersecret123"Apply complete! Resources: 6 added, 0 changed, 0 destroyed. Outputs: api_key_param_name = "/app/prod/api_key" db_host_param_name = "/app/prod/db_host" db_host_value = "mydb.xxxxx.us-east-1.rds.amazonaws.com" ssm_policy_arn = "arn:aws:iam::123456789012:policy/ssm-read-app-params-prod"

