Skip to content

Commit 59288fc

Browse files
authored
Fix: [AEA-0000] - add vpc endpoints (#17)
## Summary - Routine Change ### Details - add vpc endpoints for private subnets - add option in cdk_release_code to not deploy changes and only show diff - show cdk diff on pull requests - correct export of private subnets
1 parent 1d001dc commit 59288fc

File tree

8 files changed

+235
-1
lines changed

8 files changed

+235
-1
lines changed

.github/workflows/cdk_release_code.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ on:
1515
LOG_RETENTION_IN_DAYS:
1616
required: true
1717
type: string
18+
DEPLOY_CHANGE:
19+
type: boolean
20+
default: true
1821
secrets:
1922
CLOUD_FORMATION_DEPLOY_ROLE:
2023
required: true
@@ -107,6 +110,7 @@ jobs:
107110
shell: bash
108111

109112
- name: Deploy code
113+
if: ${{ inputs.DEPLOY_CHANGE == true}}
110114
run: |
111115
docker run \
112116
-v "$(pwd)/.build":/home/cdkuser/workspace/ \

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ jobs:
102102
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
103103
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
104104
LOG_RETENTION_IN_DAYS: 30
105+
DEPLOY_CHANGE: true
105106
secrets:
106107
CDK_PULL_IMAGE_ROLE: ${{ secrets.DEV_CDK_PULL_IMAGE_ROLE }}
107108
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}
@@ -130,6 +131,7 @@ jobs:
130131
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
131132
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
132133
LOG_RETENTION_IN_DAYS: 30
134+
DEPLOY_CHANGE: true
133135
secrets:
134136
CDK_PULL_IMAGE_ROLE: ${{ secrets.QA_CDK_PULL_IMAGE_ROLE }}
135137
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.QA_CLOUD_FORMATION_DEPLOY_ROLE }}

.github/workflows/pull_request.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,22 @@ jobs:
5252
run: |
5353
echo "commit_id=${{ github.sha }}" >> "$GITHUB_OUTPUT"
5454
55+
package_code:
56+
needs: [quality_checks, get_issue_number, get_commit_id]
57+
uses: ./.github/workflows/cdk_package_code.yml
58+
with:
59+
VERSION_NUMBER: ${{needs.get_issue_number.outputs.issue_number}}
60+
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
61+
62+
show_dev_changes:
63+
needs: [quality_checks, get_issue_number, package_code, get_commit_id]
64+
uses: ./.github/workflows/cdk_release_code.yml
65+
with:
66+
TARGET_ENVIRONMENT: dev
67+
VERSION_NUMBER: ${{needs.get_issue_number.outputs.issue_number}}
68+
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
69+
LOG_RETENTION_IN_DAYS: 30
70+
DEPLOY_CHANGE: false
71+
secrets:
72+
CDK_PULL_IMAGE_ROLE: ${{ secrets.DEV_CDK_PULL_IMAGE_ROLE }}
73+
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}

