Skip to main content

Automated Automation: How ChatGPT Can Help (And Harm!) Our Jobs

·9 mins

Depending on who you ask, IT is either one of the most, or least, safe industries when looking at what AI can do compared to humans. When OpenAI brought it’s ChatGPT chat platform out in late 2022, it made waves across quite a few industries, and burst out into the mainstream headspace.

I’ll be completely skipping past the truly scary stuff like Midjourney that can generate artistically sound images from a small text prompt, but there is one that’s potentially even scarier for us DevOps folk.

AIAC is a project that works as a surprisingly coherent Infrastructure as Code (IaC) spinoff to the OpenAI Chat. It looks to support quite a lot of languages, and while it’s honed down specifically on IaC, it is using a variant of the same model that’s used in ChatGPT.

Installation #

Comparison and Usage #

Comparing OpenAI’s ChatGPT and AIAC thankfully is quite easy since it is operated in a very similar way. My prompt to ChatGPT was “Create me a terraform script that will create two AWS EC2 instances. Both of these should be in separate subnets from each other, and only allowing port 5959 TCP between each other.”

Here’s what it spat out:

# Configure the AWS provider
provider "aws" {
  region = "us-east-1"
}

# Create a VPC
resource "aws_vpc" "my_vpc" {
  cidr_block = "10.0.0.0/16"
}

# Create two public subnets
resource "aws_subnet" "public_subnet_1" {
  vpc_id            = aws_vpc.my_vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-1a"
  map_public_ip_on_launch = true
}

resource "aws_subnet" "public_subnet_2" {
  vpc_id            = aws_vpc.my_vpc.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-east-1b"
  map_public_ip_on_launch = true
}

# Create an Internet Gateway
resource "aws_internet_gateway" "my_igw" {
  vpc_id = aws_vpc.my_vpc.id
}

# Attach the Internet Gateway to the VPC
resource "aws_vpc_gateway_attachment" "igw_attachment" {
  vpc_id       = aws_vpc.my_vpc.id
  internet_gateway_id = aws_internet_gateway.my_igw.id
}

# Create a default route table and a default route
resource "aws_route_table" "default_route_table" {
  vpc_id = aws_vpc.my_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.my_igw.id
  }
}

# Associate the default route table with the public subnets
resource "aws_route_table_association" "public_subnet_1_association" {
  subnet_id      = aws_subnet.public_subnet_1.id
  route_table_id = aws_route_table.default_route_table.id
}

resource "aws_route_table_association" "public_subnet_2_association" {
  subnet_id      = aws_subnet.public_subnet_2.id
  route_table_id = aws_route_table.default_route_table.id
}

# Create a security group for the EC2 instances
resource "aws_security_group" "ec2_security_group" {
  name        = "allow_5959"
  description = "Allow port 5959 traffic between the EC2 instances"
  vpc_id      = aws_vpc.my_vpc.id

  # Allow incoming traffic on port 5959 from within the security group
  ingress {
    from_port   = 5959

Now, there’s a few issues here. Ignoring the fact that it didn’t even make any instances, the security group doesn’t specify the source or destination. While I didn’t provide any names for it to use, it is also quite weird that it’s called everything “public_subnet_x” when I specified only to allow 5959 to each other. It also didn’t even specify TCP in the code!

Now, let’s look at what AIAC can fire off:

resource "aws_security_group" "allow_port_5959" {
  name        = "allow_port_5959"
  description = "Allow port 5959 tcps between instances"

  ingress {
    from_port   = 5959
    to_port     = 5959
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "allow_port_5959"
  }
}

# Create Subnets
resource "aws_subnet" "private_subnet_1" {
    cidr_block           = "10.255.0.0/20"
    vpc_id               = "vpc-XXXXXXXX"
    availability_zone    = "us-east-1a"
    map_public_ip_on_launch = false
    tags = {
      Name = "private_subnet_1"
    }
}

resource "aws_subnet" "private_subnet_2" {
    cidr_block           = "10.255.16.0/20"
    vpc_id               = "vpc-XXXXXXXX"
    availability_zone    = "us-east-1b"
    map_public_ip_on_launch = false
    tags = {
      Name = "private_subnet_2"
    }
}

# Create EC2 Instances
resource "aws_instance" "instance_1" {
    ami = "ami-XXXXXXXX"
    instance_type = "t2.micro"
    subnet_id = "${aws_subnet.private_subnet_1.id}"
    security_groups = [
        "${aws_security_group.allow_port_5959.id}"
    ]
    tags = {

      Name = "instance_1"
    }
}

resource "aws_instance" "instance_2" {
    ami = "ami-XXXXXXXX"
    instance_type = "t2.micro"
    subnet_id = "${aws_subnet.private_subnet_2.id}"
    security_groups = [
        "${aws_security_group.allow_port_5959.id}"
    ]
    tags = {
      Name = "instance_2"
    }
}

This is better, but still a bit problematic. I don’t blame it for just having AMI-XXXXXX since I didn’t specify an OS to use. Instance size makes sense too since it would be compatible with the AWS free tier, so no expected fleecing here. It’s even giving useful names without me having to specify, that’s great!

One bit that isn’t great however is that the security group doesn’t actually allow between instances, it allows 5959 access between everything.

One handy aspect of AIAC is that you can just press R after the code gets spat out, and it’ll just retry! You can do this with ChatGPT too just by pasting in the same text prompt you gave it last time.

Let’s try again with AIAC now shall we?

resource "aws_vpc" "vpc" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "main_vpc"
  }
}

