How To Create a VPC In AWS Using Pulumi And Golang

How To Create a VPC In AWS Using Pulumi And Golang

The source code you write as a developer is important, but it is only one part of the entire application that goes into production. To deploy an app, you’ll need resources like API gateways, S3 buckets, or VPCs as well. Configuring those resources is a task you don’t want to do manually. How about building your infrastructure as code using the same language you’ve built your app in. That is what Pulumi allows you to do!

Most of the resources deployed to any of the major cloud providers need to be deployed inside a VPC. So let’s walk through the steps to create a VPC using the Pulumi Go SDK. The complete project is available on GitHub.

A new project

If you haven’t done so already, you might want to take a quick second and head over to the Pulumi website to create a new account. It’s free forever for personal use! Once you’ve done that and installed the CLI you’re ready to start. To get going, you’ll need to create a new Pulumi project. On the command line you can specify which template you want to use (go in this case). You’ll be able to specify the project name (with the --name flag), the description (with the --description flag), and the stack name (with the --stack flag).

pulumi new go \
--name builder \
--description "An awesome Pulumi infrastructure-as-code Stack" \
--stack retgits/builderstack

In the above example, I’ve used the project name builder and the stack is called retgits/builderstack.

Using Go modules

The current Go template still relies on dep for dependency management, but with the introduction of Go modules you probably want to use that to manage dependencies. Luckily, switching from dep to Go modules only requires three commands.

go mod init github.com/retgits/builder
go mod tidy
rm Gopkg.*

The first command creates a new Go module in the directory where you run the command. The module needs a name and in this example the name of the module is github.com/retgits/builder. The second command takes the input from Gopkg.toml and creates entries in go.mod for each entry. The third command removes the Gopkg files.

Default configuration variables

To ultimate deploy to AWS, Pulumi needs to know how to connect to AWS. To connect, Pulumi uses a provider (more on that later). The configuration for that provider can be set through the command line: pulumi config set aws:<option>.

pulumi config set aws:profile default
pulumi config set aws:region us-east-1

The example above, has the required parameter aws:region which tells Pulumi which region to deploy to (in this case us-east-1) and the optional parameter aws:profile which tells Pulumi the profile to use (in this case default). The profiles you can choose are the ones you created using aws configure.

Adding more configuration variables

As a best practice, AWS suggests that you add tags to each resource you create (at least the ones that support tags). Tags are simple key/value pairs that can make it easier to manage, search for, and filter resources. The tags that I use when I deploy code to AWS, and the ones that are in the sample are version (because infrastructure-as-code makes it easy to version your infrastructure too), author (so you can see who did the deployment), team (so you can see which team did a deployment), and feature (resources are generally part of a larger app so with this you can filter all resources for an app you’re looking for). If you want to change the tags, feel free to do so in the source code. To create a VPC, you’ll need two parameters. Firstly, the name of the VPC and secondly you’ll need to specify the CIDR block.

tags:version: "0.1.0"
tags:author: <your name>
tags:team: <your team>
tags:feature: myFirstVPCWithPulumi
vpc:name: myPulumiVPC
vpc:cidr-block: "172.32.0.0/16"

There are two ways you can add configuration variables to your Pulumi config. You can either use the command line, like pulumi config set tags:version "0.1.0", or you can add them directly into the yaml file. The yaml file with all the configuration is called Pulumi.<name of your project>.yaml.

Using configuration variables in your code

The configuration variables that you’ve added to the YAML file are accessible through the pulumi.Context object. You can use the GetConfig() method to search for your key and you’ll get a string and a boolean in return. The boolean tells whether the context could find the key you’re looking for. To make that process a little easier and provide the option to have default values, you can add a method like the one below.

// getEnv searches for the requested key in the pulumi context and provides either the value of the key or the fallback.
func getEnv(ctx *pulumi.Context, key string, fallback string) string {
	if value, ok := ctx.GetConfig(key); ok {
		return value
	}
	return fallback
}

Making it all work

Now that most of the code is ready, it’s time to create a VPC. In the code below you can see a map[string]interface{} called tags which is used to tag all the resources created with for this project. Pulumi creates the VPC by executing ec2.NewVpc(). As you likely want to know the ID of the newly created VPC, Pulumi makes it easy to take output from the AWS and export it as outputs so you can see them in the Pulumi console.

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Prepare the tags that are used for each individual resource so they can be found
		// using the Resource Groups service in the AWS Console
		tags := make(map[string]interface{})
		tags["version"] = getEnv(ctx, "tags:version", "unknown")
		tags["author"] = getEnv(ctx, "tags:author", "unknown")
		tags["team"] = getEnv(ctx, "tags:team", "unknown")
		tags["feature"] = getEnv(ctx, "tags:feature", "unknown")
		tags["region"] = getEnv(ctx, "aws:region", "unknown")

		// Create a VPC for the EKS cluster
		cidrBlock := getEnv(ctx, "vpc:cidr-block", "unknown")

		vpcArgs := &ec2.VpcArgs{
			CidrBlock: cidrBlock,
			Tags:      tags,
		}

		vpcName := getEnv(ctx, "vpc:name", "unknown")

		vpc, err := ec2.NewVpc(ctx, vpcName, vpcArgs)
		if err != nil {
			fmt.Println(err.Error())
			return err
		}

		// Export IDs of the created resources to the Pulumi stack
		ctx.Export("VPC-ID", vpc.ID())
		return nil
	})
}

Running the code

While most other languages rely on the package manager of the language to install Pulumi plugins (also called providers), Go does not. In order to deploy to AWS, you’ll need to manually install the AWS provider. To install the latest version (as of writing this blog) of the AWS provider, you can run the below command.

pulumi plugin install resource aws v1.17.0

The last thing to do is run pulumi up to tell Pulumi to go create your VPC!

$ pulumi up
Previewing update (builderstack):

     Type                 Name                  Plan       
 +   pulumi:pulumi:Stack  builder-builderstack  create     
 +   └─ aws:ec2:Vpc       myPulumiVPC           create     
 
Resources:
    + 2 to create

Do you want to perform this update? yes
Updating (builderstack):

     Type                 Name                  Status      
 +   pulumi:pulumi:Stack  builder-builderstack  created     
 +   └─ aws:ec2:Vpc       myPulumiVPC           created     
 
Outputs:
    VPC-ID: "vpc-<id>"

Resources:
    + 2 created

Duration: 11s

Permalink: https://app.pulumi.com/retgits/builder/builderstack/updates/1

The permalink at the bottom of the output takes you to the Pulumi console where you can see all the details of the execution of your app and the resources that were created.

The Pulumi console also has really useful links to the AWS console to see the resources.

The Pulumi console also has really useful links to the AWS console to see the resources.

Let's connect

If you have any questions or comments, feel free to drop me a note on Twitter!

Cover image by Free-Photos from Pixabay