.github/workflows/release.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ jobs:
121121
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
122122
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
123123
LOG_RETENTION_IN_DAYS: 30
124+
DEPLOY_CHANGE: true
124125
secrets:
125126
CDK_PULL_IMAGE_ROLE: ${{ secrets.DEV_CDK_PULL_IMAGE_ROLE }}
126127
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}
@@ -148,6 +149,7 @@ jobs:
148149
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
149150
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
150151
LOG_RETENTION_IN_DAYS: 30
152+
DEPLOY_CHANGE: true
151153
secrets:
152154
CDK_PULL_IMAGE_ROLE: ${{ secrets.REF_CDK_PULL_IMAGE_ROLE }}
153155
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.REF_CLOUD_FORMATION_DEPLOY_ROLE }}
@@ -160,6 +162,7 @@ jobs:
160162
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
161163
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
162164
LOG_RETENTION_IN_DAYS: 30
165+
DEPLOY_CHANGE: true
163166
secrets:
164167
CDK_PULL_IMAGE_ROLE: ${{ secrets.QA_CDK_PULL_IMAGE_ROLE }}
165168
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.QA_CLOUD_FORMATION_DEPLOY_ROLE }}
@@ -172,6 +175,7 @@ jobs:
172175
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
173176
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
174177
LOG_RETENTION_IN_DAYS: 30
178+
DEPLOY_CHANGE: true
175179
secrets:
176180
CDK_PULL_IMAGE_ROLE: ${{ secrets.INT_CDK_PULL_IMAGE_ROLE }}
177181
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.INT_CLOUD_FORMATION_DEPLOY_ROLE }}
@@ -184,6 +188,7 @@ jobs:
184188
VERSION_NUMBER: ${{needs.tag_release.outputs.version_tag}}
185189
COMMIT_ID: ${{needs.get_commit_id.outputs.commit_id}}
186190
LOG_RETENTION_IN_DAYS: 30
191+
DEPLOY_CHANGE: true
187192
secrets:
188193
CDK_PULL_IMAGE_ROLE: ${{ secrets.PROD_CDK_PULL_IMAGE_ROLE }}
189194
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.PROD_CLOUD_FORMATION_DEPLOY_ROLE }}

