Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@scottslowe
Created September 16, 2022 20:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottslowe/e182e0aa36475dad4aad905963d09082 to your computer and use it in GitHub Desktop.
Save scottslowe/e182e0aa36475dad4aad905963d09082 to your computer and use it in GitHub Desktop.
An example of using Pulumi with Go to create a VPC, subnets, route tables, routes, Internet Gateway, NAT Gateway, etc.
package main
import (
"fmt"
"log"
"github.com/pulumi/pulumi-aws/sdk/v4/go/aws"
"github.com/pulumi/pulumi-aws/sdk/v4/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Get some values from the Pulumi stack configuration
awsRegion := config.Require(ctx, "aws:region")
keyPair := config.Require(ctx, "keypairname")
bastionAmiType := config.Get(ctx, "bastiontype")
if bastionAmiType == "" {
bastionAmiType = "t3a.small"
}
// Create a map of regions and friendly names
regionNames := make(map[string]string)
regionNames["us-west-2"] = "oregon"
regionNames["us-east-2"] = "ohio"
regionNames["ca-central-1"] = "central"
// Create a map of regions and network addresses
netAddrMap := make(map[string]string)
netAddrMap["us-west-2"] = "172.29."
netAddrMap["us-east-2"] = "172.30."
netAddrMap["ca-central-1"] = "172.26."
// Define some values to be used later
baseName := fmt.Sprintf("%s-lab", regionNames[awsRegion])
k8sTag := fmt.Sprintf("kubernetes.io/cluster/%s-mgmt", regionNames[awsRegion])
// TODO: Implement common tags and per-resource tags
// Create a common map of tags for all resources
// commonTags := make(map[string]string)
// commonTags["stackLanguage"] = "Go"
// Look up Availability Zone (AZ) information for configured region
desiredAzState := "available"
rawAzInfo, err := aws.GetAvailabilityZones(ctx, &aws.GetAvailabilityZonesArgs{
State: &desiredAzState,
})
if err != nil {
log.Printf("error getting AZs: %s", err.Error())
return err
}
// Determine how many AZs are present
numOfAZs := len(rawAzInfo.Names)
ctx.Export("numOfAZs", pulumi.Int(numOfAZs))
// Build a list of AZ names
azNames := make([]string, numOfAZs)
for idx := 0; idx < numOfAZs; idx++ {
azNames[idx] = rawAzInfo.Names[idx]
}
// Create new VPC
vpc, err := ec2.NewVpc(ctx, "vpc", &ec2.VpcArgs{
CidrBlock: pulumi.String(fmt.Sprintf("%s0.0/16", netAddrMap[awsRegion])),
EnableDnsSupport: pulumi.Bool(true),
EnableDnsHostnames: pulumi.Bool(true),
Tags: pulumi.StringMap{
"Name": pulumi.String(baseName),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error creating VPC: %s", err.Error())
return err
}
ctx.Export("vpcId", vpc.ID())
// Create public subnets in the VPC
pubSubnetIds := make([]pulumi.StringInput, numOfAZs)
for idx := 0; idx < numOfAZs; idx++ {
subnetAddr := idx * 32
subnetCidrBlock := fmt.Sprintf("%s%d.0/22", netAddrMap[awsRegion], subnetAddr)
subnet, err := ec2.NewSubnet(ctx, fmt.Sprintf("pub-subnet-%d", idx), &ec2.SubnetArgs{
VpcId: vpc.ID(),
AvailabilityZone: pulumi.String(azNames[idx]),
CidrBlock: pulumi.String(subnetCidrBlock),
MapPublicIpOnLaunch: pulumi.Bool(true),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-pub-%d", baseName, idx)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error creating public subnet: %s", err.Error())
}
pubSubnetIds[idx] = subnet.ID()
}
ctx.Export("pubSubnetIds", pulumi.StringArray(pubSubnetIds))
// Create an Internet gateway for the public subnets
gw, err := ec2.NewInternetGateway(ctx, "inetgw", &ec2.InternetGatewayArgs{
VpcId: vpc.ID(),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-gw", baseName)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error creating Internet Gateway: %s", err.Error())
}
ctx.Export("gatewayId", gw.ID())
// Adopt the default route table in the new VPC
defrt, err := ec2.NewDefaultRouteTable(ctx, "def-rt", &ec2.DefaultRouteTableArgs{
DefaultRouteTableId: vpc.DefaultRouteTableId,
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-def-rt", baseName)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error adopting default route table: %s", err.Error())
}
ctx.Export("defaultRoute", defrt.ID())
// Create a route for Internet access in the default route table
route, err := ec2.NewRoute(ctx, "inet-route", &ec2.RouteArgs{
RouteTableId: defrt.ID(),
DestinationCidrBlock: pulumi.String("0.0.0.0/0"),
GatewayId: gw.ID(),
})
if err != nil {
log.Printf("error creating route: %s", err.Error())
}
ctx.Export("inetRoute", route.ID())
// Create private subnets in the VPC
privSubnetIds := make([]pulumi.StringInput, numOfAZs)
for idx := 0; idx < numOfAZs; idx++ {
subnetAddr := (idx * 32) + 16
subnetCidrBlock := fmt.Sprintf("%s%d.0/22", netAddrMap[awsRegion], subnetAddr)
subnet, err := ec2.NewSubnet(ctx, fmt.Sprintf("priv-subnet-%d", idx), &ec2.SubnetArgs{
VpcId: vpc.ID(),
AvailabilityZone: pulumi.String(azNames[idx]),
CidrBlock: pulumi.String(subnetCidrBlock),
MapPublicIpOnLaunch: pulumi.Bool(false),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-priv-%d", baseName, idx)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error creating private subnet: %s", err.Error())
}
privSubnetIds[idx] = subnet.ID()
}
ctx.Export("privSubnetIds", pulumi.StringArray(privSubnetIds))
// Create/allocate an Elastic IP address for the NAT gateway for private subnets
eip, err := ec2.NewEip(ctx, "natgw-eip", &ec2.EipArgs{
Vpc: pulumi.Bool(true),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-natgw-eip", baseName)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error creating EIP: %s", err.Error())
}
ctx.Export("EIP", eip.AllocationId)
// Create a NAT gateway for the private subnets
natgw, err := ec2.NewNatGateway(ctx, "natgw", &ec2.NatGatewayArgs{
AllocationId: eip.ID(),
SubnetId: pubSubnetIds[0],
Tags: pulumi.StringMap{
"Name": pulumi.String(baseName),
k8sTag: pulumi.String("shared"),
},
}, pulumi.DependsOn([]pulumi.Resource{eip}))
if err != nil {
log.Printf("error creating NAT gateway: %s", err.Error())
}
ctx.Export("natGateway", natgw.ID())
// Create a new route table for Internet access from private subnets
privrt, err := ec2.NewRouteTable(ctx, "priv-rt", &ec2.RouteTableArgs{
VpcId: vpc.ID(),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-priv-rt", baseName)),
k8sTag: pulumi.String("shared"),
},
Routes: ec2.RouteTableRouteArray{
&ec2.RouteTableRouteArgs{
CidrBlock: pulumi.String("0.0.0.0/0"),
NatGatewayId: natgw.ID(),
},
},
})
if err != nil {
log.Printf("error creating private route table: %s", err.Error())
}
ctx.Export("privRouteTableId", privrt.ID())
// Associate the private subnets with the NAT gateway route table
for idx := 0; idx < numOfAZs; idx++ {
_, err := ec2.NewRouteTableAssociation(ctx, fmt.Sprintf("priv-rta-%d", idx), &ec2.RouteTableAssociationArgs{
SubnetId: privSubnetIds[idx],
RouteTableId: privrt.ID(),
})
if err != nil {
log.Printf("error associating private subnet with route table: %s", err.Error())
}
}
// Create a security group for traffic to the SSH bastion host
bastionSecGrp, err := ec2.NewSecurityGroup(ctx, "bastion-sg", &ec2.SecurityGroupArgs{
Name: pulumi.String(fmt.Sprintf("%s-bastion-sg", baseName)),
VpcId: vpc.ID(),
Description: pulumi.String("Allows SSH traffic to bastion hosts"),
Ingress: ec2.SecurityGroupIngressArray{
ec2.SecurityGroupIngressArgs{
Protocol: pulumi.String("tcp"),
ToPort: pulumi.Int(22),
FromPort: pulumi.Int(22),
Description: pulumi.String("Allow inbound SSH (TCP 22) from anywhere"),
CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")},
},
},
Egress: ec2.SecurityGroupEgressArray{
ec2.SecurityGroupEgressArgs{
Protocol: pulumi.String("-1"),
ToPort: pulumi.Int(0),
FromPort: pulumi.Int(0),
Description: pulumi.String("Allow all outbound traffic"),
CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")},
},
},
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-bastion-sg", baseName)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error creating security group: %s", err.Error())
}
ctx.Export("bastionSecGrpId", bastionSecGrp.ID())
// Create a security group for nodes in this VPC
nodeSecGrp, err := ec2.NewSecurityGroup(ctx, "node-sg", &ec2.SecurityGroupArgs{
Name: pulumi.String(fmt.Sprintf("%s-node-sg", baseName)),
VpcId: vpc.ID(),
Description: pulumi.String("Allows traffic between and among K8s nodes"),
Ingress: ec2.SecurityGroupIngressArray{
ec2.SecurityGroupIngressArgs{
Protocol: pulumi.String("tcp"),
ToPort: pulumi.Int(22),
FromPort: pulumi.Int(22),
Description: pulumi.String("Allow inbound SSH (TCP 22) from bastion hosts"),
SecurityGroups: pulumi.StringArray{bastionSecGrp.ID()},
},
ec2.SecurityGroupIngressArgs{
Protocol: pulumi.String("-1"),
ToPort: pulumi.Int(0),
FromPort: pulumi.Int(0),
Description: pulumi.String("Allow all traffic from this security group"),
Self: pulumi.Bool(true),
},
},
Egress: ec2.SecurityGroupEgressArray{
ec2.SecurityGroupEgressArgs{
Protocol: pulumi.String("-1"),
ToPort: pulumi.Int(0),
FromPort: pulumi.Int(0),
Description: pulumi.String("Allow all outbound traffic"),
CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")},
},
},
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-node-sg", baseName)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error creating security group: %s", err.Error())
}
ctx.Export("nodeSecGrpId", nodeSecGrp.ID())
// Create a security group for the AWS cloud provider to manage
k8sSecGrp, err := ec2.NewSecurityGroup(ctx, "k8s-sg", &ec2.SecurityGroupArgs{
Name: pulumi.String(fmt.Sprintf("%s-k8s-sg", baseName)),
VpcId: vpc.ID(),
Description: pulumi.String("Managed by K8s cloud provider"),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-k8s-sg", baseName)),
k8sTag: pulumi.String("owned"),
},
})
if err != nil {
log.Printf("error creating security group: %s", err.Error())
}
ctx.Export("k8sSecGrpId", k8sSecGrp.ID())
// Get AMI ID for bastion host
mostRecent := true
bastionAmi, err := ec2.LookupAmi(ctx, &ec2.LookupAmiArgs{
Owners: []string{"099720109477"},
MostRecent: &mostRecent,
Filters: []ec2.GetAmiFilter{
{Name: "name", Values: []string{"ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server*"}},
{Name: "root-device-type", Values: []string{"ebs"}},
{Name: "virtualization-type", Values: []string{"hvm"}},
{Name: "architecture", Values: []string{"x86_64"}},
},
})
if err != nil {
log.Printf("error looking up bastion AMI: %s", err.Error())
}
// Launch an instance to serve as bastion host
bastion, err := ec2.NewInstance(ctx, "bastion", &ec2.InstanceArgs{
Ami: pulumi.String(bastionAmi.Id),
InstanceType: pulumi.String(bastionAmiType),
AssociatePublicIpAddress: pulumi.Bool(true),
KeyName: pulumi.String(keyPair),
SubnetId: pubSubnetIds[0],
SourceDestCheck: pulumi.Bool(false),
VpcSecurityGroupIds: pulumi.StringArray{bastionSecGrp.ID()},
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("bastion-%s", baseName)),
k8sTag: pulumi.String("shared"),
},
})
if err != nil {
log.Printf("error launching instance: %s", err.Error())
}
ctx.Export("bastionInstanceId", bastion.ID())
ctx.Export("bastionPublicIpAddress", bastion.PublicIp)
ctx.Export("bastionPrivateIpAddress", bastion.PrivateIp)
return nil
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment