AWS CloudFormation: Infrastructure as Code on AWS

Khimananda Oli 7 min read Database
AWS CloudFormation: Infrastructure as Code on AWS

By Khimananda Oli | Last reviewed: August 2026

Manually provisioning AWS resources leads to configuration drift, security gaps, and failed audits. AWS CloudFormation: Infrastructure as Code on AWS solves this by defining your entire environment in declarative JSON or YAML templates that the service provisions automatically. If you are managing production workloads or preparing for SOC 2 compliance, mastering this native orchestration tool is non-negotiable for reliable, repeatable deployments.

How does AWS CloudFormation: Infrastructure as Code on AWS actually work?

CloudFormation operates on a declarative model: you describe the desired end state, and the service determines the API calls required to achieve it. Unlike imperative scripts where you specify every step, a CloudFormation template simply states "I need an S3 bucket with versioning enabled" or "an RDS instance in a private subnet." The engine builds a dependency graph, provisions independent resources in parallel, and handles rollback if any component fails.

YAML TemplateCloudFormation ServiceDependency GraphRollback LogicVPC + SubnetsEC2 InstancesRDS DatabaseAll resources managed as a single Stack unit
AWS CloudFormation: Infrastructure as Code on AWS processes declarative templates into orchestrated resource stacks with automatic dependency resolution

This atomic behavior is what makes CloudFormation valuable for compliance. When I help teams prepare for ISO 27001 audits, the ability to prove that infrastructure matches a version-controlled template eliminates hours of manual evidence gathering. For teams new to cloud networking fundamentals, understanding this orchestration layer is critical before attempting multi-tier architectures; see my guide on setting up a VPC on AWS for foundational context.

How do you write a production-ready CloudFormation template?

A common mistake is writing monolithic templates that try to define an entire application in one file. Production templates should be modular, parameterized, and use intrinsic functions for flexibility. Always validate locally before deploying to avoid costly rollback cycles.

Core template anatomy

Every template requires specific sections. Here is a minimal but realistic example for a secure S3 bucket with lifecycle policies:

AWSTemplateFormatVersion: '2010-09-09'
Description: Secure S3 bucket with lifecycle rules

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
  BucketPrefix:
    Type: String
    Default: myapp-assets

Resources:
  AppBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${BucketPrefix}-${Environment}-${AWS::AccountId}'
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - Id: TransitionToIA
            Status: Enabled
            Transitions:
              - StorageClass: STANDARD_IA
                TransitionInDays: 30
          - Id: DeleteOldVersions
            Status: Enabled
            NoncurrentVersionExpiration:
              NoncurrentDays: 90

Outputs:
  BucketArn:
    Value: !GetAtt AppBucket.Arn
    Export:
      Name: !Sub '${AWS::StackName}-BucketArn'

Key practices demonstrated above:

  • Never hardcode names. Use !Sub with account IDs and parameters to prevent naming collisions across accounts.
  • Enable exports for cross-stack references. This allows your application stack to consume the bucket ARN without tight coupling.
  • Apply security defaults. The PublicAccessBlockConfiguration should be explicit even if you plan to add a bucket policy later.
  • Define lifecycle rules early. Retroactively adding cost optimization is harder than baking it into the initial template. See my article on reducing your AWS bill for additional tactics.

What are the best practices for managing CloudFormation stacks safely?

Writing templates is only half the battle. Operating stacks in production requires discipline around change management, state protection, and drift detection. I have seen entire production databases deleted because someone ran delete-stack without checking dependencies.

  1. Always use Change Sets first. Never run update-stack directly in production. Generate a change set, review the transformations (especially replacements), then execute. This is your last line of defense against accidental data loss.
  2. Implement Stack Policies. A stack policy is a JSON document attached to the stack that explicitly denies updates to critical resources like RDS instances or EBS volumes. Even if a template change includes a database modification, the policy blocks it at the API level.
  3. Enable Termination Protection. Set EnableTerminationProtection: true on all production stacks. This prevents accidental deletion via CLI or console. You must explicitly disable it before intentional teardowns.
  4. Run Drift Detection Weekly. Manual console changes bypass CloudFormation and create silent failures. Schedule automated drift detection and alert on any MODIFIED or DELETED resource states.
  5. Tag Everything. Include aws:cloudformation:stack-name plus business tags (CostCenter, Owner, ComplianceScope). Tags propagate to most child resources automatically and are essential for chargebacks and audit trails.
Updated TemplateCreate Change SetReview ReplacementsValidate IAM ChangesStack Policy CheckDeny RDS Replace?Block if ProtectedApplyDrift Detection (Weekly) → Alert on MODIFIED Resources
Safe update pattern for AWS CloudFormation: Infrastructure as Code on AWS using change sets, stack policies, and scheduled drift detection

CloudFormation vs Terraform: Which should you choose in 2026?

This is the most frequent question I get from teams adopting Infrastructure as Code with Terraform. Both tools solve the same problem differently, and the right choice depends on your organizational constraints rather than technical superiority.