packages/cdk/bin/VpcResourcesApp.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const VpcResources = new VpcResourcesStack(app, "VpcResourcesStack", {
3535
app.synth()
3636

3737
addCfnGuardMetadata(VpcResources, "Custom::VpcRestrictDefaultSGCustomResourceProvider", "Handler")
38+
addCfnGuardMetadata(VpcResources, "AWS679f53fac002430cb0da5b7982bd2287", "Resource")
3839

3940
// finally run synth again with force to include the added metadata
4041
app.synth({

packages/cdk/nagSuppressions.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
2+
import {Stack} from "aws-cdk-lib"
3+
import {NagPackSuppression, NagSuppressions} from "cdk-nag"
4+
5+
export const nagSuppressions = (stack: Stack) => {
6+
safeAddNagSuppression(
7+
stack,
8+
"/VpcResourcesStack/ECRDockerEndpoint-tags/CustomResourcePolicy/Resource",
9+
[
10+
{
11+
id: "AwsSolutions-IAM5",
12+
reason: "Suppress error for wildcard permissions. This is fine here"
13+
}
14+
]
15+
)
16+
17+
safeAddNagSuppression(
18+
stack,
19+
"/VpcResourcesStack/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/Resource",
20+
[
21+
{
22+
id: "AwsSolutions-IAM4",
23+
reason: "Suppress error for using AWS managed policy. This is fine here"
24+
}
25+
]
26+
)
27+
28+
safeAddNagSuppression(
29+
stack,
30+
"/VpcResourcesStack/AWS679f53fac002430cb0da5b7982bd2287/Resource",
31+
[
32+
{
33+
id: "AwsSolutions-L1",
34+
reason: "Suppress error for using not using latest runtime. This is fine here"
35+
}
36+
]
37+
)
38+
39+
safeAddNagSuppression(
40+
stack,
41+
"/VpcResourcesStack/ECREndpoint-tags/CustomResourcePolicy/Resource",
42+
[
43+
{
44+
id: "AwsSolutions-IAM5",
45+
reason: "Suppress error for wildcard permissions. This is fine here"
46+
}
47+
]
48+
)
49+
50+
safeAddNagSuppression(
51+
stack,
52+
"/VpcResourcesStack/SecretManagerEndpoint-tags/CustomResourcePolicy/Resource",
53+
[
54+
{
55+
id: "AwsSolutions-IAM5",
56+
reason: "Suppress error for wildcard permissions. This is fine here"
57+
}
58+
]
59+
)
60+
61+
safeAddNagSuppression(
62+
stack,
63+
"/VpcResourcesStack/CloudWatchEndpoint-tags/CustomResourcePolicy/Resource",
64+
[
65+
{
66+
id: "AwsSolutions-IAM5",
67+
reason: "Suppress error for wildcard permissions. This is fine here"
68+
}
69+
]
70+
)
71+
72+
safeAddNagSuppression(
73+
stack,
74+
"/VpcResourcesStack/CloudWatchLogsEndpoint-tags/CustomResourcePolicy/Resource",
75+
[
76+
{
77+
id: "AwsSolutions-IAM5",
78+
reason: "Suppress error for wildcard permissions. This is fine here"
79+
}
80+
]
81+
)
82+
83+
safeAddNagSuppression(
84+
stack,
85+
"/VpcResourcesStack/CloudWatchEventsEndpoint-tags/CustomResourcePolicy/Resource",
86+
[
87+
{
88+
id: "AwsSolutions-IAM5",
89+
reason: "Suppress error for wildcard permissions. This is fine here"
90+
}
91+
]
92+
)
93+
94+
safeAddNagSuppression(
95+
stack,
96+
"/VpcResourcesStack/SSMEndpoint-tags/CustomResourcePolicy/Resource",
97+
[
98+
{
99+
id: "AwsSolutions-IAM5",
100+
reason: "Suppress error for wildcard permissions. This is fine here"
101+
}
102+
]
103+
)
104+
105+
safeAddNagSuppression(
106+
stack,
107+
"/VpcResourcesStack/S3Endpoint-tags/CustomResourcePolicy/Resource",
108+
[
109+
{
110+
id: "AwsSolutions-IAM5",
111+
reason: "Suppress error for wildcard permissions. This is fine here"
112+
}
113+
]
114+
)
115+
116+
}
117+
118+
const safeAddNagSuppression = (stack: Stack, path: string, suppressions: Array<NagPackSuppression>) => {
119+
try {
120+
NagSuppressions.addResourceSuppressionsByPath(stack, path, suppressions)
121+
122+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
123+
} catch(err){
124+
console.log(`Could not find path ${path}`)
125+
}
126+
}

packages/cdk/stacks/VpcResourcesStack.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ import {
1010
import {
1111
CfnSubnet,
1212
FlowLogDestination,
13+
GatewayVpcEndpoint,
14+
InterfaceVpcEndpoint,
15+
InterfaceVpcEndpointAwsService,
1316
IpAddresses,
17+
IVpcEndpoint,
18+
Peer,
1419
Vpc
1520
} from "aws-cdk-lib/aws-ec2"
1621
import {Role, ServicePrincipal} from "aws-cdk-lib/aws-iam"
1722
import {Key} from "aws-cdk-lib/aws-kms"
1823
import {LogGroup} from "aws-cdk-lib/aws-logs"
24+
import {AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId} from "aws-cdk-lib/custom-resources"
25+
26+
import {nagSuppressions} from "../nagSuppressions"
1927

2028
export interface VpcResourcesStackProps extends StackProps{
2129
readonly version: string
@@ -28,6 +36,7 @@ export interface VpcResourcesStackProps extends StackProps{
2836
*/
2937

3038
export class VpcResourcesStack extends Stack {
39+
readonly vpc : Vpc
3140
public constructor(scope: App, id: string, props: VpcResourcesStackProps){
3241
super(scope, id, props)
3342

@@ -87,6 +96,19 @@ export class VpcResourcesStack extends Stack {
8796
}
8897
}
8998

99+
this.vpc = vpc
100+
101+
// add vpc private endpoints - needed to run ECS in private subnet
102+
// copied from https://stackoverflow.com/a/69578964/9294145
103+
this.addInterfaceEndpoint("ECRDockerEndpoint", InterfaceVpcEndpointAwsService.ECR_DOCKER)
104+
this.addInterfaceEndpoint("ECREndpoint", InterfaceVpcEndpointAwsService.ECR)
105+
this.addInterfaceEndpoint("SecretManagerEndpoint", InterfaceVpcEndpointAwsService.SECRETS_MANAGER)
106+
this.addInterfaceEndpoint("CloudWatchEndpoint", InterfaceVpcEndpointAwsService.CLOUDWATCH_MONITORING)
107+
this.addInterfaceEndpoint("CloudWatchLogsEndpoint", InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS)
108+
this.addInterfaceEndpoint("CloudWatchEventsEndpoint", InterfaceVpcEndpointAwsService.EVENTBRIDGE)
109+
this.addInterfaceEndpoint("SSMEndpoint", InterfaceVpcEndpointAwsService.SSM)
110+
this.addGatewayEndpoint("S3Endpoint", InterfaceVpcEndpointAwsService.S3)
111+
90112
//Outputs
91113

92114
//Exports
@@ -106,7 +128,7 @@ export class VpcResourcesStack extends Stack {
106128
}
107129

108130
let privateSubnetIds = []
109-
for (const [i, subnet] of vpc.publicSubnets.entries()){
131+
for (const [i, subnet] of vpc.privateSubnets.entries()){
110132
const subnetIdentifier = String.fromCharCode("A".charCodeAt(0) + i)
111133
new CfnOutput(this, `PrivateSubnet${subnetIdentifier}`, {
112134
value: subnet.subnetId,
@@ -125,5 +147,52 @@ export class VpcResourcesStack extends Stack {
125147
exportName: `${props.stackName}:PrivateSubnets`
126148
})
127149

150+
nagSuppressions(this)
151+
152+
}
153+
154+
private addInterfaceEndpoint(name: string, awsService: InterfaceVpcEndpointAwsService): void {
155+
const endpoint: InterfaceVpcEndpoint = this.vpc.addInterfaceEndpoint(name, {
156+
service: awsService
157+
})
158+
this.addEndpointTag(name, endpoint)
159+
160+
endpoint.connections.allowFrom(Peer.ipv4(this.vpc.vpcCidrBlock), endpoint.connections.defaultPort!)
128161
}
162+
163+
private addGatewayEndpoint(name: string, awsService: InterfaceVpcEndpointAwsService): void {
164+
const endpoint: GatewayVpcEndpoint = this.vpc.addGatewayEndpoint(name, {
165+
service: awsService
166+
})
167+
this.addEndpointTag(name, endpoint)
168+
}
169+
170+
private addEndpointTag(name: string, endpoint: IVpcEndpoint) {
171+
// vpc endpoints do not support tagging from cdk/cloudformation
172+
// so use a custom resource to add them in
173+
new AwsCustomResource(this, `${name}-tags`, {
174+
installLatestAwsSdk: false,
175+
onUpdate: {
176+
action: "createTags",
177+
parameters: {
178+
Resources: [
179+
endpoint.vpcEndpointId
180+
],
181+
Tags: [
182+
{
183+
Key: "Name",
184+
Value: `${this.stackName}-${name}`
185+
}
186+
]
187+
},
188+
physicalResourceId: PhysicalResourceId.of(Date.now().toString()),
189+
service: "EC2"
190+
},
191+
policy: AwsCustomResourcePolicy.fromSdkCalls({
192+
resources: AwsCustomResourcePolicy.ANY_RESOURCE
193+
})
194+
})
195+
196+
}
197+
129198
}

sonar-project.properties

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,11 @@ sonar.coverage.exclusions=\
99
scripts/*\, \
1010
release.config.js, \
1111
packages/cdk/**
12+
13+
sonar.cpd.exclusions=**/*.test.*,\
14+
**/tests/*,\
15+
**/jest.config.ts, \
16+
**/jest.debug.config.ts, \
17+
scripts/*, \
18+
packages/cdk/nagSuppressions.ts, \
19+
eslint.config.mjs

0 commit comments

Comments
 (0)