How to fix a Terraform Cycle Error
Tags: terraform, ec2, securitygroup
Published: 2023-10-18 22:11:33

# 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](https://github.com/awsmine/Terraform_projects)** 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](https://registry.terraform.io/providers/hashicorp/aws/latest/docs).**
**[Terraform documentation for AMI](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami).**
**[Troubleshoot Terraform - Correct a cycle error](https://developer.hashicorp.com/terraform/tutorials/configuration-language/troubleshooting-workflow)**
**[learn-terraform-troubleshooting](https://github.com/hashicorp/learn-terraform-troubleshooting/tree/main)**

# 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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qki2foagegfywu8n8b4t.png)


## 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
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/479qk8zdz5c3he1lgcn5.png)


- Initiate the working directory

```
terraform init
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26xp5kxsobactv9wsbxj.png)


- Validate the configuration

```
terraform validate
```

- get a cycle error for the security groups sg_8080 and sg_ping. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x31bhk98xxld3uaz7cyx.png)


- 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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i12bg14ybmtnn49oxf1.png)



## 6. Deploy Your Resources


- Create the Terraform plan

```
terraform plan
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4f0qn4nuwa5ilnxvsgiu.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpa4ay9o5fzkjvov095i.png)



- Create the resources

```
terraform apply
```
- enter yes to confirm deployment


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtzl9917tfd7c888ymt8.png)

- EC2 instances - rev-mywebapp-sg_8080 and rev-mywebapp-sg_ping

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yloizouq85tn9emxk54j.png)


# Cleanup


```
terraform destroy
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbhdtr8560p8afn6s2dy.png)



# What we have done so far

We have successfully fixed the **cycle** error and deployed our resources.