I asked ChatGPT to Generate AWS Templates. Results are Shocking.

Cagdas Ozbey
TysonWorks
Published in
7 min readApr 3, 2023

The rise of ChatGPT and other generative AI technologies has started a significant amount of discussion around their potential to replace jobs. In this tutorial, I will explore the capabilities of ChatGPT by asking to generate common workloads on AWS. I will keep things fairly simple for this one, which could be expected from a Junior Cloud Solutions Architect.

Sorry for the clickbait title, the results are not shocking 🤷‍♂️

Let’s start with the first task. I will ask ChatGPT to use AWS CDK instead of CloudFormation, we don’t want to go over thousand lines of YAML/JSON (no thanks). CDK version 2.63.0 was used in the examples.

import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';

export class YourProjectNameStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Create an S3 bucket for static website hosting
const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
websiteIndexDocument: 'index.html',
websiteErrorDocument: 'error.html',
publicReadAccess: true,
removalPolicy: cdk.RemovalPolicy.DESTROY, // NOT recommended for production
});

// Create a CloudFront distribution to serve the static website
const distribution = new cloudfront.Distribution(this, 'Distribution', {
defaultBehavior: { origin: new origins.S3Origin(websiteBucket) },
defaultRootObject: 'index.html',
});

// Output the S3 bucket and CloudFront distribution domain names
new cdk.CfnOutput(this, 'BucketDomainName', {
value: websiteBucket.bucketWebsiteDomainName,
});
new cdk.CfnOutput(this, 'DistributionDomainName', {
value: distribution.distributionDomainName,
});
}
}

Construct base class was moved to separate module called ‘constructs’ in v2 version of the CDK. Although ChatGPT is importing v2 modules, it tried to use v1 ‘cdk.Construct’ class instead.

import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import { Construct } from 'constructs'

export class YourProjectNameStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {

Overall I give ChatGPT 8/10 here. It’s a quick fix if you know what you are looking for, but it can confuse beginners.

Let’s continue with the second task.

import * as cdk from 'aws-cdk-lib';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
import * as authorizers from 'aws-cdk-lib/aws-apigatewayv2-authorizers';

export class YourProjectNameStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Create a Cognito User Pool
const userPool = new cognito.UserPool(this, 'UserPool', {
selfSignUpEnabled: true,
signInAliases: {
email: true,
},
autoVerify: {
email: true,
},
});

// Create a Lambda Function
const lambdaFunction = new lambda.Function(this, 'MyFunction', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2));
return {
statusCode: 200,
body: JSON.stringify({ message: 'Success' }),
};
};
`),
});

// Create a REST API using API Gateway
const httpApi = new apigatewayv2.HttpApi(this, 'MyApi', {
apiName: 'my-api',
corsPreflight: {
allowOrigins: apigatewayv2.Cors.ALL_ORIGINS,
allowMethods: apigatewayv2.Cors.ALL_METHODS,
},
defaultAuthorizer: new authorizers.HttpUserPoolAuthorizer({
userPool,
}),
});

// Add a route for the Lambda function
httpApi.addRoutes({
path: '/my-function',
methods: [apigatewayv2.HttpMethod.ANY],
integration: new apigatewayv2_integrations.HttpLambdaIntegration({
handler: lambdaFunction,
}),
});

// Output the User Pool ID, REST API endpoint, and Lambda function ARN
new cdk.CfnOutput(this, 'UserPoolId', {
value: userPool.userPoolId,
});
new cdk.CfnOutput(this, 'ApiEndpoint', {
value: httpApi.url!,
});
new cdk.CfnOutput(this, 'LambdaFunctionArn', {
value: lambdaFunction.functionArn,
});
}
}

ChatGPT is making the same mistakes here, but this time with multiple modules. Outside of the Construct issue I’ve mentioned above, we have now issues with the ‘apigatewayv2’ and ‘authorizers’ modules. It tries to use experimental features that don’t exist in the modules we are importing.

Here’s how I would do it, without using the experimental L2 Construct Library.

import * as cdk from 'aws-cdk-lib';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigw from 'aws-cdk-lib/aws-apigateway';

import { Construct } from "constructs"

export class YourProjectNameStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Create a Cognito User Pool
const userPool = new cognito.UserPool(this, 'MyUserPool', {
userPoolName: 'my-user-pool',
selfSignUpEnabled: true,
signInAliases: {
email: true,
phone: false,
username: true,
},
autoVerify: {
email: true,
},
});

// Create a Lambda function that logs incoming events and returns a "Success" message
const logEventsFunction = new lambda.Function(this, 'LogEventsFunction', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2));
return {
statusCode: 200,
body: JSON.stringify({ message: 'Success' }),
};
};
`),
});

// Create a REST API using API Gateway
const api = new apigw.RestApi(this, 'MyApi', {
restApiName: 'My API',
defaultCorsPreflightOptions: {
allowOrigins: apigw.Cors.ALL_ORIGINS,
allowMethods: apigw.Cors.ALL_METHODS,
allowHeaders: ['*'],
},
});

// Use Cognito User Pool for REST API Authorizer
const authorizer = new apigw.CfnAuthorizer(this, 'MyApiAuthorizer', {
restApiId: api.restApiId,
name: 'MyAuthorizer',
type: apigw.AuthorizationType.COGNITO,
identitySource: 'method.request.header.Authorization',
providerArns: [userPool.userPoolArn],
});

