Hey all, I’m looking at adding VPC peering to my P...
# aws
s
Hey all, I’m looking at adding VPC peering to my Pulumi-based AWS lab infrastructure. The potential problem I see is that I use region-based stacks (i.e., one stack for us-west-2, one stack for us-east-2, etc.), and I’m not really clear how I could add VPC peering to this sort of configuration (since I’d need to know information from two different stacks). I suppose possibly a StackReference to pull the “remote” VPC ID might work? Any thoughts here? (I’m writing in Golang, if that makes any difference.)
b
You’d need to know the other VPC ID, so referencee them somehow. One option is a third stack that sets up the peering between the others.
s
So I did consider a third stack, but I need to write routes into the route tables of the first two stacks (or adopt the route tables into the management of the third stack, but that seems problematic). Or am I missing something? Maybe all I need is the route table ID, and then I can add routes?
a
Mine spans accounts, so in my "hub" account I wrote a lambda function that looks for incoming peering connections from spoke accounts and plumbs up the routes. The spoke stacks have the hub vpc info in a config file. The spoke stack just makes a peering request and the hub's lambda fires every minute or two, spoke builds its own routes
s
Hmm…haven’t messed around much with Lambda, but that seems like a pretty reasonable approach.
b
I’m going to dump some random code snippets here. In our system, we have a main VPC that is where our VPN is, and we set up new infrastructure in a new account/new VPC that we want to peer into the main VPC. Here we create a provider for the main VPC (“dev”) and then creating the peering object
Copy code
devProvider, err := aws.NewProvider(ctx, "dev-provider", &aws.ProviderArgs{
		AccessKey:         pulumi.String(okeraCfg.Require("vpc-credential-key")),
		SecretKey:         okeraCfg.RequireSecret("vpc-credential-secret"),
		AllowedAccountIds: pulumi.ToStringArray([]string{okeraCfg.Require("peer-aws-account")}),
		// TODO: region
	})
	if err != nil {
		return nil, err
	}

	devPeering, err := ec2.NewVpcPeeringConnection(ctx, "ssa-dev-peering", &ec2.VpcPeeringConnectionArgs{
		PeerOwnerId: pulumi.String(okeraCfg.Require("peer-aws-account")),
		PeerRegion:  pulumi.String(okeraCfg.Require("peer-vpc-region")),
		PeerVpcId:   pulumi.String(okeraCfg.Require("peer-vpc")),
		VpcId:       vpc.ID(),
	})
	if err != nil {
		return nil, err
	}
Here I create the peering acceptor (note it is using the devProvider):
Copy code
_, err = ec2.NewVpcPeeringConnectionAccepter(ctx, "ssa-dev-peering-acceptor", &ec2.VpcPeeringConnectionAccepterArgs{
		VpcPeeringConnectionId: devPeering.ID(),
		AutoAccept:             pulumi.Bool(true),
	}, pulumi.Provider(devProvider))
	if err != nil {
		return nil, err
	}
Here it’s added to a specific route table (this is part of creating a new subnet and a new route table in this VPC) in the new VPC:
Copy code
privateRouteTable, err := ec2.NewRouteTable(ctx, fmt.Sprintf("private-rt-ssa-%s", az), &ec2.RouteTableArgs{
			VpcId: vpc.ID(),
			Routes: ec2.RouteTableRouteArray{
				ec2.RouteTableRouteArgs{CidrBlock: pulumi.String("0.0.0.0/0"), NatGatewayId: natGateway.ID()},
				ec2.RouteTableRouteArgs{CidrBlock: pulumi.String(okeraCfg.Require("peer-vpc-cidr")), VpcPeeringConnectionId: devPeering.ID()},
			},
		})
		if err != nil {
and here we go and add the routes to the other VPC:
Copy code
peeredRoutes := make(ec2.RouteTableRouteArray, len(privateCidrs))
	for idx, privateCidr := range privateCidrs {
		peeredRoutes[idx] = ec2.RouteTableRouteArgs{CidrBlock: pulumi.String(privateCidr), VpcPeeringConnectionId: devPeering.ID()}
	}

	devVpcId := okeraCfg.Require("peer-vpc")
	devVpc, err := ec2.LookupVpc(ctx, &ec2.LookupVpcArgs{
		Id: &devVpcId,
	}, pulumi.Provider(devProvider))
	if err != nil {
		return nil, err
	}

	for idx, privateCidr := range privateCidrs {
		ec2.NewRoute(ctx, fmt.Sprintf("dev-vpc-peering-%s-route-%d", awsCfg.Require("region"), idx), &ec2.RouteArgs{
			RouteTableId:           pulumi.String(devVpc.MainRouteTableId),
			DestinationCidrBlock:   pulumi.String(privateCidr),
			VpcPeeringConnectionId: devPeering.ID(),
		}, pulumi.Provider(devProvider))
		if err != nil {
			return nil, err
		}
	}
Not sure it’s helpful (I elided some details), but to give you a sense.
s
Thank you @bored-table-20691, this confirms a few thoughts I had about how I’d need to go about doing this. Getting ready to start working on it now. Appreciate it!
Quick update: I have this (mostly) working, based in part on Itay’s code shared above. The only wrinkle I have to fix is adjusting security groups in my base infrastructure stack; it’s not clear to me (yet) if I can adjust rules in those groups from this separate “peering-specific” stack, or if I’ll have to modify the base stacks. Good progress so far though!
2nd update: Adding the security group rule using the security group ID from the base infrastructure stack worked perfectly.