What are Cycle errors?
Cycle errors are instances of circular logic in the Terraform dependency tree. Terraform analyzes the dependencies between resources in your infrastructure configuration to determine the order to perform your operations.
In this article, I am going to show you how Cycle error occurs when aws_security_group resources reference one another in their security_groups attributes. AWS cannot create the security groups because their configurations each reference the other group, which would not exist yet.
How to fix the Cycle error?
- The fix for this Cycle error is to remove the mutually dependent security group rules in your configuration, leaving the two group resources without ingress attributes.
- Instead of including the rules in the aws_security_group configuration, use the aws_security_group_rule resource and reference the security group IDs instead.
- This avoids a cycle error because the provider will have AWS create both of the aws_security_group resources first, without interdependent rules.
- It will create the rules next, and attach the rules to the groups last.
Please visit my GitHub Repository for Terraform articles on various topics being updated on constant basis.
Let’s get started!
Objectives:
1. Login to AWS Management Console
2. Create infrastructure for resources block
3. Under terraform_files resources directory - Create 4 files - main.tf
, variables.tf
, outputs.tf
and terrafprm.tfvars
.
4. Initialize Your Working Directory
5. Fix the Cycle Error
6. Deploy Your Resources
Pre-requisites:
- AWS user account with admin access, not a root account.
- Cloud9 IDE with AWS CLI.
Resources Used:
Terraform documentation.
Terraform documentation for AMI.
Troubleshoot Terraform - Correct a cycle error
learn-terraform-troubleshooting
Steps for implementation to this project:
1. Login to AWS Management Console
- Make sure you're in the N. Virginia (us-east-1) region
2. Create infrastructure for resources block
- Let’s create the following organizational structure as shown below.
3. Under terraform_files resources directory - Create 4 files - main.tf
, variables.tf
, outputs.tf
and terrafprm.tfvars
.
1. main.tf
substitute vpc_id with your own VPC
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.23"
}
}
required_version = ">= 0.14.9"
}
provider "aws" {
region = var.region
}
data "aws_ami" "linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "web_app" {
for_each = local.security_groups
ami = data.aws_ami.linux.id
availability_zone = var.az_1a
instance_type = var.instance_type
vpc_security_group_ids = [each.value]
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "${var.name}-mywebapp-${each.key}"
}
}
resource "aws_security_group" "sg_ping" {
name = "Allow Ping"
vpc_id = "<DUMMY VALUE>"
ingress {
from_port = -1
to_port = -1
protocol = "icmp"
security_groups = [aws_security_group.sg_8080.id]
}
}
resource "aws_security_group" "sg_8080" {
name = "Allow 8080"
vpc_id = "<DUMMY VALUE>"
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.sg_ping.id]
}
}
locals {
security_groups = {
sg_ping = aws_security_group.sg_ping.id,
sg_8080 = aws_security_group.sg_8080.id,
}
- 2. variables.tf
variable "region" {
description = "region"
}
variable "name" {
description = "Value of the Name tag for the EC2 instance"
}
variable "az_1a" {
description = "availability zone 1"
type = string
default = "us-east-1a"
}
variable "instance_type" {
description = "Value of the Name tag for the EC2 instance type"
type = string
default = "t2.micro"
}
- 3. outputs.tf
output "instance_id" {
description = "ID of the EC2 instance"
value = [for instance in aws_instance.web_app: instance.id]
}
output "instance_public_ip" {
description = "Public IP address of the EC2 instance"
value = [for instance in aws_instance.web_app: instance.public_ip]
}
output "instance_name" {
description = "Tags of the EC2 instance"
value = [for instance in aws_instance.web_app: instance.tags.Name]
}
- 4. terrafprm.tfvars
name = "rev"
region = "us-east-1"
4. Initialize Your Working Directory
cd terraform_files
- Terraform format
terraform fmt
- Initiate the working directory
terraform init
- Validate the configuration
terraform validate
- get a cycle error for the security groups sg_8080 and sg_ping.
- This cycle error you get due to the security groups co-dependent on each other, expecting the other to be already created and must both exist at the same time for successful creation.
5. Fix the Cycle Error
in main.tf - delete the ingress sections from the sg_ping and sg_8080 resource blocks.
Beneath the resource block of sg_ping, create a separate security group rule of type ingress named allow_ping
resource "aws_security_group_rule" "allow_ping" {
type = "ingress"
from_port = -1
to_port = -1
protocol = "icmp"
security_group_id = aws_security_group.sg_ping.id
source_security_group_id = aws_security_group.sg_8080.id
}
- Beneath the resource block of sg_8080, Create another security group rule of type ingress named allow_8080
resource "aws_security_group_rule" "allow_8080" {
type = "ingress"
from_port = 8080
to_port = 8080
protocol = "tcp"
security_group_id = aws_security_group.sg_8080.id
source_security_group_id = aws_security_group.sg_ping.id
}
- main.tf looks like this
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.23"
}
}
required_version = ">= 0.14.9"
}
provider "aws" {
region = var.region
}
data "aws_ami" "linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "web_app" {
for_each = local.security_groups
ami = data.aws_ami.linux.id
availability_zone = var.az_1a
instance_type = var.instance_type
vpc_security_group_ids = [each.value]
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "${var.name}-mywebapp-${each.key}"
}
}
resource "aws_security_group" "sg_ping" {
name = "Allow Ping"
vpc_id = "vpc-0da931f5deb73c9e2"
}
resource "aws_security_group_rule" "allow_ping" {
type = "ingress"
from_port = -1
to_port = -1
protocol = "icmp"
security_group_id = aws_security_group.sg_ping.id
source_security_group_id = aws_security_group.sg_8080.id
}
resource "aws_security_group" "sg_8080" {
name = "Allow 8080"
vpc_id = "vpc-0da931f5deb73c9e2"
}
resource "aws_security_group_rule" "allow_8080" {
type = "ingress"
from_port = 8080
to_port = 8080
protocol = "tcp"
security_group_id = aws_security_group.sg_8080.id
source_security_group_id = aws_security_group.sg_ping.id
}
locals {
security_groups = {
sg_ping = aws_security_group.sg_ping.id,
sg_8080 = aws_security_group.sg_8080.id,
}
}
- Validate the configuration:
terraform validate
- You should receive a success message stating the configuration is valid.
6. Deploy Your Resources
- Create the Terraform plan
terraform plan
- Create the resources
terraform apply
- enter yes to confirm deployment
- EC2 instances - rev-mywebapp-sg_8080 and rev-mywebapp-sg_ping
Cleanup
terraform destroy
What we have done so far
We have successfully fixed the cycle error and deployed our resources.
Top comments (0)