resource "aws_subnet" "subnet_a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-2a"

  tags = {
    Name = "subnet_A"
  }
}

resource "aws_subnet" "subnet_b" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-east-2b"

  tags = {
    Name = "subnet_B"
  }
}

resource "aws_security_group" "allow_tcp_5959" {
  name        = "allow_tcp_5959"
  description = "Allow port 5959 to be accessed between two ec2 instances"
  vpc_id      = aws_vpc.vpc.id

  ingress {
    description = "Allow Incoming 5959 between two instances"
    from_port   = 5959
    to_port     = 5959
    protocol    = "tcp"
    security_groups = [aws_security_group.allow_tcp_5959.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "instance_a" {
  ami           = "ami-xxxxxxxx"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.subnet_a.id
  vpc_security_group_ids = [aws_security_group.allow_tcp_5959.id]

tags = {
Name = "Instance A"
}
}

resource "aws_instance" "instance_b" {
  ami           = "ami-xxxxxxxx"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.subnet_b.id
  vpc_security_group_ids = [aws_security_group.allow_tcp_5959.id]

tags = {
Name = "Instance B"
}
}

Now would you look at that, the security group is fixed! The brackets look weird with those instance tags though. Let’s run this and see what happens!

Well, that was a letdown, what about the first one then?

Well, that’s a good segue to our analysis and conclusion then isn’t it?

Analysis #

Now, I’ve not had too deep a dive into how the AI model works and I’ve certainly not looked into the many moral issues in great detail. What I can say though is that AI isn’t nearly anywhere close to done. The terraform it spat out did genuinely fool me, and I had to try running it to realize that it wasn’t functioning code.

Now, my testing is indeed flawed. I’ve only tried one AI model with one language. The results though do still impress me, I can definitely use bits of that code as inspiration, and there are some other prompts I fed it that did produce some really nice code!

The prompt I gave it was " aiac get terraform that will make an autoscaling group of between 3 and 10 t3.nano instances in the eu-west-2 region. These should be in a target group that a load balancer is serving to. The instances should have Ubuntu Server 22.04 installed, along with nginx serving a default page on port 8080 TCP. The security group assigned to these instances should allow port 8080 only to the load balancer."

resource "aws_autoscaling_group" "my_asg" {
  name                            = "my_asg"
  min_size                        = 3
  desired_capacity                = 5
  max_size                        = 10
  launch_configuration            = aws_launch_configuration.example.name
  load_balancer_names             = [aws_alb.example.name]
  vpc_zone_identifier             = aws_subnet.example.*.id
  health_check_type               = "ELB"
  health_check_grace_period       = 300
  wait_for_capacity_timeout       = "10m"
  force_delete                    = true
  tag {
    key                           = "Name"
    value                         = "my-auto-scaling-group"
    propagate_at_launch            = true
  }
}

resource "aws_launch_configuration" "example" {
  image_id                         = "ami-******"
  instance_type                    = "t3.nano"
  security_groups                  = [aws_security_group.example.id]
  user_data                        = "nginx_install.sh"
}

resource "aws_alb" "example" {
  name                = "example-alb"
  security_groups     = [aws_security_group.example-alb.id]
  subnets             = [aws_subnet.example.*.id]
  internal            = true
  idle_timeout        = 630
  enable_deletion_protection = false

  tags = {
    Name = "example-alb"
  }
}

resource "aws_alb_target_group" "example" {
  name       = "example-tg"
  port       = 8080
  protocol   = "HTTP"
  vpc_id     = aws_vpc.example.id

  health_check {
    path                = "/health"
    interval            = 60
    timeout             = 3
    healthy_threshold   = 3
    unhealthy_threshold = 3
    matcher             = "200-299"
  }
}

resource "aws_security_group" "example" {
  name        = "allow-http"
  description = "Allow internet facing http access"
  vpc_id      = aws_vpc.example.id

  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = [aws_alb.example.vpc_zone_identifier]
  }
}

resource "aws_security_group" "example-alb" {
  name        = "allow-inbound-from-alb"
  description = "Allow inbound from ALB"
  vpc_id      = aws_vpc.example.id

  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    security_groups = [aws_alb.example.security_groups]
  }
}

resource "aws_launch_template" "example" {
  image_id          = "ami-0e2fe78ba8376f1cc"
  instance_type     = "t3.nano"
  key_name          = "mykey"
  user_data 		= <<EOF
#!/bin/bash
apt update
apt-get -y install nginx
systemctl start nginx
systemctl enable nginx
EOF

  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "Example Launch Template"
    }
  }
}

