Comprehensive Guide to Managing AWS Systems Manager Parameters with Terraform

AWS Systems Manager Parameter Store serves as the foundational service for storing configuration values, feature flags, database connection strings, and other runtime settings within the AWS ecosystem. Unlike the Secrets Manager service, which is engineered specifically for sensitive credentials that require automatic rotation and cross-account sharing, Parameter Store is designed for broader utility. It handles both sensitive and non-sensitive configuration data with a simpler architecture and a more favorable cost structure, particularly given that the standard tier is free. Terraform provides the necessary infrastructure-as-code capability to define these parameters programmatically, ensuring version control and consistent deployment across disparate environments. This approach allows teams to manage application configuration as code, deploy changes reliably, and integrate parameter values into other infrastructure resources seamlessly.

Architectural Context and Service Comparison

Understanding the distinction between Parameter Store and Secrets Manager is critical for architectural decision-making. Both services store configuration values, but they are designed for different use cases based on operational requirements and cost considerations.

Feature Parameter Store Secrets Manager
Primary Use Case Plain configuration values (endpoints, flags, ports) Credentials requiring automatic rotation
Cost Structure Standard tier is free; Advanced tier incurs costs $0.40 per secret per month
Encryption KMS for SecureString types KMS for all types
Rotation Manual or via automation Automatic and scheduled
Sharing Cross-account via SCP or resource policies Native cross-account sharing

Many engineering teams adopt a hybrid model, utilizing Parameter Store for general configuration items such as API endpoints, environment names, and feature flags, while reserving Secrets Manager for database credentials and API keys that must be rotated on a schedule. This strategy optimizes cost while maintaining appropriate security postures for different data classes. When selecting the service, consider whether the data requires native rotation or if it remains static for extended periods. For static data, Parameter Store is often the superior choice due to its lower latency and cost efficiency.

Provider Requirements and Versioning

Before defining parameters, the Terraform configuration must declare the necessary provider versions and dependencies. The AWS provider version significantly impacts the available features and stability of the aws_ssm_parameter resource.

```terraform
terraform {
required_version = ">= 1.5.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "us-east-1"
}
```

For organizations utilizing community-maintained wrapper modules, such as terraform-aws-modules/ssm-parameter/aws, specific version constraints apply. The recommended module versions require Terraform version >= 1.11 and AWS provider version >= 6.28. These dependencies ensure compatibility with the latest AWS API features and Terraform language constructs. It is essential to align the provider version in the root module with the requirements defined in any child modules to prevent version conflicts during initialization.

Basic Parameter Types: String and StringList

The most common parameter type is String, which stores plain text data. This type is suitable for environment names, URLs, and feature flags. The resource definition includes the parameter name, type, value, and optional metadata such as descriptions and tags.

```terraform

Simple string parameter for environment name

resource "awsssmparameter" "app_environment" {
name = "/myapp/production/environment"
type = "String"
value = "production"
description = "Application environment name"

tags = {
Environment = "production"
Service = "myapp"
}
}

Parameter for a configuration URL

resource "awsssmparameter" "apiendpoint" {
name = "/myapp/production/api
endpoint"
type = "String"
value = "https://api.example.com/v2"
description = "External API endpoint URL"

tags = {
Environment = "production"
Service = "myapp"
}
}

Feature flag as a parameter

resource "awsssmparameter" "featurenewui" {
name = "/myapp/production/features/newuienabled"
type = "String"
value = "true"
description = "Feature flag for the new UI design"

tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
```

The StringList type is utilized for storing comma-separated values, which is particularly useful for lists of allowed IP addresses (CIDR blocks), feature flag targets, or enabled regions. Unlike the String type, the value must be a single string containing comma-separated items.

```terraform

A list of allowed CIDR blocks

resource "awsssmparameter" "allowedcidrs" {
name = "/myapp/production/network/allowed
cidrs"
type = "StringList"
value = "10.0.0.0/16,172.16.0.0/12,192.168.0.0/16"
description = "Comma-separated list of allowed CIDR blocks"
}

A list of enabled regions

resource "awsssmparameter" "enabled_regions" {
name = "/myapp/production/regions"
type = "StringList"
value = "us-east-1,us-west-2,eu-west-1"
description = "Regions where the application is deployed"
}
```

Using StringList allows applications to retrieve the list as a structured object rather than parsing a raw string, reducing the likelihood of configuration errors in client code.

SecureString Parameters and Encryption Management

Sensitive data such as database passwords, API keys, and tokens must be stored as SecureString parameters. These parameters are encrypted using AWS Key Management Service (KMS). By default, SecureString parameters use the AWS managed key aws/ssm, but they can be encrypted with a customer-managed KMS key for stricter compliance requirements.

```terraform

Encrypted parameter using the default AWS managed key

resource "awsssmparameter" "db_password" {
name = "/myapp/production/database/password"
type = "SecureString"
value = "change-me-in-console"
description = "Production database password"

tags = {
Environment = "production"
Sensitive = "true"
}

lifecycle {
ignore_changes = [value]
}
}
```

A critical security consideration when storing secrets in Terraform is that the parameter value ends up in the Terraform state file. For many organizations, this is an unacceptable security risk. A common pattern to mitigate this is to create the parameter with a placeholder value in Terraform and then update the actual value manually or through a separate secrets management workflow.

```terraform

Create with placeholder, update manually after

resource "awsssmparameter" "apikey" {
name = "/myapp/production/external
api_key"
type = "SecureString"
value = "placeholder-update-after-creation"
description = "API key for the external payment service"

lifecycle {
ignore_changes = [value]
}
}
```

