AI Rising: Is Your DevOps Job on the Chopping Block? ??
Gireesh Kumar
Software Architect | Digital Transformation | Accelerating Business Growth & Innovation | TOGAF, AWS, Azure Certified | FullStack| DevOps Advocate
Hey there, DevOps pals! I’ve been having some fun experimenting with ChatGPT to write Infrastructure as Code (IaC). Can it really whip up a fully working IaC script from a set of clear instructions? Does this mean we should be looking over our shoulders? Let’s dive in and find out!
I kept my test simple: I wanted to deploy an “nginx” container as an ECS service, but, of course, I still wanted to follow good practices.
For this experiment, I chose to play around with AWS cloud and Pulumi scripting (https://www.pulumi.com/). Why Pulumi, you ask? I found it easy to work with my favorite scripting language, TypeScript. Feel free to try out ChatGPT with your own favorite cloud and scripting combo — get creative with your prompt crafting!
I aimed to create a virtual private cloud (VPC) with two private and two public subnets, spread over two availability zones (AZs). My goal was to deploy the ECS on the private subnets and have the Application Load Balancer (ALB) sit on the public subnets. I wanted the ECS to only accept traffic from the ALB and for the nginx service to be available on port 80 using HTTP.
So, I started off by giving ChatGPT a really detailed step-by-step guide…
Act as AWS Cloud expert and expert in pulumi scripts, create fully functional script in TypeScript for the below AWS deployment
All resource name start with "pulumi-test-"
All resources will add a tag "pulumi-test"
Add all required comments
1. Create a VPC named - "pulumi-test-vpc" (all below actions are on this VPC)
2. Internet Gateway ("pulumi-test-igw") and attach to the vpc
3. Create 2 public and 2 private subnets, in 2 AZs
4. Create NAT Gateway in 1 AZ 1 public subnet and route the traffic on 2 private subnet via this NAT Gateway
5. All 4 subnet will route the internet traffic via the internet gateway
6. Create a security group ("pulumi-test-SG-internet-incoming"), allow all outgoing traffic, but allow incoming traffic on HTTP port 80 only
7. Create a security group ( "pulumi-test-SG-internal-only"), allow all outgoing traffic, but allow incoming on "TCP" only from the above security group - "pulumi-test-SG-internet-incoming"
8. use existing role "ecsTaskExecutionRole"
9. Create a new Farget ECS Cluster ("pulumi-test-cluster") on private subnets
10. Deploy new ECS service with below details (pulumi-test-ngix-ecs-service)
a. container-name: "nginx-container"
b. Image : "public.ecr.aws/nginx/nginx:perl"
c. cpu: .2
d. Memory: .5 GB
e. Port_mapping: http on port 80
f. Do not allow public IP
g. Create this service in Private Subnets
h. Attach Security group - "pulumi-test-SG-internal-only"
i. Add log configuraitons
11. Create an an Application load balancer on public subnet
12. Attach security group "pulumi-test-SG-internet-incoming" to the application load balancer
13. Create an HTTP listner on port 80 to expose the ECS Service "pulumi-test-ngix-ecs-service"
Here is what it generated. I was so impressed with the output, but it's not a working code, as it had many compilation issues.
import * as aws from "@pulumi/aws"
import * as awsx from "@pulumi/awsx";
import * as pulumi from "@pulumi/pulumi";
// 1. Create VPC
const vpc = new aws.ec2.Vpc("pulumi-test-vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsHostnames: true,
tags: {
"pulumi-test": "true"
}
});
// 2. Create Internet Gateway and attach it to the VPC
const ig = new aws.ec2.InternetGateway("pulumi-test-igw", {
vpcId: vpc.id,
tags: {
"pulumi-test": "true"
}
});
// 3. Create Subnets
// We'll create a separate subnet in each of two AZs
for (let i = 0; i < 2; i++) {
// public subnet
new aws.ec2.Subnet(`pulumi-test-public-subnet-${i}`, {
vpcId: vpc.id,
cidrBlock: `10.0.${i}.0/24`,
mapPublicIpOnLaunch: true,
availabilityZone: `us-west-2${String.fromCharCode(97 + i)}`, // 'a' or 'b'
tags: {
"pulumi-test": "true"
}
});
// private subnet
new aws.ec2.Subnet(`pulumi-test-private-subnet-${i}`, {
vpcId: vpc.id,
cidrBlock: `10.0.${i + 2}.0/24`,
mapPublicIpOnLaunch: false,
availabilityZone: `us-west-2${String.fromCharCode(97 + i)}`, // 'a' or 'b'
tags: {
"pulumi-test": "true"
}
});
}
//4. NAT Gateway and route tables would be created here...
//6. Security Group to allow incoming traffic on HTTP port 80
const sg1 = new aws.ec2.SecurityGroup("pulumi-test-SG-internet-incoming", {
vpcId: vpc.id,
egress: [{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"]
}],
ingress: [{
protocol: "tcp",
fromPort: 80,
toPort: 80,
cidrBlocks: ["0.0.0.0/0"]
}],
tags: {
"pulumi-test": "true"
}
});
//7. Security Group to allow incoming TCP traffic from the above security group
const sg2 = new aws.ec2.SecurityGroup("pulumi-test-SG-internal-only", {
vpcId: vpc.id,
egress: [{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"]
}],
ingress: [{
protocol: "tcp",
fromPort: 0,
toPort: 0,
securityGroups: [sg1.id]
}],
tags: {
"pulumi-test": "true"
}
});
//8. ECS Task Execution Role - assuming it's already created
//9. Create ECS Cluster
const cluster = new awsx.ecs.Cluster("pulumi-test-cluster", {
vpc: vpc,
// Continue from the previous script
// 10. Create ECS Fargate service
const taskDefinition = new awsx.ecs.FargateTaskDefinition("pulumi-test-ngix-ecs-service-taskdef", {
container: {
image: "public.ecr.aws/nginx/nginx:perl",
cpu: 256, // 0.2 vCPU
memory: 512, // 0.5 GB
portMappings: [{ containerPort: 80, hostPort: 80, protocol: "tcp" }],
logConfiguration: {
logDriver: "awslogs",
options: {
"awslogs-group": "/ecs/pulumi-test-ngix-ecs-service",
"awslogs-region": "us-west-2",
"awslogs-stream-prefix": "ecs"
},
},
},
});
const service = new awsx.ecs.FargateService("pulumi-test-ngix-ecs-service", {
cluster: cluster,
taskDefinition: taskDefinition,
securityGroups: [sg2.id],
subnetIds: ["subnet-private-1-id", "subnet-private-2-id"], // Replace with private subnet IDs
assignPublicIp: false,
});
// 11. Create an Application Load Balancer
const alb = new awsx.lb.ApplicationLoadBalancer("pulumi-test-alb", {
securityGroups: [sg1],
subnets: ["subnet-public-1-id", "subnet-public-2-id"], // Replace with public subnet IDs
external: true,
});
// 12. Attach security group to the application load balancer
// This is done in the ALB creation step above.
// 13. Create an HTTP listener on port 80
const web = alb.createListener("web", {
port: 80,
defaultAction: {
type: "forward",
targetGroupArn: service.targetGroup.arn,
},
});
});;
Next,, I decided to keep it short and simple.
Act as AWS infrastructure architect and pulumi script expert, create deployment scripts in pulum
To deploy nginx container image (public.ecr.aws/nginx/nginx:perl) as ECS container service deployed on HTTP port 80, ECS should be deployed on the private subnet
Should also deploy an Application load balancer on public subnet, exposing the nginx container service via HTTP port 80.
ECS should accept only incoming traffic from the Application load balanceri
Here is the output
import * as pulumi from "@pulumi/pulumi"
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
// VPC
const vpc = new aws.ec2.Vpc("vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsSupport: true,
enableDnsHostnames: true,
});
// Internet Gateway
const ig = new aws.ec2.InternetGateway("ig", {
vpcId: vpc.id,
});
// Route Table for Public Subnet
const publicRouteTable = new aws.ec2.RouteTable("publicRouteTable", {
vpcId: vpc.id,
routes: [
{
cidrBlock: "0.0.0.0/0",
gatewayId: ig.id,
},
],
});
// Public Subnet
const publicSubnet = new aws.ec2.Subnet("publicSubnet", {
vpcId: vpc.id,
cidrBlock: "10.0.0.0/24",
mapPublicIpOnLaunch: true,
});
// Associate the public subnet with the route table
const publicSubnetAssociation = new aws.ec2.RouteTableAssociation("publicSubnetAssociation", {
subnetId: publicSubnet.id,
routeTableId: publicRouteTable.id,
});
// Private Subnet
const privateSubnet = new aws.ec2.Subnet("privateSubnet", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
});
// ECS Cluster
const cluster = new awsx.ecs.Cluster("cluster", {
vpc: vpc,
});
// NGINX Service
const nginx = new awsx.ecs.FargateService("nginx", {
cluster: cluster,
taskDefinitionArgs: {
containers: {
nginx: {
image: "public.ecr.aws/nginx/nginx:perl",
portMappings: [{ containerPort: 80 }],
},
},
},
subnets: [privateSubnet.id],
});
// Application Load Balancer
const alb = new awsx.lb.ApplicationLoadBalancer("alb", {
subnets: [publicSubnet.id],
securityGroups: cluster.securityGroups,
});
const web = alb.createTargetGroup("web", { port: 80 });
const listener = web.createListener("web-listener", { port: 80, protocol: "HTTP" });
// Security Group Rule
const albSecurityGroup = alb.securityGroups[0];
const nginxSecurityGroup = new aws.ec2.SecurityGroup("nginxSecurityGroup", {
vpcId: vpc.id,
});
const securityGroupRule = new aws.ec2.SecurityGroupRule("nginxAllowInboundFromAlb", {
type: "ingress",
fromPort: 80,
toPort: 80,
protocol: "tcp",
sourceSecurityGroupId: albSecurityGroup.id,
securityGroupId: nginxSecurityGroup.id,
});
// Expose the ALB endpoint
export const url = listener.endpoint.hostname;;
Next, I thought I’d mix things up a bit and ask it to use the “@pulumi/awsx” library.
领英推荐
import * as pulumi from "@pulumi/pulumi"
import * as awsx from "@pulumi/awsx";
// Create a new VPC with a public and private subnet.
const vpc = new awsx.ec2.Vpc("my-vpc", {
cidrBlock: "10.0.0.0/16",
subnets: [
{ type: "public", name: "public" },
{ type: "private", name: "private" }
],
});
// Create an ECS cluster.
const cluster = new awsx.ecs.Cluster("my-cluster", { vpc });
// Create a load balancer on the public subnet.
const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer(
"my-alb", { vpc, subnets: vpc.publicSubnetIds });
const atg = alb.createTargetGroup("my-atg", { port: 80, protocol: "HTTP" });
const web = atg.createListener("web", { port: 80, protocol: "HTTP" });
// Create a security group to open ingress from the load balancer to the ECS service.
const sg = alb.createSecurityGroup("my-sg", { ingress: [{ protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: [vpc.vpc.cidrBlock] }] });
// Create an ECS service running the NGINX container, deployed to the private subnet.
const nginx = new awsx.ecs.FargateService("my-nginx", {
cluster,
taskDefinitionArgs: {
containers: {
nginx: {
image: "public.ecr.aws/nginx/nginx:perl",
portMappings: [{ containerPort: 80 }],
},
},
},
subnets: vpc.privateSubnetIds,
securityGroups: [sg],
assignPublicIp: false,
});
// Connect the ECS service to the load balancer.
atg.addTarget(nginx);
// Export the URL of the load balancer.
export const url = web.url;;
Now, here’s the catch: None of these attempts were successful right off the bat. They all ran into errors, and in many instances, certain functions or properties didn’t even exist. It seems like ChatGPT either isn’t fully up to speed with these libraries, or it made some educated guesses here and there, using what it thought were likely parameters.
That said, it’s worth noting that ChatGPT did manage to come up with some pretty close-to-accurate code samples.
After generating these three samples (which, let’s be honest, took a bit of back-and-forth with the prompts), I found myself having to manually tweak these scripts based on the latest Pulumi library to get them to work the way I wanted.
So, here we have it — the final, working scripts that I’ve co-authored (or, should I say, “co-corrected”) with ChatGPT.
import * as pulumi from "@pulumi/pulumi"
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
const config = new pulumi.Config();
const tags = { "pulumi-test": "true" };
const _tagsWithName = (name: String) => {
return { Name: `${name}`, ...tags };
};
const vpc = new aws.ec2.Vpc("pulumi-test-vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsHostnames: true,
enableDnsSupport: true,
tags: _tagsWithName("pulumi-test-vpc"),
});
const igw = new aws.ec2.InternetGateway("pulumi-test-igw", {
vpcId: vpc.id,
tags: _tagsWithName("pulumi-test-igw"),
});
const azs = aws.getAvailabilityZones({});
const publicSubnets: Array<aws.ec2.Subnet> = [];
const privateSubnets: Array<aws.ec2.Subnet> = [];
for (let i = 0; i < 2; i++) {
publicSubnets.push(
new aws.ec2.Subnet(`pulumi-test-public-subnet-${i}`, {
cidrBlock: `10.0.${i}.0/24`,
vpcId: vpc.id,
mapPublicIpOnLaunch: true,
availabilityZone: azs.then((azs) => azs.names[i]),
tags: _tagsWithName(`pulumi-test-public-subnet-${i}`),
})
);
privateSubnets.push(
new aws.ec2.Subnet(`pulumi-test-private-subnet-${i}`, {
cidrBlock: `10.0.${i + 2}.0/24`,
vpcId: vpc.id,
mapPublicIpOnLaunch: false,
availabilityZone: azs.then((azs) => azs.names[i]),
tags: _tagsWithName(`pulumi-test-private-subnet-${i}`),
})
);
}
const eip = new aws.ec2.Eip("pulumi-test-eip", {
vpc: true,
tags: _tagsWithName("pulumi-test-eip"),
});
const natGateway = new aws.ec2.NatGateway("pulumi-test-natgw", {
allocationId: eip.id,
subnetId: publicSubnets[0].id,
tags: _tagsWithName("pulumi-test-natgw"),
});
const publicRouteTable = new aws.ec2.DefaultRouteTable(
"pulumi-test-public-routetable",
{
defaultRouteTableId: vpc.defaultRouteTableId,
routes: [
{
cidrBlock: "0.0.0.0/0",
gatewayId: igw.id,
},
],
tags: _tagsWithName("pulumi-test-public-routetable"),
}
);
const privateRouteTable = new aws.ec2.RouteTable(
"pulumi-test-private-routetable",
{
vpcId: vpc.id,
routes: [
{
cidrBlock: "0.0.0.0/0",
natGatewayId: natGateway.id,
},
],
tags: _tagsWithName("pulumi-test-private-routetable"),
}
);
let idx = 0;
publicSubnets.forEach((subnet) => {
const routeTableAssociation = new aws.ec2.RouteTableAssociation(
`pulumi-test-public-subnet-association-${idx}`,
{
routeTableId: publicRouteTable.id,
subnetId: subnet.id,
}
);
idx++;
});
idx = 0;
privateSubnets.forEach((subnet) => {
const routeTableAssociation = new aws.ec2.RouteTableAssociation(
`pulumi-test-private-subnet-association-${idx}`,
{
routeTableId: privateRouteTable.id,
subnetId: subnet.id,
}
);
idx++;
});
const sgIncomingTraffic = new aws.ec2.SecurityGroup(
"pulumi-test-SG-internet-incoming",
{
vpcId: vpc.id,
description: "SG Allow all incoming traffic on http",
egress: [
{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"],
},
],
ingress: [
{
protocol: "tcp",
fromPort: 80,
toPort: 80,
cidrBlocks: ["0.0.0.0/0"],
// Allow HTTP traffic
},
],
tags: _tagsWithName("pulumi-test-SG-internet-incoming"),
}
);
const sqInternal = new aws.ec2.SecurityGroup(
"pulumi-test-SG-internal-only",
{
vpcId: vpc.id,
description: "SG allow only internal routing",
egress: [
{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"],
},
],
ingress: [
{
protocol: "tcp", // Change this based on your requirements
fromPort: 0, // Adjust the port range as needed
toPort: 65535,
securityGroups: [sgIncomingTraffic.id], // Allow traffic from exampleSecurityGroup
},
],
tags: _tagsWithName("pulumi-test-SG-internal-only"),
}
);
const executionRole = aws.iam.getRole({
name: "ecsTaskExecutionRole",
});
// Create an ECS Cluster with Fargate support
const ecsCluster = new aws.ecs.Cluster("pulumi-test-cluster", {
capacityProviders: ["FARGATE"],
tags: _tagsWithName("pulumi-test-cluster"),
});
// Add the ECS service as a target for the Application Load Balancer
// Create an Application Load Balancer
const albx = new awsx.lb.ApplicationLoadBalancer("pulumi-ai-test-alb", {
securityGroups: [sgIncomingTraffic.id],
internal: false,
subnets: publicSubnets
});
const fservicex = new awsx.ecs.FargateService("service", {
cluster: ecsCluster.arn,
// assignPublicIp: false,
desiredCount: 2,
networkConfiguration: {
subnets: privateSubnets.map((ps) => ps.id),
securityGroups: [sqInternal.id],
},
taskDefinitionArgs: {
executionRole: { roleArn: executionRole.then(val => val.arn)},
container: {
image: "nginx:latest",
cpu: 512,
memory: 128,
essential: true,
portMappings: [
{
targetGroup: albx.defaultTargetGroup,
},
],
},
},
});
export const ecsclusterid = ecsCluster.id;
export const publicSubnetIds = publicSubnets.map((ps) => ps.id);
export const privateSubnetIds = privateSubnets.map((ps) => ps.id);
export const securityGroupIdPub = sgIncomingTraffic.id;
export const securityGroupIdPvt = sqInternal.id;
export const vpcId = vpc.id;
export const dnsName = albx.loadBalancer.dnsName;;
Here’s a fun fact to add to the mix: Pulumi has its very own AI system based on GPT-4, known as Pulumi AI (https://www.pulumi.com/ai/). It’s pretty similar to our friend ChatGPT in terms of interaction. That being said, I personally found the ChatGPT interface to be a touch more user-friendly. Pulumi AI, while cool, doesn’t always seem to keep up with the context. But hey, it’s all about finding the tool that fits you best, right?
I also tried Google Bard with the same prompts, but I liked the ChatGPT response better.
As you can see, these cutting-edge developments in AI are here for us to use and benefit from, not to fear. Rest assured, your job isn’t going anywhere — unless, of course, you don’t step up your game. If you don’t level up, the real ‘risk’ might come not from AI or ChatGPT, but rather from your savvy colleague who’s making the most of these tools! So, let’s seize the day, embrace these AI tools, and keep our skills sharp and shiny.
So, the trick is to be savvy enough to level up. Think about offloading some of your workloads to these tools. They can act like your personal assistant, supercharging your creativity and productivity. But remember, while these tools can be a great help, you can’t just blindly trust what they do. You should always know what’s going on under the hood when using them.
Let’s move to the next phase, ChatOps.AI :-) AI Driven DevOps conversations
What do you think about all this, folks? I’d love to hear your thoughts and experiences. Feel free to share your comments below. Let’s learn from each other!
Consultant/Experienced Scrum Master/Agile Leader and Sr. Business Analyst
1 年It's not for everyone
Consultant/Experienced Scrum Master/Agile Leader and Sr. Business Analyst
1 年Chat GPT
DevOps Developer
1 年Good article. Cant see ChatGPT or any other AI tools taking over production releases anytime soon. AI is good but never better than a human brain :)
Senior Engineering Manager - DevOps/AIOps/SRE/Platform Engineering | Lifelong Learner | Executive MBA | DevOps/Cloud Architect | 4x AWS | 4x Azure | CK{A,S,AD} | CSPO | SAFe? SDP | UAE Golden Visa Holder
1 年Very nice article Gireesh Kumar and an interesting experiment. Thanks for sharing your experience and keep it on ??