AppConfig Integration with AWS SAM and CloudFormation for Infrastructure as Code#
Imagine you’re managing a growing microservices platform where configuration changes happen frequently—feature flags get toggled, database connection strings rotate, and experiment parameters need adjustment without requiring code deployments. If you’re manually creating and updating AWS AppConfig resources through the console, you’re missing a critical opportunity to treat your entire infrastructure, including configurations, as code.
AppConfig is AWS’s fully managed service for safely deploying application configurations across your infrastructure with built-in validation, deployment strategies, and rollback capabilities. The real power emerges when you integrate it with Infrastructure as Code tools like CloudFormation and AWS SAM. This integration lets you version control your entire deployment story—not just your Lambda functions or containers, but the configurations that guide their behavior at runtime.
In this guide, we’ll explore how to manage AppConfig resources through CloudFormation and AWS SAM, automating configuration deployments as part of your infrastructure pipeline and ensuring your configuration changes are trackable, reproducible, and reversible.
Understanding AppConfig Architecture and Its IaC Benefits#
Before diving into templates, let’s establish what AppConfig actually manages. At its core, AppConfig organizes configurations hierarchically: you create an Application, within which live Environments (like development, staging, production), and within each Environment sit Configuration Profiles that define the actual configuration data.
When you manually create these through the console, you’re performing undocumented infrastructure changes. Your team members can’t easily see what changed or when, rolling back requires remembering exact values, and new team members have no reference for how your configuration management is supposed to work. Infrastructure as Code solves this problem elegantly.
With CloudFormation, every AppConfig resource becomes a declarative statement in a version-controlled template. When you want to update a feature flag’s value or create a new environment, you modify the template and deploy it. The entire history lives in your Git repository, and rollbacks are as simple as reverting a commit.
CloudFormation Support for AppConfig Resources#
CloudFormation provides comprehensive support for AppConfig through several resource types that map directly to the AppConfig API model. Understanding which resources are available and how they connect is fundamental to building effective IaC configurations.
The AWS::AppConfig::Application resource represents your top-level namespace. It’s straightforward—you give it a name and optional description. This is where everything else lives.
The AWS::AppConfig::Environment resource creates an environment within your application. Think of this as a logical grouping for your configurations—development, staging, or production environments each get their own. Environments can have deployment target filters if you’re using on-premises integrations.
The AWS::AppConfig::ConfigurationProfile resource defines where your actual configuration data lives. This is a critical resource because it determines the source of truth for your configuration—it could be inline (stored directly in AppConfig), stored in Parameter Store, or even in Secrets Manager. The profile also allows you to specify a JSON Schema for validation, ensuring that any configuration update conforms to your expected structure before it ever reaches your applications.
The AWS::AppConfig::DeploymentStrategy resource codifies your deployment approach. Rather than hardcoding deployment behavior when you push configurations, you define it once in your infrastructure template. You can specify how quickly the configuration should deploy to targets, growth rates for gradual rollouts, and bake time between deployment segments—all captured as infrastructure.
Finally, the AWS::AppConfig::Deployment resource actually deploys a configuration profile to an environment using a deployment strategy. This is where the rubber meets the road: you’re pushing a specific version of your configuration to specific targets following a defined strategy.
There’s also AWS::AppConfig::HostedConfigurationVersion for storing configuration data directly in AppConfig, and AWS::AppConfig::Extension and AWS::AppConfig::ExtensionAssociation for attaching behaviors to AppConfig events—though these are more advanced patterns.
Building a Practical CloudFormation Template#
Let’s construct a real-world example. Suppose you’re building a SaaS application with a feature management system backed by AppConfig. You need environments for development and production, a configuration profile that stores your feature flags, and a deployment strategy that rolls out changes gradually in production while deploying immediately to development.
Here’s a CloudFormation template that implements this:
AWSTemplateFormatVersion: '2010-09-09'
Description: AppConfig setup for feature flag management
Parameters:
Environment:
Type: String
Default: development
AllowedValues:
- development
- production
Description: The deployment environment
Resources:
# Create the AppConfig Application
FeatureFlagApp:
Type: AWS::AppConfig::Application
Properties:
Name: my-saas-app
Description: Feature flag configuration management
# Create development environment
DevelopmentEnv:
Type: AWS::AppConfig::Environment
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: development
Description: Development environment for feature flags
# Create production environment
ProductionEnv:
Type: AWS::AppConfig::Environment
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: production
Description: Production environment for feature flags
# Define configuration profile for feature flags
FeatureFlagProfile:
Type: AWS::AppConfig::ConfigurationProfile
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: feature-flags
Description: Feature flag configuration stored in Parameter Store
LocationUri: 'ssm-parameter://my-app/feature-flags'
ValidatorTokens:
- Type: JSON_SCHEMA
Content: !Sub |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"newCheckoutFlow": { "type": "boolean" },
"analyticsV2": { "type": "boolean" },
"betaPricing": { "type": "boolean" }
},
"required": ["newCheckoutFlow", "analyticsV2", "betaPricing"]
}
# Gradual deployment strategy for production
ProductionDeploymentStrategy:
Type: AWS::AppConfig::DeploymentStrategy
Properties:
Name: prod-gradual-rollout
Description: Gradual rollout for production
DeploymentDurationInMinutes: 30
FinalBakeTimeInMinutes: 10
GrowthFactor: 25
GrowthType: Linear
# Immediate deployment strategy for development
DevelopmentDeploymentStrategy:
Type: AWS::AppConfig::DeploymentStrategy
Properties:
Name: dev-immediate
Description: Immediate deployment for development
DeploymentDurationInMinutes: 0
FinalBakeTimeInMinutes: 0
GrowthFactor: 100
GrowthType: Linear
Outputs:
ApplicationId:
Description: AppConfig Application ID
Value: !Ref FeatureFlagApp
Export:
Name: !Sub '${AWS::StackName}-AppId'
FeatureFlagProfileId:
Description: Configuration Profile ID
Value: !Ref FeatureFlagProfile
Export:
Name: !Sub '${AWS::StackName}-ProfileId'This template creates a complete AppConfig infrastructure. Notice that the configuration profile points to an SSM Parameter Store location—ssm-parameter://my-app/feature-flags. This means your actual feature flag JSON lives in Parameter Store, and AppConfig manages deployments of that configuration to your applications. The JSON Schema validator ensures that any configuration update must match the expected structure before deployment proceeds.
The deployment strategies encapsulate your operational philosophy: development environments deploy immediately (no risk, fast feedback), while production uses a gradual rollout over 30 minutes with 25% growth between segments and a 10-minute bake time before moving to the next segment.
Integrating with AWS SAM#
AWS SAM (Serverless Application Model) is CloudFormation’s purpose-built extension for serverless workloads. It simplifies Lambda function definitions and integrates seamlessly with other AWS resources. When you’re using Lambda-based applications, SAM makes it natural to define your AppConfig infrastructure alongside your application code.
SAM’s real advantage is that it abstracts common patterns and generates CloudFormation underneath. For AppConfig, this means you can define your configuration infrastructure using the same CloudFormation resource types, but within a SAM template that also handles your functions, API Gateways, and other serverless components—all in one coherent file.
Here’s how you’d structure a SAM template that deploys a Lambda function configured to use AppConfig:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2013-12-31
Description: Serverless application with AppConfig integration
Globals:
Function:
Runtime: python3.11
Timeout: 30
Environment:
Variables:
APPCONFIG_APPLICATION: !Ref FeatureFlagApp
APPCONFIG_ENVIRONMENT: !Ref ProductionEnv
APPCONFIG_PROFILE: !Ref FeatureFlagProfile
Resources:
# AppConfig Application
FeatureFlagApp:
Type: AWS::AppConfig::Application
Properties:
Name: serverless-feature-flags
Description: Feature flags for serverless app
# Production Environment
ProductionEnv:
Type: AWS::AppConfig::Environment
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: production
# Configuration Profile pointing to Parameter Store
FeatureFlagProfile:
Type: AWS::AppConfig::ConfigurationProfile
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: feature-flags
LocationUri: 'ssm-parameter://serverless-app/flags'
ValidatorTokens:
- Type: JSON_SCHEMA
Content: !Sub |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"enableNewFeature": { "type": "boolean" },
"maxRetries": { "type": "integer", "minimum": 1 }
}
}
# Deployment Strategy
ProdStrategy:
Type: AWS::AppConfig::DeploymentStrategy
Properties:
Name: prod-safe-rollout
DeploymentDurationInMinutes: 15
FinalBakeTimeInMinutes: 5
GrowthFactor: 20
GrowthType: Linear
# Lambda Function
ProcessorFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.lambda_handler
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- appconfig:GetConfiguration
Resource: '*'
- Effect: Allow
Action:
- ssm:GetParameter
Resource: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/serverless-app/flags'
# API Gateway to trigger the function
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: prod
TracingEnabled: true
ApiIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref ApiGateway
IntegrationType: AWS_PROXY
IntegrationUri: !Sub 'arn:aws:apigatewayv2:${AWS::Region}:lambda:path/2015-03-31/functions/${ProcessorFunction.Arn}/invocations'
Outputs:
ApplicationId:
Value: !Ref FeatureFlagApp
EnvironmentId:
Value: !Ref ProductionEnv
ProfileId:
Value: !Ref FeatureFlagProfileThe power here is that your entire serverless stack—function, API, permissions, and configuration infrastructure—lives in one SAM template. When you deploy this, you’re establishing the complete runtime environment your Lambda function expects to operate within.
Handling Configuration Updates Without Code Changes#
The whole premise of AppConfig is that you can change configuration without redeploying code. Through CloudFormation and SAM, you extend this principle further: you can make configuration changes by updating your infrastructure template.
Let’s say you want to enable a new feature flag or adjust a feature’s rollout percentage. You’d modify your template’s Parameter Store value or AppConfig configuration, then deploy the template update. Here’s a practical workflow:
First, you create a Parameter Store parameter that contains your feature flags:
aws ssm put-parameter \
--name /serverless-app/flags \
--type String \
--value '{
"enableNewCheckout": false,
"analyticsV2Rollout": 10,
"betaPricingEnabled": false
}' \
--overwriteYour CloudFormation template references this parameter through the AppConfig configuration profile’s LocationUri. When you want to change a flag value, you update the parameter:
aws ssm put-parameter \
--name /serverless-app/flags \
--type String \
--value '{
"enableNewCheckout": true,
"analyticsV2Rollout": 10,
"betaPricingEnabled": false
}' \
--overwriteThen you deploy a new AppConfig deployment to push this configuration to your targets:
aws appconfig create-deployment \
--application-id <app-id> \
--environment-id <env-id> \
--configuration-profile-id <profile-id> \
--configuration-version <version> \
--deployment-strategy-id <strategy-id>But here’s where infrastructure as code becomes even more powerful: you can automate this entire workflow in your CloudFormation template. You can use custom resources or even Lambda-based CloudFormation hooks to create deployments as part of your stack updates.
More practically, you might structure your template to create a new HostedConfigurationVersion each time you deploy:
FeatureFlagVersion:
Type: AWS::AppConfig::HostedConfigurationVersion
Properties:
ApplicationId: !Ref FeatureFlagApp
ConfigurationProfileId: !Ref FeatureFlagProfile
Description: Feature flag configuration v1
Content: |
{
"enableNewCheckout": true,
"analyticsV2Rollout": 25,
"betaPricingEnabled": false
}
ContentType: application/json
FeatureFlagDeployment:
Type: AWS::AppConfig::Deployment
Properties:
ApplicationId: !Ref FeatureFlagApp
ConfigurationProfileId: !Ref FeatureFlagProfile
EnvironmentId: !Ref ProductionEnv
DeploymentStrategyId: !Ref ProdStrategy
ConfigurationVersion: !Ref FeatureFlagVersionWith this approach, every configuration change is captured in your CloudFormation template version history. You deploy your configuration changes through the same pipeline as your code—reviewed, tested, and tracked.
Feature Flag Rollouts Through Infrastructure Updates#
Feature flags are a particularly powerful use case for AppConfig integrated with infrastructure as code. You can model sophisticated rollout strategies entirely in your CloudFormation templates, adjusting who gets which features through infrastructure updates rather than code changes.
Imagine you’re rolling out a new checkout experience. You want to gradually increase the percentage of users who see it, starting at 5%, moving to 20%, then 50%, and finally 100%. Rather than making these changes manually or coding them into your application, you control them through AppConfig deployment strategies and configuration versions.
Here’s how you’d structure this:
CheckoutFeatureProfile:
Type: AWS::AppConfig::ConfigurationProfile
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: checkout-rollout
LocationUri: 'hosted'
ValidatorTokens:
- Type: JSON_SCHEMA
Content: |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"newCheckoutEnabled": { "type": "boolean" },
"rolloutPercentage": { "type": "integer", "minimum": 0, "maximum": 100 }
},
"required": ["newCheckoutEnabled", "rolloutPercentage"]
}
# Phase 1: 5% rollout
CheckoutV1:
Type: AWS::AppConfig::HostedConfigurationVersion
Properties:
ApplicationId: !Ref FeatureFlagApp
ConfigurationProfileId: !Ref CheckoutFeatureProfile
Description: Initial 5% rollout
Content: |
{
"newCheckoutEnabled": true,
"rolloutPercentage": 5
}
ContentType: application/json
CheckoutDeploymentPhase1:
Type: AWS::AppConfig::Deployment
Properties:
ApplicationId: !Ref FeatureFlagApp
ConfigurationProfileId: !Ref CheckoutFeatureProfile
EnvironmentId: !Ref ProductionEnv
DeploymentStrategyId: !Ref GradualRollout
ConfigurationVersion: !Ref CheckoutV1
# When you're ready to proceed, update CheckoutV2 and CheckoutDeploymentPhase2
CheckoutV2:
Type: AWS::AppConfig::HostedConfigurationVersion
Properties:
ApplicationId: !Ref FeatureFlagApp
ConfigurationProfileId: !Ref CheckoutFeatureProfile
Description: Expand to 20% rollout
Content: |
{
"newCheckoutEnabled": true,
"rolloutPercentage": 20
}
ContentType: application/jsonYour applications retrieve the configuration from AppConfig and make decisions based on the rollout percentage. When you’re confident in the feature, you update the template to increase the percentage, triggering another deployment that follows your deployment strategy’s gradual rollout pattern.
If something goes wrong during the rollout, you don’t need to debug application code or restart services. You simply revert your CloudFormation stack to a previous version, which automatically rolls back the configuration to its prior state. The entire audit trail lives in your CloudFormation stack events.
Practical Application: Connecting Lambda to AppConfig#
When your Lambda functions need to read from AppConfig, you use the AppConfig Data Plane API. SAM and CloudFormation handle the infrastructure setup; your application code handles the retrieval and caching strategy.
Here’s a Python example that shows how your Lambda function would retrieve configuration from AppConfig:
import json
import boto3
import os
from datetime import datetime, timedelta
appconfig = boto3.client('appconfig')
# Configuration cache
config_cache = {
'data': None,
'expires_at': None
}
def get_feature_flags():
"""Retrieve feature flags from AppConfig with local caching"""
now = datetime.utcnow()
# Return cached config if still valid
if config_cache['data'] and config_cache['expires_at'] > now:
return config_cache['data']
# Fetch fresh configuration
response = appconfig.get_configuration(
Application=os.environ['APPCONFIG_APPLICATION'],
Environment=os.environ['APPCONFIG_ENVIRONMENT'],
ConfigurationProfile=os.environ['APPCONFIG_PROFILE'],
ClientId='my-lambda-function'
)
config = json.loads(response['Content'].read())
# Cache for 60 seconds
config_cache['data'] = config
config_cache['expires_at'] = now + timedelta(seconds=60)
return config
def lambda_handler(event, context):
"""Main Lambda handler using feature flags from AppConfig"""
try:
flags = get_feature_flags()
if flags.get('enableNewCheckout', False):
# Use new checkout flow
result = process_with_new_checkout()
else:
# Use legacy checkout flow
result = process_with_legacy_checkout()
return {
'statusCode': 200,
'body': json.dumps(result)
}
except Exception as e:
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
def process_with_new_checkout():
return {'message': 'Using new checkout experience'}
def process_with_legacy_checkout():
return {'message': 'Using legacy checkout experience'}Notice the caching strategy. AppConfig API calls have costs and latency implications, so your functions should cache the configuration locally for short periods (seconds to minutes depending on your tolerance for stale data). When AppConfig receives a configuration change through your deployment, existing Lambda instances continue using cached data until the cache expires. New invocations after cache expiration fetch the updated configuration.
Your Lambda’s IAM role needs permission to call appconfig:GetConfiguration. The SAM template earlier showed this in the Policies section:
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- appconfig:GetConfiguration
Resource: '*'With both the infrastructure defined in SAM and the application code retrieving configurations correctly, your entire feature management system works without code deployments.
Managing Configuration Validation#
One of AppConfig’s strengths is built-in validation before configurations reach your applications. Through CloudFormation, you define validators as part of your configuration profile, ensuring that only valid configurations get deployed.
JSON Schema is the most common validator. By specifying a schema in your ConfigurationProfile, AppConfig validates any new configuration version against that schema before allowing deployment:
ValidatorTokens:
- Type: JSON_SCHEMA
Content: |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"featureEnabled": { "type": "boolean" },
"maxConnections": {
"type": "integer",
"minimum": 1,
"maximum": 1000
},
"retryPolicy": {
"type": "object",
"properties": {
"maxAttempts": { "type": "integer" },
"backoffMultiplier": { "type": "number" }
},
"required": ["maxAttempts", "backoffMultiplier"]
}
},
"required": ["featureEnabled", "maxConnections"]
}This schema enforces that any configuration update must include a boolean featureEnabled and an integer maxConnections between 1 and 1000. It also allows an optional nested retryPolicy object with specific required properties. If someone tries to deploy a configuration that doesn’t conform, AppConfig rejects it before it ever reaches your applications.
For critical applications, you can also use AWS Lambda validators—custom validation logic run by AppConfig during deployment. You’d define this through a CloudFormation extension, allowing you to perform complex business logic validation that goes beyond JSON Schema.
Orchestrating Multi-Environment Deployments#
When working with multiple environments, CloudFormation’s stack parameters and conditions enable elegant deployment orchestration. You can use the same template to deploy different AppConfig configurations to development, staging, and production environments with appropriate deployment strategies for each.
Here’s a pattern that leverages this:
Parameters:
DeploymentEnvironment:
Type: String
AllowedValues:
- development
- staging
- production
Conditions:
IsProduction: !Equals [!Ref DeploymentEnvironment, production]
IsStaging: !Equals [!Ref DeploymentEnvironment, staging]
Resources:
ImmediateStrategy:
Type: AWS::AppConfig::DeploymentStrategy
Condition: IsProduction
Properties:
Name: immediate-deployment
DeploymentDurationInMinutes: 0
GrowthFactor: 100
GradualStrategy:
Type: AWS::AppConfig::DeploymentStrategy
Condition: IsProduction
Properties:
Name: gradual-rollout
DeploymentDurationInMinutes: 30
FinalBakeTimeInMinutes: 10
GrowthFactor: 20
ProdEnvironment:
Type: AWS::AppConfig::Environment
Condition: IsProduction
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: production
StagingEnvironment:
Type: AWS::AppConfig::Environment
Condition: IsStaging
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: staging
DevEnvironment:
Type: AWS::AppConfig::Environment
Properties:
ApplicationId: !Ref FeatureFlagApp
Name: developmentBy parameterizing the environment, you can deploy the same template three times—once for each environment—and CloudFormation creates the appropriate resources and deployment strategies for each. Production gets gradual rollout with bake times, staging gets faster deployments, and development gets immediate changes.
Deployment Best Practices for AppConfig and IaC#
When adopting AppConfig with CloudFormation and SAM, certain practices lead to more maintainable, reliable systems. First, always use configuration profiles that reference external sources (Parameter Store, Secrets Manager) rather than embedding all configuration in your CloudFormation template. This keeps configurations flexible and allows updates without full stack redeployments.
Second, version your configuration profiles explicitly. Use HostedConfigurationVersion resources with descriptive descriptions that document what changed and why. When something goes wrong, you can pinpoint exactly which configuration version caused the issue and roll back with precision.
Third, implement gradual deployment strategies in production environments, even when you’re confident in changes. The extra 15-30 minutes for a configuration rollout is negligible compared to the blast radius of a misconfigured parameter hitting all your users simultaneously.
Fourth, use JSON Schema validators liberally. The upfront cost of writing good schemas pays dividends by catching configuration errors before they reach production. Consider schemas as contracts between your configuration and your application code.
Finally, integrate AppConfig deployments into your broader CI/CD pipeline. Rather than manually updating configurations, have your deployment pipeline validate changes against schemas, run integration tests with new configurations, and only deploy to production after human approval. CloudFormation stack policies and change sets support this workflow well.
Monitoring and Troubleshooting AppConfig Deployments#
CloudFormation’s integration with CloudWatch and CloudTrail means your AppConfig deployments get automatically logged and monitorable. Every configuration deployment appears in CloudTrail, showing who initiated it, when, and what changed.
You can create CloudWatch alarms that trigger when AppConfig deployments fail or take longer than expected:
DeploymentFailureAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: appconfig-deployment-failure
MetricName: DeploymentFailure
Namespace: AWS/AppConfig
Statistic: Sum
Period: 300
EvaluationPeriods: 1
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
AlarmActions:
- !Ref AlertTopicWhen troubleshooting configuration issues, check the AppConfig deployment history through the AWS console or CLI:
aws appconfig list-deployments \
--application-id <app-id> \
--environment-id <env-id>This shows deployment state, growth factor, and completion percentage. If a deployment is stuck in progress, you can initiate a rollback to the previous configuration version.
Conclusion#
Integrating AppConfig with CloudFormation and AWS SAM represents a maturation of your infrastructure management practice. Rather than treating configuration as a runtime concern managed separately from your infrastructure, you elevate it to a first-class infrastructure component—versioned, validated, tracked, and deployable through the same pipeline as your code.
The benefits compound: your configurations become auditable, your deployments become reproducible, your rollbacks become safe, and your entire operational knowledge about how the system should be configured lives in version control alongside your code. Feature flag rollouts, configuration updates, and infrastructure changes all follow the same pattern—declare desired state in your templates, validate through CloudFormation’s change sets, deploy with confidence.
Start by capturing your current AppConfig resources in a CloudFormation template, then gradually automate more of your configuration management. Integrate Lambda functions that read from AppConfig, use JSON Schema validators to enforce contracts, and leverage deployment strategies to manage risk. As your comfort with this approach grows, you’ll find yourself thinking about configuration changes differently—not as manual updates, but as infrastructure changes that follow the same rigor and accountability as any other part of your system.