By setting ignore_changes = [value] in the lifecycle block, Terraform prevents overwriting the value if it is changed manually in the AWS Console or via another automation tool. This ensures that the source of truth for the secret remains the AWS service, not the Terraform state.

When a custom KMS key is required, the key_id attribute specifies the KMS key ID or ARN. This is crucial for organizations that enforce encryption key rotation or require specific key policies for data isolation.

Attribute Description
type Must be SecureString for encryption
key_id KMS key ID or ARN for custom encryption
value The secret data to be encrypted
lifecycle Configure ignore_changes to prevent state overwrite

Advanced Configuration and Wrapper Modules

For complex deployments managing multiple parameters, the terraform-aws-modules/ssm-parameter/aws module offers a wrapper interface that reduces boilerplate code. This module supports features such as value type guessing, ignoring value changes, and managing resources with less repetition.

The module accepts a locals block containing a map of parameters, allowing for dynamic creation of multiple resources. The following configuration demonstrates the creation of String, SecureString, and StringList parameters within a single module call.

```terraform
locals {
parameters = {
# String
"stringsimple" = {
value = "string
value123"
}

"string" = {
  type            = "String"
  value           = "string_value123"
  tier            = "Intelligent-Tiering"
  allowed_pattern = "[a-z0-9_]+"
}

# SecureString
"secure" = {
  type        = "SecureString"
  value       = "secret123123!!!"
  tier        = "Advanced"
  description = "My awesome password!"
}

"secure_encrypted_true" = {
  secure_type = true
  value       = "secret123123!!!"
  key_id      = "c938de44-1c09-4c91-89fd-b5881f06f317"
}

# StringList
"list_as_autoguess_type" = {
  values = ["item1", "item2"]
}

}
}

module "ssm_params" {
source = "terraform-aws-modules/ssm-parameter/aws"

name = "my-parameter-group"
parameters = local.parameters
}
```

The wrapper module provides several attributes for fine-tuning behavior:

Attribute Description Default
create Whether to create the SSM Parameter true
data_type Data type (text, aws:ssm:integration, aws:ec2:image) null
description Description of the parameter null
ignore_value_changes Whether to ignore changes in value false
allowed_pattern Regular expression to validate the value null
overwrite Overwrite existing parameter null

The data_type attribute is particularly relevant for AMI IDs, where the value must be in the aws:ec2:image format. The allowed_pattern attribute enforces value validity using regular expressions, providing an additional layer of configuration integrity.

Data Sources and Resource Integration

Terraform can read existing SSM parameters to reference their values in other resources. This is useful for referencing parameters created by other Terraform configurations or manually.

```terraform

Read an existing parameter

data "awsssmparameter" "vpcid" {
name = "/infrastructure/production/vpc
id"
}

Use the parameter value in other resources

resource "awssecuritygroup" "app" {
nameprefix = "myapp-"
vpc
id = data.awsssmparameter.vpc_id.value
}

Read a SecureString parameter (the value will be decrypted)

data "awsssmparameter" "dbpassword" {
name = "/myapp/production/database/password"
with
decryption = true
}
```

The with_decryption flag is essential when reading SecureString parameters. When set to true, the AWS provider decrypts the value using KMS, making it available for use in other resources. If this flag is omitted, the value remains encrypted and cannot be used directly.

Tier Selection and Pricing Considerations

SSM Parameter Store offers two pricing tiers: Standard and Advanced. The choice of tier impacts the number of requests allowed per second and the parameter size limit.

  • Standard Tier: This tier is free for basic usage. It allows up to 3,000 requests per 1,000 parameters per second. It is suitable for most application configuration needs where high-throughput reads are not a bottleneck.
  • Advanced Tier: This tier incurs costs but offers higher throughput and larger parameter sizes. It is necessary for applications that require high-frequency reads or store large configuration blocks.

The tier attribute in the wrapper module allows specifying the tier explicitly. For example, setting tier = "Intelligent-Tiering" or tier = "Advanced" ensures the parameter is provisioned with the correct performance characteristics. If not specified, the default is typically Standard, which is cost-effective for the majority of use cases.

Conclusion

AWS Systems Manager Parameter Store, when managed through Terraform, provides a robust and cost-effective mechanism for managing application configuration. The distinction between Standard and Advanced tiers, along with the choice between Parameter Store and Secrets Manager, must be aligned with operational requirements and cost constraints. Terraform's ability to define parameters as code ensures consistency and version control, while the use of SecureString and KMS encryption addresses security needs for sensitive data.

For teams managing large numbers of parameters, the wrapper module terraform-aws-modules/ssm-parameter/aws offers a streamlined approach, reducing code complexity and supporting features like value pattern validation and type guessing. However, for simple deployments, direct use of the aws_ssm_parameter resource provides greater transparency and control.

When dealing with secrets, the risk of storing values in the Terraform state file must be mitigated. Employing the lifecycle ignore_changes block allows teams to maintain control over secret values outside of Terraform, ensuring that the AWS service remains the source of truth. By integrating data sources, teams can dynamically reference parameter values in other infrastructure components, creating a cohesive and manageable configuration landscape. This combination of simplicity, security, and flexibility makes Parameter Store a vital component of modern AWS infrastructure management.

Sources

  1. Terraform AWS SSM Parameter Module
  2. OneUptime Blog: Create Systems Manager Parameters with Terraform

Related Posts