CriteriaAWS CloudFormationTerraform
State ManagementManaged by AWS (no backend config)Self-managed (S3+DynamoDB, Terraform Cloud)
Multi-Cloud SupportAWS onlyAWS, Azure, GCP, K8s, SaaS providers
New AWS Feature CoverageDay-zero support for all servicesWeeks-to-months lag for new services
Language / SyntaxYAML / JSON (declarative)HCL (domain-specific, more expressive)
Module EcosystemLimited public registryVast Terraform Registry
Drift DetectionBuilt-in, freeRequires plan or paid Cloud tier
Compliance EvidenceNative AWS Config integrationRequires third-party tooling
Learning CurveModerate (verbose YAML)Steeper (HCL + state concepts)

My recommendation: Choose CloudFormation if you are AWS-exclusive, need immediate access to new services, or operate under strict compliance frameworks where managed state reduces audit scope. Choose Terraform if you are multi-cloud, have strong platform engineering capacity to manage state backends securely, or need advanced abstractions like for_each and dynamic blocks that YAML struggles with. Many mature organizations use both: CloudFormation for core platform/networking layers and Terraform for application infrastructure.

How do you handle secrets and sensitive data in CloudFormation?

Never store secrets in template parameters or resource properties. CloudFormation templates are stored in plaintext in S3 and visible in the console history. Instead, integrate with AWS Secrets Manager or SSM Parameter Store and reference them dynamically.

Resources:
  AppSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub '/myapp/${Environment}/db-password'
      GenerateSecretString:
        SecretStringTemplate: '{"username": "admin"}'
        GenerateStringKey: password
        PasswordLength: 32
        ExcludeCharacters: '"@/\'

  DBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      MasterUsername: !Sub '{{resolve:secretsmanager:${AppSecret}:SecretString:username}}'
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${AppSecret}:SecretString:password}}'

The {{resolve:secretsmanager}} syntax fetches values at deployment time without embedding them in the template. For existing secrets created outside CloudFormation, use the full ARN in the resolve directive. This pattern satisfies SOC 2 requirements for secret separation and ensures credentials rotate independently of infrastructure deployments. Pair this with IAM least-privilege policies so only the CloudFormation execution role can read specific secret paths.

CloudFormationTemplate (No Secrets)resolve:secretsmanagerSecrets ManagerEncrypted at RestAuto-RotationKMS-BackedResolved ValueRDS / LambdaReceives Credential❌ NEVER in Template Body
Secure secret handling in AWS CloudFormation: Infrastructure as Code on AWS resolves credentials at deploy time without plaintext exposure

Next Steps for Reliable AWS Infrastructure

AWS CloudFormation: Infrastructure as Code on AWS remains the most tightly integrated IaC solution for AWS-native environments in 2026. Start with small, focused stacks, enforce change sets and stack policies from day one, and integrate secret resolution before your first production deployment. Treat your templates as production code: version them, review them, and test them in isolated accounts before promoting changes. If your team needs help designing compliant, scalable CloudFormation architectures or migrating from manual provisioning, reach out to discuss your infrastructure needs.

Frequently Asked Questions

Yes, the service itself is free. You only pay for the underlying AWS resources like EC2 instances or S3 buckets that your stacks create and manage.

CloudFormation is AWS-native with first-party service support, while Terraform uses HCL and supports multi-cloud. CloudFormation offers deeper integration with AWS features like StackSets and drift detection without external state files.

Templates uploaded directly are limited to 51,200 bytes. Store larger templates in Amazon S3 to increase the limit to 1 megabyte, which accommodates most complex enterprise infrastructure definitions.

Never hardcode secrets. Use AWS Secrets Manager or Systems Manager Parameter Store references. CloudFormation resolves these secure values at runtime, keeping sensitive data out of version control and template bodies.

Yes, many properties support in-place updates. Check the resource documentation for Update requires replacement flags. Use change sets to preview modifications before applying them to avoid unintended resource recreation.

This status indicates a resource failed provisioning during initial creation. Check the Events tab for specific error messages, fix the configuration issue, then delete the failed stack and retry deployment.

Nested stacks break monolithic templates into reusable components with separate lifecycles. Parent stacks reference child templates via URLs, enabling modular architecture and bypassing the 500-resource limit per individual stack.

No, you must initiate drift detection manually through the console or CLI. It compares actual resource configurations against expected template values, identifying unauthorized changes made outside of CloudFormation management.

Use the aws cloudformation validate-template CLI command or cfn-lint for comprehensive static analysis. These tools catch syntax errors, invalid resource properties, and best practice violations before costly deployment failures occur.

Export output values from one stack using the Export field, then import them in another stack with Fn::ImportValue. Note that exported values cannot be modified while other stacks actively reference them.

Yes, specify inline code for small functions or reference S3 buckets for larger packages. Use AWS::Lambda::LayerVersion for shared dependencies and CodeDeploy resources for safe production deployments with traffic shifting.

StackSets deploy stacks across multiple accounts and regions simultaneously. Use them for organizational baselines like security guardrails, logging infrastructure, or standardized networking configurations managed centrally from an administrator account.

Define conditions in the Conditions section using intrinsic functions like Fn::Equals. Apply Condition attributes to resources and outputs to provision them only when specific parameter values or environment contexts match.

Updates disable rollback by default to preserve partial progress. Enable rollback on failure in update settings or use change sets to safely preview impacts before committing modifications to live production environments.

Minimum permissions include cloudformation:CreateStack, UpdateStack, DeleteStack, and DescribeStacks. Additionally grant permissions for every AWS resource type your templates manage, following least-privilege principles using specific resource ARNs.