// Serve HTTP requests for the Lambda function
const lambdaIntegration = new apigw.LambdaIntegration(logEventsFunction);
const logEventsResource = api.root.addResource('logEvents');
logEventsResource.addMethod('POST', lambdaIntegration, {
authorizationType: apigw.AuthorizationType.COGNITO,
authorizer: { authorizerId: authorizer.ref },
});

// Output the User Pool ID, REST API endpoint, and Lambda function ARN
new cdk.CfnOutput(this, 'UserPoolId', {
value: userPool.userPoolId,
});
new cdk.CfnOutput(this, 'ApiEndpoint', {
value: api.url,
});
new cdk.CfnOutput(this, 'LambdaFunctionArn', {
value: logEventsFunction.functionArn,
});
}
}

I give ChatGPT 6/10 for this one, if you are not comfortable with CDK, you could spend hours fixing the template.

Let’s move on the final task.

import * as cdk from 'aws-cdk-lib';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

export class YourProjectNameStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Create a Cognito User Pool
const userPool = new cognito.UserPool(this, 'MyUserPool', {
userPoolName: 'my-user-pool',
selfSignUpEnabled: true,
signInAliases: {
email: true,
phone: false,
username: true,
},
autoVerify: {
email: true,
},
});

// Create an AppSync API
const api = new appsync.GraphqlApi(this, 'MyApi', {
name: 'my-api',
schema: appsync.Schema.fromAsset('graphql/schema.graphql'),
authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.USER_POOL,
userPoolConfig: {
userPool,
},
},
},
xrayEnabled: true,
});

// Create a DynamoDB table
const table = new dynamodb.Table(this, 'MyTable', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
});

// Create a data source for the AppSync API
const dataSource = api.addDynamoDbDataSource('MyDataSource', table);

// Add a GraphQL resolver for the data source
dataSource.createResolver({
typeName: 'Query',
fieldName: 'getItems',
requestMappingTemplate: appsync.MappingTemplate.dynamoDbScanTable(),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(),
});

// Output the AppSync API URL and Cognito User Pool ID
new cdk.CfnOutput(this, 'GraphqlApiUrl', {
value: api.graphqlUrl,
});

new cdk.CfnOutput(this, 'UserPoolId', {
value: userPool.userPoolId,
});
}
}

ChatGPT is off again, this time by a large margin. Here’s you would accomplish the same task without using the experimental L2 Construct Library

import * as cdk from 'aws-cdk-lib';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as iam from "aws-cdk-lib/aws-iam";
import { readFileSync } from "fs";
import { Construct } from "constructs";

export class YourProjectNameStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Create a Cognito User Pool
const userPool = new cognito.UserPool(this, 'MyUserPool', {
userPoolName: 'my-user-pool',
selfSignUpEnabled: true,
signInAliases: {
email: true,
phone: false,
username: true,
},
autoVerify: {
email: true,
},
});

// Create a DynamoDB table
const table = new dynamodb.Table(this, 'MyTable', {
tableName: 'my-table',
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});

// Create AppSync GraphQL API with Cognito User Pool as the authorizer
const api = new appsync.CfnGraphQLApi(this, 'MyApi', {
name: 'my-api',
authenticationType: "AMAZON_COGNITO_USER_POOLS",
userPoolConfig: {
userPoolId: userPool.userPoolId,
awsRegion: this.region
},
});

const schema = new appsync.CfnGraphQLSchema(this, "graphql-api-schema", {
apiId: api.attrApiId,
definition: readFileSync("./schema.graphql").toString(),
});
schema.addDependsOn(api);

const tableRole = new iam.Role(this, 'dynamo-role', {
assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com')
});
tableRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonDynamoDBFullAccess'));

const dataSource = new appsync.CfnDataSource(this, 'ItemsDataSource', {
apiId: api.attrApiId,
name: 'table-source',
type: 'AMAZON_DYNAMODB',
dynamoDbConfig: {
tableName: table.tableName,
awsRegion: this.region
},
serviceRoleArn: tableRole.roleArn
});

const getAllResolver = new appsync.CfnResolver(this, 'GetAllQueryResolver', {
apiId: api.attrApiId,
typeName: 'Query',
fieldName: 'all',
dataSourceName: dataSource.name,
requestMappingTemplate: `{
"version": "2017-02-28",
"operation": "Scan",
"limit": $util.defaultIfNull($ctx.args.limit, 20),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null))
}`,
responseMappingTemplate: `$util.toJson($ctx.result)`
});
getAllResolver.addDependsOn(schema);

// Add the rest of the resolvers

new cdk.CfnOutput(this, 'GraphqlApiUrl', {
value: api.attrGraphQlUrl as string,
});

new cdk.CfnOutput(this, 'UserPoolId', {
value: userPool.userPoolId,
});
}
}

I give it 5/10 here. It will take some time to fix and will confuse you again. I would say you are better off starting from scratch for this one.

Overall ChatGPT can be helpful guiding you through implementing different workloads. In our case we had to manually intervene and fix the issues. I would advise against beginners relying on ChatGPT or other AI tools to complete their tasks without first grasping the fundamentals.

--

--