resource "aws_lb" "example" {
  name            = "example-lb"
  internal        = false
  load_balancer_type = "application"
  security_groups = aws_security_group.example.id
  subnets         = [var.public_subnet]

  idle_timeout    = 60
  enable_deletion_protection = false

  tags = {
    Name = "example-lb"
  }
}

resource "aws_alb_target_group" "example" {
  name     = "example-target-group"
  port     = 8080
  protocol = "HTTP"
  vpc_id   = aws_vpc.example.id

  health_check {
    path                = "/healthz"
    protocol            = "HTTP"
    port                = "traffic-port"
    timeout             = 3
    interval            = 30
    unhealthy_threshold = 3
    healthy_threshold   = 3
  }
}

resource "aws_autoscaling_group" "example" {
  name                        = "example-asg"
  max_size                    = 10
  min_size                    = 3
  desired_capacity            = 5
  health_check_grace_period   = 300
  health_check_type           = "ELB"
  force_delete                = true
  termination_policies        = ["OldestInstance","ClosestToNextInstanceHour"]
  vpc_zone_identifier         = var.availability_zones
  launch_template {
    id      = aws_launch_template.example.id
    version = "$Latest"
  }
  target_group_arns            = [aws_alb_target_group.example.arn]

  lifecycle {
    create_before_destroy = true
  }

  tag {
    key                 = "Name"
    value               = "example"
    propagate_at_launch = true
  }
}

resource "aws_security_group" "example" {
  name        = "example-sg"
  description = "Allow traffic on port 8080 from the load balancer only"
  vpc_id      = data.aws_vpc.selected.id

  # Ingress
  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["aws_lb.example.dns_name/32"]
  }

  # Egress - Allow internet access
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

This code is not complete. However, it’s pretty damn good for a prompt so specific isn’t it? It still spat out messages saying stuff was undefined and that stuff needed to be in double rather than single quotes, but it’s definitely good for a bit of inspiration if you’re facing (code) writers block.

Conclusion #

The articles you may see that say something like AI IS STEALING OUR JOBS may be true in some industries unfortunately. Judging by what ChatGPT can do, I think we’re safe for now. With that being said, this is such a good learning tool. With the literal copy of this code, I can use this as an interviewing tool for code that looks kind of right, but isn’t.

This at the very least can be used for inspiration for parts of larger projects where you just aren’t too sure what a less opinionated bit of code would look like.