https://pulumi.com logo
Title
s

salmon-account-74572

08/19/2021, 11:14 PM
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

bored-table-20691

08/19/2021, 11:24 PM
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

salmon-account-74572

08/20/2021, 12:25 AM
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

acoustic-window-73051

08/20/2021, 2:31 PM
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

salmon-account-74572

08/20/2021, 4:30 PM
Hmm…haven’t messed around much with Lambda, but that seems like a pretty reasonable approach.
b

bored-table-20691

08/20/2021, 5:12 PM
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
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):
_, 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:
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:
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

salmon-account-74572

08/20/2021, 5:36 PM
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.