A deep dive into AWS Systems Manager (SSM) Parameter Store hierarchy.
Overview:
- How AWS Systems Manager (SSM) Parameter Store hierarchy enables structured secrets storage & configuration management.
- How twtech can leverage AWS Systems
Manager (SSM) Parameter Store hierarchy for environmental
isolation, access control, automation, and scaling.
Breakdown:
- The concept: Parameter Hierarchy,
- Parameter Naming Rules & Structure,
- Why Hierarchy Matters,
- Hierarchical Access: Path Queries & Recursion,
- Access Control by Hierarchy,
- Example Hierarchical Designs,
- Hierarchical Retrieval Patterns in Code,
- Hierarchy and CloudFormation,
- Hierarchy for Multi-Account Strategy,
- Best Practices for Hierarchical Design,
- Common Pitfalls,
- Example Hierarchical Strategy (Recommended),
- twtech-insights.
1. The concept: Parameter Hierarchy
AWS SSM Parameter Store organizes parameters hierarchically, similar to a directory structure.
Each parameter name is a path, separated by slashes (/):
/<environment>/<application>/<component>/<key>
Example:
/prod/web/api/db/password/dev/web/api/db/password/shared/logging/level
This
hierarchy is purely logical — it helps you organize,
isolate, and secure your configuration data.
2. Parameter
Naming Rules & Structure
|
Feature |
Description |
|
Separator |
|
|
Root |
Always starts with a slash |
|
Max
Depth |
Up to 15 levels deep. |
|
Max
Name Length |
1011 characters total. |
|
Allowed
Characters |
|
|
Case-sensitive |
Yes — |
3. Why Hierarchy
Matters
a. Environment Segregation
Separate
configs by environment:
/dev/.../qa/.../prod/...
→ Makes it
easy to grant access to dev teams only
under /dev/*.
b. Application or Service Segregation
Each
app gets its own tree:
/prod/payment/api/key/prod/payment/db/password/prod/analytics/s3/bucket
c. Cross-Account or Shared Resources
Store reusable parameters under /shared/
or /global/.
d. Least Privilege IAM Control
# IAM
policies can restrict access to parameter paths:
{ "Effect": "Allow", "Action": ["ssm:GetParameter", "ssm:GetParametersByPath"], "Resource": "arn:aws:ssm:us-east-2:acccountID:parameter/prod/payment/*"}
e. Automation with GetParametersByPath
Instead of
fetching each parameter one by one:
aws ssm get-parameters-by-path --path "/prod/web/api" --with-decryption
→ Returns all
configs for that service/environment at once.
4. Hierarchical
Access: Path Queries & Recursion
SSM Parameter
Store supports:
- Non-recursive queries – only direct children under a
given path.
- Recursive queries – includes all sub-paths.
Example:
Parameters:
/prod/web/api/db/password/prod/web/api/db/host/prod/web/frontend/url
Command:
aws ssm get-parameters-by-path --path "/prod/web/api" --recursive
→ Returns
both db/password and db/host.
5. Access Control
by Hierarchy
Hierarchical
paths integrate tightly with IAM policies, tags, and KMS key
policies for security isolation.
Example: Policy for Dev Environment
{ "Effect": "Allow", "Action": ["ssm:GetParameter", "ssm:GetParametersByPath"], "Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/dev/*"}
Example: Policy for Shared Read Access
{ "Effect": "Allow", "Action": "ssm:GetParameter", "Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/shared/*"}
Combine with KMS Key Policies
twtech can
encrypt /prod/* parameters
with one CMK and /dev/* with
another, ensuring data boundaries even at encryption layer.
6. Example
Hierarchical Designs
Pattern 1: Environment-Centric
/dev/app1/db/username/dev/app1/db/password/prod/app1/db/username/prod/app1/db/password
Use case: Simpler for
teams that deploy the same app across multiple stages.
Pattern 2: Service-Centric
/app1/dev/db/username/app1/dev/db/password/app1/prod/db/username/app1/prod/db/password
Use case: Better if
each app manages its own pipeline and lifecycle.
Pattern 3: Shared Config Pattern
/shared/logging/level/shared/audit/s3/bucket/prod/app1/.../prod/app2/...
Use case: Central
governance configs for multiple systems.
Pattern 4: Cross-Account / Multi-Region
Combine hierarchy naming with region identifiers:
/us-east-2/prod/app1/db/endpoint/us-west-1/prod/app1/db/endpoint
Use case: Multi-region
redundancy or AWS Organization-level deployment.
7. Hierarchical Retrieval Patterns in Code
# Python Example (Recursive Fetch)
import boto3ssm = boto3.client('ssm')params = ssm.get_parameters_by_path( Path='/prod/web/api', Recursive=True, WithDecryption=True)config = {p['Name']: p['Value'] for p in params['Parameters']}print(config)
twtech can
then map the structure dynamically to its application’s configuration model.
8.
Hierarchy and CloudFormation
twtech can
reference hierarchical SSM parameters directly in CloudFormation:
Plaintext Example:
EnvironmentName: {{resolve:ssm:/prod/web/api/env}}
Encrypted Example:
DBPassword: {{resolve:ssm-secure:/prod/web/api/db/password:1}}
twtech can
parameterize your stack templates by simply switching the base path:
Parameters: EnvPath: Type: String Default: /prodResources: MyLambda: Type: AWS::Lambda::Function Properties: Environment: Variables: DB_PASSWORD: {{resolve:ssm-secure:${EnvPath}/web/api/db/password}}
9. Hierarchy for
Multi-Account Strategy
In multi-account
setups (Dev, QA, Prod), you can:
- Use consistent parameter paths across accounts.
- Manage replication via AWS Lambda or EventBridge.
- Encrypt per-account with its own KMS CMK.
- Grant central read access (e.g., audit account).
Example:
/organization/dev/app1/.../organization/qa/app1/.../organization/prod/app1/...
This makes it
easy for pipelines to promote parameters across stages.
10. Best
Practices for Hierarchical Design
- Define a global naming convention (enforced by IaC).
- Use short, predictable prefixes (e.g.
/prod,/shared).
- Tag parameters with environment, owner, app name.
- Grant IAM least privilege using
parameter/path/*.
- Use customer-managed CMKs per environment or app.
- Avoid duplicate parameters — use
/shared/for common configs.
- Automate creation via CloudFormation or Terraform.
- Leverage versioning — e.g. promote version N from
/dev/...to/prod/....
Common Pitfalls
❌ Flat
parameter names — makes access control impossible.
❌ Mixing secrets and config in the same path.
❌ Using
the same KMS key for all paths.
❌ Hardcoding
environment names in code (instead of path prefix injection).
❌ Ignoring
case sensitivity — /Prod ≠ /prod.
Example
Hierarchical Strategy (Recommended)
/<organization>/<environment>/<application>/<component>/<key>
Example:
/acme/dev/web/api/url/acme/dev/web/api/key/acme/prod/web/api/url/acme/prod/web/api/key/acme/shared/monitoring/endpoint
→ Provides
clean separation, consistent naming, and scalable IAM policies.
twtech-insights
Here’s a production-ready AWS CloudFormation template that builds a secure SSM Parameter Store hierarchy
for /dev, /qa, and /prod — complete with:
- Environment-specific SSM parameters
(SecureString)
- Dedicated KMS keys per environment
- Granular IAM roles (read-only
access scoped by parameter path)
- Tagging and parameter hierarchy
enforcement
# CloudFormation
Template: ssm-hierarchy-with-kms-roles.yaml
AWSTemplateFormatVersion:
'2010-09-09'
Description: >
SSM Parameter Store hierarchy with KMS encryption and IAM access roles for /dev, /qa, /prod.
Parameters:
OrgPrefix:
Type: String
Default: acme
Description: twtech Prefix for hierarchical
parameter naming (e.g., /acme/dev/...).
SSMParameterValue:
Type: String
Default: twtechValue
Description: Example value stored in SecureString parameters.
Resources:
#############################################
# KMS Keys (one per environment)
#############################################
DevKmsKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for /twtech dev SSM
parameters
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Statement:
- Sid: twtechAllowRootAccountAccess
Effect: Allow
Principal:
AWS: !Sub
arn:aws:iam::${AWS::AccountId}:root
Action: "kms:*"
Resource: "*"
Tags:
- Key: Environment
Value: dev
QaKmsKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for /twtech qa SSM
parameters
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Statement:
- Sid: twtechAllowRootAccountAccess
Effect: Allow
Principal:
AWS: !Sub
arn:aws:iam::${AWS::AccountId}:root
Action: "kms:*"
Resource: "*"
Tags:
- Key: Environment
Value: qa
ProdKmsKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for /twtech prod SSM
parameters
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Statement:
- Sid: twtechAllowRootAccountAccess
Effect: Allow
Principal:
AWS: !Sub
arn:aws:iam::${AWS::AccountId}:root
Action: "kms:*"
Resource: "*"
Tags:
- Key: Environment
Value: prod
#############################################
# Example SSM Parameters
#############################################
DevParameterExample:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub
"/${OrgPrefix}/dev/app/config/value"
Type: SecureString
Value: twtechRef SSMParameterValue
KeyId: twtechRef DevKmsKey
Tags:
Environment: dev
Application: app
QaParameterExample:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub
"/${OrgPrefix}/qa/app/config/value"
Type: SecureString
Value: twtechRef SSMParameterValue
KeyId: twtechRef QaKmsKey
Tags:
Environment: qa
Application: app
ProdParameterExample:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub
"/${OrgPrefix}/prod/app/config/value"
Type: SecureString
Value: twtechRef SSMParameterValue
KeyId: twtechRef ProdKmsKey
Tags:
Environment: prod
Application: app
#############################################
# IAM Roles (Scoped Read-Only Access)
#############################################
DevReadRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub
"${OrgPrefix}-dev-ssm-read-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Sub
arn:aws:iam::${AWS::AccountId}:root
Action: sts:AssumeRole
Policies:
- PolicyName: twtechDevSSMReadPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
- ssm:GetParametersByPath
Resource: !Sub
arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${OrgPrefix}/dev/*
- Effect: Allow
Action: kms:Decrypt
Resource: !GetAtt DevKmsKey.Arn
Tags:
- Key: Environment
Value: dev
QaReadRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub
"${OrgPrefix}-qa-ssm-read-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Sub
arn:aws:iam::${AWS::AccountId}:root
Action: sts:AssumeRole
Policies:
- PolicyName: twtechQaSSMReadPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
- ssm:GetParametersByPath
Resource: !Sub
arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${OrgPrefix}/qa/*
- Effect: Allow
Action: kms:Decrypt
Resource: !GetAtt QaKmsKey.Arn
Tags:
- Key: Environment
Value: qa
ProdReadRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub
"${OrgPrefix}-prod-ssm-read-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Sub
arn:aws:iam::${AWS::AccountId}:root
Action: sts:AssumeRole
Policies:
- PolicyName: twtechProdSSMReadPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
- ssm:GetParametersByPath
Resource: !Sub
arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${OrgPrefix}/prod/*
- Effect: Allow
Action: kms:Decrypt
Resource: !GetAtt
ProdKmsKey.Arn
Tags:
- Key: Environment
Value: prod
DevSSMPath:
Description: Base SSM path for dev environment
Value: !Sub "/${OrgPrefix}/dev/"
QaSSMPath:
Description: Base SSM path for qa environment
Value: !Sub "/${OrgPrefix}/qa/"
ProdSSMPath:
Description:
Base SSM path for prod environment
Value: !Sub "/${OrgPrefix}/prod/"
DevReadRoleArn:
Description: IAM Role ARN for dev SSM read access
Value: !GetAtt DevReadRole.Arn
QaReadRoleArn:
Description: IAM Role ARN for qa SSM read access
Value: !GetAtt QaReadRole.Arn
ProdReadRoleArn:
Description: IAM Role ARN for prod SSM read access
Value: !GetAtt ProdReadRole.Arn
What This Stack
Provides
|
Component |
Description |
|
KMS Keys |
Separate CMKs for /dev, /qa, /prod —
full key rotation, independent policies. |
|
SSM Parameters |
SecureString entries with
hierarchical naming: /acme/dev/..., /acme/qa/..., /acme/prod/.... |
|
IAM Roles |
Environment-scoped roles with
read-only access (both SSM and KMS). |
|
Tags |
Environments, applications, and key
associations tagged for governance. |
Deployment
# bash
aws cloudformation
deploy \
--template-file twtech-ssm-hierarchy-with-kms-roles.yaml \
--stack-name twtech-ssm-hierarchy-stack \
--capabilities CAPABILITY_NAMED_IAM
Next Steps (Optional Enhancements)
- Add Parameter Policies (expiration
or rotation reminders).
- Extend to cross-account KMS grants
for shared environments.
- Add EventBridge rule to replicate
parameters across accounts.
- Integrate with CodePipeline or Terraform
to populate configs dynamically.
twtech -insights
A
production-ready AWS CloudFormation template that builds a secure SSM Parameter
Store hierarchy for /dev, /qa, and /prod into a multi-account version, (where
/dev, /qa, and /prod are
within separate AWS accounts) …using AWS Organizations + KMS
grants + cross-account IAM roles
#
Multi-account-ssm-parameter-hierarchy-stackset· yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
StackSet template to
create a secure SSM Parameter Store hierarchy per-environment
(/dev, /qa, /prod) in a
*member* AWS account. Designed to be deployed via
CloudFormation StackSets
from a management (org) account.
Features:
- KMS CMK per-account
with least-privileged key policy to allow a central
access role to use
the key.
- IAM role in the
member account that the central account can assume to
access SSM
parameters in this account under the environment prefix.
- Optional
Lambda-backed custom resource that issues a KMS Grant so the
central principal
can use the key without changing the key policy often.
- Creates a sample
secure parameter under the environment prefix (encrypted
with the CMK).
Parameters:
EnvironmentName:
Type: String
Description:
Environment name (dev, qa, prod)
AllowedValues: [dev,
qa, prod]
CentralAccountId:
Type: String
Description: AWS
Account ID of the central/security/account that will access parameters
CentralAccessRoleName:
Type: String
Description: IAM role
name in the central account that will assume the cross-account role in this
member account
Default:
CentralSSMAccessRole
CreateKMSGrant:
Type: String
AllowedValues: [true,
false]
Default: true
Description: Whether
to create a KMS Grant via a Lambda custom resource for the central principal.
Use `true` for dynamic grant creation.
ParameterSampleValue:
Type: String
Description: A sample
value to store under the environment prefix (secure string)
NoEcho: true
Default:
"SampleSecretValue"
Mappings: {}
Resources:
# ----------------------------- KMS key for
this account -----------------------------
EnvKmsKey:
Type: AWS::KMS::Key
Properties:
Description: !Sub
"SSM parameter key for ${EnvironmentName} hierarchy"
KeyPolicy: !Sub |
{
"Version":"2012-10-17",
"Id":"key-default-1",
"Statement":[
{
"Sid":"twtechAllow administration of the key",
"Effect":"Allow",
"Principal":{"AWS":"arn:aws:iam::${AWS::AccountId}:root"},
"Action":"kms:*",
"Resource":"*"
},
{
"Sid":"twtechAllowCloudFormationToUseKey",
"Effect":"Allow",
"Principal":{"Service":"cloudformation.amazonaws.com"},
"Action":["kms:Encrypt","kms:Decrypt","kms:ReEncrypt*","kms:GenerateDataKey*","kms:DescribeKey"],
"Resource":"*"
},
{
"Sid":"twtechAllowCentralAccessRole",
"Effect":"Allow",
"Principal":{"AWS":"arn:aws:iam::${CentralAccountId}:role/${CentralAccessRoleName}"},
"Action":["kms:Decrypt","kms:Encrypt","kms:ReEncrypt*","kms:GenerateDataKey*","kms:DescribeKey"],
"Resource":"*"
}
]
}
EnvKmsAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: !Sub
"alias/ssm-${EnvironmentName}"
TargetKeyId: twtechRef
EnvKmsKey
# ----------------------------- Cross-account
role -----------------------------
CentralAssumableRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub
"SSM-CrossAccount-Access-${EnvironmentName}"
AssumeRolePolicyDocument:
Version:
'2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub
"arn:aws:iam::${CentralAccountId}:root"
Action:
sts:AssumeRole
Condition: {}
Description: >-
Role that the
central account uses to assume access into this member account
for
reading/writing SSM parameters under /${EnvironmentName}.
Policies:
- PolicyName: twtechSSMAccessForCentral
PolicyDocument:
Version:
'2012-10-17'
Statement:
- Effect:
Allow
Action:
-
ssm:GetParameter
-
ssm:GetParametersByPath
-
ssm:PutParameter
-
ssm:DeleteParameter
Resource:
!Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${EnvironmentName}/*"
#
----------------------------- Sample secure parameter
-----------------------------
SampleSecureParameter:
Type:
AWS::SSM::Parameter
Properties:
Name: !Sub
"/${EnvironmentName}/sample/secret"
Type: SecureString
Value: twtechRef
ParameterSampleValue
KeyId: twtechRef
EnvKmsKey
Tier: Standard
# ----------------------------- Lambda to
create KMS grant -----------------------------
KMGrantLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub
"KMSGrantLambdaRole-${EnvironmentName}-${AWS::StackName}"
AssumeRolePolicyDocument:
Version:
'2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
-
arn:aws:iam::aws:policy/service-role/twtechAWSLambdaBasicExecutionRole
Policies:
- PolicyName: twtechKMSGrantCreatePolicy
PolicyDocument:
Version:
'2012-10-17'
Statement:
- Effect:
Allow
Action:
-
kms:CreateGrant
-
kms:ListGrants
-
kms:RetireGrant
-
kms:RevokeGrant
-
kms:DescribeKey
Resource: !Ref EnvKmsKey
KMGrantLambda:
Type:
AWS::Lambda::Function
Condition:
CreateKMSGrantCondition
Properties:
Handler:
index.handler
Runtime: python3.9
Role: !GetAtt
KMGrantLambdaRole.Arn
Timeout: 60
MemorySize: 128
Description: Creates
a KMS Grant that allows the central access role to use the CMK
Code:
ZipFile: |
import json
import boto3
import
cfnresponse
import os
kms = boto3.client('kms')
def
handler(event, context):
physical_id
= 'KMSGrant-{}-{}'.format(os.environ.get('ENV'), context.log_stream_name)
props =
event.get('ResourceProperties', {})
key_id =
props.get('KeyId')
grantee_principal = props.get('GranteePrincipal')
try:
if
event['RequestType'] in ('Create','Update'):
grant = kms.create_grant(KeyId=key_id,
GranteePrincipal=grantee_principal,
Operations=['Encrypt','Decrypt','GenerateDataKey','GenerateDataKeyWithoutPlaintext','DescribeKey'],
Name='CFNGrant')
# return GrantId as part of physical
resource id
physical_id = grant['GrantId']
cfnresponse.send(event, context, cfnresponse.SUCCESS,
{'GrantId':grant['GrantId']}, physical_id)
elif
event['RequestType'] == 'Delete':
# try to revoke grants
with matching Name 'CFNGrant' or using GrantId if we stored it
# CloudFormation will
pass the PhysicalResourceId we returned on Create
grant_id = event.get('PhysicalResourceId')
try:
kms.revoke_grant(KeyId=key_id, GrantId=grant_id)
except Exception:
# best-effort
pass
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, physical_id)
except
Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {'Message':str(e)},
physical_id)
Environment:
Variables:
ENV: !Ref
EnvironmentName
KMGrantCustomResource:
Type:
Custom::CreateKMSGrant
Condition:
CreateKMSGrantCondition
Properties:
ServiceToken:
!GetAtt KMGrantLambda.Arn
KeyId: !Ref EnvKmsKey
GranteePrincipal:
!Sub "arn:aws:iam::${CentralAccountId}:role/${CentralAccessRoleName}"
Conditions:
CreateKMSGrantCondition:
!Equals [ !Ref CreateKMSGrant, "true" ]
Outputs:
KmsKeyArn:
Description: ARN of
the KMS key used to encrypt SSM parameters in this account
Value: !GetAtt
EnvKmsKey.Arn
CrossAccountRoleArn:
Description: ARN of
the role to be assumed by the central account to access SSM parameters in this
account
Value: !GetAtt
CentralAssumableRole.Arn
SampleParameterName:
Description: The
sample secure parameter created for this environment
Value: !Ref
SampleSecureParameter
# End of template
!Sub Does
# !Sub stands for Fn::Sub, short for “Substitute”
# !Sub allows you to embed variables (like parameter values, resource names, or pseudo parameters) inside a string.
# What !Ref Does
# !Ref (short for Ref function) returns a value that depends on what you’re referencing:
No comments:
Post a Comment