AWS CDK in Go is a way to define your cloud infrastructure using familiar Go code, which then synthesizes into AWS CloudFormation templates.

Here’s a simple Go application that defines an S3 bucket and a Lambda function, and then deploys them to AWS.

package main

import (
	"github.com/aws/aws-cdk-go/awscdk/v2"
	"github.com/aws/aws-cdk-go/awscdk/v2/awsiam"
	"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
	"github.com/aws/aws-cdk-go/awscdk/v2/awslogs"
	"github.com/aws/constructs-go/constructs/v10"
	"github.com/aws/jsii-runtime-go/runtime"
	"os"
)

type CdkGoStackProps struct {
	awscdk.StackProps
}

func NewCdkGoStack(scope constructs.Construct, id string, props *CdkGoStackProps) awscdk.Stack {
	var sprops awscdk.StackProps
	if props != nil {
		sprops = props.StackProps
	}
	stack := awscdk.NewStack(scope, &id, &sprops)

	// Define an S3 bucket
	bucket := awscdk.NewBucket(stack, jsii.String("MyGoBucket"), &awscdk.BucketProps{
		Versioned: awsi.Bool(true),
		RemovalPolicy: awscdk.RemovalPolicy_DESTROY,
	})

	// Define a Lambda function
	lambdaFunc := awslambda.NewFunction(stack, jsii.String("MyGoLambda"), &awslambda.FunctionProps{
		Runtime: awslambda.Runtime_PROVIDED_AL2(), // Use a custom runtime or a managed one
		Handler: "main",                            // Entrypoint in your Go binary
		Code: awslambda.Code_FromAsset(jsii.String("lambda/"), &awscdk.AssetOptions{}),
		MemorySize: jsii.Number(128),
		Timeout: awscdk.Duration_Seconds(jsii.Number(30)),
		LogRetention: awslogs.RetentionDays_ONE_WEEK,
		Environment: &map[string]*string{
			"BUCKET_NAME": bucket.BucketName(),
		},
	})

	// Grant the Lambda function read/write access to the S3 bucket
	bucket.GrantReadWrite(lambdaFunc, nil)

	return stack
}

func main() {
	app := awscdk.NewApp(nil)

	NewCdkGoStack(app, "CdkGoStack", &CdkGoStackProps{
		awscdk.StackProps{
			Env: &awscdk.Environment{
				Account: jsii.String(os.Getenv("CDK_DEFAULT_ACCOUNT")),
				Region:  jsii.String(os.Getenv("CDK_DEFAULT_REGION")),
			},
		},
	})

	app.Synth(nil)
}

This defines an S3 bucket and a Lambda function. The Lambda function is configured to use a custom Go runtime (or a managed one if you adapt it) and is granted permissions to read and write to the bucket. The bucket name is passed as an environment variable to the Lambda function.

The core problem CDK solves is the "snowflake" infrastructure issue. Historically, you’d manually create resources, document them, and hope for consistency. If you needed to replicate an environment, you’d start over, often introducing subtle differences. CDK allows you to define your infrastructure as code, making it versionable, testable, and repeatable. It acts as an abstraction layer over CloudFormation, letting you use your favorite programming language to describe your AWS resources.

Internally, the CDK works by:

  1. Constructs: You write code using constructs, which are the basic building blocks. A construct can represent a single AWS resource (like an S3 bucket) or a higher-level abstraction that combines multiple resources (like a fully managed Kubernetes cluster).
  2. Synthesis: When you run cdk synth, the CDK application traverses your construct tree. Each construct has a bind method that associates it with its parent stack and context. It then calls a render or similar method to produce CloudFormation template snippets. These snippets are aggregated and validated.
  3. CloudFormation: The output of cdk synth is a CloudFormation JSON or YAML template. This template is what you deploy to AWS using cdk deploy. CloudFormation then provisions and manages the resources.

The Runtime: awslambda.Runtime_PROVIDED_AL2() line is crucial for Go Lambdas. AWS Lambda provides managed runtimes for languages like Python and Node.js. For Go, you typically need to package your application as a self-contained executable that adheres to the Lambda runtime interface. This means your Go program must listen on a specific port (8080 by default) and respond to Lambda runtime API requests. The Code: awslambda.Code_FromAsset(jsii.String("lambda/"), &awscdk.AssetOptions{}) points to a directory where your compiled Go binary and any necessary files reside.

When you grant permissions, like bucket.GrantReadWrite(lambdaFunc, nil), the CDK automatically generates the necessary IAM policies and attaches them to the Lambda function’s execution role. This saves you from manually creating and configuring IAM policies, which is a common source of errors and security misconfigurations. The RemovalPolicy: awscdk.RemovalPolicy_DESTROY on the bucket means that when you delete the stack, the bucket and its contents will also be deleted. Be cautious with this in production environments.

A detail often missed is how the Go CDK bindings handle primitive types. You’ll notice jsii.String("MyGoBucket") instead of "MyGoBucket". The jsii runtime is a bridge between the TypeScript-based CDK core and other language bindings (like Go, Java, Python). It serializes and deserializes data between these languages. For simple strings, numbers, and booleans, you often need to use these jsii wrappers to ensure proper type conversion and interoperability with the underlying JavaScript/TypeScript logic that powers the CDK.

The next step after defining your infrastructure is to manage its lifecycle.

Want structured learning?

Take the full Cdk course →