Skip to content

Commit

Permalink
feat: added vpc peering article
Browse files Browse the repository at this point in the history
  • Loading branch information
bauer01 committed Feb 20, 2023
1 parent efcb0b5 commit 265c3a7
Show file tree
Hide file tree
Showing 13 changed files with 1,428 additions and 0 deletions.
8 changes: 8 additions & 0 deletions 6-vpc-peering-cdk/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
AWS_PROFILE=
ACCEPTER_ACCOUNT_ID=
REQUEST_ACCOUNT_ID=

# Setup after accepter stack deployed
ACCEPTER_VPC_ID=
ACCEPTER_CIDR=
ACCEPTER_ROLE_ARN=
9 changes: 9 additions & 0 deletions 6-vpc-peering-cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
cdk.context.json
6 changes: 6 additions & 0 deletions 6-vpc-peering-cdk/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
2 changes: 2 additions & 0 deletions 6-vpc-peering-cdk/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
use-node-version=18.14.0
engine-strict=true
15 changes: 15 additions & 0 deletions 6-vpc-peering-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Cross-account VPC Peering with CDK

## Setup

You need to have [pnpm](https://pnpm.io/installation) installed and configured AWS profile. You can also use sso commands from [package.json](./package.json) if you use [AWS Identity Center](https://aws.amazon.com/iam/identity-center).

1. See [.env.example](.env.example) variables and create new ```.env``` file with filled values.
2. Install dependencies with:

```bash
pnpm i
```

3. Deploy accepter stack first with ```pnpm run deploy:accepter``` or ```pnpm run ssod:deploy:accepter``` for SSO.
4. Deploy requester stack as the second with ```pnpm run deploy:requester``` or ```pnpm run ssod:deploy:requester``` for SSO.
58 changes: 58 additions & 0 deletions 6-vpc-peering-cdk/bin/stacks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env node
import 'source-map-support/register'
import * as cdk from 'aws-cdk-lib'
import { RequesterVpcStack } from '../lib/RequesterVpcStack'
import { AccepterVpcStack } from '../lib/AccepterVpcStack'
import * as ec2 from 'aws-cdk-lib/aws-ec2'

const accepterAccountId = process.env.ACCEPTER_ACCOUNT_ID
if (!accepterAccountId) {
throw new Error('ACCEPTER_ACCOUNT_ID is not set')
}

const accepterCidr = process.env.ACCEPTER_CIDR
if (!accepterCidr) {
throw new Error('ACCEPTER_CIDR is not set')
}

const requesterAccountId = process.env.REQUEST_ACCOUNT_ID
if (!requesterAccountId) {
throw new Error('REQUEST_ACCOUNT_ID is not set')
}

const cdkDefaultAccount = process.env.CDK_DEFAULT_ACCOUNT

const app = new cdk.App()

if (cdkDefaultAccount === accepterAccountId) {
new AccepterVpcStack(app, 'AccepterVpcStack', {
requesterAccountId,
accepterVpcCidrBlock: ec2.IpAddresses.cidr(accepterCidr),
env: {
region: 'eu-central-1',
account: accepterAccountId
}
})
}

if (cdkDefaultAccount === requesterAccountId) {
const accepterVpcId = process.env.ACCEPTER_VPC_ID
if (!accepterVpcId) {
throw new Error('ACCEPTER_VPC_ID is not set')
}
const accepterRoleArn = process.env.ACCEPTER_ROLE_ARN
if (!accepterRoleArn) {
throw new Error('ACCEPTER_ROLE_ARN is not set')
}
new RequesterVpcStack(app, 'RequesterVpcStack', {
accepterAccountId: accepterAccountId,
accepterRegion: 'eu-central-1',
accepterRoleArn,
accepterVpcId,
accepterCidr,
env: {
region: 'eu-central-1',
account: requesterAccountId
}
})
}
42 changes: 42 additions & 0 deletions 6-vpc-peering-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"app": "npx ts-node --prefer-ts-exts bin/stacks.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"pnpm-lock.yaml",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false
}
}
43 changes: 43 additions & 0 deletions 6-vpc-peering-cdk/lib/AccepterVpcStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as ec2 from 'aws-cdk-lib/aws-ec2'

interface StackProps extends cdk.StackProps {
requesterAccountId: string
accepterVpcCidrBlock: ec2.IIpAddresses
}

export class AccepterVpcStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props)

const { accepterVpcCidrBlock, requesterAccountId } = props

new ec2.Vpc(this, 'AccepterVpcStack', {
ipAddresses: accepterVpcCidrBlock,
maxAzs: 3,
subnetConfiguration: [
{
name: 'isolated',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
cidrMask: 20
}
],
enableDnsHostnames: true,
enableDnsSupport: true
})

const peeringRole = new iam.Role(this, "AcceptVpcPeeringFromRequesterAccountRole", {
roleName: "AcceptVpcPeeringFromRequesterAccountRole",
assumedBy: new iam.AccountPrincipal(requesterAccountId)
})
peeringRole.addToPolicy(new iam.PolicyStatement({
actions: [
"ec2:AcceptVpcPeeringConnection",
"ec2:ModifyVpcPeeringConnectionOptions"
],
resources: ["*"]
}))
}
}
70 changes: 70 additions & 0 deletions 6-vpc-peering-cdk/lib/RequesterVpcStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
// import { AllowVPCPeeringDNSResolution } from './constructs/AllowVPCPeeringDNSResolution'

interface StackProps extends cdk.StackProps {
accepterCidr: string
accepterVpcId: string
accepterAccountId: string
accepterRegion: string
accepterRoleArn: string
}

export class RequesterVpcStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props)

const { accepterCidr, accepterVpcId, accepterAccountId, accepterRegion, accepterRoleArn } = props

