Overview
This guide covers the essential commands and workflows for developing CloudFormation templates on your local machine before pushing to CI/CD. Everything runs through helper scripts and the Rain CLI, so the same commands work locally and in your pipelines.
Daily development cycle
The typical workflow for making infrastructure changes:
# 1. Create a feature branch
git checkout -b feature/add-lambda
# 2. Modify templates under templates/
# Edit YAML templates
# 3. Update parameters for your target environment
# Edit parameters/test/<template-name>.yml
# 4. Validate templates locally
./scripts/validate-templates.sh
# 5. Run cfn-lint for quick feedback
cfn-lint templates/*.yml
# 6. Preview changes with Rain changeset
rain deploy templates/my-template.yml --config parameters/test/my-template.yml --changeset
# 7. Deploy changes (if preview looks good)
./scripts/deploy-templates.sh -e test
# 8. Test your infrastructure
# Run manual tests
# 9. Commit and push
git add .
git commit -m "feat: add Lambda function template"
git push origin feature/add-lambda
Essential script commands
The starter kit provides helper scripts for all operations. Run them from the repository root.
Validation
./scripts/validate-templates.sh
This script runs Checkov security scanning:
- Checks dependencies (Python, pip)
- Installs requirements if Checkov is not present
- Runs Checkov with
.checkov.ymlconfiguration - Returns exit status based on scan results
Note: Run cfn-lint separately (
cfn-lint templates/*.yml) for template syntax validation. The CI/CD workflow runs both tools.
Deployment
./scripts/deploy-templates.sh -e test
./scripts/deploy-templates.sh -e staging
./scripts/deploy-templates.sh -e production
Important: The
-eflag selects which parameter folder to use (parameters/<environment>/).
The deploy script:
- Iterates over all templates in
templates/ - Matches each template to its parameter file
- Builds and executes Rain commands
- Uses
--yesfor non-interactive execution
Provisioning new environments
./scripts/provision-repo.sh
Interactive helper that scaffolds:
- GitHub Actions workflow for the environment
- Parameter file with OIDC provider configuration
Run this when adding new environments to your deployment matrix.
Working with Rain directly
While scripts are convenient, Rain gives you more control for previews and targeted operations.
Preview changes with changesets
rain deploy templates/oidc-provider.yml --config parameters/test/oidc-provider.yml --changeset
Shows exactly what CloudFormation will create, modify, or destroy. Review carefully before approving.
Deploy a single template
rain deploy templates/my-template.yml --config parameters/test/my-template.yml --yes
The --yes flag suppresses confirmation prompts. Remove it for interactive deployments.
Deploy without parameters
rain deploy templates/simple-template.yml --yes
For templates that don't require parameter files.
Check stack status
rain ls
rain ls --all
Shows deployed stacks and their status.
View stack details
rain cat my-stack-name
Displays the current template for a deployed stack.
Remove a stack
rain rm my-stack-name --yes
Destroys all resources in the stack. CloudFormation handles deletion order.
View stack outputs
aws cloudformation describe-stacks \
--stack-name my-stack-name \
--query 'Stacks[0].Outputs'
Code quality checks
cfn-lint
Run cfn-lint for template validation:
# Validate all templates
cfn-lint templates/*.yml
# Validate a single template
cfn-lint templates/oidc-provider.yml
# Validate with specific configuration
cfn-lint --config .cfnlintrc templates/*.yml
Common issues cfn-lint catches:
- Invalid property names or values
- Missing required properties
- Incorrect intrinsic function usage
- AWS service limits violations
- Region-specific resource availability
Example output:
E3001: Invalid Property Resources/Bucket/Properties/InvalidProperty
templates/storage.yml:10:7
For detailed configuration options, see the cfn-lint reference.
Checkov
Run Checkov for security scanning:
# Scan all templates
checkov -d templates/ --framework cloudformation
# Scan with configuration file
checkov -d templates/ -c .checkov.yml
# Scan a single template
checkov -f templates/storage.yml --framework cloudformation
Security issues Checkov detects:
- Unencrypted S3 buckets
- Publicly accessible resources
- Missing logging and monitoring
- Overly permissive IAM policies
- Insecure network configurations
- Compliance violations (PCI-DSS, HIPAA, SOC2)
Example output:
Check: CKV_AWS_18: "Ensure S3 bucket has server-side encryption enabled"
FAILED for resource: AWS::S3::Bucket.DataBucket
File: /templates/storage.yml:10-15
Suppressing false positives:
Resources:
LogBucket:
# checkov:skip=CKV_AWS_18:Encryption not required for access logs
Type: AWS::S3::Bucket
For detailed configuration options, see the Checkov reference.
Switching environments
Because the deploy script requires an explicit -e flag, you can deploy to multiple accounts in a single session:
./scripts/deploy-templates.sh -e test
./scripts/deploy-templates.sh -e staging
./scripts/deploy-templates.sh -e production
Each command reads from the matching parameters/<environment>/ folder. Keep those files up to date when you introduce new templates.
Working with multiple AWS accounts
Using AWS CLI profiles
Configure profiles in ~/.aws/config:
[profile test-account]
sso_start_url = https://my-org.awsapps.com/start
sso_region = us-east-1
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
[profile production-account]
sso_start_url = https://my-org.awsapps.com/start
sso_region = us-east-1
sso_account_id = 210987654321
sso_role_name = AdministratorAccess
region = us-east-1
Switch profiles:
export AWS_PROFILE=test-account
./scripts/deploy-templates.sh -e test
export AWS_PROFILE=production-account
./scripts/deploy-templates.sh -e production
Rain relies on AWS CLI configuration. Set AWS_PROFILE or AWS_DEFAULT_REGION before running locally.
Using Granted
Granted simplifies multi-account access:
# Install via Homebrew
brew install common-fate/granted/granted
# Configure SSO
granted sso populate
# Assume role
assume test-account
# Deploy to test
./scripts/deploy-templates.sh -e test
# Switch to production
assume production-account
./scripts/deploy-templates.sh -e production
Learn more: Setting up the AWS CLI with AWS SSO
Testing infrastructure
Manual testing
After applying changes, manually verify resources:
# Example: Verify S3 bucket
aws s3 ls s3://my-bucket-name
# Example: Check Lambda function
aws lambda get-function --function-name my-function
# Example: Test IAM role
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/MyRole \
--role-session-name test
# Example: Describe CloudFormation stack
aws cloudformation describe-stacks --stack-name my-stack
Testing template changes safely
When experimenting with large infrastructure changes:
- Create a scratch parameter folder (for example
parameters/lab/) - Run Rain directly against it
- Keep the folder out of version control by adding it to
.gitignore
This lets you iterate freely without affecting shared environments.
Best practices
Before making changes
- Pull latest from main
- Review existing templates for patterns
- Check parameter files for the target environment
- Plan resource naming conventions
During development
- Make small, incremental changes
- Run
cfn-lint templates/*.ymlfrequently - Run
./scripts/validate-templates.shbefore commits - Use Rain changesets to preview changes
- Tag resources appropriately
Before committing
- Run full validation:
./scripts/validate-templates.sh - Review changeset output carefully
- Check for sensitive data in templates (no hardcoded secrets)
- Update parameter files if needed
- Write clear commit messages
After deploying
- Verify resources in AWS Console
- Test functionality manually
- Document any manual steps required
- Update outputs if needed
Next steps
- Understand the CI/CD Workflows for automated deployments
- Learn about Environment management strategies
- Explore the Rain reference for advanced deployment patterns
- Review the Templates reference for template organization