AWS Parameter Store with Terraform: Configuration as Code

AWS Systems Manager Parameter Store is the go-to service for storing configuration values, feature flags, database connection strings, and other settings that applications need at runtime. Unlike Secrets Manager, which is designed specifically for secrets with automatic rotation, Parameter Store is simpler, cheaper and works well for both sensitive and non-sensitive configuration data. The standard tier is free. Terraform makes it easy to define parameters as code, version-control them, and deploy them consistently across environments.

Parameter Store provides a hierarchical key-value store with built-in tagging, versioning, and optional KMS encryption. Because the data lives outside of Terraform state files, teams can share configuration across pipelines, prevent drift, and keep sensitive values out of repositories.

What Parameter Store Offers in Practice

Parameter Store supports String, StringList, and SecureString types. SecureString parameters are encrypted at rest using a customer managed KMS key or the AWS managed key. Standard tier parameters are free, have a 4 KB limit per value, and cap at 10,000 parameters per region. Advanced tier parameters increase limits and add advanced features such as higher throughput and larger payloads.

A common Terraform pattern for writing a parameter includes lifecycle protection to avoid overwriting values that were updated outside Terraform and tagging for environment tracking.

```hcl
resource "awsssmparameter" "example" {
name = "/myapp/${var.environment}/setting"
type = "String"
value = var.setting_value

kmskeyid = var.usekms ? awskmskey.parameterstore.arn : null

lifecycle {
ignore_changes = [value]
}

tags = {
Environment = var.environment
ManagedBy = "terraform"
}
}
```

The lifecycle block with ignore_changes on value is useful for secrets that may be rotated manually or by another process. Tags such as Environment and ManagedBy provide ownership and cost allocation.

Parameter Store vs Secrets Manager

Both services store configuration values, but they are designed for different use cases.

Feature Parameter Store Secrets Manager
Primary use Plain configuration values, feature flags, port numbers Credentials that need automatic rotation
Cost Standard tier is free; Advanced tier is paid $0.40 per secret per month
Rotation Manual or custom Built-in automatic rotation
Cross-account sharing Via IAM and parameter policies Native cross-account secret sharing
Typical data Endpoints, AMI IDs, VPC IDs Database passwords, API keys

Use Parameter Store when you have plain configuration values, you want to store data for free with the standard tier, or you need a simple hierarchy for organizing config across services and environments.

Use Secrets Manager when you need automatic secret rotation, you need cross-account secret sharing, or you are storing credentials that need to be rotated on a schedule.

Many teams use both: Parameter Store for general configuration and Secrets Manager for credentials that need rotation.

Reading Existing Parameters with Terraform

Terraform can read parameters created manually or by another configuration. The data source awsssmparameter fetches a value at plan time.

hcl data "aws_ssm_parameter" "vpc_id" { name = "/infrastructure/production/vpc_id" }

The retrieved value is available as data.awsssmparameter.vpc_id.value and can be used directly in other resources.

hcl resource "aws_security_group" "app" { name_prefix = "myapp-" vpc_id = data.aws_ssm_parameter.vpc_id.value }

For SecureString parameters the value is decrypted automatically when with_decryption is set to true.

hcl data "aws_ssm_parameter" "db_password" { name = "/myapp/production/database/password" with_decryption = true }

When a SecureString is read, use the value directly in the resource configuration and avoid storing SecureString values in variables or outputs to reduce exposure.

Fetching and Using Parameter Values in Resources

A common pattern is to store an AMI ID in Parameter Store and reference it dynamically when provisioning EC2 instances.

hcl data "aws_ssm_parameter" "ami_id" { name = "/my-app/latest-ami-id" }

Access the parameter value:

hcl ami = data.aws_ssm_parameter.ami_id.value

Use the value in a resource:

hcl resource "aws_instance" "example" { ami = data.aws_ssm_parameter.ami_id.value instance_type = "t3.micro" }

This approach promotes reusability and dynamic infrastructure provisioning. If the parameter value changes, running terraform refresh updates the state before the next plan.