const vpc = new ec2.Vpc(this, 'RequesterVpc', {
ipAddresses: ec2.IpAddresses.cidr('172.51.0.0/16'),
maxAzs: 3,
subnetConfiguration: [
{
name: 'isolated',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
cidrMask: 20
}
],
enableDnsHostnames: true,
enableDnsSupport: true
})

const peeringConnection = new ec2.CfnVPCPeeringConnection(
this,
'RequesterToAccepterPeering',
{
vpcId: vpc.vpcId,
peerVpcId: accepterVpcId,
peerOwnerId: accepterAccountId,
peerRegion: accepterRegion,
peerRoleArn: accepterRoleArn,
tags: [
{
key: 'Name',
value: 'requester->accepter'
}
]
}
)

vpc.privateSubnets.forEach(({ routeTable: { routeTableId } }, index) => {
const route = new ec2.CfnRoute(
this,
'IsolatedSubnetPeeringConnectionRoute' + index,
{
destinationCidrBlock: accepterCidr,
routeTableId,
vpcPeeringConnectionId: peeringConnection.ref
}
)
route.addDependency(peeringConnection)
})

// new AllowVPCPeeringDNSResolution(this, "PeerConnectionDnsResolution", {
// vpcPeering: peeringConnection,
// accepterRoleArn
// })
}
}
70 changes: 70 additions & 0 deletions 6-vpc-peering-cdk/lib/constructs/AllowVPCPeeringDNSResolution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* Based on https://gist.githubusercontent.com/lfittl/78aef8a950bd1210fa67275994cb394d/raw/bf9962498cbd43116f5bd583cf8591b68cc4fec5/custom-vpc-peering-dns.ts */
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as logs from 'aws-cdk-lib/aws-logs'
import { AwsCustomResource, AwsCustomResourcePolicy, AwsSdkCall, PhysicalResourceId } from 'aws-cdk-lib/custom-resources'
import { Construct } from 'constructs'

export interface AllowVPCPeeringDNSResolutionProps {
vpcPeering: ec2.CfnVPCPeeringConnection,
accepterRoleArn: string
}

export class AllowVPCPeeringDNSResolution extends Construct {
constructor(scope: Construct, id: string, props: AllowVPCPeeringDNSResolutionProps) {
super(scope, id)

const { vpcPeering, accepterRoleArn } = props

const assumedRoleArn = accepterRoleArn

const onCreate: AwsSdkCall = {
service: "EC2",
action: "modifyVpcPeeringConnectionOptions",
// assumedRoleArn, // Probably still buggy https://github.com/aws/aws-cdk/issues/13601
parameters: {
VpcPeeringConnectionId: vpcPeering.ref,
AccepterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: true,
},
RequesterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: true
}
},
physicalResourceId: PhysicalResourceId.of(`allowVPCPeeringDNSResolution:${vpcPeering.ref}`)
}
const onUpdate = onCreate
const onDelete: AwsSdkCall = {
service: "EC2",
action: "modifyVpcPeeringConnectionOptions",
// assumedRoleArn,
parameters: {
VpcPeeringConnectionId: vpcPeering.ref,
AccepterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: false,
},
RequesterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: false
}
},
}

const customResource = new AwsCustomResource(this, "allow-peering-dns-resolution-t", {
policy: AwsCustomResourcePolicy.fromStatements([
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: ["*"],
actions: [
"ec2:ModifyVpcPeeringConnectionOptions",
]
}),
]),
logRetention: logs.RetentionDays.ONE_DAY,
onCreate,
onUpdate,
onDelete,
})

customResource.node.addDependency(vpcPeering)
}
}
43 changes: 43 additions & 0 deletions 6-vpc-peering-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "6-vpc-peering-cdk",
"version": "1.0.0",
"bin": {
"6-vpc-peering-cdk": "bin/6-vpc-peering-cdk.js"
},
"engines": {
"node": "18",
"pnpm": ">=7"
},
"scripts": {
"sso": "env-cmd -f ./.env bash -c \"ssocreds -p \\$AWS_PROFILE\"",
"env": "env-cmd -f ./.env",
"build": "tsc",
"watch": "tsc -w",
"sso:diff:accepter": "pnpm run sso && pnpm run diff:accepter",
"sso:diff:requester": "pnpm run sso && pnpm run diff:requester",
"sso:deploy:accepter": "pnpm run sso && pnpm run deploy:accepter",
"sso:deploy:requester": "pnpm run sso && pnpm run deploy:requester",
"sso:destroy:accepter": "pnpm run sso && pnpm run destroy:accepter",
"sso:destroy:requester": "pnpm run sso && pnpm run destroy:requester",
"diff:accepter": "pnpm run env -- cdk diff \"AccepterVpcStack\"",
"diff:requester": "pnpm run env -- cdk diff \"RequesterVpcStack\"",
"deploy:accepter": "pnpm run env -- cdk deploy \"AccepterVpcStack\"",
"deploy:requester": "pnpm run env -- cdk deploy \"RequesterVpcStack\"",
"destroy:accepter": "pnpm run env -- cdk destroy \"AccepterVpcStack\"",
"destroy:requester": "pnpm run env -- cdk destroy \"RequesterVpcStack\""
},
"devDependencies": {
"@types/node": "18.13.0",
"@types/source-map-support": "^0.5.6",
"aws-cdk": "2.64.0",
"aws-sso-creds-helper": "^1.10.3",
"env-cmd": "^10.1.0",
"ts-node": "^10.9.1",
"typescript": "~4.9.5"
},
"dependencies": {
"aws-cdk-lib": "2.64.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.21"
}
}
Loading

0 comments on commit 265c3a7

Please sign in to comment.