Skip to main content
This lesson shows how to use CloudFormation’s cfn-init helper to apply the configuration defined in a template’s Metadata to an EC2 instance. Key concepts covered:
  • How to pass stack and region pseudo-parameters into instance UserData using Fn::Base64 + Fn::Sub.
  • Ensuring the --resource you pass to cfn-init matches the logical resource name that contains AWS::CloudFormation::Init (for example, MyInstance).
  • Placing package, service, file, and other configuration under Metadata: AWS::CloudFormation::Init so cfn-init can read and apply it.
Quick example: minimal Resources snippet that calls cfn-init from UserData and installs + starts Apache (httpd):
Resources:
  MyInstance:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true
    Properties:
      ImageId: ami-0eb9d6fc9fab44d24
      IamInstanceProfile: MyCfnInstanceRole
      SecurityGroupIds:
        - !Ref MySecurityGroup
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
How this works
  • UserData is base64-encoded via Fn::Base64. Fn::Sub (or !Sub) injects pseudo-parameters such as ${AWS::StackName} and ${AWS::Region} into the script.
  • cfn-init reads the AWS::CloudFormation::Init metadata for the resource named by --resource (here, MyInstance) and applies that config: installing packages and managing services.
  • IamInstanceProfile must grant the instance permission to call CloudFormation and S3 (if cfn-init needs to fetch files). SecurityGroupIds provides network access (for example, to serve HTTP).
Table of common AWS::CloudFormation::Init sections
SectionPurposeExample
packagesInstall OS packagesyum: { httpd: [] }
filesCreate files with content and permissions"/var/www/html/index.html": { "content": "<h1>Hello</h1>" }
commandsRun arbitrary commands during init01_restart: { command: "systemctl restart httpd" }
servicesManage services (sysvinit/systemd/upstart)sysvinit: { httpd: { enabled: true } }
Parameters example (if you include template parameters):
Parameters:
  MyVPC:
    Type: AWS::EC2::VPC::Id
    Description: Select the VPC to launch the EC2 instance in
Best practices and troubleshooting
  • Make sure the --resource passed to cfn-init exactly matches the logical resource name that contains AWS::CloudFormation::Init. If they differ, cfn-init will not find or apply the configuration.
  • Use -v (verbose) with cfn-init to capture logs in /var/log/cfn-init.log and /var/log/cloud-init.log for troubleshooting.
  • Ensure the instance role (IamInstanceProfile) includes permissions for cloudformation:DescribeStackResource, s3:GetObject (if using S3 artifacts), and other actions required by your init tasks.
Ensure the --resource argument passed to cfn-init matches the logical resource name that has AWS::CloudFormation::Init in the template. If they differ, cfn-init will not find the configuration.
Deployment and verification
  1. Upload the template and create the stack.
  2. CloudFormation will create the security group and the EC2 instance. Wait for the EC2 instance status checks to pass before accessing the instance.
  3. Once status checks are complete, select the instance and copy the Public IPv4 address. Paste that address into a browser to confirm Apache is serving the expected page.
A screenshot of the AWS EC2 Instances console showing a single running
t3.micro instance (i-0827c39cca0f17744) in the us-east-2 region with 3/3
status checks passed. The top bar shows controls like Connect, Instance state,
Actions, and Launch
instances.
You should see the Apache landing page, confirming the packages were installed and the service was started by cfn-init. When CloudFormation finishes creating the stack, the CloudFormation console will display the stack with a CREATE_COMPLETE status.
An AWS CloudFormation Stacks console screen showing one stack named
"DemoStack" with status "CREATE_COMPLETE." The stack's created time is listed
as 2025-07-14 12:18:38
UTC+0400.
Cleanup When the demo is complete, delete the stack using CloudFormation’s “Delete stack” action to remove resources and avoid ongoing charges.
Always delete demo stacks (or confirm resources are torn down) to avoid unexpected AWS charges. Verify the EC2 instance and associated EBS volumes are terminated.
Further reading

Watch Video