For larger projects, consider using modules to encapsulate the logic of fetching and using parameters, promoting code reusability and maintainability.

Steps to follow:

  • Define a data source: Use the data "awsssmparameter" resource to fetch the desired parameter from SSM. Specify the parameter name using the name attribute.
  • Access the value: The retrieved parameter value is available in the data...value attribute.
  • Utilize the value: Use the accessed value directly within your resource configurations, such as setting the AMI ID for an EC2 instance.

Secure Strings, Error Handling, and State Management

Secure Strings: Handle SecureString parameters directly within resource configurations to avoid exposing sensitive data. Do not pass SecureString values through variables or outputs.

Error Handling: If the parameter doesn't exist, Terraform will throw an error. You can use the try() function to handle this gracefully.

hcl ami = try(data.aws_ssm_parameter.ami_id.value, "ami-fallback")

Alternative for Secrets: Consider using AWS Secrets Manager for managing sensitive information instead of SSM Parameter Store when automatic rotation or cross-account sharing is required.

Modularity: For larger projects, consider using modules to encapsulate the logic of fetching and using parameters, promoting code reusability and maintainability.

You might need to use terraform refresh to update the state when parameter values are changed outside Terraform.

Module-Based Read and Write Access

The community provides modules that wrap read and write access to the AWS SSM Parameter Store. A write example creates a new String parameter.

```hcl
module "store_write" {
source = "cloudposse/ssm-parameter-store/aws"

parameterwrite = [
{
name = "/cp/prod/app/database/master
password"
value = "password1"
type = "String"
overwrite = "true"
description = "Production database master password"
}
]

tags = {
ManagedBy = "Terraform"
}
}
```

A read example retrieves a value from the parameter store by name.

```hcl
module "store_read" {
source = "cloudposse/ssm-parameter-store/aws"

parameterread = ["/cp/prod/app/database/masterpassword"]
}
```

In Cloud Posse's examples, modules are left unpinned to reflect the latest released versions. For your own projects, we strongly advise pinning each module to the exact version you're using. This practice ensures the stability of your infrastructure.

Tips for working with the module include consulting AWS details on what values can be used, reviewing the AWS API for PutParameter, and referencing the Terraform awsssmparameter resource page and Terraform awsssmparameter data page.

Operational Best Practices

Keep parameters organized with a clear hierarchy such as /service/environment/name. Use tags for Environment and ManagedBy to support auditing.

Protect writes with lifecycle ignore_changes on value when parameters may be rotated outside Terraform. This prevents accidental overwrites of secrets that were updated manually.

Limit standard tier parameters to 4 KB per value and stay within the 10,000 parameters per region limit. Move to Advanced tier for larger payloads or higher throughput needs.

Separate sensitive and non-sensitive data. Use SecureString with with_decryption true for secrets, and prefer Secrets Manager when rotation and cross-account sharing are required.

Avoid hardcoding values in code. Fetching AMI IDs, VPC IDs, database endpoints, and feature flags from Parameter Store keeps infrastructure code portable and environment agnostic.

Conclusion

AWS Systems Manager Parameter Store provides a straightforward, cost-effective way to manage application configuration on AWS. With Terraform, parameters become part of declarative infrastructure that is version controlled and consistently deployed.

Reading parameters with data sources enables dynamic provisioning where EC2 instances, security groups, and other resources consume live configuration without code changes. Writing parameters as code with lifecycle controls and tagging ensures safe updates and clear ownership.

The choice between Parameter Store and Secrets Manager remains important. Parameter Store excels for plain configuration, free standard tier storage, and hierarchical organization. Secrets Manager adds automatic rotation, cross-account sharing, and a managed rotation schedule at a per-secret cost.

Combining both services in practice gives teams the best of both worlds: Parameter Store for general configuration and Secrets Manager for credentials that need rotation. Using Terraform to define, read, and reference these values centralizes configuration management, reduces drift, and improves security by separating sensitive data from the codebase.

Sources

  1. oneuptime.com blog post
  2. nulldog.com article
  3. Terraform Foundation GitHub

Related Posts