Parameters allow us to deploy the same infrastructure templates to different environments (staging, production) with different configurations.

We organize parameters in JSON files per environment:
parameters/
├── staging.json
└── prod.json
staging.json:
{
"Environment": "staging",
"CorsOrigin": "*",
"AdminEmail": "admin-staging@example.com",
"LogRetentionDays": "7",
"EnableDebugLogs": "true",
"MinCapacity": "1",
"MaxCapacity": "10"
}
prod.json:
{
"Environment": "prod",
"CorsOrigin": "https://travelguide.com",
"AdminEmail": "admin@example.com",
"LogRetentionDays": "30",
"EnableDebugLogs": "false",
"MinCapacity": "2",
"MaxCapacity": "100"
}
Parameters are converted from JSON to CloudFormation format:
# Convert JSON to AWS CLI format
params_override=$(python -c "import json, sys; \
data=json.load(sys.stdin); \
print(' '.join([f'ParameterKey={k},ParameterValue={v}' \
for k,v in data.items()]))" < $PARAMS_FILE)
# Deploy with parameters
aws cloudformation deploy \
--template-file template.yaml \
--stack-name my-stack \
--parameter-overrides $params_override
In CloudFormation template:
Parameters:
Environment:
Type: String
Default: staging
AllowedValues: [staging, prod]
Description: Deployment environment
CorsOrigin:
Type: String
Default: "*"
Description: CORS origin for API Gateway
AdminEmail:
Type: String
Description: Admin email for notifications
LogRetentionDays:
Type: Number
Default: 7
Description: CloudWatch Logs retention in days
EnableDebugLogs:
Type: String
Default: "false"
AllowedValues: ["true", "false"]
Description: Enable debug logging
Resources:
CreateArticleFunction:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
ENVIRONMENT: !Ref Environment
DEBUG: !Ref EnableDebugLogs
CORS_ORIGIN: !Ref CorsOrigin
ApiGateway:
Type: AWS::Serverless::Api
Properties:
Cors:
AllowOrigin: !Sub "'${CorsOrigin}'"
AllowHeaders: "'*'"
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
RetentionInDays: !Ref LogRetentionDays
| Parameter | Staging | Production | Reason |
|---|---|---|---|
| CorsOrigin | * | https://domain.com | Security |
| LogRetention | 7 days | 30 days | Cost vs compliance |
| DebugLogs | true | false | Performance |
| MinCapacity | 1 | 2 | Availability |
| MaxCapacity | 10 | 100 | Scale |
Current Approach (Basic):
{
"DatabasePassword": "hardcoded-password"
}
Recommended Approach (Secure):
1. AWS Systems Manager Parameter Store:
Parameters:
DatabasePasswordSSM:
Type: AWS::SSM::Parameter::Value<String>
Default: /travelguide/staging/db-password
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
DB_PASSWORD: !Ref DatabasePasswordSSM
2. AWS Secrets Manager:
Resources:
DatabaseSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub '${AWS::StackName}-db-secret'
GenerateSecretString:
SecretStringTemplate: '{"username": "admin"}'
GenerateStringKey: "password"
PasswordLength: 32
Function:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
SECRET_ARN: !Ref DatabaseSecret
#!/bin/bash
set -e
ENVIRONMENT=$1 # staging or prod
PARAMS_FILE="parameters/${ENVIRONMENT}.json"
if [ ! -f "$PARAMS_FILE" ]; then
echo "Error: Parameter file not found: $PARAMS_FILE"
exit 1
fi
# Convert JSON to parameter overrides
params_override=$(python -c "import json, sys; \
data=json.load(sys.stdin); \
print(' '.join([f'ParameterKey={k},ParameterValue={v}' \
for k,v in data.items()]))" < $PARAMS_FILE)
# Deploy stack
aws cloudformation deploy \
--template-file template.yaml \
--stack-name "travel-guide-${ENVIRONMENT}" \
--parameter-overrides $params_override \
--capabilities CAPABILITY_IAM \
--no-fail-on-empty-changeset
echo "✅ Deployed to ${ENVIRONMENT}"
Never commit secrets to Git
.gitignore for sensitive parameter filesValidate parameters before deployment
# Validate JSON syntax
jq empty < parameters/staging.json
Document parameters in README
## Parameters
- `Environment`: Deployment environment (staging/prod)
- `CorsOrigin`: CORS origin for API Gateway
- `AdminEmail`: Email for admin notifications
Use parameter constraints
Parameters:
InstanceType:
Type: String
AllowedValues: [t3.micro, t3.small, t3.medium]
Default: t3.micro
Environment-specific naming
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'travelguide-${Environment}-images'
❌ Hardcoding values in templates
# Bad
Environment:
Variables:
API_URL: "https://api.staging.example.com"
✅ Use parameters
# Good
Environment:
Variables:
API_URL: !Sub "https://api.${Environment}.example.com"
❌ Committing secrets to Git
{
"DatabasePassword": "super-secret-password"
}
✅ Use Secrets Manager
DatabaseSecret:
Type: AWS::SecretsManager::Secret
Properties:
GenerateSecretString: {}