Terraform aws_cloudformation_stack: Managing AWS CloudFormation from Terraform

Terraform's aws_cloudformation_stack resource provides a bridge between existing CloudFormation infrastructure and Terraform state management. It allows teams to keep CloudFormation templates for legacy runbooks while gaining Terraform's planning, state, and multi-provider capabilities. The resource can deploy templates from S3, GitHub, local files, or inline JSON, pass parameters, apply tags, and expose CloudFormation outputs for consumption by native Terraform resources.

Why use Terraform to manage CloudFormation stacks

Using Terraform to manage and run your current CloudFormation templates makes it easy to switch to Terraform while keeping your existing infrastructure setup. This is useful when you have completed runbooks in CloudFormation and are seeking an easier way to migrate existing CloudFormation scripts to HCP Terraform. This approach allows for a smoother transition while maintaining the integrity of your current infrastructure setup.

The pattern is especially valuable during migrations. Plan your migration. If you are moving from CloudFormation to Terraform, import existing stacks first and then gradually replace them with native Terraform resources. Managing CloudFormation stacks through Terraform gives you the best of both worlds. You keep your existing CloudFormation templates and gain Terraform's state management, planning, and multi-provider capabilities. Whether you are maintaining legacy templates or deploying stack sets across an organization, Terraform handles CloudFormation stacks cleanly and predictably.

Prerequisites and provider configuration

You will need:
- Terraform 1.0 or later
- AWS CLI configured with appropriate permissions
- Familiarity with both CloudFormation template syntax and Terraform HCL

Provider configuration used in examples:

```hcl
terraform {
requiredversion = ">= 1.0"
required
providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}

provider "aws" {
region = "us-east-1"
}
```

Another common provider setup is:

hcl provider "aws" { region = "us-west-2" }

For provider version pinning with CloudFormation integration:

hcl terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } }

Deploying a CloudFormation stack from S3 or remote URL

Suppose you have a CloudFormation template stored in an S3 bucket. You can use the aws_cloudformation_stack resource in Terraform to deploy the template.

S3 template URL example

```hcl
provider "aws" {
region = "us-west-2"
}

resource "awscloudformationstack" "example" {
name = "example-stack"
template_url = "https://my-bucket.s3.amazonaws.com/templates/my-template.yaml"
parameters = {
ParameterKey = "ParameterValue"
}
tags = {
Name = "example-stack"
}
}
```

GitHub template URL example

```hcl
provider "aws" {
region = "us-west-2"
}

resource "awscloudformationstack" "example" {
name = "example-stack"
template_url = "https://raw.githubusercontent.com/my-repo/cloudformation-templates/main/templates/my-template.yaml"
parameters = {
ParameterKey = "ParameterValue"
}
tags = {
Name = "example-stack"
}
}
```

Using template_url avoids checking large templates into the Terraform working directory and works well for runbooks already published to S3 or version control.

Deploying from local files with template_body

When templates live alongside Terraform code, template_body is used.

Basic S3 bucket stack

```hcl
variable "bucket_name" {
default = "my-bucket-from-cfn"
}

resource "awscloudformationstack" "s3bucketstack" {
name = "s3-bucket-stack"
templatebody = file("${path.module}/s3bucket.yaml")
parameters = {
BucketName = var.bucket_name
}
}

output "s3bucketname" {
value = awscloudformationstack.s3bucketstack.outputs.MyS3Bucket
}
```

Explanation:
- CloudFormation Template: This template defines a simple S3 bucket resource.
- Terraform Code:
- aws_cloudformation_stack resource: This resource deploys the CloudFormation stack.
- template_body: Specifies the path to the CloudFormation template file.
- parameters: Passes values to the parameters defined in the CloudFormation template.
- Output: Displays the S3 bucket name created by the CloudFormation stack.

Running the code:
- Save the CloudFormation template as s3_bucket.yaml and the Terraform code as main.tf.
- Run terraform init to initialize the Terraform working directory.
- Run terraform apply to deploy the infrastructure.

This example shows how to integrate CloudFormation within a Terraform workflow.

EC2 instance stack with parameters

hcl resource "aws_cloudformation_stack" "ec2_instance" { name = "ec2-instance-stack" parameters = { KeyName = var.key_name InstanceType = var.instance_type } template_body = file("${var.cf_file}") tags = tomap({"Name" = "CF-EC2-Stack"}) }

Terraform will perform the following actions for a similar configuration:

```

awscloudformationstack.ec2_instance will be created

  • resource "awscloudformationstack" "ec2instance" {
    • id = (known after apply)
    • name = "ec2-instance-stack"
    • outputs = (known after apply)
    • parameters = {
      • "InstanceType" = "t2.micro"
      • "KeyName" = "owntest"


        }
    • policy
    body = (known after apply)
  • tags = {
    • "Name" = "CF-EC2-Stack"


      }
  • tagsall = {
    • "Name" = "CF-EC2-Stack"


      }
  • templatebody = <<-EOT

    Date: 08/12/2021

Author: Yousaf K Hamza

Description: CloudForamtion Stack creation with EC2 instance and Security Group in a YAML Format

----------------------------------------------------

CloudFormation Stack in YAML

----------------------------------------------------

AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample:
Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based
on the region in which the stack is run. This example creates an EC2 security group
for the instance to give you SSH access. WARNING This template creates an Amazon
EC2 instance
```

This is a combined infrastructure deployment using AWS CloudFormation and Terraform.

Inline CloudFormation templates with jsonencode

The aws_cloudformation_stack resource lets you deploy a CloudFormation template directly from Terraform.

