-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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