LAB21_AWS_SSM Parameter Store: Standard and SecureString Parameters

SSM Parameter Store: Standard and SecureString Parameters

☁ AWS ⭐ Beginner SSM Parameter Store SecureString KMS Encryption Data Sources

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.

Your Objectives
  • 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"
✓ Expected Output After Apply
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"