```hcl
resource "awscloudformationstack" "vpc_stack" {
name = "my-vpc-stack"

template_body = jsonencode({
AWSTemplateFormatVersion = "2010-09-09"
Description = "VPC created via CloudFormation managed by Terraform"
Parameters = {
VpcCidr = {
Type = "String"
Default = "10.0.0.0/16"
Description = "CIDR block for the VPC"
}
}
Resources = {
MyVPC = {
Type = "AWS::EC2::VPC"
Properties = {
CidrBlock = { Ref = "VpcCidr" }
EnableDnsSupport = true
EnableDnsHostnames = true
Tags = [
{
Key = "Name"
Value = "terraform-managed-cfn-vpc"
}
]
}
}
}
Outputs = {
VpcId = {
Description = "The VPC ID"
Value = { Ref = "MyVPC" }
}
}
})

parameters = {
VpcCidr = "10.0.0.0/16"
}

tags = {
ManagedBy = "Terraform"
}
}
```

Access CloudFormation outputs via aws_cloudformation_stack.vpc_stack.outputs.

Referencing CloudFormation outputs in Terraform

CloudFormation stack outputs are the bridge between your CloudFormation resources and the rest of your Terraform configuration.

One powerful pattern is creating resources in CloudFormation and then referencing their outputs in Terraform.

```hcl
resource "awscloudformationstack" "databasestack" {
name = "database-stack"
template
body = file("${path.module}/templates/database.yaml")
parameters = {
DBInstanceClass = "db.r5.large"
DBName = "myapp"
}
capabilities = ["CAPABILITY_IAM"]
}

resource "awsssmparameter" "dbendpoint" {
name = "/myapp/database/endpoint"
type = "String"
value = aws
cloudformationstack.databasestack.outputs["DBEndpoint"]
}

resource "awsecstaskdefinition" "app" {
family = "my-app"
container
definitions = jsonencode([
{
name = "app"
image = "my-app:latest"
environment = [
{
name = "DBHOST"
value = aws
cloudformationstack.databasestack.outputs["DBEndpoint"]
}
]
}
])
}
```

Store templates in version control. Keep your CloudFormation YAML/JSON templates alongside your Terraform code.

Importing existing CloudFormation stacks

If you have existing CloudFormation stacks that you want Terraform to manage, you can import them.

hcl resource "aws_cloudformation_stack" "existing_stack" { name = "my-existing-stack" template_body = file("${path.module}/templates/existing.yaml") }

Then run the import command:
terraform import aws_cloudformation_stack.existing_stack my-existing-stack

Importing existing stacks first preserves history and allows gradual replacement with native Terraform resources.

Capabilities, timeouts and stack sets

Always specify capabilities. If your template creates IAM resources, you must include the appropriate capabilities or the stack creation will fail.

hcl capabilities = ["CAPABILITY_IAM"]

Set appropriate timeouts. Complex stacks can take a while to create. Set timeout_in_minutes to avoid unnecessary failures.

Use stack sets for multi-account. If you are working with AWS Organizations, stack sets are the right tool for deploying consistent configurations across accounts.

Example stack set configuration fragment:

hcl organizational_unit_ids = ["ou-abc123def456"] stack_set_instance_region = "us-east-1"

Resource attributes comparison

Attribute Purpose Typical Usage
name CloudFormation stack name example-stack, ec2-instance-stack
template_url Remote template location S3 URL or GitHub raw URL
template_body Inline or file template content file("${path.module}/s3_bucket.yaml") or jsonencode({...})
parameters Map of parameter name to value InstanceType = "t2.micro"
tags Resource tags applied to stack Name = "CF-EC2-Stack"
capabilities Required capabilities for IAM ["CAPABILITY_IAM"]
outputs Read-only map of stack outputs aws_cloudformation_stack.database_stack.outputs["DBEndpoint"]

Best practices for mixed Terraform-CloudFormation workflows

  • Store templates in version control. Keep your CloudFormation YAML/JSON templates alongside your Terraform code.
  • Set appropriate timeouts. Complex stacks can take a while to create. Set timeout_in_minutes to avoid unnecessary failures.
  • Always specify capabilities. If your template creates IAM resources, you must include the appropriate capabilities or the stack creation will fail.
  • Use stack sets for multi-account. If you are working with AWS Organizations, stack sets are the right tool for deploying consistent configurations across accounts.
  • Plan your migration. If you are moving from CloudFormation to Terraform, import existing stacks first and then gradually replace them with native Terraform resources.

This guide covers the full spectrum - from simple stacks to stack sets for multi-account deployments.

Conclusion

Managing CloudFormation stacks through Terraform gives you the best of both worlds. You keep your existing CloudFormation templates and gain Terraform's state management, planning, and multi-provider capabilities. Whether you are maintaining legacy templates or deploying stack sets across an organization, Terraform handles CloudFormation stacks cleanly and predictably.

The aws_cloudformation_stack resource supports template deployment via template_url for S3 and GitHub sources, via template_body for local files and inline JSON, parameter passing, tagging, capabilities handling, and output consumption. Combined with import workflows and stack set support, teams can migrate incrementally from CloudFormation runbooks to Terraform without rewriting infrastructure immediately.

For more on managing AWS infrastructure with Terraform, see our guide on creating Organizations and SCPs in Terraform.

Sources

  1. HashiCorp Validated Patterns
  2. GitHub CloudFormation Stack Deployment Through Terraform
  3. Nulldog Using CloudFormation Files in Terraform
  4. OneUptime Create CloudFormation Stacks in Terraform

Related Posts