├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── app.py
├── arch.png
├── cdk.json
├── cfn_templates
├── README.md
├── ServiceAStack.template.json
├── ServiceBStack.template.json
└── workshop_parent_stack.yaml
├── config.yml
├── requirements.txt
├── setup.py
├── source.bat
├── src
├── ec2
│ ├── curl-pkg
│ │ ├── scanner.py
│ │ ├── service_a_caller.py
│ │ ├── service_a_caller_sigv4.py
│ │ └── service_a_unknownapi.py
│ └── user_data.sh
└── lambda
│ ├── backend
│ └── lambda_function.py
│ ├── caller_nosigv4
│ └── lambda_function.py
│ ├── caller_sigv4
│ └── lambda_function.py
│ ├── ddbinit
│ ├── cfnresponse.py
│ └── lambda_function.py
│ ├── guardduty_helper
│ ├── cfnresponse.py
│ └── lambda_function.py
│ └── layer
│ └── .keep
└── zerotrust_service2service_workshop
├── __init__.py
├── service_a_stack.py
└── service_b_stack.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | package-lock.json
3 | __pycache__
4 | .pytest_cache
5 | .env
6 | .venv
7 | *.egg-info
8 |
9 | # CDK asset staging directory
10 | .cdk.staging
11 | cdk.out
12 |
13 | # VS Code stuff
14 | *.code-workspace
15 | .vscode/*
16 | .DS_Store
17 |
18 | # etc.
19 | src/lambda/layer/python
20 | src/ec2/.env
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Zero Trust Workshop - Service2Service Episode - Code
2 | This repository contains the source code, AWS environment build, of the [Zero Trust Service2Service Workshop](https://zerotrust-service2service.workshop.aws/) workshop. It does not contain the source code of the workshop's website.
3 |
4 |
5 | There are two main CDK stacks in this code:
6 | - ServiceA
7 | - ServiceB
8 |
9 |
10 | High Level Architecture:
11 |
12 |
13 |
14 | # How To Use
15 |
16 | ## Requirements
17 | - python3
18 | - node
19 | - aws-cdk
20 |
21 | ## Steps
22 | 1. Clone this repo & `cd aws-zerotrust-service2service-workshop`
23 |
24 | 1. Create and activate virtual environment:
25 | ```bash
26 | python3 -m venv .venv
27 | source .venv/bin/activate
28 | ```
29 |
30 | 1. Once the virtual environment is activated, install the required dependencies:
31 |
32 | ```bash
33 | pip install -r requirements.txt
34 | ```
35 |
36 | 1. You should now be able to run CDK commands. Start by listing available stacks:
37 | ```bash
38 | cdk ls
39 | ```
40 |
41 | 1. For example, if you want to synthesize the CloudFormation template for a stack:
42 | ```bash
43 | cdk synth
44 | ```
45 |
46 | ### Specific to this app
47 | ```bash
48 | # While in the root directory of this repo:
49 | pip3 install aws_requests_auth -t src/lambda/layer/python
50 | ```
51 | Why? I create a Lambda layer that contains the `aws_requests_auth` package used by Lambda functions. I avoid pushing the package's files to the repo (.gitignore). So you need to pip install the package after cloning this repo. Then at `cdk deploy` time CDK uses packages installed in `./src/lambda/layer/python` to create the Lambda Layer.
52 |
53 |
57 |
58 | ## Useful commands
59 |
60 | * `cdk ls` list all stacks in the app
61 | * `cdk synth` emits the synthesized CloudFormation template
62 | * `cdk deploy` deploy this stack to your default AWS account/region
63 | * `cdk diff` compare deployed stack with current state
64 | * `cdk docs` open CDK documentation
65 |
66 |
67 | ## Repo structure
68 | This project is initialized by `cdk init` and has the standard structure of a Python project.
69 |
70 |
71 | ```markdown
72 | .
73 | ├── cfn_templates <-- Directory for synthesized CFN templates
74 | ├── src <-- Directory for Lambda and EC2 source codes
75 | ├── zerotrust_service2service_workshop <-- Directory for main CDK stacks
76 | ├── app.py <-- The entry point for this application.
77 | ├── config.yml <-- [Not a CDK thing] Static variables used in this app.
78 | ├── README.md <-- This instructions file
79 | ├── setup.py <-- Defines package's construction and dependencies.
80 | ```
81 |
82 | ## Security
83 |
84 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
85 |
86 | ## License
87 |
88 | This library is licensed under the MIT-0 License. See the LICENSE file.
89 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | #!/usr/bin/env python3
5 |
6 | import yaml
7 |
8 | from aws_cdk import core as cdk
9 |
10 | from zerotrust_service2service_workshop.service_a_stack import ServiceAStack
11 | from zerotrust_service2service_workshop.service_b_stack import ServiceBStack
12 |
13 | app = cdk.App()
14 |
15 | # ServiceB is deployed first, so ServiceA (caller) knows about the ServiceB
16 | service_b_stack = ServiceBStack(app,"ServiceBStack",
17 | # Uncomment the next line if you want to deploy the stack into a specific Account and Region.
18 | # Not used for the workshop, as we synth and use the output CloudFormation for deployment.
19 | #env=core.Environment(account='123456789012', region='us-east-1'),
20 | )
21 |
22 | service_a_stack = ServiceAStack(app,"ServiceAStack",
23 | # Below lines make dependency between stacks; good for single-account deployment.
24 | # To enable for multi-account deployment, we use Systems Manager paramters here.
25 | # Additional work will be required to let one account read parameters from other account,
26 | # as well grant Lambda and EC2 roles to GetSecretValue from Secrets Manager.
27 | #api_endpoint=service_b_stack.api_endpoint,
28 | #api_secret=service_b_stack.api_secret,
29 |
30 | # Uncomment the next line if you want to deploy the stack into a specific Account and Region.
31 | # Not used for the workshop, as we synth and use the output CloudFormation for deployment.
32 | #env=core.Environment(account='123456789012', region='us-east-1'),
33 | )
34 |
35 | # NOTE comment this for final synth, uncomment while developing
36 | service_a_stack.add_dependency(service_b_stack)
37 |
38 | with open("./config.yml","r") as f:
39 | configs = yaml.safe_load(f)
40 |
41 | # Tagging the resources deployed by this app.
42 | cdk.Tags.of(app).add("Project", "ZeroTrustWorkshop")
43 | cdk.Tags.of(service_a_stack).add("Owner", "AccountA")
44 | cdk.Tags.of(service_a_stack).add("Service", "ServiceA")
45 | cdk.Tags.of(service_b_stack).add("Owner", "AccountB")
46 | cdk.Tags.of(service_b_stack).add("Service", "ServiceB")
47 |
48 | app.synth()
--------------------------------------------------------------------------------
/arch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-zerotrust-service2service-workshop/cdeef8df51c9c2747b50be71d2365ea5ab277af9/arch.png
--------------------------------------------------------------------------------
/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "python3 app.py",
3 | "context": {
4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
5 | "@aws-cdk/core:enableStackNameDuplicates": "true",
6 | "aws-cdk:enableDiffNoFail": "true",
7 | "@aws-cdk/core:stackRelativeExports": "true",
8 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
9 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
10 | "@aws-cdk/aws-kms:defaultKeyPolicies": true,
11 | "@aws-cdk/aws-s3:grantWriteWithoutAcl": true,
12 | "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true,
13 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
14 | "@aws-cdk/aws-efs:defaultEncryptionAtRest": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/cfn_templates/README.md:
--------------------------------------------------------------------------------
1 | ## AWS CloudFormation Templates
2 | This folder contains the CloudFormation templates for review purpose only. The CDK app should, ideally, be deployed using `cdk deploy`.
3 |
4 | - **ServiceAStack.template.json / ServiceBStack.template.json**
5 |
6 | The CloudFormation templates [synthesized](https://docs.aws.amazon.com/cdk/latest/guide/cli.html#cli-synth) from the CDK stacks in this app at the time of the commit. So, they are static and won't be updated automatically upon making changes into the CDK app.
7 |
8 | - **workshop_parent_stack.yaml**
9 |
10 | The parent template for the above two stacks, ServiceA and ServiceB, to simplify and enforce the order of deployment for the two.
11 |
12 | :information_source: If you just want to deploy the workshop as is in your environment, and not via CDK, please follow the instructions on the workshop website: https://zerotrust-service2service.workshop.aws/2-environment-build/on-your-own.html
--------------------------------------------------------------------------------
/cfn_templates/ServiceAStack.template.json:
--------------------------------------------------------------------------------
1 | {
2 | "Parameters": {
3 | "EEAssetsBucket": {
4 | "Type": "String",
5 | "Description": "Region-specific assets S3 bucket name."
6 | },
7 | "EEAssetsKeyPrefix": {
8 | "Type": "String",
9 | "Description": "S3 key prefix where this modules assets are stored. (e.g. modules/my_module/v1/)"
10 | },
11 | "SsmParameterValueworkshopparamsservicebapisecretarnC96584B6F00A464EAD1953AFF4B05118Parameter": {
12 | "Type": "AWS::SSM::Parameter::Value",
13 | "Default": "/workshop/params/service-b-api-secret-arn"
14 | },
15 | "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter": {
16 | "Type": "AWS::SSM::Parameter::Value",
17 | "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
18 | }
19 | },
20 | "Resources": {
21 | "ServiceAInstanceRole919D8950": {
22 | "Type": "AWS::IAM::Role",
23 | "Properties": {
24 | "AssumeRolePolicyDocument": {
25 | "Statement": [
26 | {
27 | "Action": "sts:AssumeRole",
28 | "Effect": "Allow",
29 | "Principal": {
30 | "Service": {
31 | "Fn::Join": [
32 | "",
33 | [
34 | "ec2.",
35 | {
36 | "Ref": "AWS::URLSuffix"
37 | }
38 | ]
39 | ]
40 | }
41 | }
42 | }
43 | ],
44 | "Version": "2012-10-17"
45 | },
46 | "Description": "Allows EC2 instances to call AWS services on your behalf.",
47 | "ManagedPolicyArns": [
48 | {
49 | "Fn::Join": [
50 | "",
51 | [
52 | "arn:",
53 | {
54 | "Ref": "AWS::Partition"
55 | },
56 | ":iam::aws:policy/AmazonSSMManagedInstanceCore"
57 | ]
58 | ]
59 | },
60 | {
61 | "Fn::Join": [
62 | "",
63 | [
64 | "arn:",
65 | {
66 | "Ref": "AWS::Partition"
67 | },
68 | ":iam::aws:policy/AmazonAPIGatewayInvokeFullAccess"
69 | ]
70 | ]
71 | }
72 | ],
73 | "Tags": [
74 | {
75 | "Key": "Owner",
76 | "Value": "AccountA"
77 | },
78 | {
79 | "Key": "Project",
80 | "Value": "ZeroTrustWorkshop"
81 | },
82 | {
83 | "Key": "Service",
84 | "Value": "ServiceA"
85 | }
86 | ]
87 | },
88 | "Metadata": {
89 | "aws:cdk:path": "ServiceAStack/ServiceAInstanceRole/Resource"
90 | }
91 | },
92 | "ServiceAInstanceRoleDefaultPolicyA4A6E2D5": {
93 | "Type": "AWS::IAM::Policy",
94 | "Properties": {
95 | "PolicyDocument": {
96 | "Statement": [
97 | {
98 | "Action": [
99 | "secretsmanager:GetSecretValue",
100 | "secretsmanager:DescribeSecret"
101 | ],
102 | "Effect": "Allow",
103 | "Resource": {
104 | "Ref": "SsmParameterValueworkshopparamsservicebapisecretarnC96584B6F00A464EAD1953AFF4B05118Parameter"
105 | }
106 | },
107 | {
108 | "Action": "lambda:InvokeFunction",
109 | "Effect": "Allow",
110 | "Resource": {
111 | "Fn::GetAtt": [
112 | "CallerOneE9C89DF9",
113 | "Arn"
114 | ]
115 | }
116 | },
117 | {
118 | "Action": "lambda:InvokeFunction",
119 | "Effect": "Allow",
120 | "Resource": {
121 | "Fn::GetAtt": [
122 | "CallerTwoDF240F92",
123 | "Arn"
124 | ]
125 | }
126 | },
127 | {
128 | "Action": "lambda:InvokeFunction",
129 | "Effect": "Allow",
130 | "Resource": {
131 | "Fn::GetAtt": [
132 | "CallerThreeE06DAEE6",
133 | "Arn"
134 | ]
135 | }
136 | },
137 | {
138 | "Action": [
139 | "ssm:SendCommand",
140 | "ssm:ListCommandInvocations"
141 | ],
142 | "Effect": "Allow",
143 | "Resource": "*"
144 | }
145 | ],
146 | "Version": "2012-10-17"
147 | },
148 | "PolicyName": "ServiceAInstanceRoleDefaultPolicyA4A6E2D5",
149 | "Roles": [
150 | {
151 | "Ref": "ServiceAInstanceRole919D8950"
152 | }
153 | ]
154 | },
155 | "Metadata": {
156 | "aws:cdk:path": "ServiceAStack/ServiceAInstanceRole/DefaultPolicy/Resource"
157 | }
158 | },
159 | "ServiceALambdaRoleA5CC16AA": {
160 | "Type": "AWS::IAM::Role",
161 | "Properties": {
162 | "AssumeRolePolicyDocument": {
163 | "Statement": [
164 | {
165 | "Action": "sts:AssumeRole",
166 | "Effect": "Allow",
167 | "Principal": {
168 | "Service": "lambda.amazonaws.com"
169 | }
170 | }
171 | ],
172 | "Version": "2012-10-17"
173 | },
174 | "Description": "Allows Lambda functions to call AWS services on your behalf.",
175 | "ManagedPolicyArns": [
176 | {
177 | "Fn::Join": [
178 | "",
179 | [
180 | "arn:",
181 | {
182 | "Ref": "AWS::Partition"
183 | },
184 | ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
185 | ]
186 | ]
187 | }
188 | ],
189 | "Tags": [
190 | {
191 | "Key": "Owner",
192 | "Value": "AccountA"
193 | },
194 | {
195 | "Key": "Project",
196 | "Value": "ZeroTrustWorkshop"
197 | },
198 | {
199 | "Key": "Service",
200 | "Value": "ServiceA"
201 | }
202 | ]
203 | },
204 | "Metadata": {
205 | "aws:cdk:path": "ServiceAStack/ServiceALambdaRole/Resource"
206 | }
207 | },
208 | "ServiceALambdaRoleDefaultPolicy42CFEBA3": {
209 | "Type": "AWS::IAM::Policy",
210 | "Properties": {
211 | "PolicyDocument": {
212 | "Statement": [
213 | {
214 | "Action": [
215 | "secretsmanager:GetSecretValue",
216 | "secretsmanager:DescribeSecret"
217 | ],
218 | "Effect": "Allow",
219 | "Resource": {
220 | "Ref": "SsmParameterValueworkshopparamsservicebapisecretarnC96584B6F00A464EAD1953AFF4B05118Parameter"
221 | }
222 | },
223 | {
224 | "Action": "ssm:GetParameter*",
225 | "Effect": "Allow",
226 | "Resource": {
227 | "Fn::Join": [
228 | "",
229 | [
230 | "arn:",
231 | {
232 | "Ref": "AWS::Partition"
233 | },
234 | ":ssm:",
235 | {
236 | "Ref": "AWS::Region"
237 | },
238 | ":",
239 | {
240 | "Ref": "AWS::AccountId"
241 | },
242 | ":parameter/workshop/params/*"
243 | ]
244 | ]
245 | }
246 | }
247 | ],
248 | "Version": "2012-10-17"
249 | },
250 | "PolicyName": "ServiceALambdaRoleDefaultPolicy42CFEBA3",
251 | "Roles": [
252 | {
253 | "Ref": "ServiceALambdaRoleA5CC16AA"
254 | }
255 | ]
256 | },
257 | "Metadata": {
258 | "aws:cdk:path": "ServiceAStack/ServiceALambdaRole/DefaultPolicy/Resource"
259 | }
260 | },
261 | "ServiceAVPCE249ED38": {
262 | "Type": "AWS::EC2::VPC",
263 | "Properties": {
264 | "CidrBlock": "10.199.0.0/16",
265 | "EnableDnsHostnames": true,
266 | "EnableDnsSupport": true,
267 | "InstanceTenancy": "default",
268 | "Tags": [
269 | {
270 | "Key": "Name",
271 | "Value": "ServiceAStack/ServiceAVPC"
272 | },
273 | {
274 | "Key": "Owner",
275 | "Value": "AccountA"
276 | },
277 | {
278 | "Key": "Project",
279 | "Value": "ZeroTrustWorkshop"
280 | },
281 | {
282 | "Key": "Service",
283 | "Value": "ServiceA"
284 | }
285 | ]
286 | },
287 | "Metadata": {
288 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/Resource"
289 | }
290 | },
291 | "ServiceAVPCPrivateSubnet1Subnet86779CD2": {
292 | "Type": "AWS::EC2::Subnet",
293 | "Properties": {
294 | "CidrBlock": "10.199.0.0/24",
295 | "VpcId": {
296 | "Ref": "ServiceAVPCE249ED38"
297 | },
298 | "AvailabilityZone": {
299 | "Fn::Select": [
300 | 0,
301 | {
302 | "Fn::GetAZs": ""
303 | }
304 | ]
305 | },
306 | "MapPublicIpOnLaunch": false,
307 | "Tags": [
308 | {
309 | "Key": "aws-cdk:subnet-name",
310 | "Value": "Private"
311 | },
312 | {
313 | "Key": "aws-cdk:subnet-type",
314 | "Value": "Private"
315 | },
316 | {
317 | "Key": "Name",
318 | "Value": "ServiceAStack/ServiceAVPC/PrivateSubnet1"
319 | },
320 | {
321 | "Key": "Owner",
322 | "Value": "AccountA"
323 | },
324 | {
325 | "Key": "Project",
326 | "Value": "ZeroTrustWorkshop"
327 | },
328 | {
329 | "Key": "Service",
330 | "Value": "ServiceA"
331 | }
332 | ]
333 | },
334 | "Metadata": {
335 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet1/Subnet"
336 | }
337 | },
338 | "ServiceAVPCPrivateSubnet1RouteTable9D097A5C": {
339 | "Type": "AWS::EC2::RouteTable",
340 | "Properties": {
341 | "VpcId": {
342 | "Ref": "ServiceAVPCE249ED38"
343 | },
344 | "Tags": [
345 | {
346 | "Key": "Name",
347 | "Value": "ServiceAStack/ServiceAVPC/PrivateSubnet1"
348 | },
349 | {
350 | "Key": "Owner",
351 | "Value": "AccountA"
352 | },
353 | {
354 | "Key": "Project",
355 | "Value": "ZeroTrustWorkshop"
356 | },
357 | {
358 | "Key": "Service",
359 | "Value": "ServiceA"
360 | }
361 | ]
362 | },
363 | "Metadata": {
364 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet1/RouteTable"
365 | }
366 | },
367 | "ServiceAVPCPrivateSubnet1RouteTableAssociation79A5CE3D": {
368 | "Type": "AWS::EC2::SubnetRouteTableAssociation",
369 | "Properties": {
370 | "RouteTableId": {
371 | "Ref": "ServiceAVPCPrivateSubnet1RouteTable9D097A5C"
372 | },
373 | "SubnetId": {
374 | "Ref": "ServiceAVPCPrivateSubnet1Subnet86779CD2"
375 | }
376 | },
377 | "Metadata": {
378 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet1/RouteTableAssociation"
379 | }
380 | },
381 | "ServiceAVPCPrivateSubnet1DefaultRoute4F719C33": {
382 | "Type": "AWS::EC2::Route",
383 | "Properties": {
384 | "RouteTableId": {
385 | "Ref": "ServiceAVPCPrivateSubnet1RouteTable9D097A5C"
386 | },
387 | "DestinationCidrBlock": "0.0.0.0/0",
388 | "NatGatewayId": {
389 | "Ref": "ServiceAVPCPublicSubnet1NATGateway2B928AEE"
390 | }
391 | },
392 | "Metadata": {
393 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet1/DefaultRoute"
394 | }
395 | },
396 | "ServiceAVPCPrivateSubnet2Subnet86E1F16D": {
397 | "Type": "AWS::EC2::Subnet",
398 | "Properties": {
399 | "CidrBlock": "10.199.1.0/24",
400 | "VpcId": {
401 | "Ref": "ServiceAVPCE249ED38"
402 | },
403 | "AvailabilityZone": {
404 | "Fn::Select": [
405 | 1,
406 | {
407 | "Fn::GetAZs": ""
408 | }
409 | ]
410 | },
411 | "MapPublicIpOnLaunch": false,
412 | "Tags": [
413 | {
414 | "Key": "aws-cdk:subnet-name",
415 | "Value": "Private"
416 | },
417 | {
418 | "Key": "aws-cdk:subnet-type",
419 | "Value": "Private"
420 | },
421 | {
422 | "Key": "Name",
423 | "Value": "ServiceAStack/ServiceAVPC/PrivateSubnet2"
424 | },
425 | {
426 | "Key": "Owner",
427 | "Value": "AccountA"
428 | },
429 | {
430 | "Key": "Project",
431 | "Value": "ZeroTrustWorkshop"
432 | },
433 | {
434 | "Key": "Service",
435 | "Value": "ServiceA"
436 | }
437 | ]
438 | },
439 | "Metadata": {
440 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet2/Subnet"
441 | }
442 | },
443 | "ServiceAVPCPrivateSubnet2RouteTable2B8268AF": {
444 | "Type": "AWS::EC2::RouteTable",
445 | "Properties": {
446 | "VpcId": {
447 | "Ref": "ServiceAVPCE249ED38"
448 | },
449 | "Tags": [
450 | {
451 | "Key": "Name",
452 | "Value": "ServiceAStack/ServiceAVPC/PrivateSubnet2"
453 | },
454 | {
455 | "Key": "Owner",
456 | "Value": "AccountA"
457 | },
458 | {
459 | "Key": "Project",
460 | "Value": "ZeroTrustWorkshop"
461 | },
462 | {
463 | "Key": "Service",
464 | "Value": "ServiceA"
465 | }
466 | ]
467 | },
468 | "Metadata": {
469 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet2/RouteTable"
470 | }
471 | },
472 | "ServiceAVPCPrivateSubnet2RouteTableAssociation5EFF9610": {
473 | "Type": "AWS::EC2::SubnetRouteTableAssociation",
474 | "Properties": {
475 | "RouteTableId": {
476 | "Ref": "ServiceAVPCPrivateSubnet2RouteTable2B8268AF"
477 | },
478 | "SubnetId": {
479 | "Ref": "ServiceAVPCPrivateSubnet2Subnet86E1F16D"
480 | }
481 | },
482 | "Metadata": {
483 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet2/RouteTableAssociation"
484 | }
485 | },
486 | "ServiceAVPCPrivateSubnet2DefaultRouteCF09EC6E": {
487 | "Type": "AWS::EC2::Route",
488 | "Properties": {
489 | "RouteTableId": {
490 | "Ref": "ServiceAVPCPrivateSubnet2RouteTable2B8268AF"
491 | },
492 | "DestinationCidrBlock": "0.0.0.0/0",
493 | "NatGatewayId": {
494 | "Ref": "ServiceAVPCPublicSubnet1NATGateway2B928AEE"
495 | }
496 | },
497 | "Metadata": {
498 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PrivateSubnet2/DefaultRoute"
499 | }
500 | },
501 | "ServiceAVPCPublicSubnet1Subnet2A84C481": {
502 | "Type": "AWS::EC2::Subnet",
503 | "Properties": {
504 | "CidrBlock": "10.199.2.0/24",
505 | "VpcId": {
506 | "Ref": "ServiceAVPCE249ED38"
507 | },
508 | "AvailabilityZone": {
509 | "Fn::Select": [
510 | 0,
511 | {
512 | "Fn::GetAZs": ""
513 | }
514 | ]
515 | },
516 | "MapPublicIpOnLaunch": true,
517 | "Tags": [
518 | {
519 | "Key": "aws-cdk:subnet-name",
520 | "Value": "Public"
521 | },
522 | {
523 | "Key": "aws-cdk:subnet-type",
524 | "Value": "Public"
525 | },
526 | {
527 | "Key": "Name",
528 | "Value": "ServiceAStack/ServiceAVPC/PublicSubnet1"
529 | },
530 | {
531 | "Key": "Owner",
532 | "Value": "AccountA"
533 | },
534 | {
535 | "Key": "Project",
536 | "Value": "ZeroTrustWorkshop"
537 | },
538 | {
539 | "Key": "Service",
540 | "Value": "ServiceA"
541 | }
542 | ]
543 | },
544 | "Metadata": {
545 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet1/Subnet"
546 | }
547 | },
548 | "ServiceAVPCPublicSubnet1RouteTable1D7D7009": {
549 | "Type": "AWS::EC2::RouteTable",
550 | "Properties": {
551 | "VpcId": {
552 | "Ref": "ServiceAVPCE249ED38"
553 | },
554 | "Tags": [
555 | {
556 | "Key": "Name",
557 | "Value": "ServiceAStack/ServiceAVPC/PublicSubnet1"
558 | },
559 | {
560 | "Key": "Owner",
561 | "Value": "AccountA"
562 | },
563 | {
564 | "Key": "Project",
565 | "Value": "ZeroTrustWorkshop"
566 | },
567 | {
568 | "Key": "Service",
569 | "Value": "ServiceA"
570 | }
571 | ]
572 | },
573 | "Metadata": {
574 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet1/RouteTable"
575 | }
576 | },
577 | "ServiceAVPCPublicSubnet1RouteTableAssociation167E7963": {
578 | "Type": "AWS::EC2::SubnetRouteTableAssociation",
579 | "Properties": {
580 | "RouteTableId": {
581 | "Ref": "ServiceAVPCPublicSubnet1RouteTable1D7D7009"
582 | },
583 | "SubnetId": {
584 | "Ref": "ServiceAVPCPublicSubnet1Subnet2A84C481"
585 | }
586 | },
587 | "Metadata": {
588 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet1/RouteTableAssociation"
589 | }
590 | },
591 | "ServiceAVPCPublicSubnet1DefaultRoute6488E472": {
592 | "Type": "AWS::EC2::Route",
593 | "Properties": {
594 | "RouteTableId": {
595 | "Ref": "ServiceAVPCPublicSubnet1RouteTable1D7D7009"
596 | },
597 | "DestinationCidrBlock": "0.0.0.0/0",
598 | "GatewayId": {
599 | "Ref": "ServiceAVPCIGW8DA0A08B"
600 | }
601 | },
602 | "DependsOn": [
603 | "ServiceAVPCVPCGWA7CF038F"
604 | ],
605 | "Metadata": {
606 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet1/DefaultRoute"
607 | }
608 | },
609 | "ServiceAVPCPublicSubnet1EIP11A7229E": {
610 | "Type": "AWS::EC2::EIP",
611 | "Properties": {
612 | "Domain": "vpc",
613 | "Tags": [
614 | {
615 | "Key": "Name",
616 | "Value": "ServiceAStack/ServiceAVPC/PublicSubnet1"
617 | },
618 | {
619 | "Key": "Owner",
620 | "Value": "AccountA"
621 | },
622 | {
623 | "Key": "Project",
624 | "Value": "ZeroTrustWorkshop"
625 | },
626 | {
627 | "Key": "Service",
628 | "Value": "ServiceA"
629 | }
630 | ]
631 | },
632 | "Metadata": {
633 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet1/EIP"
634 | }
635 | },
636 | "ServiceAVPCPublicSubnet1NATGateway2B928AEE": {
637 | "Type": "AWS::EC2::NatGateway",
638 | "Properties": {
639 | "SubnetId": {
640 | "Ref": "ServiceAVPCPublicSubnet1Subnet2A84C481"
641 | },
642 | "AllocationId": {
643 | "Fn::GetAtt": [
644 | "ServiceAVPCPublicSubnet1EIP11A7229E",
645 | "AllocationId"
646 | ]
647 | },
648 | "Tags": [
649 | {
650 | "Key": "Name",
651 | "Value": "ServiceAStack/ServiceAVPC/PublicSubnet1"
652 | },
653 | {
654 | "Key": "Owner",
655 | "Value": "AccountA"
656 | },
657 | {
658 | "Key": "Project",
659 | "Value": "ZeroTrustWorkshop"
660 | },
661 | {
662 | "Key": "Service",
663 | "Value": "ServiceA"
664 | }
665 | ]
666 | },
667 | "Metadata": {
668 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet1/NATGateway"
669 | }
670 | },
671 | "ServiceAVPCPublicSubnet2Subnet7C59D581": {
672 | "Type": "AWS::EC2::Subnet",
673 | "Properties": {
674 | "CidrBlock": "10.199.3.0/24",
675 | "VpcId": {
676 | "Ref": "ServiceAVPCE249ED38"
677 | },
678 | "AvailabilityZone": {
679 | "Fn::Select": [
680 | 1,
681 | {
682 | "Fn::GetAZs": ""
683 | }
684 | ]
685 | },
686 | "MapPublicIpOnLaunch": true,
687 | "Tags": [
688 | {
689 | "Key": "aws-cdk:subnet-name",
690 | "Value": "Public"
691 | },
692 | {
693 | "Key": "aws-cdk:subnet-type",
694 | "Value": "Public"
695 | },
696 | {
697 | "Key": "Name",
698 | "Value": "ServiceAStack/ServiceAVPC/PublicSubnet2"
699 | },
700 | {
701 | "Key": "Owner",
702 | "Value": "AccountA"
703 | },
704 | {
705 | "Key": "Project",
706 | "Value": "ZeroTrustWorkshop"
707 | },
708 | {
709 | "Key": "Service",
710 | "Value": "ServiceA"
711 | }
712 | ]
713 | },
714 | "Metadata": {
715 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet2/Subnet"
716 | }
717 | },
718 | "ServiceAVPCPublicSubnet2RouteTable6BDDDE93": {
719 | "Type": "AWS::EC2::RouteTable",
720 | "Properties": {
721 | "VpcId": {
722 | "Ref": "ServiceAVPCE249ED38"
723 | },
724 | "Tags": [
725 | {
726 | "Key": "Name",
727 | "Value": "ServiceAStack/ServiceAVPC/PublicSubnet2"
728 | },
729 | {
730 | "Key": "Owner",
731 | "Value": "AccountA"
732 | },
733 | {
734 | "Key": "Project",
735 | "Value": "ZeroTrustWorkshop"
736 | },
737 | {
738 | "Key": "Service",
739 | "Value": "ServiceA"
740 | }
741 | ]
742 | },
743 | "Metadata": {
744 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet2/RouteTable"
745 | }
746 | },
747 | "ServiceAVPCPublicSubnet2RouteTableAssociationFD1BD8B1": {
748 | "Type": "AWS::EC2::SubnetRouteTableAssociation",
749 | "Properties": {
750 | "RouteTableId": {
751 | "Ref": "ServiceAVPCPublicSubnet2RouteTable6BDDDE93"
752 | },
753 | "SubnetId": {
754 | "Ref": "ServiceAVPCPublicSubnet2Subnet7C59D581"
755 | }
756 | },
757 | "Metadata": {
758 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet2/RouteTableAssociation"
759 | }
760 | },
761 | "ServiceAVPCPublicSubnet2DefaultRouteFF84431A": {
762 | "Type": "AWS::EC2::Route",
763 | "Properties": {
764 | "RouteTableId": {
765 | "Ref": "ServiceAVPCPublicSubnet2RouteTable6BDDDE93"
766 | },
767 | "DestinationCidrBlock": "0.0.0.0/0",
768 | "GatewayId": {
769 | "Ref": "ServiceAVPCIGW8DA0A08B"
770 | }
771 | },
772 | "DependsOn": [
773 | "ServiceAVPCVPCGWA7CF038F"
774 | ],
775 | "Metadata": {
776 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/PublicSubnet2/DefaultRoute"
777 | }
778 | },
779 | "ServiceAVPCIGW8DA0A08B": {
780 | "Type": "AWS::EC2::InternetGateway",
781 | "Properties": {
782 | "Tags": [
783 | {
784 | "Key": "Name",
785 | "Value": "ServiceAStack/ServiceAVPC"
786 | },
787 | {
788 | "Key": "Owner",
789 | "Value": "AccountA"
790 | },
791 | {
792 | "Key": "Project",
793 | "Value": "ZeroTrustWorkshop"
794 | },
795 | {
796 | "Key": "Service",
797 | "Value": "ServiceA"
798 | }
799 | ]
800 | },
801 | "Metadata": {
802 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/IGW"
803 | }
804 | },
805 | "ServiceAVPCVPCGWA7CF038F": {
806 | "Type": "AWS::EC2::VPCGatewayAttachment",
807 | "Properties": {
808 | "VpcId": {
809 | "Ref": "ServiceAVPCE249ED38"
810 | },
811 | "InternetGatewayId": {
812 | "Ref": "ServiceAVPCIGW8DA0A08B"
813 | }
814 | },
815 | "Metadata": {
816 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/VPCGW"
817 | }
818 | },
819 | "ServiceAVPCAPIGWVPCEndpoint2EDA772E": {
820 | "Type": "AWS::EC2::VPCEndpoint",
821 | "Properties": {
822 | "ServiceName": {
823 | "Fn::Join": [
824 | "",
825 | [
826 | "com.amazonaws.",
827 | {
828 | "Ref": "AWS::Region"
829 | },
830 | ".execute-api"
831 | ]
832 | ]
833 | },
834 | "VpcId": {
835 | "Ref": "ServiceAVPCE249ED38"
836 | },
837 | "PrivateDnsEnabled": true,
838 | "SecurityGroupIds": [
839 | {
840 | "Fn::GetAtt": [
841 | "APIGWVPCEndpointSecurityGroup512E5E3C",
842 | "GroupId"
843 | ]
844 | }
845 | ],
846 | "SubnetIds": [
847 | {
848 | "Ref": "ServiceAVPCPrivateSubnet1Subnet86779CD2"
849 | },
850 | {
851 | "Ref": "ServiceAVPCPrivateSubnet2Subnet86E1F16D"
852 | }
853 | ],
854 | "VpcEndpointType": "Interface"
855 | },
856 | "Metadata": {
857 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/APIGWVPCEndpoint/Resource"
858 | }
859 | },
860 | "ServiceAVPCSSMVPCEndpointSecurityGroupAA79E59D": {
861 | "Type": "AWS::EC2::SecurityGroup",
862 | "Properties": {
863 | "GroupDescription": "ServiceAStack/ServiceAVPC/SSMVPCEndpoint/SecurityGroup",
864 | "SecurityGroupEgress": [
865 | {
866 | "CidrIp": "0.0.0.0/0",
867 | "Description": "Allow all outbound traffic by default",
868 | "IpProtocol": "-1"
869 | }
870 | ],
871 | "SecurityGroupIngress": [
872 | {
873 | "CidrIp": {
874 | "Fn::GetAtt": [
875 | "ServiceAVPCE249ED38",
876 | "CidrBlock"
877 | ]
878 | },
879 | "Description": {
880 | "Fn::Join": [
881 | "",
882 | [
883 | "from ",
884 | {
885 | "Fn::GetAtt": [
886 | "ServiceAVPCE249ED38",
887 | "CidrBlock"
888 | ]
889 | },
890 | ":443"
891 | ]
892 | ]
893 | },
894 | "FromPort": 443,
895 | "IpProtocol": "tcp",
896 | "ToPort": 443
897 | }
898 | ],
899 | "Tags": [
900 | {
901 | "Key": "Owner",
902 | "Value": "AccountA"
903 | },
904 | {
905 | "Key": "Project",
906 | "Value": "ZeroTrustWorkshop"
907 | },
908 | {
909 | "Key": "Service",
910 | "Value": "ServiceA"
911 | }
912 | ],
913 | "VpcId": {
914 | "Ref": "ServiceAVPCE249ED38"
915 | }
916 | },
917 | "Metadata": {
918 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/SSMVPCEndpoint/SecurityGroup/Resource"
919 | }
920 | },
921 | "ServiceAVPCSSMVPCEndpointFFB834BA": {
922 | "Type": "AWS::EC2::VPCEndpoint",
923 | "Properties": {
924 | "ServiceName": {
925 | "Fn::Join": [
926 | "",
927 | [
928 | "com.amazonaws.",
929 | {
930 | "Ref": "AWS::Region"
931 | },
932 | ".ssm"
933 | ]
934 | ]
935 | },
936 | "VpcId": {
937 | "Ref": "ServiceAVPCE249ED38"
938 | },
939 | "PrivateDnsEnabled": true,
940 | "SecurityGroupIds": [
941 | {
942 | "Fn::GetAtt": [
943 | "ServiceAVPCSSMVPCEndpointSecurityGroupAA79E59D",
944 | "GroupId"
945 | ]
946 | }
947 | ],
948 | "SubnetIds": [
949 | {
950 | "Ref": "ServiceAVPCPrivateSubnet1Subnet86779CD2"
951 | },
952 | {
953 | "Ref": "ServiceAVPCPrivateSubnet2Subnet86E1F16D"
954 | }
955 | ],
956 | "VpcEndpointType": "Interface"
957 | },
958 | "Metadata": {
959 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/SSMVPCEndpoint/Resource"
960 | }
961 | },
962 | "ServiceAVPCSSMMSGVPCEndpointSecurityGroupE36CA564": {
963 | "Type": "AWS::EC2::SecurityGroup",
964 | "Properties": {
965 | "GroupDescription": "ServiceAStack/ServiceAVPC/SSMMSGVPCEndpoint/SecurityGroup",
966 | "SecurityGroupEgress": [
967 | {
968 | "CidrIp": "0.0.0.0/0",
969 | "Description": "Allow all outbound traffic by default",
970 | "IpProtocol": "-1"
971 | }
972 | ],
973 | "SecurityGroupIngress": [
974 | {
975 | "CidrIp": {
976 | "Fn::GetAtt": [
977 | "ServiceAVPCE249ED38",
978 | "CidrBlock"
979 | ]
980 | },
981 | "Description": {
982 | "Fn::Join": [
983 | "",
984 | [
985 | "from ",
986 | {
987 | "Fn::GetAtt": [
988 | "ServiceAVPCE249ED38",
989 | "CidrBlock"
990 | ]
991 | },
992 | ":443"
993 | ]
994 | ]
995 | },
996 | "FromPort": 443,
997 | "IpProtocol": "tcp",
998 | "ToPort": 443
999 | }
1000 | ],
1001 | "Tags": [
1002 | {
1003 | "Key": "Owner",
1004 | "Value": "AccountA"
1005 | },
1006 | {
1007 | "Key": "Project",
1008 | "Value": "ZeroTrustWorkshop"
1009 | },
1010 | {
1011 | "Key": "Service",
1012 | "Value": "ServiceA"
1013 | }
1014 | ],
1015 | "VpcId": {
1016 | "Ref": "ServiceAVPCE249ED38"
1017 | }
1018 | },
1019 | "Metadata": {
1020 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/SSMMSGVPCEndpoint/SecurityGroup/Resource"
1021 | }
1022 | },
1023 | "ServiceAVPCSSMMSGVPCEndpointEECBB610": {
1024 | "Type": "AWS::EC2::VPCEndpoint",
1025 | "Properties": {
1026 | "ServiceName": {
1027 | "Fn::Join": [
1028 | "",
1029 | [
1030 | "com.amazonaws.",
1031 | {
1032 | "Ref": "AWS::Region"
1033 | },
1034 | ".ssmmessages"
1035 | ]
1036 | ]
1037 | },
1038 | "VpcId": {
1039 | "Ref": "ServiceAVPCE249ED38"
1040 | },
1041 | "PrivateDnsEnabled": true,
1042 | "SecurityGroupIds": [
1043 | {
1044 | "Fn::GetAtt": [
1045 | "ServiceAVPCSSMMSGVPCEndpointSecurityGroupE36CA564",
1046 | "GroupId"
1047 | ]
1048 | }
1049 | ],
1050 | "SubnetIds": [
1051 | {
1052 | "Ref": "ServiceAVPCPrivateSubnet1Subnet86779CD2"
1053 | },
1054 | {
1055 | "Ref": "ServiceAVPCPrivateSubnet2Subnet86E1F16D"
1056 | }
1057 | ],
1058 | "VpcEndpointType": "Interface"
1059 | },
1060 | "Metadata": {
1061 | "aws:cdk:path": "ServiceAStack/ServiceAVPC/SSMMSGVPCEndpoint/Resource"
1062 | }
1063 | },
1064 | "OtherVPC4F26C84D": {
1065 | "Type": "AWS::EC2::VPC",
1066 | "Properties": {
1067 | "CidrBlock": "10.199.0.0/24",
1068 | "EnableDnsHostnames": true,
1069 | "EnableDnsSupport": true,
1070 | "InstanceTenancy": "default",
1071 | "Tags": [
1072 | {
1073 | "Key": "Name",
1074 | "Value": "ServiceAStack/OtherVPC"
1075 | },
1076 | {
1077 | "Key": "Owner",
1078 | "Value": "AccountA"
1079 | },
1080 | {
1081 | "Key": "Project",
1082 | "Value": "ZeroTrustWorkshop"
1083 | },
1084 | {
1085 | "Key": "Service",
1086 | "Value": "ServiceA"
1087 | }
1088 | ]
1089 | },
1090 | "Metadata": {
1091 | "aws:cdk:path": "ServiceAStack/OtherVPC/Resource"
1092 | }
1093 | },
1094 | "OtherVPCPrivateSubnet1SubnetD46635C4": {
1095 | "Type": "AWS::EC2::Subnet",
1096 | "Properties": {
1097 | "CidrBlock": "10.199.0.0/26",
1098 | "VpcId": {
1099 | "Ref": "OtherVPC4F26C84D"
1100 | },
1101 | "AvailabilityZone": {
1102 | "Fn::Select": [
1103 | 0,
1104 | {
1105 | "Fn::GetAZs": ""
1106 | }
1107 | ]
1108 | },
1109 | "MapPublicIpOnLaunch": false,
1110 | "Tags": [
1111 | {
1112 | "Key": "aws-cdk:subnet-name",
1113 | "Value": "Private"
1114 | },
1115 | {
1116 | "Key": "aws-cdk:subnet-type",
1117 | "Value": "Private"
1118 | },
1119 | {
1120 | "Key": "Name",
1121 | "Value": "ServiceAStack/OtherVPC/PrivateSubnet1"
1122 | },
1123 | {
1124 | "Key": "Owner",
1125 | "Value": "AccountA"
1126 | },
1127 | {
1128 | "Key": "Project",
1129 | "Value": "ZeroTrustWorkshop"
1130 | },
1131 | {
1132 | "Key": "Service",
1133 | "Value": "ServiceA"
1134 | }
1135 | ]
1136 | },
1137 | "Metadata": {
1138 | "aws:cdk:path": "ServiceAStack/OtherVPC/PrivateSubnet1/Subnet"
1139 | }
1140 | },
1141 | "OtherVPCPrivateSubnet1RouteTable4E6301AA": {
1142 | "Type": "AWS::EC2::RouteTable",
1143 | "Properties": {
1144 | "VpcId": {
1145 | "Ref": "OtherVPC4F26C84D"
1146 | },
1147 | "Tags": [
1148 | {
1149 | "Key": "Name",
1150 | "Value": "ServiceAStack/OtherVPC/PrivateSubnet1"
1151 | },
1152 | {
1153 | "Key": "Owner",
1154 | "Value": "AccountA"
1155 | },
1156 | {
1157 | "Key": "Project",
1158 | "Value": "ZeroTrustWorkshop"
1159 | },
1160 | {
1161 | "Key": "Service",
1162 | "Value": "ServiceA"
1163 | }
1164 | ]
1165 | },
1166 | "Metadata": {
1167 | "aws:cdk:path": "ServiceAStack/OtherVPC/PrivateSubnet1/RouteTable"
1168 | }
1169 | },
1170 | "OtherVPCPrivateSubnet1RouteTableAssociation6A7E4D33": {
1171 | "Type": "AWS::EC2::SubnetRouteTableAssociation",
1172 | "Properties": {
1173 | "RouteTableId": {
1174 | "Ref": "OtherVPCPrivateSubnet1RouteTable4E6301AA"
1175 | },
1176 | "SubnetId": {
1177 | "Ref": "OtherVPCPrivateSubnet1SubnetD46635C4"
1178 | }
1179 | },
1180 | "Metadata": {
1181 | "aws:cdk:path": "ServiceAStack/OtherVPC/PrivateSubnet1/RouteTableAssociation"
1182 | }
1183 | },
1184 | "OtherVPCPrivateSubnet1DefaultRoute73035179": {
1185 | "Type": "AWS::EC2::Route",
1186 | "Properties": {
1187 | "RouteTableId": {
1188 | "Ref": "OtherVPCPrivateSubnet1RouteTable4E6301AA"
1189 | },
1190 | "DestinationCidrBlock": "0.0.0.0/0",
1191 | "NatGatewayId": {
1192 | "Ref": "OtherVPCPublicSubnet1NATGateway982870F4"
1193 | }
1194 | },
1195 | "Metadata": {
1196 | "aws:cdk:path": "ServiceAStack/OtherVPC/PrivateSubnet1/DefaultRoute"
1197 | }
1198 | },
1199 | "OtherVPCPublicSubnet1Subnet7C6060EB": {
1200 | "Type": "AWS::EC2::Subnet",
1201 | "Properties": {
1202 | "CidrBlock": "10.199.0.64/26",
1203 | "VpcId": {
1204 | "Ref": "OtherVPC4F26C84D"
1205 | },
1206 | "AvailabilityZone": {
1207 | "Fn::Select": [
1208 | 0,
1209 | {
1210 | "Fn::GetAZs": ""
1211 | }
1212 | ]
1213 | },
1214 | "MapPublicIpOnLaunch": true,
1215 | "Tags": [
1216 | {
1217 | "Key": "aws-cdk:subnet-name",
1218 | "Value": "Public"
1219 | },
1220 | {
1221 | "Key": "aws-cdk:subnet-type",
1222 | "Value": "Public"
1223 | },
1224 | {
1225 | "Key": "Name",
1226 | "Value": "ServiceAStack/OtherVPC/PublicSubnet1"
1227 | },
1228 | {
1229 | "Key": "Owner",
1230 | "Value": "AccountA"
1231 | },
1232 | {
1233 | "Key": "Project",
1234 | "Value": "ZeroTrustWorkshop"
1235 | },
1236 | {
1237 | "Key": "Service",
1238 | "Value": "ServiceA"
1239 | }
1240 | ]
1241 | },
1242 | "Metadata": {
1243 | "aws:cdk:path": "ServiceAStack/OtherVPC/PublicSubnet1/Subnet"
1244 | }
1245 | },
1246 | "OtherVPCPublicSubnet1RouteTable065F9C73": {
1247 | "Type": "AWS::EC2::RouteTable",
1248 | "Properties": {
1249 | "VpcId": {
1250 | "Ref": "OtherVPC4F26C84D"
1251 | },
1252 | "Tags": [
1253 | {
1254 | "Key": "Name",
1255 | "Value": "ServiceAStack/OtherVPC/PublicSubnet1"
1256 | },
1257 | {
1258 | "Key": "Owner",
1259 | "Value": "AccountA"
1260 | },
1261 | {
1262 | "Key": "Project",
1263 | "Value": "ZeroTrustWorkshop"
1264 | },
1265 | {
1266 | "Key": "Service",
1267 | "Value": "ServiceA"
1268 | }
1269 | ]
1270 | },
1271 | "Metadata": {
1272 | "aws:cdk:path": "ServiceAStack/OtherVPC/PublicSubnet1/RouteTable"
1273 | }
1274 | },
1275 | "OtherVPCPublicSubnet1RouteTableAssociation50BFFA65": {
1276 | "Type": "AWS::EC2::SubnetRouteTableAssociation",
1277 | "Properties": {
1278 | "RouteTableId": {
1279 | "Ref": "OtherVPCPublicSubnet1RouteTable065F9C73"
1280 | },
1281 | "SubnetId": {
1282 | "Ref": "OtherVPCPublicSubnet1Subnet7C6060EB"
1283 | }
1284 | },
1285 | "Metadata": {
1286 | "aws:cdk:path": "ServiceAStack/OtherVPC/PublicSubnet1/RouteTableAssociation"
1287 | }
1288 | },
1289 | "OtherVPCPublicSubnet1DefaultRouteE4767CAE": {
1290 | "Type": "AWS::EC2::Route",
1291 | "Properties": {
1292 | "RouteTableId": {
1293 | "Ref": "OtherVPCPublicSubnet1RouteTable065F9C73"
1294 | },
1295 | "DestinationCidrBlock": "0.0.0.0/0",
1296 | "GatewayId": {
1297 | "Ref": "OtherVPCIGWD56B3F3B"
1298 | }
1299 | },
1300 | "DependsOn": [
1301 | "OtherVPCVPCGW40BC5735"
1302 | ],
1303 | "Metadata": {
1304 | "aws:cdk:path": "ServiceAStack/OtherVPC/PublicSubnet1/DefaultRoute"
1305 | }
1306 | },
1307 | "OtherVPCPublicSubnet1EIPC039E78F": {
1308 | "Type": "AWS::EC2::EIP",
1309 | "Properties": {
1310 | "Domain": "vpc",
1311 | "Tags": [
1312 | {
1313 | "Key": "Name",
1314 | "Value": "ServiceAStack/OtherVPC/PublicSubnet1"
1315 | },
1316 | {
1317 | "Key": "Owner",
1318 | "Value": "AccountA"
1319 | },
1320 | {
1321 | "Key": "Project",
1322 | "Value": "ZeroTrustWorkshop"
1323 | },
1324 | {
1325 | "Key": "Service",
1326 | "Value": "ServiceA"
1327 | }
1328 | ]
1329 | },
1330 | "Metadata": {
1331 | "aws:cdk:path": "ServiceAStack/OtherVPC/PublicSubnet1/EIP"
1332 | }
1333 | },
1334 | "OtherVPCPublicSubnet1NATGateway982870F4": {
1335 | "Type": "AWS::EC2::NatGateway",
1336 | "Properties": {
1337 | "SubnetId": {
1338 | "Ref": "OtherVPCPublicSubnet1Subnet7C6060EB"
1339 | },
1340 | "AllocationId": {
1341 | "Fn::GetAtt": [
1342 | "OtherVPCPublicSubnet1EIPC039E78F",
1343 | "AllocationId"
1344 | ]
1345 | },
1346 | "Tags": [
1347 | {
1348 | "Key": "Name",
1349 | "Value": "ServiceAStack/OtherVPC/PublicSubnet1"
1350 | },
1351 | {
1352 | "Key": "Owner",
1353 | "Value": "AccountA"
1354 | },
1355 | {
1356 | "Key": "Project",
1357 | "Value": "ZeroTrustWorkshop"
1358 | },
1359 | {
1360 | "Key": "Service",
1361 | "Value": "ServiceA"
1362 | }
1363 | ]
1364 | },
1365 | "Metadata": {
1366 | "aws:cdk:path": "ServiceAStack/OtherVPC/PublicSubnet1/NATGateway"
1367 | }
1368 | },
1369 | "OtherVPCIGWD56B3F3B": {
1370 | "Type": "AWS::EC2::InternetGateway",
1371 | "Properties": {
1372 | "Tags": [
1373 | {
1374 | "Key": "Name",
1375 | "Value": "ServiceAStack/OtherVPC"
1376 | },
1377 | {
1378 | "Key": "Owner",
1379 | "Value": "AccountA"
1380 | },
1381 | {
1382 | "Key": "Project",
1383 | "Value": "ZeroTrustWorkshop"
1384 | },
1385 | {
1386 | "Key": "Service",
1387 | "Value": "ServiceA"
1388 | }
1389 | ]
1390 | },
1391 | "Metadata": {
1392 | "aws:cdk:path": "ServiceAStack/OtherVPC/IGW"
1393 | }
1394 | },
1395 | "OtherVPCVPCGW40BC5735": {
1396 | "Type": "AWS::EC2::VPCGatewayAttachment",
1397 | "Properties": {
1398 | "VpcId": {
1399 | "Ref": "OtherVPC4F26C84D"
1400 | },
1401 | "InternetGatewayId": {
1402 | "Ref": "OtherVPCIGWD56B3F3B"
1403 | }
1404 | },
1405 | "Metadata": {
1406 | "aws:cdk:path": "ServiceAStack/OtherVPC/VPCGW"
1407 | }
1408 | },
1409 | "OtherVPCAPIGWVPC2EndpointSecurityGroup73C5FCBF": {
1410 | "Type": "AWS::EC2::SecurityGroup",
1411 | "Properties": {
1412 | "GroupDescription": "ServiceAStack/OtherVPC/APIGWVPC2Endpoint/SecurityGroup",
1413 | "SecurityGroupEgress": [
1414 | {
1415 | "CidrIp": "0.0.0.0/0",
1416 | "Description": "Allow all outbound traffic by default",
1417 | "IpProtocol": "-1"
1418 | }
1419 | ],
1420 | "SecurityGroupIngress": [
1421 | {
1422 | "CidrIp": {
1423 | "Fn::GetAtt": [
1424 | "OtherVPC4F26C84D",
1425 | "CidrBlock"
1426 | ]
1427 | },
1428 | "Description": {
1429 | "Fn::Join": [
1430 | "",
1431 | [
1432 | "from ",
1433 | {
1434 | "Fn::GetAtt": [
1435 | "OtherVPC4F26C84D",
1436 | "CidrBlock"
1437 | ]
1438 | },
1439 | ":443"
1440 | ]
1441 | ]
1442 | },
1443 | "FromPort": 443,
1444 | "IpProtocol": "tcp",
1445 | "ToPort": 443
1446 | }
1447 | ],
1448 | "Tags": [
1449 | {
1450 | "Key": "Owner",
1451 | "Value": "AccountA"
1452 | },
1453 | {
1454 | "Key": "Project",
1455 | "Value": "ZeroTrustWorkshop"
1456 | },
1457 | {
1458 | "Key": "Service",
1459 | "Value": "ServiceA"
1460 | }
1461 | ],
1462 | "VpcId": {
1463 | "Ref": "OtherVPC4F26C84D"
1464 | }
1465 | },
1466 | "Metadata": {
1467 | "aws:cdk:path": "ServiceAStack/OtherVPC/APIGWVPC2Endpoint/SecurityGroup/Resource"
1468 | }
1469 | },
1470 | "OtherVPCAPIGWVPC2EndpointB5879E6B": {
1471 | "Type": "AWS::EC2::VPCEndpoint",
1472 | "Properties": {
1473 | "ServiceName": {
1474 | "Fn::Join": [
1475 | "",
1476 | [
1477 | "com.amazonaws.",
1478 | {
1479 | "Ref": "AWS::Region"
1480 | },
1481 | ".execute-api"
1482 | ]
1483 | ]
1484 | },
1485 | "VpcId": {
1486 | "Ref": "OtherVPC4F26C84D"
1487 | },
1488 | "PrivateDnsEnabled": true,
1489 | "SecurityGroupIds": [
1490 | {
1491 | "Fn::GetAtt": [
1492 | "OtherVPCAPIGWVPC2EndpointSecurityGroup73C5FCBF",
1493 | "GroupId"
1494 | ]
1495 | }
1496 | ],
1497 | "SubnetIds": [
1498 | {
1499 | "Ref": "OtherVPCPrivateSubnet1SubnetD46635C4"
1500 | }
1501 | ],
1502 | "VpcEndpointType": "Interface"
1503 | },
1504 | "Metadata": {
1505 | "aws:cdk:path": "ServiceAStack/OtherVPC/APIGWVPC2Endpoint/Resource"
1506 | }
1507 | },
1508 | "OtherVPCSSMVPC2EndpointSecurityGroup769F1881": {
1509 | "Type": "AWS::EC2::SecurityGroup",
1510 | "Properties": {
1511 | "GroupDescription": "ServiceAStack/OtherVPC/SSMVPC2Endpoint/SecurityGroup",
1512 | "SecurityGroupEgress": [
1513 | {
1514 | "CidrIp": "0.0.0.0/0",
1515 | "Description": "Allow all outbound traffic by default",
1516 | "IpProtocol": "-1"
1517 | }
1518 | ],
1519 | "SecurityGroupIngress": [
1520 | {
1521 | "CidrIp": {
1522 | "Fn::GetAtt": [
1523 | "OtherVPC4F26C84D",
1524 | "CidrBlock"
1525 | ]
1526 | },
1527 | "Description": {
1528 | "Fn::Join": [
1529 | "",
1530 | [
1531 | "from ",
1532 | {
1533 | "Fn::GetAtt": [
1534 | "OtherVPC4F26C84D",
1535 | "CidrBlock"
1536 | ]
1537 | },
1538 | ":443"
1539 | ]
1540 | ]
1541 | },
1542 | "FromPort": 443,
1543 | "IpProtocol": "tcp",
1544 | "ToPort": 443
1545 | }
1546 | ],
1547 | "Tags": [
1548 | {
1549 | "Key": "Owner",
1550 | "Value": "AccountA"
1551 | },
1552 | {
1553 | "Key": "Project",
1554 | "Value": "ZeroTrustWorkshop"
1555 | },
1556 | {
1557 | "Key": "Service",
1558 | "Value": "ServiceA"
1559 | }
1560 | ],
1561 | "VpcId": {
1562 | "Ref": "OtherVPC4F26C84D"
1563 | }
1564 | },
1565 | "Metadata": {
1566 | "aws:cdk:path": "ServiceAStack/OtherVPC/SSMVPC2Endpoint/SecurityGroup/Resource"
1567 | }
1568 | },
1569 | "OtherVPCSSMVPC2Endpoint15D9FFBC": {
1570 | "Type": "AWS::EC2::VPCEndpoint",
1571 | "Properties": {
1572 | "ServiceName": {
1573 | "Fn::Join": [
1574 | "",
1575 | [
1576 | "com.amazonaws.",
1577 | {
1578 | "Ref": "AWS::Region"
1579 | },
1580 | ".ssm"
1581 | ]
1582 | ]
1583 | },
1584 | "VpcId": {
1585 | "Ref": "OtherVPC4F26C84D"
1586 | },
1587 | "PrivateDnsEnabled": true,
1588 | "SecurityGroupIds": [
1589 | {
1590 | "Fn::GetAtt": [
1591 | "OtherVPCSSMVPC2EndpointSecurityGroup769F1881",
1592 | "GroupId"
1593 | ]
1594 | }
1595 | ],
1596 | "SubnetIds": [
1597 | {
1598 | "Ref": "OtherVPCPrivateSubnet1SubnetD46635C4"
1599 | }
1600 | ],
1601 | "VpcEndpointType": "Interface"
1602 | },
1603 | "Metadata": {
1604 | "aws:cdk:path": "ServiceAStack/OtherVPC/SSMVPC2Endpoint/Resource"
1605 | }
1606 | },
1607 | "OtherVPCSSMMSGVPC2EndpointSecurityGroupE4C6B3AF": {
1608 | "Type": "AWS::EC2::SecurityGroup",
1609 | "Properties": {
1610 | "GroupDescription": "ServiceAStack/OtherVPC/SSMMSGVPC2Endpoint/SecurityGroup",
1611 | "SecurityGroupEgress": [
1612 | {
1613 | "CidrIp": "0.0.0.0/0",
1614 | "Description": "Allow all outbound traffic by default",
1615 | "IpProtocol": "-1"
1616 | }
1617 | ],
1618 | "SecurityGroupIngress": [
1619 | {
1620 | "CidrIp": {
1621 | "Fn::GetAtt": [
1622 | "OtherVPC4F26C84D",
1623 | "CidrBlock"
1624 | ]
1625 | },
1626 | "Description": {
1627 | "Fn::Join": [
1628 | "",
1629 | [
1630 | "from ",
1631 | {
1632 | "Fn::GetAtt": [
1633 | "OtherVPC4F26C84D",
1634 | "CidrBlock"
1635 | ]
1636 | },
1637 | ":443"
1638 | ]
1639 | ]
1640 | },
1641 | "FromPort": 443,
1642 | "IpProtocol": "tcp",
1643 | "ToPort": 443
1644 | }
1645 | ],
1646 | "Tags": [
1647 | {
1648 | "Key": "Owner",
1649 | "Value": "AccountA"
1650 | },
1651 | {
1652 | "Key": "Project",
1653 | "Value": "ZeroTrustWorkshop"
1654 | },
1655 | {
1656 | "Key": "Service",
1657 | "Value": "ServiceA"
1658 | }
1659 | ],
1660 | "VpcId": {
1661 | "Ref": "OtherVPC4F26C84D"
1662 | }
1663 | },
1664 | "Metadata": {
1665 | "aws:cdk:path": "ServiceAStack/OtherVPC/SSMMSGVPC2Endpoint/SecurityGroup/Resource"
1666 | }
1667 | },
1668 | "OtherVPCSSMMSGVPC2Endpoint53F44D6B": {
1669 | "Type": "AWS::EC2::VPCEndpoint",
1670 | "Properties": {
1671 | "ServiceName": {
1672 | "Fn::Join": [
1673 | "",
1674 | [
1675 | "com.amazonaws.",
1676 | {
1677 | "Ref": "AWS::Region"
1678 | },
1679 | ".ssmmessages"
1680 | ]
1681 | ]
1682 | },
1683 | "VpcId": {
1684 | "Ref": "OtherVPC4F26C84D"
1685 | },
1686 | "PrivateDnsEnabled": true,
1687 | "SecurityGroupIds": [
1688 | {
1689 | "Fn::GetAtt": [
1690 | "OtherVPCSSMMSGVPC2EndpointSecurityGroupE4C6B3AF",
1691 | "GroupId"
1692 | ]
1693 | }
1694 | ],
1695 | "SubnetIds": [
1696 | {
1697 | "Ref": "OtherVPCPrivateSubnet1SubnetD46635C4"
1698 | }
1699 | ],
1700 | "VpcEndpointType": "Interface"
1701 | },
1702 | "Metadata": {
1703 | "aws:cdk:path": "ServiceAStack/OtherVPC/SSMMSGVPC2Endpoint/Resource"
1704 | }
1705 | },
1706 | "MainVPCSecurityGroupA08D57AE": {
1707 | "Type": "AWS::EC2::SecurityGroup",
1708 | "Properties": {
1709 | "GroupDescription": "ServiceAStack/MainVPCSecurityGroup",
1710 | "GroupName": "MainVPCSecurityGroup",
1711 | "SecurityGroupEgress": [
1712 | {
1713 | "CidrIp": "0.0.0.0/0",
1714 | "Description": "Allow all outbound traffic by default",
1715 | "IpProtocol": "-1"
1716 | }
1717 | ],
1718 | "Tags": [
1719 | {
1720 | "Key": "Owner",
1721 | "Value": "AccountA"
1722 | },
1723 | {
1724 | "Key": "Project",
1725 | "Value": "ZeroTrustWorkshop"
1726 | },
1727 | {
1728 | "Key": "Service",
1729 | "Value": "ServiceA"
1730 | }
1731 | ],
1732 | "VpcId": {
1733 | "Ref": "ServiceAVPCE249ED38"
1734 | }
1735 | },
1736 | "Metadata": {
1737 | "aws:cdk:path": "ServiceAStack/MainVPCSecurityGroup/Resource"
1738 | }
1739 | },
1740 | "ServiceASecurityGroupF9B81400": {
1741 | "Type": "AWS::EC2::SecurityGroup",
1742 | "Properties": {
1743 | "GroupDescription": "ServiceAStack/ServiceASecurityGroup",
1744 | "GroupName": "ServiceASecurityGroup",
1745 | "SecurityGroupEgress": [
1746 | {
1747 | "CidrIp": "0.0.0.0/0",
1748 | "Description": "Allow all outbound traffic by default",
1749 | "IpProtocol": "-1"
1750 | }
1751 | ],
1752 | "Tags": [
1753 | {
1754 | "Key": "Owner",
1755 | "Value": "AccountA"
1756 | },
1757 | {
1758 | "Key": "Project",
1759 | "Value": "ZeroTrustWorkshop"
1760 | },
1761 | {
1762 | "Key": "Service",
1763 | "Value": "ServiceA"
1764 | }
1765 | ],
1766 | "VpcId": {
1767 | "Ref": "ServiceAVPCE249ED38"
1768 | }
1769 | },
1770 | "Metadata": {
1771 | "aws:cdk:path": "ServiceAStack/ServiceASecurityGroup/Resource"
1772 | }
1773 | },
1774 | "APIGWVPCEndpointSecurityGroup512E5E3C": {
1775 | "Type": "AWS::EC2::SecurityGroup",
1776 | "Properties": {
1777 | "GroupDescription": "ServiceAStack/APIGWVPCEndpointSecurityGroup",
1778 | "GroupName": "APIGWVPCEndpointSecurityGroup",
1779 | "SecurityGroupEgress": [
1780 | {
1781 | "CidrIp": "0.0.0.0/0",
1782 | "Description": "Allow all outbound traffic by default",
1783 | "IpProtocol": "-1"
1784 | }
1785 | ],
1786 | "SecurityGroupIngress": [
1787 | {
1788 | "CidrIp": {
1789 | "Fn::GetAtt": [
1790 | "ServiceAVPCE249ED38",
1791 | "CidrBlock"
1792 | ]
1793 | },
1794 | "Description": {
1795 | "Fn::Join": [
1796 | "",
1797 | [
1798 | "from ",
1799 | {
1800 | "Fn::GetAtt": [
1801 | "ServiceAVPCE249ED38",
1802 | "CidrBlock"
1803 | ]
1804 | },
1805 | ":443"
1806 | ]
1807 | ]
1808 | },
1809 | "FromPort": 443,
1810 | "IpProtocol": "tcp",
1811 | "ToPort": 443
1812 | }
1813 | ],
1814 | "Tags": [
1815 | {
1816 | "Key": "Name",
1817 | "Value": "APIGWVPCEndpointSecurityGroup"
1818 | },
1819 | {
1820 | "Key": "Owner",
1821 | "Value": "AccountA"
1822 | },
1823 | {
1824 | "Key": "Project",
1825 | "Value": "ZeroTrustWorkshop"
1826 | },
1827 | {
1828 | "Key": "Service",
1829 | "Value": "ServiceA"
1830 | }
1831 | ],
1832 | "VpcId": {
1833 | "Ref": "ServiceAVPCE249ED38"
1834 | }
1835 | },
1836 | "Metadata": {
1837 | "aws:cdk:path": "ServiceAStack/APIGWVPCEndpointSecurityGroup/Resource"
1838 | }
1839 | },
1840 | "WorkshopLayer518F319D": {
1841 | "Type": "AWS::Lambda::LayerVersion",
1842 | "Properties": {
1843 | "Content": {
1844 | "S3Bucket": {
1845 | "Ref": "EEAssetsBucket"
1846 | },
1847 | "S3Key": {
1848 | "Fn::Join": [
1849 | "",
1850 | [
1851 | {
1852 | "Ref": "EEAssetsKeyPrefix"
1853 | },
1854 | "lambda/layer/lambda-code.zip"
1855 | ]
1856 | ]
1857 | }
1858 | },
1859 | "CompatibleRuntimes": [
1860 | "python3.8"
1861 | ],
1862 | "Description": "The layer containing external packages used in this workshop."
1863 | },
1864 | "Metadata": {
1865 | "aws:cdk:path": "ServiceAStack/WorkshopLayer/Resource"
1866 | }
1867 | },
1868 | "CallerOneE9C89DF9": {
1869 | "Type": "AWS::Lambda::Function",
1870 | "Properties": {
1871 | "Code": {
1872 | "S3Bucket": {
1873 | "Ref": "EEAssetsBucket"
1874 | },
1875 | "S3Key": {
1876 | "Fn::Join": [
1877 | "",
1878 | [
1879 | {
1880 | "Ref": "EEAssetsKeyPrefix"
1881 | },
1882 | "lambda/caller_nosigv4/lambda-code.zip"
1883 | ]
1884 | ]
1885 | }
1886 | },
1887 | "Role": {
1888 | "Fn::GetAtt": [
1889 | "ServiceALambdaRoleA5CC16AA",
1890 | "Arn"
1891 | ]
1892 | },
1893 | "Environment": {
1894 | "Variables": {
1895 | "api_resource": "orders",
1896 | "api_region": {
1897 | "Ref": "AWS::Region"
1898 | },
1899 | "api_id_parameter": "/workshop/params/service-b-api-id",
1900 | "api_secret_parameter": "/workshop/params/service-b-api-secret-arn"
1901 | }
1902 | },
1903 | "Handler": "lambda_function.lambda_handler",
1904 | "Layers": [
1905 | {
1906 | "Ref": "WorkshopLayer518F319D"
1907 | }
1908 | ],
1909 | "Runtime": "python3.8",
1910 | "Tags": [
1911 | {
1912 | "Key": "Owner",
1913 | "Value": "AccountA"
1914 | },
1915 | {
1916 | "Key": "Project",
1917 | "Value": "ZeroTrustWorkshop"
1918 | },
1919 | {
1920 | "Key": "Service",
1921 | "Value": "ServiceA"
1922 | }
1923 | ],
1924 | "Timeout": 60,
1925 | "VpcConfig": {
1926 | "SecurityGroupIds": [
1927 | {
1928 | "Fn::GetAtt": [
1929 | "MainVPCSecurityGroupA08D57AE",
1930 | "GroupId"
1931 | ]
1932 | }
1933 | ],
1934 | "SubnetIds": [
1935 | {
1936 | "Ref": "ServiceAVPCPrivateSubnet2Subnet86E1F16D"
1937 | }
1938 | ]
1939 | }
1940 | },
1941 | "DependsOn": [
1942 | "ServiceALambdaRoleDefaultPolicy42CFEBA3",
1943 | "ServiceALambdaRoleA5CC16AA"
1944 | ],
1945 | "Metadata": {
1946 | "aws:cdk:path": "ServiceAStack/CallerOne/Resource"
1947 | }
1948 | },
1949 | "CallerTwoDF240F92": {
1950 | "Type": "AWS::Lambda::Function",
1951 | "Properties": {
1952 | "Code": {
1953 | "S3Bucket": {
1954 | "Ref": "EEAssetsBucket"
1955 | },
1956 | "S3Key": {
1957 | "Fn::Join": [
1958 | "",
1959 | [
1960 | {
1961 | "Ref": "EEAssetsKeyPrefix"
1962 | },
1963 | "lambda/caller_nosigv4/lambda-code.zip"
1964 | ]
1965 | ]
1966 | }
1967 | },
1968 | "Role": {
1969 | "Fn::GetAtt": [
1970 | "ServiceALambdaRoleA5CC16AA",
1971 | "Arn"
1972 | ]
1973 | },
1974 | "Environment": {
1975 | "Variables": {
1976 | "api_resource": "orders",
1977 | "api_region": {
1978 | "Ref": "AWS::Region"
1979 | },
1980 | "api_id_parameter": "/workshop/params/service-b-api-id",
1981 | "api_secret_parameter": "/workshop/params/service-b-api-secret-arn"
1982 | }
1983 | },
1984 | "Handler": "lambda_function.lambda_handler",
1985 | "Layers": [
1986 | {
1987 | "Ref": "WorkshopLayer518F319D"
1988 | }
1989 | ],
1990 | "Runtime": "python3.8",
1991 | "Tags": [
1992 | {
1993 | "Key": "Owner",
1994 | "Value": "AccountA"
1995 | },
1996 | {
1997 | "Key": "Project",
1998 | "Value": "ZeroTrustWorkshop"
1999 | },
2000 | {
2001 | "Key": "Service",
2002 | "Value": "ServiceA"
2003 | }
2004 | ],
2005 | "Timeout": 60,
2006 | "VpcConfig": {
2007 | "SecurityGroupIds": [
2008 | {
2009 | "Fn::GetAtt": [
2010 | "MainVPCSecurityGroupA08D57AE",
2011 | "GroupId"
2012 | ]
2013 | }
2014 | ],
2015 | "SubnetIds": [
2016 | {
2017 | "Ref": "ServiceAVPCPrivateSubnet1Subnet86779CD2"
2018 | }
2019 | ]
2020 | }
2021 | },
2022 | "DependsOn": [
2023 | "ServiceALambdaRoleDefaultPolicy42CFEBA3",
2024 | "ServiceALambdaRoleA5CC16AA"
2025 | ],
2026 | "Metadata": {
2027 | "aws:cdk:path": "ServiceAStack/CallerTwo/Resource"
2028 | }
2029 | },
2030 | "CallerThreeE06DAEE6": {
2031 | "Type": "AWS::Lambda::Function",
2032 | "Properties": {
2033 | "Code": {
2034 | "S3Bucket": {
2035 | "Ref": "EEAssetsBucket"
2036 | },
2037 | "S3Key": {
2038 | "Fn::Join": [
2039 | "",
2040 | [
2041 | {
2042 | "Ref": "EEAssetsKeyPrefix"
2043 | },
2044 | "lambda/caller_sigv4/lambda-code.zip"
2045 | ]
2046 | ]
2047 | }
2048 | },
2049 | "Role": {
2050 | "Fn::GetAtt": [
2051 | "ServiceALambdaRoleA5CC16AA",
2052 | "Arn"
2053 | ]
2054 | },
2055 | "Environment": {
2056 | "Variables": {
2057 | "api_resource": "orders",
2058 | "api_region": {
2059 | "Ref": "AWS::Region"
2060 | },
2061 | "api_id_parameter": "/workshop/params/service-b-api-id",
2062 | "api_secret_parameter": "/workshop/params/service-b-api-secret-arn"
2063 | }
2064 | },
2065 | "Handler": "lambda_function.lambda_handler",
2066 | "Layers": [
2067 | {
2068 | "Ref": "WorkshopLayer518F319D"
2069 | }
2070 | ],
2071 | "Runtime": "python3.8",
2072 | "Tags": [
2073 | {
2074 | "Key": "Owner",
2075 | "Value": "AccountA"
2076 | },
2077 | {
2078 | "Key": "Project",
2079 | "Value": "ZeroTrustWorkshop"
2080 | },
2081 | {
2082 | "Key": "Service",
2083 | "Value": "ServiceA"
2084 | }
2085 | ],
2086 | "Timeout": 60,
2087 | "VpcConfig": {
2088 | "SecurityGroupIds": [
2089 | {
2090 | "Fn::GetAtt": [
2091 | "MainVPCSecurityGroupA08D57AE",
2092 | "GroupId"
2093 | ]
2094 | }
2095 | ],
2096 | "SubnetIds": [
2097 | {
2098 | "Ref": "ServiceAVPCPrivateSubnet1Subnet86779CD2"
2099 | }
2100 | ]
2101 | }
2102 | },
2103 | "DependsOn": [
2104 | "ServiceALambdaRoleDefaultPolicy42CFEBA3",
2105 | "ServiceALambdaRoleA5CC16AA"
2106 | ],
2107 | "Metadata": {
2108 | "aws:cdk:path": "ServiceAStack/CallerThree/Resource"
2109 | }
2110 | },
2111 | "GuardDutyHelperServiceRoleDA0369C0": {
2112 | "Type": "AWS::IAM::Role",
2113 | "Properties": {
2114 | "AssumeRolePolicyDocument": {
2115 | "Statement": [
2116 | {
2117 | "Action": "sts:AssumeRole",
2118 | "Effect": "Allow",
2119 | "Principal": {
2120 | "Service": "lambda.amazonaws.com"
2121 | }
2122 | }
2123 | ],
2124 | "Version": "2012-10-17"
2125 | },
2126 | "ManagedPolicyArns": [
2127 | {
2128 | "Fn::Join": [
2129 | "",
2130 | [
2131 | "arn:",
2132 | {
2133 | "Ref": "AWS::Partition"
2134 | },
2135 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2136 | ]
2137 | ]
2138 | }
2139 | ],
2140 | "Tags": [
2141 | {
2142 | "Key": "Owner",
2143 | "Value": "AccountA"
2144 | },
2145 | {
2146 | "Key": "Project",
2147 | "Value": "ZeroTrustWorkshop"
2148 | },
2149 | {
2150 | "Key": "Service",
2151 | "Value": "ServiceA"
2152 | }
2153 | ]
2154 | },
2155 | "Metadata": {
2156 | "aws:cdk:path": "ServiceAStack/GuardDutyHelper/ServiceRole/Resource"
2157 | }
2158 | },
2159 | "GuardDutyHelperServiceRoleDefaultPolicyACC9622B": {
2160 | "Type": "AWS::IAM::Policy",
2161 | "Properties": {
2162 | "PolicyDocument": {
2163 | "Statement": [
2164 | {
2165 | "Action": [
2166 | "guardduty:CreateDetector",
2167 | "guardduty:CreateSampleFindings",
2168 | "guardduty:ListDetectors"
2169 | ],
2170 | "Effect": "Allow",
2171 | "Resource": "*"
2172 | },
2173 | {
2174 | "Action": "iam:CreateServiceLinkedRole",
2175 | "Condition": {
2176 | "StringLike": {
2177 | "iam:AWSServiceName": "guardduty.amazonaws.com"
2178 | }
2179 | },
2180 | "Effect": "Allow",
2181 | "Resource": "arn:aws:iam::*:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty*"
2182 | }
2183 | ],
2184 | "Version": "2012-10-17"
2185 | },
2186 | "PolicyName": "GuardDutyHelperServiceRoleDefaultPolicyACC9622B",
2187 | "Roles": [
2188 | {
2189 | "Ref": "GuardDutyHelperServiceRoleDA0369C0"
2190 | }
2191 | ]
2192 | },
2193 | "Metadata": {
2194 | "aws:cdk:path": "ServiceAStack/GuardDutyHelper/ServiceRole/DefaultPolicy/Resource"
2195 | }
2196 | },
2197 | "GuardDutyHelper7B2CAB0B": {
2198 | "Type": "AWS::Lambda::Function",
2199 | "Properties": {
2200 | "Code": {
2201 | "S3Bucket": {
2202 | "Ref": "EEAssetsBucket"
2203 | },
2204 | "S3Key": {
2205 | "Fn::Join": [
2206 | "",
2207 | [
2208 | {
2209 | "Ref": "EEAssetsKeyPrefix"
2210 | },
2211 | "lambda/guardduty_helper/lambda-code.zip"
2212 | ]
2213 | ]
2214 | }
2215 | },
2216 | "Role": {
2217 | "Fn::GetAtt": [
2218 | "GuardDutyHelperServiceRoleDA0369C0",
2219 | "Arn"
2220 | ]
2221 | },
2222 | "Environment": {
2223 | "Variables": {
2224 | "region": {
2225 | "Ref": "AWS::Region"
2226 | }
2227 | }
2228 | },
2229 | "Handler": "lambda_function.lambda_handler",
2230 | "Runtime": "python3.8",
2231 | "Tags": [
2232 | {
2233 | "Key": "Owner",
2234 | "Value": "AccountA"
2235 | },
2236 | {
2237 | "Key": "Project",
2238 | "Value": "ZeroTrustWorkshop"
2239 | },
2240 | {
2241 | "Key": "Service",
2242 | "Value": "ServiceA"
2243 | }
2244 | ],
2245 | "Timeout": 60
2246 | },
2247 | "DependsOn": [
2248 | "GuardDutyHelperServiceRoleDefaultPolicyACC9622B",
2249 | "GuardDutyHelperServiceRoleDA0369C0"
2250 | ],
2251 | "Metadata": {
2252 | "aws:cdk:path": "ServiceAStack/GuardDutyHelper/Resource"
2253 | }
2254 | },
2255 | "GDInit": {
2256 | "Type": "AWS::CloudFormation::CustomResource",
2257 | "Properties": {
2258 | "ServiceToken": {
2259 | "Fn::GetAtt": [
2260 | "GuardDutyHelper7B2CAB0B",
2261 | "Arn"
2262 | ]
2263 | }
2264 | },
2265 | "UpdateReplacePolicy": "Delete",
2266 | "DeletionPolicy": "Delete",
2267 | "Metadata": {
2268 | "aws:cdk:path": "ServiceAStack/GDInit/Default"
2269 | }
2270 | },
2271 | "LogGroup0B487484F": {
2272 | "Type": "AWS::Logs::LogGroup",
2273 | "Properties": {
2274 | "LogGroupName": {
2275 | "Fn::Join": [
2276 | "",
2277 | [
2278 | "/aws/lambda/",
2279 | {
2280 | "Ref": "CallerOneE9C89DF9"
2281 | }
2282 | ]
2283 | ]
2284 | },
2285 | "RetentionInDays": 731
2286 | },
2287 | "UpdateReplacePolicy": "Delete",
2288 | "DeletionPolicy": "Delete",
2289 | "Metadata": {
2290 | "aws:cdk:path": "ServiceAStack/LogGroup0/Resource"
2291 | }
2292 | },
2293 | "LogGroup106AAD846": {
2294 | "Type": "AWS::Logs::LogGroup",
2295 | "Properties": {
2296 | "LogGroupName": {
2297 | "Fn::Join": [
2298 | "",
2299 | [
2300 | "/aws/lambda/",
2301 | {
2302 | "Ref": "CallerTwoDF240F92"
2303 | }
2304 | ]
2305 | ]
2306 | },
2307 | "RetentionInDays": 731
2308 | },
2309 | "UpdateReplacePolicy": "Delete",
2310 | "DeletionPolicy": "Delete",
2311 | "Metadata": {
2312 | "aws:cdk:path": "ServiceAStack/LogGroup1/Resource"
2313 | }
2314 | },
2315 | "LogGroup2477F707C": {
2316 | "Type": "AWS::Logs::LogGroup",
2317 | "Properties": {
2318 | "LogGroupName": {
2319 | "Fn::Join": [
2320 | "",
2321 | [
2322 | "/aws/lambda/",
2323 | {
2324 | "Ref": "CallerThreeE06DAEE6"
2325 | }
2326 | ]
2327 | ]
2328 | },
2329 | "RetentionInDays": 731
2330 | },
2331 | "UpdateReplacePolicy": "Delete",
2332 | "DeletionPolicy": "Delete",
2333 | "Metadata": {
2334 | "aws:cdk:path": "ServiceAStack/LogGroup2/Resource"
2335 | }
2336 | },
2337 | "LogGroup34356AF39": {
2338 | "Type": "AWS::Logs::LogGroup",
2339 | "Properties": {
2340 | "LogGroupName": {
2341 | "Fn::Join": [
2342 | "",
2343 | [
2344 | "/aws/lambda/",
2345 | {
2346 | "Ref": "GuardDutyHelper7B2CAB0B"
2347 | }
2348 | ]
2349 | ]
2350 | },
2351 | "RetentionInDays": 731
2352 | },
2353 | "UpdateReplacePolicy": "Delete",
2354 | "DeletionPolicy": "Delete",
2355 | "Metadata": {
2356 | "aws:cdk:path": "ServiceAStack/LogGroup3/Resource"
2357 | }
2358 | },
2359 | "ScheduleRuleOne938E8D32": {
2360 | "Type": "AWS::Events::Rule",
2361 | "Properties": {
2362 | "Description": "Scheduler running every minute",
2363 | "ScheduleExpression": "rate(1 minute)",
2364 | "State": "ENABLED",
2365 | "Targets": [
2366 | {
2367 | "Arn": {
2368 | "Fn::GetAtt": [
2369 | "CallerOneE9C89DF9",
2370 | "Arn"
2371 | ]
2372 | },
2373 | "Id": "Target0"
2374 | },
2375 | {
2376 | "Arn": {
2377 | "Fn::GetAtt": [
2378 | "CallerTwoDF240F92",
2379 | "Arn"
2380 | ]
2381 | },
2382 | "Id": "Target1"
2383 | },
2384 | {
2385 | "Arn": {
2386 | "Fn::GetAtt": [
2387 | "CallerThreeE06DAEE6",
2388 | "Arn"
2389 | ]
2390 | },
2391 | "Id": "Target2"
2392 | }
2393 | ]
2394 | },
2395 | "Metadata": {
2396 | "aws:cdk:path": "ServiceAStack/ScheduleRuleOne/Resource"
2397 | }
2398 | },
2399 | "ScheduleRuleOneAllowEventRuleServiceAStackCallerOneAA641A7DDF8D8A4E": {
2400 | "Type": "AWS::Lambda::Permission",
2401 | "Properties": {
2402 | "Action": "lambda:InvokeFunction",
2403 | "FunctionName": {
2404 | "Fn::GetAtt": [
2405 | "CallerOneE9C89DF9",
2406 | "Arn"
2407 | ]
2408 | },
2409 | "Principal": "events.amazonaws.com",
2410 | "SourceArn": {
2411 | "Fn::GetAtt": [
2412 | "ScheduleRuleOne938E8D32",
2413 | "Arn"
2414 | ]
2415 | }
2416 | },
2417 | "Metadata": {
2418 | "aws:cdk:path": "ServiceAStack/ScheduleRuleOne/AllowEventRuleServiceAStackCallerOneAA641A7D"
2419 | }
2420 | },
2421 | "ScheduleRuleOneAllowEventRuleServiceAStackCallerTwoC32695FAB03BE227": {
2422 | "Type": "AWS::Lambda::Permission",
2423 | "Properties": {
2424 | "Action": "lambda:InvokeFunction",
2425 | "FunctionName": {
2426 | "Fn::GetAtt": [
2427 | "CallerTwoDF240F92",
2428 | "Arn"
2429 | ]
2430 | },
2431 | "Principal": "events.amazonaws.com",
2432 | "SourceArn": {
2433 | "Fn::GetAtt": [
2434 | "ScheduleRuleOne938E8D32",
2435 | "Arn"
2436 | ]
2437 | }
2438 | },
2439 | "Metadata": {
2440 | "aws:cdk:path": "ServiceAStack/ScheduleRuleOne/AllowEventRuleServiceAStackCallerTwoC32695FA"
2441 | }
2442 | },
2443 | "ScheduleRuleOneAllowEventRuleServiceAStackCallerThreeE51BB7A51B959AFE": {
2444 | "Type": "AWS::Lambda::Permission",
2445 | "Properties": {
2446 | "Action": "lambda:InvokeFunction",
2447 | "FunctionName": {
2448 | "Fn::GetAtt": [
2449 | "CallerThreeE06DAEE6",
2450 | "Arn"
2451 | ]
2452 | },
2453 | "Principal": "events.amazonaws.com",
2454 | "SourceArn": {
2455 | "Fn::GetAtt": [
2456 | "ScheduleRuleOne938E8D32",
2457 | "Arn"
2458 | ]
2459 | }
2460 | },
2461 | "Metadata": {
2462 | "aws:cdk:path": "ServiceAStack/ScheduleRuleOne/AllowEventRuleServiceAStackCallerThreeE51BB7A5"
2463 | }
2464 | },
2465 | "ServiceAInstanceInstanceProfileE6B75074": {
2466 | "Type": "AWS::IAM::InstanceProfile",
2467 | "Properties": {
2468 | "Roles": [
2469 | {
2470 | "Ref": "ServiceAInstanceRole919D8950"
2471 | }
2472 | ]
2473 | },
2474 | "Metadata": {
2475 | "aws:cdk:path": "ServiceAStack/ServiceAInstance/InstanceProfile"
2476 | }
2477 | },
2478 | "ServiceAInstance15E8E518": {
2479 | "Type": "AWS::EC2::Instance",
2480 | "Properties": {
2481 | "AvailabilityZone": {
2482 | "Fn::Select": [
2483 | 0,
2484 | {
2485 | "Fn::GetAZs": ""
2486 | }
2487 | ]
2488 | },
2489 | "IamInstanceProfile": {
2490 | "Ref": "ServiceAInstanceInstanceProfileE6B75074"
2491 | },
2492 | "ImageId": {
2493 | "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter"
2494 | },
2495 | "InstanceType": "t3.micro",
2496 | "SecurityGroupIds": [
2497 | {
2498 | "Fn::GetAtt": [
2499 | "ServiceASecurityGroupF9B81400",
2500 | "GroupId"
2501 | ]
2502 | }
2503 | ],
2504 | "SubnetId": {
2505 | "Ref": "ServiceAVPCPrivateSubnet1Subnet86779CD2"
2506 | },
2507 | "Tags": [
2508 | {
2509 | "Key": "Name",
2510 | "Value": "ServiceAStack/ServiceAInstance"
2511 | },
2512 | {
2513 | "Key": "Owner",
2514 | "Value": "AccountA"
2515 | },
2516 | {
2517 | "Key": "Project",
2518 | "Value": "ZeroTrustWorkshop"
2519 | },
2520 | {
2521 | "Key": "Service",
2522 | "Value": "ServiceA"
2523 | }
2524 | ],
2525 | "UserData": {
2526 | "Fn::Base64": {
2527 | "Fn::Join": [
2528 | "",
2529 | [
2530 | "#!/bin/bash\npip3 install aws-requests-auth boto3 python-dotenv\ncd /tmp\n# NOTE update with latest Event Engine S3 URL\ncurl -O https://ee-assets-prod-us-east-1.s3.amazonaws.com/modules/1a656bee298f48fcad1bd4938e19b40a/v1/curl-pkg.zip\nunzip -qo curl-pkg.zip -d /tmp/workshop/\nchmod -R 777 /tmp/workshop/\n\ntouch /tmp/workshop/.env\n\necho python3 /tmp/workshop/scanner.py > /usr/bin/runscanner\nchmod 755 /usr/bin/runscanner\n\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\ncrontab /tmp/workshop/cron.txt\n\necho api_resource=orders >> /tmp/workshop/.env\necho api_region=",
2531 | {
2532 | "Ref": "AWS::Region"
2533 | },
2534 | " >> /tmp/workshop/.env\necho api_id_parameter=/workshop/params/service-b-api-id >> /tmp/workshop/.env\necho api_secret_parameter=/workshop/params/service-b-api-secret-arn >> /tmp/workshop/.env\necho unwanted_callers_parameter=/workshop/params/service-a-unwanted-callers-list >> /tmp/workshop/.env\necho unknown_api_id_parameter=/workshop/params/unknown-api-id >> /tmp/workshop/.env"
2535 | ]
2536 | ]
2537 | }
2538 | }
2539 | },
2540 | "DependsOn": [
2541 | "ServiceAInstanceRoleDefaultPolicyA4A6E2D5",
2542 | "ServiceAInstanceRole919D8950"
2543 | ],
2544 | "Metadata": {
2545 | "aws:cdk:path": "ServiceAStack/ServiceAInstance/Resource"
2546 | }
2547 | },
2548 | "OtherInstanceInstanceSecurityGroup7D24BF50": {
2549 | "Type": "AWS::EC2::SecurityGroup",
2550 | "Properties": {
2551 | "GroupDescription": "ServiceAStack/OtherInstance/InstanceSecurityGroup",
2552 | "SecurityGroupEgress": [
2553 | {
2554 | "CidrIp": "0.0.0.0/0",
2555 | "Description": "Allow all outbound traffic by default",
2556 | "IpProtocol": "-1"
2557 | }
2558 | ],
2559 | "Tags": [
2560 | {
2561 | "Key": "Name",
2562 | "Value": "ServiceAStack/OtherInstance"
2563 | },
2564 | {
2565 | "Key": "Owner",
2566 | "Value": "AccountA"
2567 | },
2568 | {
2569 | "Key": "Project",
2570 | "Value": "ZeroTrustWorkshop"
2571 | },
2572 | {
2573 | "Key": "Service",
2574 | "Value": "ServiceA"
2575 | }
2576 | ],
2577 | "VpcId": {
2578 | "Ref": "OtherVPC4F26C84D"
2579 | }
2580 | },
2581 | "Metadata": {
2582 | "aws:cdk:path": "ServiceAStack/OtherInstance/InstanceSecurityGroup/Resource"
2583 | }
2584 | },
2585 | "OtherInstanceInstanceProfile327ED654": {
2586 | "Type": "AWS::IAM::InstanceProfile",
2587 | "Properties": {
2588 | "Roles": [
2589 | {
2590 | "Ref": "ServiceAInstanceRole919D8950"
2591 | }
2592 | ]
2593 | },
2594 | "Metadata": {
2595 | "aws:cdk:path": "ServiceAStack/OtherInstance/InstanceProfile"
2596 | }
2597 | },
2598 | "OtherInstanceB97E7A26": {
2599 | "Type": "AWS::EC2::Instance",
2600 | "Properties": {
2601 | "AvailabilityZone": {
2602 | "Fn::Select": [
2603 | 0,
2604 | {
2605 | "Fn::GetAZs": ""
2606 | }
2607 | ]
2608 | },
2609 | "IamInstanceProfile": {
2610 | "Ref": "OtherInstanceInstanceProfile327ED654"
2611 | },
2612 | "ImageId": {
2613 | "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter"
2614 | },
2615 | "InstanceType": "t3.micro",
2616 | "SecurityGroupIds": [
2617 | {
2618 | "Fn::GetAtt": [
2619 | "OtherInstanceInstanceSecurityGroup7D24BF50",
2620 | "GroupId"
2621 | ]
2622 | }
2623 | ],
2624 | "SubnetId": {
2625 | "Ref": "OtherVPCPrivateSubnet1SubnetD46635C4"
2626 | },
2627 | "Tags": [
2628 | {
2629 | "Key": "Name",
2630 | "Value": "ServiceAStack/OtherInstance"
2631 | },
2632 | {
2633 | "Key": "Owner",
2634 | "Value": "AccountA"
2635 | },
2636 | {
2637 | "Key": "Project",
2638 | "Value": "ZeroTrustWorkshop"
2639 | },
2640 | {
2641 | "Key": "Service",
2642 | "Value": "ServiceA"
2643 | }
2644 | ],
2645 | "UserData": {
2646 | "Fn::Base64": {
2647 | "Fn::Join": [
2648 | "",
2649 | [
2650 | "#!/bin/bash\npip3 install aws-requests-auth boto3 python-dotenv\ncd /tmp\n# NOTE update with latest Event Engine S3 URL\ncurl -O https://ee-assets-prod-us-east-1.s3.amazonaws.com/modules/1a656bee298f48fcad1bd4938e19b40a/v1/curl-pkg.zip\nunzip -qo curl-pkg.zip -d /tmp/workshop/\nchmod -R 777 /tmp/workshop/\n\ntouch /tmp/workshop/.env\n\necho python3 /tmp/workshop/scanner.py > /usr/bin/runscanner\nchmod 755 /usr/bin/runscanner\n\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\necho \"* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1\" >> /tmp/workshop/cron.txt\ncrontab /tmp/workshop/cron.txt\n\necho api_resource=orders >> /tmp/workshop/.env\necho api_region=",
2651 | {
2652 | "Ref": "AWS::Region"
2653 | },
2654 | " >> /tmp/workshop/.env\necho api_id_parameter=/workshop/params/service-b-api-id >> /tmp/workshop/.env\necho api_secret_parameter=/workshop/params/service-b-api-secret-arn >> /tmp/workshop/.env"
2655 | ]
2656 | ]
2657 | }
2658 | }
2659 | },
2660 | "DependsOn": [
2661 | "ServiceAInstanceRoleDefaultPolicyA4A6E2D5",
2662 | "ServiceAInstanceRole919D8950"
2663 | ],
2664 | "Metadata": {
2665 | "aws:cdk:path": "ServiceAStack/OtherInstance/Resource"
2666 | }
2667 | },
2668 | "UnwantedCallersListParameter51EAAB6E": {
2669 | "Type": "AWS::SSM::Parameter",
2670 | "Properties": {
2671 | "Type": "StringList",
2672 | "Value": {
2673 | "Fn::Join": [
2674 | "",
2675 | [
2676 | {
2677 | "Fn::GetAtt": [
2678 | "CallerOneE9C89DF9",
2679 | "Arn"
2680 | ]
2681 | },
2682 | ",",
2683 | {
2684 | "Fn::GetAtt": [
2685 | "CallerTwoDF240F92",
2686 | "Arn"
2687 | ]
2688 | },
2689 | ",",
2690 | {
2691 | "Fn::GetAtt": [
2692 | "CallerThreeE06DAEE6",
2693 | "Arn"
2694 | ]
2695 | },
2696 | ",",
2697 | {
2698 | "Ref": "OtherInstanceB97E7A26"
2699 | }
2700 | ]
2701 | ]
2702 | },
2703 | "Name": "/workshop/params/service-a-unwanted-callers-list",
2704 | "Tags": {
2705 | "Owner": "AccountA",
2706 | "Project": "ZeroTrustWorkshop",
2707 | "Service": "ServiceA"
2708 | }
2709 | },
2710 | "Metadata": {
2711 | "aws:cdk:path": "ServiceAStack/UnwantedCallersListParameter/Resource"
2712 | }
2713 | },
2714 | "CDKMetadata": {
2715 | "Type": "AWS::CDK::Metadata",
2716 | "Properties": {
2717 | "Analytics": "v2:deflate64:H4sIAAAAAAAAE1VSTW/CMAz9LdxDWMeJ26DaEBLaojJxT1MXMpqkShxQVfW/L/2gLSc/f8TPfnFEo2hD3xYf/OGWIrutamEs0PqEXNxInGvGLVeAYElstEPrBZIEnPFWAIm9Q6MmN9dz/OOx9Nii8DSTKI1uSMtTuzXdeXED3HEHRHJF68QUfYPWMlNIUXX0IzoEdq4FMGtyWUBDQLzT+lyKNnlmMWFW3jnCyacaOtYJJcYj/PK0p+jjU2zrnBGSt/ONxYT5NDBPLT4PrDXfHPeB5MGHmYIwoWAWCpMM3haDhlcFGklXl3MBYdxPnZVGahyKR/cEwluJ1d4aX3ZjvgSe68+laEjBVZpxWh95BfYM1g07vPhfXovncnPMwCrpXPcthbm40MZcRvonDkLfww4hm/jhi3wrv3OqPRMr9eUoHc7uZHY0TdMQVuHV6NWabmi0Xvw5KZfWa5QKaNLbf9kJk16FAgAA"
2718 | },
2719 | "Metadata": {
2720 | "aws:cdk:path": "ServiceAStack/CDKMetadata/Default"
2721 | },
2722 | "Condition": "CDKMetadataAvailable"
2723 | }
2724 | },
2725 | "Outputs": {
2726 | "InstanceSession": {
2727 | "Value": {
2728 | "Fn::Join": [
2729 | "",
2730 | [
2731 | "https://console.aws.amazon.com/systems-manager/session-manager/",
2732 | {
2733 | "Ref": "ServiceAInstance15E8E518"
2734 | },
2735 | "?region=",
2736 | {
2737 | "Ref": "AWS::Region"
2738 | }
2739 | ]
2740 | ]
2741 | }
2742 | },
2743 | "InstanceRoleArn": {
2744 | "Value": {
2745 | "Fn::GetAtt": [
2746 | "ServiceAInstanceRole919D8950",
2747 | "Arn"
2748 | ]
2749 | }
2750 | },
2751 | "ServiceAAccountID": {
2752 | "Value": {
2753 | "Ref": "AWS::AccountId"
2754 | }
2755 | },
2756 | "VPCEndpointID": {
2757 | "Value": {
2758 | "Ref": "ServiceAVPCAPIGWVPCEndpoint2EDA772E"
2759 | }
2760 | }
2761 | },
2762 | "Conditions": {
2763 | "CDKMetadataAvailable": {
2764 | "Fn::Or": [
2765 | {
2766 | "Fn::Or": [
2767 | {
2768 | "Fn::Equals": [
2769 | {
2770 | "Ref": "AWS::Region"
2771 | },
2772 | "af-south-1"
2773 | ]
2774 | },
2775 | {
2776 | "Fn::Equals": [
2777 | {
2778 | "Ref": "AWS::Region"
2779 | },
2780 | "ap-east-1"
2781 | ]
2782 | },
2783 | {
2784 | "Fn::Equals": [
2785 | {
2786 | "Ref": "AWS::Region"
2787 | },
2788 | "ap-northeast-1"
2789 | ]
2790 | },
2791 | {
2792 | "Fn::Equals": [
2793 | {
2794 | "Ref": "AWS::Region"
2795 | },
2796 | "ap-northeast-2"
2797 | ]
2798 | },
2799 | {
2800 | "Fn::Equals": [
2801 | {
2802 | "Ref": "AWS::Region"
2803 | },
2804 | "ap-south-1"
2805 | ]
2806 | },
2807 | {
2808 | "Fn::Equals": [
2809 | {
2810 | "Ref": "AWS::Region"
2811 | },
2812 | "ap-southeast-1"
2813 | ]
2814 | },
2815 | {
2816 | "Fn::Equals": [
2817 | {
2818 | "Ref": "AWS::Region"
2819 | },
2820 | "ap-southeast-2"
2821 | ]
2822 | },
2823 | {
2824 | "Fn::Equals": [
2825 | {
2826 | "Ref": "AWS::Region"
2827 | },
2828 | "ca-central-1"
2829 | ]
2830 | },
2831 | {
2832 | "Fn::Equals": [
2833 | {
2834 | "Ref": "AWS::Region"
2835 | },
2836 | "cn-north-1"
2837 | ]
2838 | },
2839 | {
2840 | "Fn::Equals": [
2841 | {
2842 | "Ref": "AWS::Region"
2843 | },
2844 | "cn-northwest-1"
2845 | ]
2846 | }
2847 | ]
2848 | },
2849 | {
2850 | "Fn::Or": [
2851 | {
2852 | "Fn::Equals": [
2853 | {
2854 | "Ref": "AWS::Region"
2855 | },
2856 | "eu-central-1"
2857 | ]
2858 | },
2859 | {
2860 | "Fn::Equals": [
2861 | {
2862 | "Ref": "AWS::Region"
2863 | },
2864 | "eu-north-1"
2865 | ]
2866 | },
2867 | {
2868 | "Fn::Equals": [
2869 | {
2870 | "Ref": "AWS::Region"
2871 | },
2872 | "eu-south-1"
2873 | ]
2874 | },
2875 | {
2876 | "Fn::Equals": [
2877 | {
2878 | "Ref": "AWS::Region"
2879 | },
2880 | "eu-west-1"
2881 | ]
2882 | },
2883 | {
2884 | "Fn::Equals": [
2885 | {
2886 | "Ref": "AWS::Region"
2887 | },
2888 | "eu-west-2"
2889 | ]
2890 | },
2891 | {
2892 | "Fn::Equals": [
2893 | {
2894 | "Ref": "AWS::Region"
2895 | },
2896 | "eu-west-3"
2897 | ]
2898 | },
2899 | {
2900 | "Fn::Equals": [
2901 | {
2902 | "Ref": "AWS::Region"
2903 | },
2904 | "me-south-1"
2905 | ]
2906 | },
2907 | {
2908 | "Fn::Equals": [
2909 | {
2910 | "Ref": "AWS::Region"
2911 | },
2912 | "sa-east-1"
2913 | ]
2914 | },
2915 | {
2916 | "Fn::Equals": [
2917 | {
2918 | "Ref": "AWS::Region"
2919 | },
2920 | "us-east-1"
2921 | ]
2922 | },
2923 | {
2924 | "Fn::Equals": [
2925 | {
2926 | "Ref": "AWS::Region"
2927 | },
2928 | "us-east-2"
2929 | ]
2930 | }
2931 | ]
2932 | },
2933 | {
2934 | "Fn::Or": [
2935 | {
2936 | "Fn::Equals": [
2937 | {
2938 | "Ref": "AWS::Region"
2939 | },
2940 | "us-west-1"
2941 | ]
2942 | },
2943 | {
2944 | "Fn::Equals": [
2945 | {
2946 | "Ref": "AWS::Region"
2947 | },
2948 | "us-west-2"
2949 | ]
2950 | }
2951 | ]
2952 | }
2953 | ]
2954 | }
2955 | }
2956 | }
--------------------------------------------------------------------------------
/cfn_templates/ServiceBStack.template.json:
--------------------------------------------------------------------------------
1 | {
2 | "Parameters": {
3 | "EEAssetsBucket": {
4 | "Type": "String",
5 | "Description": "Region-specific assets S3 bucket name."
6 | },
7 | "EEAssetsKeyPrefix": {
8 | "Type": "String",
9 | "Description": "S3 key prefix where this modules assets are stored. (e.g. modules/my_module/v1/)"
10 | }
11 | },
12 | "Resources": {
13 | "OrdersTable315BB997": {
14 | "Type": "AWS::DynamoDB::Table",
15 | "Properties": {
16 | "KeySchema": [
17 | {
18 | "AttributeName": "order_id",
19 | "KeyType": "HASH"
20 | }
21 | ],
22 | "AttributeDefinitions": [
23 | {
24 | "AttributeName": "order_id",
25 | "AttributeType": "S"
26 | }
27 | ],
28 | "ProvisionedThroughput": {
29 | "ReadCapacityUnits": 5,
30 | "WriteCapacityUnits": 5
31 | },
32 | "Tags": [
33 | {
34 | "Key": "Owner",
35 | "Value": "AccountB"
36 | },
37 | {
38 | "Key": "Project",
39 | "Value": "ZeroTrustWorkshop"
40 | },
41 | {
42 | "Key": "Service",
43 | "Value": "ServiceB"
44 | }
45 | ]
46 | },
47 | "UpdateReplacePolicy": "Delete",
48 | "DeletionPolicy": "Delete",
49 | "Metadata": {
50 | "aws:cdk:path": "ServiceBStack/OrdersTable/Resource"
51 | }
52 | },
53 | "DDBInitLambdaServiceRole3DD2CE49": {
54 | "Type": "AWS::IAM::Role",
55 | "Properties": {
56 | "AssumeRolePolicyDocument": {
57 | "Statement": [
58 | {
59 | "Action": "sts:AssumeRole",
60 | "Effect": "Allow",
61 | "Principal": {
62 | "Service": "lambda.amazonaws.com"
63 | }
64 | }
65 | ],
66 | "Version": "2012-10-17"
67 | },
68 | "ManagedPolicyArns": [
69 | {
70 | "Fn::Join": [
71 | "",
72 | [
73 | "arn:",
74 | {
75 | "Ref": "AWS::Partition"
76 | },
77 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
78 | ]
79 | ]
80 | }
81 | ],
82 | "Tags": [
83 | {
84 | "Key": "Owner",
85 | "Value": "AccountB"
86 | },
87 | {
88 | "Key": "Project",
89 | "Value": "ZeroTrustWorkshop"
90 | },
91 | {
92 | "Key": "Service",
93 | "Value": "ServiceB"
94 | }
95 | ]
96 | },
97 | "Metadata": {
98 | "aws:cdk:path": "ServiceBStack/DDBInitLambda/ServiceRole/Resource"
99 | }
100 | },
101 | "DDBInitLambdaServiceRoleDefaultPolicy8008F5BF": {
102 | "Type": "AWS::IAM::Policy",
103 | "Properties": {
104 | "PolicyDocument": {
105 | "Statement": [
106 | {
107 | "Action": [
108 | "dynamodb:BatchWriteItem",
109 | "dynamodb:PutItem",
110 | "dynamodb:UpdateItem",
111 | "dynamodb:DeleteItem"
112 | ],
113 | "Effect": "Allow",
114 | "Resource": [
115 | {
116 | "Fn::GetAtt": [
117 | "OrdersTable315BB997",
118 | "Arn"
119 | ]
120 | },
121 | {
122 | "Ref": "AWS::NoValue"
123 | }
124 | ]
125 | }
126 | ],
127 | "Version": "2012-10-17"
128 | },
129 | "PolicyName": "DDBInitLambdaServiceRoleDefaultPolicy8008F5BF",
130 | "Roles": [
131 | {
132 | "Ref": "DDBInitLambdaServiceRole3DD2CE49"
133 | }
134 | ]
135 | },
136 | "Metadata": {
137 | "aws:cdk:path": "ServiceBStack/DDBInitLambda/ServiceRole/DefaultPolicy/Resource"
138 | }
139 | },
140 | "DDBInitLambdaF97CE5FA": {
141 | "Type": "AWS::Lambda::Function",
142 | "Properties": {
143 | "Code": {
144 | "S3Bucket": {
145 | "Ref": "EEAssetsBucket"
146 | },
147 | "S3Key": {
148 | "Fn::Join": [
149 | "",
150 | [
151 | {
152 | "Ref": "EEAssetsKeyPrefix"
153 | },
154 | "lambda/ddbinit/lambda-code.zip"
155 | ]
156 | ]
157 | }
158 | },
159 | "Role": {
160 | "Fn::GetAtt": [
161 | "DDBInitLambdaServiceRole3DD2CE49",
162 | "Arn"
163 | ]
164 | },
165 | "Environment": {
166 | "Variables": {
167 | "TABLE_NAME": {
168 | "Ref": "OrdersTable315BB997"
169 | }
170 | }
171 | },
172 | "Handler": "lambda_function.lambda_handler",
173 | "Runtime": "python3.8",
174 | "Tags": [
175 | {
176 | "Key": "Owner",
177 | "Value": "AccountB"
178 | },
179 | {
180 | "Key": "Project",
181 | "Value": "ZeroTrustWorkshop"
182 | },
183 | {
184 | "Key": "Service",
185 | "Value": "ServiceB"
186 | }
187 | ],
188 | "Timeout": 60
189 | },
190 | "DependsOn": [
191 | "DDBInitLambdaServiceRoleDefaultPolicy8008F5BF",
192 | "DDBInitLambdaServiceRole3DD2CE49"
193 | ],
194 | "Metadata": {
195 | "aws:cdk:path": "ServiceBStack/DDBInitLambda/Resource"
196 | }
197 | },
198 | "DDBInit": {
199 | "Type": "AWS::CloudFormation::CustomResource",
200 | "Properties": {
201 | "ServiceToken": {
202 | "Fn::GetAtt": [
203 | "DDBInitLambdaF97CE5FA",
204 | "Arn"
205 | ]
206 | }
207 | },
208 | "UpdateReplacePolicy": "Delete",
209 | "DeletionPolicy": "Delete",
210 | "Metadata": {
211 | "aws:cdk:path": "ServiceBStack/DDBInit/Default"
212 | }
213 | },
214 | "BackendLambdaServiceRole98E4BC89": {
215 | "Type": "AWS::IAM::Role",
216 | "Properties": {
217 | "AssumeRolePolicyDocument": {
218 | "Statement": [
219 | {
220 | "Action": "sts:AssumeRole",
221 | "Effect": "Allow",
222 | "Principal": {
223 | "Service": "lambda.amazonaws.com"
224 | }
225 | }
226 | ],
227 | "Version": "2012-10-17"
228 | },
229 | "ManagedPolicyArns": [
230 | {
231 | "Fn::Join": [
232 | "",
233 | [
234 | "arn:",
235 | {
236 | "Ref": "AWS::Partition"
237 | },
238 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
239 | ]
240 | ]
241 | }
242 | ],
243 | "Tags": [
244 | {
245 | "Key": "Owner",
246 | "Value": "AccountB"
247 | },
248 | {
249 | "Key": "Project",
250 | "Value": "ZeroTrustWorkshop"
251 | },
252 | {
253 | "Key": "Service",
254 | "Value": "ServiceB"
255 | }
256 | ]
257 | },
258 | "Metadata": {
259 | "aws:cdk:path": "ServiceBStack/BackendLambda/ServiceRole/Resource"
260 | }
261 | },
262 | "BackendLambdaServiceRoleDefaultPolicy922C20D8": {
263 | "Type": "AWS::IAM::Policy",
264 | "Properties": {
265 | "PolicyDocument": {
266 | "Statement": [
267 | {
268 | "Action": [
269 | "dynamodb:BatchGetItem",
270 | "dynamodb:GetRecords",
271 | "dynamodb:GetShardIterator",
272 | "dynamodb:Query",
273 | "dynamodb:GetItem",
274 | "dynamodb:Scan",
275 | "dynamodb:ConditionCheckItem"
276 | ],
277 | "Effect": "Allow",
278 | "Resource": [
279 | {
280 | "Fn::GetAtt": [
281 | "OrdersTable315BB997",
282 | "Arn"
283 | ]
284 | },
285 | {
286 | "Ref": "AWS::NoValue"
287 | }
288 | ]
289 | }
290 | ],
291 | "Version": "2012-10-17"
292 | },
293 | "PolicyName": "BackendLambdaServiceRoleDefaultPolicy922C20D8",
294 | "Roles": [
295 | {
296 | "Ref": "BackendLambdaServiceRole98E4BC89"
297 | }
298 | ]
299 | },
300 | "Metadata": {
301 | "aws:cdk:path": "ServiceBStack/BackendLambda/ServiceRole/DefaultPolicy/Resource"
302 | }
303 | },
304 | "BackendLambdaD93C7B96": {
305 | "Type": "AWS::Lambda::Function",
306 | "Properties": {
307 | "Code": {
308 | "S3Bucket": {
309 | "Ref": "EEAssetsBucket"
310 | },
311 | "S3Key": {
312 | "Fn::Join": [
313 | "",
314 | [
315 | {
316 | "Ref": "EEAssetsKeyPrefix"
317 | },
318 | "lambda/backend/lambda-code.zip"
319 | ]
320 | ]
321 | }
322 | },
323 | "Role": {
324 | "Fn::GetAtt": [
325 | "BackendLambdaServiceRole98E4BC89",
326 | "Arn"
327 | ]
328 | },
329 | "Environment": {
330 | "Variables": {
331 | "TABLE_NAME": {
332 | "Ref": "OrdersTable315BB997"
333 | }
334 | }
335 | },
336 | "Handler": "lambda_function.lambda_handler",
337 | "Runtime": "python3.8",
338 | "Tags": [
339 | {
340 | "Key": "Owner",
341 | "Value": "AccountB"
342 | },
343 | {
344 | "Key": "Project",
345 | "Value": "ZeroTrustWorkshop"
346 | },
347 | {
348 | "Key": "Service",
349 | "Value": "ServiceB"
350 | }
351 | ],
352 | "Timeout": 60
353 | },
354 | "DependsOn": [
355 | "BackendLambdaServiceRoleDefaultPolicy922C20D8",
356 | "BackendLambdaServiceRole98E4BC89"
357 | ],
358 | "Metadata": {
359 | "aws:cdk:path": "ServiceBStack/BackendLambda/Resource"
360 | }
361 | },
362 | "LogGroup0B487484F": {
363 | "Type": "AWS::Logs::LogGroup",
364 | "Properties": {
365 | "LogGroupName": {
366 | "Fn::Join": [
367 | "",
368 | [
369 | "/aws/lambda/",
370 | {
371 | "Ref": "BackendLambdaD93C7B96"
372 | }
373 | ]
374 | ]
375 | },
376 | "RetentionInDays": 731
377 | },
378 | "UpdateReplacePolicy": "Delete",
379 | "DeletionPolicy": "Delete",
380 | "Metadata": {
381 | "aws:cdk:path": "ServiceBStack/LogGroup0/Resource"
382 | }
383 | },
384 | "LogGroup106AAD846": {
385 | "Type": "AWS::Logs::LogGroup",
386 | "Properties": {
387 | "LogGroupName": {
388 | "Fn::Join": [
389 | "",
390 | [
391 | "/aws/lambda/",
392 | {
393 | "Ref": "DDBInitLambdaF97CE5FA"
394 | }
395 | ]
396 | ]
397 | },
398 | "RetentionInDays": 731
399 | },
400 | "UpdateReplacePolicy": "Delete",
401 | "DeletionPolicy": "Delete",
402 | "Metadata": {
403 | "aws:cdk:path": "ServiceBStack/LogGroup1/Resource"
404 | }
405 | },
406 | "APISecretDDFC1759": {
407 | "Type": "AWS::SecretsManager::Secret",
408 | "Properties": {
409 | "GenerateSecretString": {
410 | "ExcludePunctuation": true
411 | },
412 | "Tags": [
413 | {
414 | "Key": "Owner",
415 | "Value": "AccountB"
416 | },
417 | {
418 | "Key": "Project",
419 | "Value": "ZeroTrustWorkshop"
420 | },
421 | {
422 | "Key": "Service",
423 | "Value": "ServiceB"
424 | }
425 | ]
426 | },
427 | "Metadata": {
428 | "aws:cdk:path": "ServiceBStack/APISecret/Resource"
429 | }
430 | },
431 | "APIAccessLogs06429C94": {
432 | "Type": "AWS::Logs::LogGroup",
433 | "Properties": {
434 | "RetentionInDays": 731
435 | },
436 | "UpdateReplacePolicy": "Delete",
437 | "DeletionPolicy": "Delete",
438 | "Metadata": {
439 | "aws:cdk:path": "ServiceBStack/APIAccessLogs/Resource"
440 | }
441 | },
442 | "ServiceBAPIE44D7D31": {
443 | "Type": "AWS::ApiGateway::RestApi",
444 | "Properties": {
445 | "EndpointConfiguration": {
446 | "Types": [
447 | "PRIVATE"
448 | ]
449 | },
450 | "Name": "ServiceBAPI",
451 | "Policy": {
452 | "Statement": [
453 | {
454 | "Action": "execute-api:Invoke",
455 | "Condition": {
456 | "IpAddress": {
457 | "aws:VpcSourceIp": "10.199.0.0/24"
458 | }
459 | },
460 | "Effect": "Allow",
461 | "Principal": {
462 | "AWS": "*"
463 | },
464 | "Resource": "arn:aws:execute-api:*:*:*"
465 | }
466 | ],
467 | "Version": "2012-10-17"
468 | },
469 | "Tags": [
470 | {
471 | "Key": "Owner",
472 | "Value": "AccountB"
473 | },
474 | {
475 | "Key": "Project",
476 | "Value": "ZeroTrustWorkshop"
477 | },
478 | {
479 | "Key": "Service",
480 | "Value": "ServiceB"
481 | }
482 | ]
483 | },
484 | "Metadata": {
485 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/Resource"
486 | }
487 | },
488 | "ServiceBAPICloudWatchRoleC361BFB0": {
489 | "Type": "AWS::IAM::Role",
490 | "Properties": {
491 | "AssumeRolePolicyDocument": {
492 | "Statement": [
493 | {
494 | "Action": "sts:AssumeRole",
495 | "Effect": "Allow",
496 | "Principal": {
497 | "Service": "apigateway.amazonaws.com"
498 | }
499 | }
500 | ],
501 | "Version": "2012-10-17"
502 | },
503 | "ManagedPolicyArns": [
504 | {
505 | "Fn::Join": [
506 | "",
507 | [
508 | "arn:",
509 | {
510 | "Ref": "AWS::Partition"
511 | },
512 | ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
513 | ]
514 | ]
515 | }
516 | ],
517 | "Tags": [
518 | {
519 | "Key": "Owner",
520 | "Value": "AccountB"
521 | },
522 | {
523 | "Key": "Project",
524 | "Value": "ZeroTrustWorkshop"
525 | },
526 | {
527 | "Key": "Service",
528 | "Value": "ServiceB"
529 | }
530 | ]
531 | },
532 | "Metadata": {
533 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/CloudWatchRole/Resource"
534 | }
535 | },
536 | "ServiceBAPIAccount8B73474F": {
537 | "Type": "AWS::ApiGateway::Account",
538 | "Properties": {
539 | "CloudWatchRoleArn": {
540 | "Fn::GetAtt": [
541 | "ServiceBAPICloudWatchRoleC361BFB0",
542 | "Arn"
543 | ]
544 | }
545 | },
546 | "DependsOn": [
547 | "ServiceBAPIE44D7D31"
548 | ],
549 | "Metadata": {
550 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/Account"
551 | }
552 | },
553 | "ServiceBAPIDeployment29735B73e92fbac3e400e1abf84f44d12033039d": {
554 | "Type": "AWS::ApiGateway::Deployment",
555 | "Properties": {
556 | "RestApiId": {
557 | "Ref": "ServiceBAPIE44D7D31"
558 | },
559 | "Description": "Automatically created by the RestApi construct"
560 | },
561 | "DependsOn": [
562 | "ServiceBAPIAPICustomResponse6222BB1A",
563 | "ServiceBAPIordersGETF0920508",
564 | "ServiceBAPIordersCF9E93BB"
565 | ],
566 | "Metadata": {
567 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/Deployment/Resource"
568 | }
569 | },
570 | "ServiceBAPIDeploymentStageapi7F2D4453": {
571 | "Type": "AWS::ApiGateway::Stage",
572 | "Properties": {
573 | "RestApiId": {
574 | "Ref": "ServiceBAPIE44D7D31"
575 | },
576 | "AccessLogSetting": {
577 | "DestinationArn": {
578 | "Fn::GetAtt": [
579 | "APIAccessLogs06429C94",
580 | "Arn"
581 | ]
582 | },
583 | "Format": "$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] \"$context.httpMethod $context.resourcePath $context.protocol\" $context.status $context.responseLength $context.requestId"
584 | },
585 | "DeploymentId": {
586 | "Ref": "ServiceBAPIDeployment29735B73e92fbac3e400e1abf84f44d12033039d"
587 | },
588 | "StageName": "api",
589 | "Tags": [
590 | {
591 | "Key": "Owner",
592 | "Value": "AccountB"
593 | },
594 | {
595 | "Key": "Project",
596 | "Value": "ZeroTrustWorkshop"
597 | },
598 | {
599 | "Key": "Service",
600 | "Value": "ServiceB"
601 | }
602 | ]
603 | },
604 | "Metadata": {
605 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/DeploymentStage.api/Resource"
606 | }
607 | },
608 | "ServiceBAPIordersCF9E93BB": {
609 | "Type": "AWS::ApiGateway::Resource",
610 | "Properties": {
611 | "ParentId": {
612 | "Fn::GetAtt": [
613 | "ServiceBAPIE44D7D31",
614 | "RootResourceId"
615 | ]
616 | },
617 | "PathPart": "orders",
618 | "RestApiId": {
619 | "Ref": "ServiceBAPIE44D7D31"
620 | }
621 | },
622 | "Metadata": {
623 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/Default/orders/Resource"
624 | }
625 | },
626 | "ServiceBAPIordersGETApiPermissionServiceBStackServiceBAPIE4D14DD6GETordersA57D3873": {
627 | "Type": "AWS::Lambda::Permission",
628 | "Properties": {
629 | "Action": "lambda:InvokeFunction",
630 | "FunctionName": {
631 | "Fn::GetAtt": [
632 | "BackendLambdaD93C7B96",
633 | "Arn"
634 | ]
635 | },
636 | "Principal": "apigateway.amazonaws.com",
637 | "SourceArn": {
638 | "Fn::Join": [
639 | "",
640 | [
641 | "arn:",
642 | {
643 | "Ref": "AWS::Partition"
644 | },
645 | ":execute-api:",
646 | {
647 | "Ref": "AWS::Region"
648 | },
649 | ":",
650 | {
651 | "Ref": "AWS::AccountId"
652 | },
653 | ":",
654 | {
655 | "Ref": "ServiceBAPIE44D7D31"
656 | },
657 | "/",
658 | {
659 | "Ref": "ServiceBAPIDeploymentStageapi7F2D4453"
660 | },
661 | "/GET/orders"
662 | ]
663 | ]
664 | }
665 | },
666 | "Metadata": {
667 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/Default/orders/GET/ApiPermission.ServiceBStackServiceBAPIE4D14DD6.GET..orders"
668 | }
669 | },
670 | "ServiceBAPIordersGETApiPermissionTestServiceBStackServiceBAPIE4D14DD6GETorders8C1D7B4C": {
671 | "Type": "AWS::Lambda::Permission",
672 | "Properties": {
673 | "Action": "lambda:InvokeFunction",
674 | "FunctionName": {
675 | "Fn::GetAtt": [
676 | "BackendLambdaD93C7B96",
677 | "Arn"
678 | ]
679 | },
680 | "Principal": "apigateway.amazonaws.com",
681 | "SourceArn": {
682 | "Fn::Join": [
683 | "",
684 | [
685 | "arn:",
686 | {
687 | "Ref": "AWS::Partition"
688 | },
689 | ":execute-api:",
690 | {
691 | "Ref": "AWS::Region"
692 | },
693 | ":",
694 | {
695 | "Ref": "AWS::AccountId"
696 | },
697 | ":",
698 | {
699 | "Ref": "ServiceBAPIE44D7D31"
700 | },
701 | "/test-invoke-stage/GET/orders"
702 | ]
703 | ]
704 | }
705 | },
706 | "Metadata": {
707 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/Default/orders/GET/ApiPermission.Test.ServiceBStackServiceBAPIE4D14DD6.GET..orders"
708 | }
709 | },
710 | "ServiceBAPIordersGETF0920508": {
711 | "Type": "AWS::ApiGateway::Method",
712 | "Properties": {
713 | "HttpMethod": "GET",
714 | "ResourceId": {
715 | "Ref": "ServiceBAPIordersCF9E93BB"
716 | },
717 | "RestApiId": {
718 | "Ref": "ServiceBAPIE44D7D31"
719 | },
720 | "ApiKeyRequired": true,
721 | "AuthorizationType": "NONE",
722 | "Integration": {
723 | "IntegrationHttpMethod": "POST",
724 | "Type": "AWS_PROXY",
725 | "Uri": {
726 | "Fn::Join": [
727 | "",
728 | [
729 | "arn:",
730 | {
731 | "Ref": "AWS::Partition"
732 | },
733 | ":apigateway:",
734 | {
735 | "Ref": "AWS::Region"
736 | },
737 | ":lambda:path/2015-03-31/functions/",
738 | {
739 | "Fn::GetAtt": [
740 | "BackendLambdaD93C7B96",
741 | "Arn"
742 | ]
743 | },
744 | "/invocations"
745 | ]
746 | ]
747 | }
748 | }
749 | },
750 | "Metadata": {
751 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/Default/orders/GET/Resource"
752 | }
753 | },
754 | "ServiceBAPIApiKey7D3CB3FC": {
755 | "Type": "AWS::ApiGateway::ApiKey",
756 | "Properties": {
757 | "Enabled": true,
758 | "StageKeys": [
759 | {
760 | "RestApiId": {
761 | "Ref": "ServiceBAPIE44D7D31"
762 | },
763 | "StageName": {
764 | "Ref": "ServiceBAPIDeploymentStageapi7F2D4453"
765 | }
766 | }
767 | ],
768 | "Tags": [
769 | {
770 | "Key": "Owner",
771 | "Value": "AccountB"
772 | },
773 | {
774 | "Key": "Project",
775 | "Value": "ZeroTrustWorkshop"
776 | },
777 | {
778 | "Key": "Service",
779 | "Value": "ServiceB"
780 | }
781 | ],
782 | "Value": {
783 | "Fn::Join": [
784 | "",
785 | [
786 | "{{resolve:secretsmanager:",
787 | {
788 | "Ref": "APISecretDDFC1759"
789 | },
790 | ":SecretString:::}}"
791 | ]
792 | ]
793 | }
794 | },
795 | "Metadata": {
796 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/ApiKey/Resource"
797 | }
798 | },
799 | "ServiceBAPIUsagePlan3100CE00": {
800 | "Type": "AWS::ApiGateway::UsagePlan",
801 | "Properties": {
802 | "ApiStages": [
803 | {
804 | "ApiId": {
805 | "Ref": "ServiceBAPIE44D7D31"
806 | },
807 | "Stage": {
808 | "Ref": "ServiceBAPIDeploymentStageapi7F2D4453"
809 | },
810 | "Throttle": {}
811 | }
812 | ],
813 | "Tags": [
814 | {
815 | "Key": "Owner",
816 | "Value": "AccountB"
817 | },
818 | {
819 | "Key": "Project",
820 | "Value": "ZeroTrustWorkshop"
821 | },
822 | {
823 | "Key": "Service",
824 | "Value": "ServiceB"
825 | }
826 | ]
827 | },
828 | "Metadata": {
829 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/UsagePlan/Resource"
830 | }
831 | },
832 | "ServiceBAPIUsagePlanUsagePlanKeyResourceServiceBStackServiceBAPIApiKey135BE178BB785C7D": {
833 | "Type": "AWS::ApiGateway::UsagePlanKey",
834 | "Properties": {
835 | "KeyId": {
836 | "Ref": "ServiceBAPIApiKey7D3CB3FC"
837 | },
838 | "KeyType": "API_KEY",
839 | "UsagePlanId": {
840 | "Ref": "ServiceBAPIUsagePlan3100CE00"
841 | }
842 | },
843 | "Metadata": {
844 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/UsagePlan/UsagePlanKeyResource:ServiceBStackServiceBAPIApiKey135BE178"
845 | }
846 | },
847 | "ServiceBAPIAPICustomResponse6222BB1A": {
848 | "Type": "AWS::ApiGateway::GatewayResponse",
849 | "Properties": {
850 | "ResponseType": "DEFAULT_4XX",
851 | "RestApiId": {
852 | "Ref": "ServiceBAPIE44D7D31"
853 | },
854 | "ResponseTemplates": {
855 | "application/json": "{ 'message': $context.error.messageString, 'workshopmsg': 'hit-apigw'}"
856 | }
857 | },
858 | "Metadata": {
859 | "aws:cdk:path": "ServiceBStack/ServiceBAPI/APICustomResponse/Resource"
860 | }
861 | },
862 | "UnknownAPID2B322C2": {
863 | "Type": "AWS::ApiGateway::RestApi",
864 | "Properties": {
865 | "EndpointConfiguration": {
866 | "Types": [
867 | "PRIVATE"
868 | ]
869 | },
870 | "Name": "UnknownAPI",
871 | "Policy": {
872 | "Statement": [
873 | {
874 | "Action": "execute-api:Invoke",
875 | "Condition": {
876 | "IpAddress": {
877 | "aws:VpcSourceIp": "10.199.0.0/24"
878 | }
879 | },
880 | "Effect": "Allow",
881 | "Principal": {
882 | "AWS": "*"
883 | },
884 | "Resource": "arn:aws:execute-api:*:*:*"
885 | }
886 | ],
887 | "Version": "2012-10-17"
888 | },
889 | "Tags": [
890 | {
891 | "Key": "Owner",
892 | "Value": "AccountB"
893 | },
894 | {
895 | "Key": "Project",
896 | "Value": "ZeroTrustWorkshop"
897 | },
898 | {
899 | "Key": "Service",
900 | "Value": "ServiceB"
901 | }
902 | ]
903 | },
904 | "Metadata": {
905 | "aws:cdk:path": "ServiceBStack/UnknownAPI/Resource"
906 | }
907 | },
908 | "UnknownAPICloudWatchRoleF096B94B": {
909 | "Type": "AWS::IAM::Role",
910 | "Properties": {
911 | "AssumeRolePolicyDocument": {
912 | "Statement": [
913 | {
914 | "Action": "sts:AssumeRole",
915 | "Effect": "Allow",
916 | "Principal": {
917 | "Service": "apigateway.amazonaws.com"
918 | }
919 | }
920 | ],
921 | "Version": "2012-10-17"
922 | },
923 | "ManagedPolicyArns": [
924 | {
925 | "Fn::Join": [
926 | "",
927 | [
928 | "arn:",
929 | {
930 | "Ref": "AWS::Partition"
931 | },
932 | ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
933 | ]
934 | ]
935 | }
936 | ],
937 | "Tags": [
938 | {
939 | "Key": "Owner",
940 | "Value": "AccountB"
941 | },
942 | {
943 | "Key": "Project",
944 | "Value": "ZeroTrustWorkshop"
945 | },
946 | {
947 | "Key": "Service",
948 | "Value": "ServiceB"
949 | }
950 | ]
951 | },
952 | "Metadata": {
953 | "aws:cdk:path": "ServiceBStack/UnknownAPI/CloudWatchRole/Resource"
954 | }
955 | },
956 | "UnknownAPIAccount8D3ECFAB": {
957 | "Type": "AWS::ApiGateway::Account",
958 | "Properties": {
959 | "CloudWatchRoleArn": {
960 | "Fn::GetAtt": [
961 | "UnknownAPICloudWatchRoleF096B94B",
962 | "Arn"
963 | ]
964 | }
965 | },
966 | "DependsOn": [
967 | "UnknownAPID2B322C2"
968 | ],
969 | "Metadata": {
970 | "aws:cdk:path": "ServiceBStack/UnknownAPI/Account"
971 | }
972 | },
973 | "UnknownAPIDeploymentA635BB29ed6d4d2be8b239d028981d248820bffc": {
974 | "Type": "AWS::ApiGateway::Deployment",
975 | "Properties": {
976 | "RestApiId": {
977 | "Ref": "UnknownAPID2B322C2"
978 | },
979 | "Description": "Automatically created by the RestApi construct"
980 | },
981 | "DependsOn": [
982 | "UnknownAPIPUT4F8AF472"
983 | ],
984 | "Metadata": {
985 | "aws:cdk:path": "ServiceBStack/UnknownAPI/Deployment/Resource"
986 | }
987 | },
988 | "UnknownAPIDeploymentStageapi6102BF0D": {
989 | "Type": "AWS::ApiGateway::Stage",
990 | "Properties": {
991 | "RestApiId": {
992 | "Ref": "UnknownAPID2B322C2"
993 | },
994 | "DeploymentId": {
995 | "Ref": "UnknownAPIDeploymentA635BB29ed6d4d2be8b239d028981d248820bffc"
996 | },
997 | "StageName": "api",
998 | "Tags": [
999 | {
1000 | "Key": "Owner",
1001 | "Value": "AccountB"
1002 | },
1003 | {
1004 | "Key": "Project",
1005 | "Value": "ZeroTrustWorkshop"
1006 | },
1007 | {
1008 | "Key": "Service",
1009 | "Value": "ServiceB"
1010 | }
1011 | ]
1012 | },
1013 | "Metadata": {
1014 | "aws:cdk:path": "ServiceBStack/UnknownAPI/DeploymentStage.api/Resource"
1015 | }
1016 | },
1017 | "UnknownAPIPUT4F8AF472": {
1018 | "Type": "AWS::ApiGateway::Method",
1019 | "Properties": {
1020 | "HttpMethod": "PUT",
1021 | "ResourceId": {
1022 | "Fn::GetAtt": [
1023 | "UnknownAPID2B322C2",
1024 | "RootResourceId"
1025 | ]
1026 | },
1027 | "RestApiId": {
1028 | "Ref": "UnknownAPID2B322C2"
1029 | },
1030 | "AuthorizationType": "NONE",
1031 | "Integration": {
1032 | "IntegrationResponses": [
1033 | {
1034 | "ResponseTemplates": {
1035 | "application/json": "{'message':'SUCCESS Mock PUT'}"
1036 | },
1037 | "StatusCode": "200"
1038 | }
1039 | ],
1040 | "RequestTemplates": {
1041 | "application/json": "{'statusCode': 200}"
1042 | },
1043 | "Type": "MOCK"
1044 | },
1045 | "MethodResponses": [
1046 | {
1047 | "StatusCode": "200"
1048 | }
1049 | ]
1050 | },
1051 | "Metadata": {
1052 | "aws:cdk:path": "ServiceBStack/UnknownAPI/Default/PUT/Resource"
1053 | }
1054 | },
1055 | "APIIDParameterC20CF59D": {
1056 | "Type": "AWS::SSM::Parameter",
1057 | "Properties": {
1058 | "Type": "String",
1059 | "Value": {
1060 | "Ref": "ServiceBAPIE44D7D31"
1061 | },
1062 | "Name": "/workshop/params/service-b-api-id",
1063 | "Tags": {
1064 | "Owner": "AccountB",
1065 | "Project": "ZeroTrustWorkshop",
1066 | "Service": "ServiceB"
1067 | }
1068 | },
1069 | "Metadata": {
1070 | "aws:cdk:path": "ServiceBStack/APIIDParameter/Resource"
1071 | }
1072 | },
1073 | "APISecretNameParameter6B8748BE": {
1074 | "Type": "AWS::SSM::Parameter",
1075 | "Properties": {
1076 | "Type": "String",
1077 | "Value": {
1078 | "Ref": "APISecretDDFC1759"
1079 | },
1080 | "Name": "/workshop/params/service-b-api-secret-arn",
1081 | "Tags": {
1082 | "Owner": "AccountB",
1083 | "Project": "ZeroTrustWorkshop",
1084 | "Service": "ServiceB"
1085 | }
1086 | },
1087 | "Metadata": {
1088 | "aws:cdk:path": "ServiceBStack/APISecretNameParameter/Resource"
1089 | }
1090 | },
1091 | "UnknownAPIIDParameterA5054313": {
1092 | "Type": "AWS::SSM::Parameter",
1093 | "Properties": {
1094 | "Type": "String",
1095 | "Value": {
1096 | "Ref": "UnknownAPID2B322C2"
1097 | },
1098 | "Name": "/workshop/params/unknown-api-id",
1099 | "Tags": {
1100 | "Owner": "AccountB",
1101 | "Project": "ZeroTrustWorkshop",
1102 | "Service": "ServiceB"
1103 | }
1104 | },
1105 | "Metadata": {
1106 | "aws:cdk:path": "ServiceBStack/UnknownAPIIDParameter/Resource"
1107 | }
1108 | },
1109 | "APICallsDashboardAAA80A22": {
1110 | "Type": "AWS::CloudWatch::Dashboard",
1111 | "Properties": {
1112 | "DashboardBody": {
1113 | "Fn::Join": [
1114 | "",
1115 | [
1116 | "{\"start\":\"-PT2H\",\"widgets\":[{\"type\":\"metric\",\"width\":20,\"height\":6,\"x\":0,\"y\":0,\"properties\":{\"view\":\"timeSeries\",\"title\":\"Number of API Calls\",\"region\":\"",
1117 | {
1118 | "Ref": "AWS::Region"
1119 | },
1120 | "\",\"metrics\":[[\"AWS/ApiGateway\",\"Count\",\"ApiName\",\"ServiceBAPI\",{\"label\":\"Total API Calls\",\"stat\":\"SampleCount\"}],[\"AWS/ApiGateway\",\"4XXError\",\"ApiName\",\"ServiceBAPI\",{\"label\":\"Unauthorized Calls\",\"stat\":\"Sum\"}]],\"yAxis\":{\"left\":{\"min\":0}},\"period\":60}}]}"
1121 | ]
1122 | ]
1123 | }
1124 | },
1125 | "Metadata": {
1126 | "aws:cdk:path": "ServiceBStack/APICallsDashboard/Resource"
1127 | }
1128 | },
1129 | "CDKMetadata": {
1130 | "Type": "AWS::CDK::Metadata",
1131 | "Properties": {
1132 | "Analytics": "v2:deflate64:H4sIAAAAAAAAE21Sy07DMBD8lt6NS9RTb/QhegBEVOADNs42NY29kR+qIiv/ju2kEKGePGvvzs6Ot+BFseaPiye42gdRX5ZBkEEePhyIC9uddAkGFDo0bEfaOuOFY0e05I1AtvPWkfoLT3qO373rvEsoltbSSdIDS32CXfGtFxd0W7DI6l6Dorri4ROqNpdmMLAWVFUDD89ei1Senua4RKOktZlYguLhSGN9PktqpehzXkaRjxrLwys1B0O+Sy83PDCLwqCzCjQ0aKIDOU45I4rSO9mAwyv0kSMri9O6TSenwW9wIwR57dgeu5Z6hTqzzKJobpNljuDmWTbjrplv6M5Up6sJxU4vmEeb0JeNTGUL2Zb7QUo7jPojcRd/Mzf5fzUNEg2xKu2BkbqZLcFsIwYmWvL1FZw487AHe64ITJb5GwzDwMo+atbLFV/zYrX4tlI+mOiPVMiP4/kD31c7VoYCAAA="
1133 | },
1134 | "Metadata": {
1135 | "aws:cdk:path": "ServiceBStack/CDKMetadata/Default"
1136 | },
1137 | "Condition": "CDKMetadataAvailable"
1138 | }
1139 | },
1140 | "Outputs": {
1141 | "ServiceBAPIEndpoint2651A72E": {
1142 | "Value": {
1143 | "Fn::Join": [
1144 | "",
1145 | [
1146 | "https://",
1147 | {
1148 | "Ref": "ServiceBAPIE44D7D31"
1149 | },
1150 | ".execute-api.",
1151 | {
1152 | "Ref": "AWS::Region"
1153 | },
1154 | ".",
1155 | {
1156 | "Ref": "AWS::URLSuffix"
1157 | },
1158 | "/",
1159 | {
1160 | "Ref": "ServiceBAPIDeploymentStageapi7F2D4453"
1161 | },
1162 | "/"
1163 | ]
1164 | ]
1165 | }
1166 | },
1167 | "UnknownAPIEndpoint2B1A1A24": {
1168 | "Value": {
1169 | "Fn::Join": [
1170 | "",
1171 | [
1172 | "https://",
1173 | {
1174 | "Ref": "UnknownAPID2B322C2"
1175 | },
1176 | ".execute-api.",
1177 | {
1178 | "Ref": "AWS::Region"
1179 | },
1180 | ".",
1181 | {
1182 | "Ref": "AWS::URLSuffix"
1183 | },
1184 | "/",
1185 | {
1186 | "Ref": "UnknownAPIDeploymentStageapi6102BF0D"
1187 | },
1188 | "/"
1189 | ]
1190 | ]
1191 | }
1192 | },
1193 | "APIMethodARN": {
1194 | "Value": {
1195 | "Fn::Join": [
1196 | "",
1197 | [
1198 | "arn:",
1199 | {
1200 | "Ref": "AWS::Partition"
1201 | },
1202 | ":execute-api:",
1203 | {
1204 | "Ref": "AWS::Region"
1205 | },
1206 | ":",
1207 | {
1208 | "Ref": "AWS::AccountId"
1209 | },
1210 | ":",
1211 | {
1212 | "Ref": "ServiceBAPIE44D7D31"
1213 | },
1214 | "/",
1215 | {
1216 | "Ref": "ServiceBAPIDeploymentStageapi7F2D4453"
1217 | },
1218 | "/GET/orders"
1219 | ]
1220 | ]
1221 | }
1222 | }
1223 | },
1224 | "Conditions": {
1225 | "CDKMetadataAvailable": {
1226 | "Fn::Or": [
1227 | {
1228 | "Fn::Or": [
1229 | {
1230 | "Fn::Equals": [
1231 | {
1232 | "Ref": "AWS::Region"
1233 | },
1234 | "af-south-1"
1235 | ]
1236 | },
1237 | {
1238 | "Fn::Equals": [
1239 | {
1240 | "Ref": "AWS::Region"
1241 | },
1242 | "ap-east-1"
1243 | ]
1244 | },
1245 | {
1246 | "Fn::Equals": [
1247 | {
1248 | "Ref": "AWS::Region"
1249 | },
1250 | "ap-northeast-1"
1251 | ]
1252 | },
1253 | {
1254 | "Fn::Equals": [
1255 | {
1256 | "Ref": "AWS::Region"
1257 | },
1258 | "ap-northeast-2"
1259 | ]
1260 | },
1261 | {
1262 | "Fn::Equals": [
1263 | {
1264 | "Ref": "AWS::Region"
1265 | },
1266 | "ap-south-1"
1267 | ]
1268 | },
1269 | {
1270 | "Fn::Equals": [
1271 | {
1272 | "Ref": "AWS::Region"
1273 | },
1274 | "ap-southeast-1"
1275 | ]
1276 | },
1277 | {
1278 | "Fn::Equals": [
1279 | {
1280 | "Ref": "AWS::Region"
1281 | },
1282 | "ap-southeast-2"
1283 | ]
1284 | },
1285 | {
1286 | "Fn::Equals": [
1287 | {
1288 | "Ref": "AWS::Region"
1289 | },
1290 | "ca-central-1"
1291 | ]
1292 | },
1293 | {
1294 | "Fn::Equals": [
1295 | {
1296 | "Ref": "AWS::Region"
1297 | },
1298 | "cn-north-1"
1299 | ]
1300 | },
1301 | {
1302 | "Fn::Equals": [
1303 | {
1304 | "Ref": "AWS::Region"
1305 | },
1306 | "cn-northwest-1"
1307 | ]
1308 | }
1309 | ]
1310 | },
1311 | {
1312 | "Fn::Or": [
1313 | {
1314 | "Fn::Equals": [
1315 | {
1316 | "Ref": "AWS::Region"
1317 | },
1318 | "eu-central-1"
1319 | ]
1320 | },
1321 | {
1322 | "Fn::Equals": [
1323 | {
1324 | "Ref": "AWS::Region"
1325 | },
1326 | "eu-north-1"
1327 | ]
1328 | },
1329 | {
1330 | "Fn::Equals": [
1331 | {
1332 | "Ref": "AWS::Region"
1333 | },
1334 | "eu-south-1"
1335 | ]
1336 | },
1337 | {
1338 | "Fn::Equals": [
1339 | {
1340 | "Ref": "AWS::Region"
1341 | },
1342 | "eu-west-1"
1343 | ]
1344 | },
1345 | {
1346 | "Fn::Equals": [
1347 | {
1348 | "Ref": "AWS::Region"
1349 | },
1350 | "eu-west-2"
1351 | ]
1352 | },
1353 | {
1354 | "Fn::Equals": [
1355 | {
1356 | "Ref": "AWS::Region"
1357 | },
1358 | "eu-west-3"
1359 | ]
1360 | },
1361 | {
1362 | "Fn::Equals": [
1363 | {
1364 | "Ref": "AWS::Region"
1365 | },
1366 | "me-south-1"
1367 | ]
1368 | },
1369 | {
1370 | "Fn::Equals": [
1371 | {
1372 | "Ref": "AWS::Region"
1373 | },
1374 | "sa-east-1"
1375 | ]
1376 | },
1377 | {
1378 | "Fn::Equals": [
1379 | {
1380 | "Ref": "AWS::Region"
1381 | },
1382 | "us-east-1"
1383 | ]
1384 | },
1385 | {
1386 | "Fn::Equals": [
1387 | {
1388 | "Ref": "AWS::Region"
1389 | },
1390 | "us-east-2"
1391 | ]
1392 | }
1393 | ]
1394 | },
1395 | {
1396 | "Fn::Or": [
1397 | {
1398 | "Fn::Equals": [
1399 | {
1400 | "Ref": "AWS::Region"
1401 | },
1402 | "us-west-1"
1403 | ]
1404 | },
1405 | {
1406 | "Fn::Equals": [
1407 | {
1408 | "Ref": "AWS::Region"
1409 | },
1410 | "us-west-2"
1411 | ]
1412 | }
1413 | ]
1414 | }
1415 | ]
1416 | }
1417 | }
1418 | }
--------------------------------------------------------------------------------
/cfn_templates/workshop_parent_stack.yaml:
--------------------------------------------------------------------------------
1 | Description: 'AWS CloudFormation Template for Zero Trust Service-to-Service Workshop.
2 | This is a parent template for two nested stacks, ServiceA and ServiceB,
3 | each deploys multiple resources for the purpose of the workshop.
4 | This parent stack is to simplify and enforce the order of deployment for the two nested stacks.'
5 |
6 | Metadata:
7 | License: 'MIT No Attribution
8 |
9 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy of
12 | this software and associated documentation files (the "Software"), to deal in
13 | the Software without restriction, including without limitation the rights to
14 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15 | the Software, and to permit persons to whom the Software is furnished to do so.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.'
23 |
24 | Author: 'angabini@amazon.com'
25 |
26 | Parameters:
27 | EEAssetsBucket:
28 | Type: String
29 | Description: Region-specific assets S3 bucket name. Leave as default.
30 | EEAssetsKeyPrefix:
31 | Type: String
32 | Description: S3 key prefix where assets are stored. Leave as default.
33 |
34 | Resources:
35 | ServiceBStack:
36 | Type: AWS::CloudFormation::Stack
37 | Properties:
38 | TemplateURL: !Sub https://${EEAssetsBucket}.s3.amazonaws.com/${EEAssetsKeyPrefix}stacks/ServiceBStack.template.json
39 | Parameters:
40 | EEAssetsBucket: !Sub ${EEAssetsBucket}
41 | EEAssetsKeyPrefix: !Sub ${EEAssetsKeyPrefix}
42 | UpdateReplacePolicy: Delete
43 | DeletionPolicy: Delete
44 |
45 | ServiceAStack:
46 | Type: AWS::CloudFormation::Stack
47 | Properties:
48 | TemplateURL: !Sub https://${EEAssetsBucket}.s3.amazonaws.com/${EEAssetsKeyPrefix}stacks/ServiceAStack.template.json
49 | Parameters:
50 | EEAssetsBucket: !Sub ${EEAssetsBucket}
51 | EEAssetsKeyPrefix: !Sub ${EEAssetsKeyPrefix}
52 | UpdateReplacePolicy: Delete
53 | DeletionPolicy: Delete
54 | DependsOn: ServiceBStack
--------------------------------------------------------------------------------
/config.yml:
--------------------------------------------------------------------------------
1 | description: >
2 | Static variables used in the CDK stacks are defined here.
3 | This is only to reduce the rework when updating names, etc., in the CDK code for the workshop.
4 |
5 | lambda_timeout: 60
6 | instance_type: "t3.micro"
7 |
8 | main_vpc_cidr: "10.199.0.0/16"
9 | allowed_cidr: &allowed_cidr "10.199.0.0/24"
10 | other_vpc_cidr: *allowed_cidr
11 |
12 | api_resource: "orders"
13 |
14 | api_resource_policy:
15 | {
16 | "Version": "2012-10-17",
17 | "Statement": [
18 | {
19 | "Effect": "Allow",
20 | "Principal": "*",
21 | "Action": "execute-api:Invoke",
22 | "Resource": "arn:aws:execute-api:*:*:*",
23 | "Condition": {
24 | "IpAddress": {
25 | "aws:VpcSourceIp": *allowed_cidr
26 | }
27 | }
28 | }
29 | ]
30 | }
31 |
32 | # Systems Manager parameters
33 | params_path: "/workshop/params/"
34 |
35 | # dev mode flag: set this to true only for development time; to update and/or deploy the stacks
36 | # via `cdk deploy` rather than CloudFormation. Setting this to true, CDK will use `.from_asset`
37 | # for lambdas source code, located locally under "./src/lambda/", which will need a `cdk bootstrap` first.
38 | # Set to false for final synth so can have portable deployable CloudFormation templates; in this case CDK
39 | # will use `.from_bucket` for lambdas - getting the code from an existing bucket (AWS Event Engine bucket by default).
40 | dev_mode: false
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | -e .
2 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 |
4 | with open("README.md") as fp:
5 | long_description = fp.read()
6 |
7 |
8 | setuptools.setup(
9 | name="zerotrust_service2service_workshop",
10 | version="1.0.1",
11 |
12 | description="CDK code for AWS ZeroTrust Service2Service Workshop.",
13 | long_description=long_description,
14 | long_description_content_type="text/markdown",
15 |
16 | author="Faraz Angabini (angabini)",
17 |
18 | package_dir={"": "zerotrust_service2service_workshop"},
19 | packages=setuptools.find_packages(where="zerotrust_service2service_workshop"),
20 |
21 | install_requires=[
22 | "pyyaml",
23 | "aws-cdk.core",
24 | "aws-cdk.aws-ec2",
25 | "aws-cdk.aws-s3",
26 | "aws-cdk.aws-lambda",
27 | "aws-cdk.aws-logs",
28 | "aws-cdk.aws-iam",
29 | "aws-cdk.aws-apigateway",
30 | "aws_cdk.aws_secretsmanager",
31 | "aws_cdk.aws_ssm",
32 | "aws_cdk.aws_dynamodb",
33 | "aws_cdk.custom_resources",
34 | "aws_cdk.aws_cloudwatch",
35 | "aws_cdk.aws_events",
36 | "aws_cdk.aws_events_targets",
37 | "aws_cdk.aws_autoscaling",
38 | "aws_cdk.aws_guardduty",
39 |
40 | ],
41 |
42 | python_requires=">=3.6",
43 |
44 | classifiers=[
45 | "Development Status :: 4 - Beta",
46 |
47 | "Intended Audience :: Developers",
48 |
49 | "Programming Language :: JavaScript",
50 | "Programming Language :: Python :: 3 :: Only",
51 | "Programming Language :: Python :: 3.6",
52 | "Programming Language :: Python :: 3.7",
53 | "Programming Language :: Python :: 3.8",
54 |
55 | "Topic :: Software Development :: Code Generators",
56 | "Topic :: Utilities",
57 |
58 | "Typing :: Typed",
59 | ],
60 | )
61 |
--------------------------------------------------------------------------------
/source.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem The sole purpose of this script is to make the command
4 | rem
5 | rem source .venv/bin/activate
6 | rem
7 | rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows.
8 | rem On Windows, this command just runs this batch file (the argument is ignored).
9 | rem
10 | rem Now we don't need to document a Windows command for activating a virtualenv.
11 |
12 | echo Executing .venv\Scripts\activate.bat for you
13 | .venv\Scripts\activate.bat
14 |
--------------------------------------------------------------------------------
/src/ec2/curl-pkg/scanner.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import json
5 | import time
6 | import os
7 | import boto3
8 | from dotenv import load_dotenv
9 |
10 | import service_a_caller
11 | import service_a_unknownapi
12 |
13 | load_dotenv()
14 |
15 | region = os.environ["api_region"]
16 |
17 | boto3_session = boto3.session.Session(region_name=region)
18 | ssm_client = boto3_session.client('ssm')
19 |
20 | # color codes to format the output
21 | GREEN = '\033[92m'
22 | WARNING = '\033[93m'
23 | OTHER = '\033[95m'
24 | FAIL = '\033[91m'
25 | ENDC = '\033[0m'
26 | BOLD = '\033[1m'
27 | CROSS = '\u2718'
28 | CHECK = '\u2714'
29 |
30 | def get_ssm_cmd(instance_id):
31 | cmd1 = "python3 /tmp/workshop/service_a_caller_sigv4.py"
32 | response = ssm_client.send_command(InstanceIds=[instance_id],
33 | DocumentName='AWS-RunShellScript',
34 | Parameters={"commands": [cmd1]}
35 | )
36 | command_id = response.get('Command', {}).get("CommandId", None)
37 | while True:
38 | response = ssm_client.list_command_invocations(CommandId=command_id, Details=True)
39 | if len(response['CommandInvocations']) == 0:
40 | time.sleep(0.5)
41 | continue
42 | invocation = response['CommandInvocations'][0]
43 | if invocation['Status'] not in ('Pending', 'InProgress', 'Cancelling'):
44 | break
45 | time.sleep(0.5)
46 | command_plugin = invocation['CommandPlugins'][-1]
47 | output = command_plugin['Output']
48 | return output
49 |
50 | def get_response(caller):
51 | lambda_client = boto3_session.client('lambda')
52 | response = lambda_client.invoke(
53 | FunctionName=caller
54 | )
55 | payload = json.loads(response['Payload'].read())
56 | return payload
57 |
58 | def parse_result(response):
59 | response = str(response)
60 | if "SUCCESS" in response:
61 | result = ("Allowed","-")
62 | elif "not authorized" in response:
63 | if "hit-apigw" in response:
64 | result = ("Blocked","API Gateway")
65 | else:
66 | result = ("Blocked","VPC endpoint")
67 | elif "Missing Authentication Token" in response:
68 | result = ("Blocked","API Gateway")
69 | elif "ConnectTimeout" in response:
70 | result = ("Blocked","Security Group")
71 | else:
72 | result = (response[:100],"unknown")
73 |
74 | return result
75 | def print_results(callers):
76 |
77 | # Print result table's header
78 | titles = ['check', 'result','enforced@']
79 | #longest_string = max(map(len, checks))
80 | longest_string = 24
81 | line = ' '.join(str(x).ljust(longest_string + 4) for x in titles)
82 | print(BOLD+line+ENDC)
83 | print('-' * len(line))
84 |
85 | for i, caller in enumerate(callers):
86 | if caller[0] == "service_a_caller":
87 | check_label ="Expected Caller"
88 | response = service_a_caller.main()
89 | elif caller[0] == "service_a_unknownapi":
90 | check_label ="Expected Caller-Unknown API"
91 | response = service_a_unknownapi.main()
92 | elif caller[0][:3] != "arn":
93 | check_label = f'Unwanted Caller #{i-1}'
94 | response = get_ssm_cmd(caller[0])
95 | else:
96 | check_label = f'Unwanted Caller #{i-1}'
97 | response = get_response(caller[0])
98 |
99 | result = parse_result(response)
100 |
101 | row = [
102 | check_label,
103 | result[0],
104 | result[1]
105 | ]
106 |
107 | line = ' '.join(str(x).ljust(longest_string + 4) for x in row)
108 | if row[1] == "Allowed" and caller[1] == "wanted":
109 | print(GREEN+line+CHECK+ENDC)
110 | elif row[1] == "Allowed" and caller[1] == "unwanted":
111 | print(FAIL+line+CROSS+ENDC)
112 | elif row[1] == "Blocked" and caller[1] == "wanted":
113 | print(FAIL+line+CROSS+ENDC)
114 | elif row[1] == "Blocked" and caller[1] == "unwanted":
115 | print(GREEN+line+CHECK+ENDC)
116 | elif row[1] == "Blocked?" and caller[1] == "wanted":
117 | print(WARNING+line+CROSS+ENDC)
118 | elif row[1] == "Blocked?" and caller[1] == "unwanted":
119 | print(GREEN+line+CHECK+ENDC)
120 |
121 | elif row[1] == "MEH":
122 | print(WARNING+line+ENDC)
123 | else:
124 | print(OTHER+line+ENDC)
125 | def main():
126 | unwanted_callers_str = ssm_client.get_parameter(Name=os.environ["unwanted_callers_parameter"])['Parameter']['Value']
127 | unwanted_callers = unwanted_callers_str.split(",")
128 |
129 | all_callers = [("service_a_caller","wanted")]
130 | all_callers.extend([("service_a_unknownapi","unwanted")])
131 | all_callers.extend([(c,"unwanted") for c in unwanted_callers ])
132 | print("\n> Started scanning ...\n")
133 | print_results(all_callers)
134 | print("\n> Finished scanning.\n")
135 |
136 | if __name__ == "__main__":
137 | main()
--------------------------------------------------------------------------------
/src/ec2/curl-pkg/service_a_caller.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | from dotenv import load_dotenv
5 | import os
6 | import boto3
7 | import requests
8 |
9 | load_dotenv()
10 | region = os.environ["api_region"]
11 |
12 | def call_api(api_id: str, api_key=None):
13 | host = api_id+'.execute-api.'+region+'.amazonaws.com'
14 | base_url = f'https://{host}/api'
15 | get_url = f'{base_url}/{os.environ["api_resource"]}'
16 |
17 | try:
18 | response = requests.get(get_url, headers={'x-api-key': api_key}, timeout=2)
19 | except requests.exceptions.RequestException as e:
20 | raise SystemExit(e)
21 | return response
22 |
23 | def main():
24 | boto3_session = boto3.session.Session(region_name=region)
25 |
26 | client = boto3_session.client('ssm')
27 | api_id = client.get_parameter(Name=os.environ["api_id_parameter"])['Parameter']['Value']
28 | api_secret_arn = client.get_parameter(Name=os.environ["api_secret_parameter"])['Parameter']['Value']
29 |
30 | client = boto3_session.client('secretsmanager')
31 | api_key = client.get_secret_value(SecretId=api_secret_arn)["SecretString"]
32 |
33 | response = call_api(api_id, api_key)
34 | return response.text
35 |
36 | if __name__ == "__main__":
37 | print(main())
--------------------------------------------------------------------------------
/src/ec2/curl-pkg/service_a_caller_sigv4.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | from dotenv import load_dotenv
5 | import os
6 | import boto3
7 | import requests
8 |
9 | load_dotenv()
10 | region = os.environ["api_region"]
11 |
12 | # Simplifies making Amazon SigV4 calls with the python requests library
13 | from aws_requests_auth.boto_utils import BotoAWSRequestsAuth
14 | def call_api(api_id: str, api_key=None):
15 | host = api_id+'.execute-api.'+region+'.amazonaws.com'
16 | base_url = f'https://{host}/api'
17 | get_url = f'{base_url}/{os.environ["api_resource"]}'
18 |
19 | # Get authentication token - SigV4
20 | auth = BotoAWSRequestsAuth(aws_host=host, aws_region=region, aws_service='execute-api')
21 | try:
22 | response = requests.get(get_url, headers={'x-api-key': api_key}, timeout=2, auth=auth)
23 | except requests.exceptions.RequestException as e:
24 | raise SystemExit(e)
25 | return response
26 |
27 | def main():
28 | boto3_session = boto3.session.Session(region_name=region)
29 |
30 | client = boto3_session.client('ssm')
31 | api_id = client.get_parameter(Name=os.environ["api_id_parameter"])['Parameter']['Value']
32 | api_secret_arn = client.get_parameter(Name=os.environ["api_secret_parameter"])['Parameter']['Value']
33 |
34 | client = boto3_session.client('secretsmanager')
35 | api_key = client.get_secret_value(SecretId=api_secret_arn)["SecretString"]
36 |
37 | response = call_api(api_id, api_key)
38 | return response.text
39 |
40 | if __name__ == "__main__":
41 | print(main())
--------------------------------------------------------------------------------
/src/ec2/curl-pkg/service_a_unknownapi.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | from dotenv import load_dotenv
5 | import os
6 | import boto3
7 | import requests
8 |
9 | load_dotenv()
10 | region = os.environ["api_region"]
11 |
12 | def call_api(api_id: str):
13 | host = api_id+'.execute-api.'+region+'.amazonaws.com'
14 | base_url = f'https://{host}/api'
15 |
16 | try:
17 | response = requests.put(base_url, timeout=2)
18 | except requests.exceptions.RequestException as e:
19 | raise SystemExit(e)
20 | return response
21 |
22 | def main():
23 | boto3_session = boto3.session.Session(region_name=region)
24 |
25 | client = boto3_session.client('ssm')
26 | api_id = client.get_parameter(Name=os.environ["unknown_api_id_parameter"])['Parameter']['Value']
27 |
28 | response = call_api(api_id)
29 | return response.text
30 |
31 | if __name__ == "__main__":
32 | print(main())
--------------------------------------------------------------------------------
/src/ec2/user_data.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | pip3 install aws-requests-auth boto3 python-dotenv
3 | cd /tmp
4 | # NOTE update with latest Event Engine S3 URL
5 | curl -O https://ee-assets-prod-us-east-1.s3.amazonaws.com/modules/1a656bee298f48fcad1bd4938e19b40a/v1/curl-pkg.zip
6 | unzip -qo curl-pkg.zip -d /tmp/workshop/
7 | chmod -R 777 /tmp/workshop/
8 |
9 | touch /tmp/workshop/.env
10 |
11 | echo python3 /tmp/workshop/scanner.py > /usr/bin/runscanner
12 | chmod 755 /usr/bin/runscanner
13 |
14 | echo "* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1" >> /tmp/workshop/cron.txt
15 | echo "* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1" >> /tmp/workshop/cron.txt
16 | echo "* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1" >> /tmp/workshop/cron.txt
17 | echo "* * * * * python3 /tmp/workshop/scanner.py > /dev/null 2>&1" >> /tmp/workshop/cron.txt
18 | crontab /tmp/workshop/cron.txt
19 |
--------------------------------------------------------------------------------
/src/lambda/backend/lambda_function.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import json
5 | import os
6 | import boto3
7 | from botocore.exceptions import ClientError
8 |
9 | TABLE_NAME = os.environ['TABLE_NAME']
10 |
11 | def backend_logic(orders):
12 |
13 | response = orders
14 | response.insert(0,"SUCCESS")
15 |
16 | return {
17 | 'statusCode': 200,
18 | 'headers': {
19 | 'Content-Type': 'text/plain'
20 | },
21 | 'body': json.dumps(response)
22 | }
23 |
24 | def lambda_handler(event, context):
25 |
26 | dynamodb = boto3.resource('dynamodb')
27 | table = dynamodb.Table(TABLE_NAME)
28 |
29 | try:
30 | response = table.scan()
31 | except ClientError as e:
32 | print(e.response['Error']['Message'])
33 | else:
34 | orders = response['Items']
35 |
36 | return backend_logic(orders)
--------------------------------------------------------------------------------
/src/lambda/caller_nosigv4/lambda_function.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import os
5 | import boto3
6 | import requests
7 |
8 | region = os.environ["api_region"]
9 |
10 | def call_api(api_id: str, api_key=None):
11 | host = api_id+'.execute-api.'+region+'.amazonaws.com'
12 | base_url = f'https://{host}/api'
13 | get_url = f'{base_url}/{os.environ["api_resource"]}'
14 |
15 | response = requests.get(get_url, headers={'x-api-key': api_key}, timeout=2)
16 | return response
17 |
18 | def lambda_handler(event, context):
19 | client = boto3.client('ssm')
20 | api_id = client.get_parameter(Name=os.environ["api_id_parameter"])['Parameter']['Value']
21 | api_secret_arn = client.get_parameter(Name=os.environ["api_secret_parameter"])['Parameter']['Value']
22 |
23 | client = boto3.client('secretsmanager')
24 | api_key = client.get_secret_value(SecretId=api_secret_arn)["SecretString"]
25 |
26 | response = call_api(api_id, api_key)
27 | return response.text
--------------------------------------------------------------------------------
/src/lambda/caller_sigv4/lambda_function.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import os
5 | import boto3
6 | import requests
7 |
8 | # Simplifies making Amazon SigV4 calls with the python requests library
9 | from aws_requests_auth.boto_utils import BotoAWSRequestsAuth
10 |
11 | region = os.environ["api_region"]
12 |
13 | def call_api(api_id: str, api_key=None):
14 | host = api_id+'.execute-api.'+region+'.amazonaws.com'
15 | base_url = f'https://{host}/api'
16 | get_url = f'{base_url}/{os.environ["api_resource"]}'
17 |
18 | # Get authentication token - SigV4
19 | auth = BotoAWSRequestsAuth(aws_host=host, aws_region=region, aws_service='execute-api')
20 | response = requests.get(get_url, headers={'x-api-key': api_key}, timeout=2, auth=auth)
21 | return response
22 |
23 | def lambda_handler(event, context):
24 | client = boto3.client('ssm')
25 | api_id = client.get_parameter(Name=os.environ["api_id_parameter"])['Parameter']['Value']
26 | api_secret_arn = client.get_parameter(Name=os.environ["api_secret_parameter"])['Parameter']['Value']
27 |
28 | client = boto3.client('secretsmanager')
29 | api_key = client.get_secret_value(SecretId=api_secret_arn)["SecretString"]
30 |
31 | response = call_api(api_id, api_key)
32 | return response.text
--------------------------------------------------------------------------------
/src/lambda/ddbinit/cfnresponse.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | from __future__ import print_function
5 | import urllib3
6 | import json
7 |
8 | SUCCESS = "SUCCESS"
9 | FAILED = "FAILED"
10 |
11 | http = urllib3.PoolManager()
12 |
13 | def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None):
14 | responseUrl = event['ResponseURL']
15 |
16 | print(responseUrl)
17 |
18 | responseBody = {
19 | 'Status' : responseStatus,
20 | 'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name),
21 | 'PhysicalResourceId' : physicalResourceId or context.log_stream_name,
22 | 'StackId' : event['StackId'],
23 | 'RequestId' : event['RequestId'],
24 | 'LogicalResourceId' : event['LogicalResourceId'],
25 | 'NoEcho' : noEcho,
26 | 'Data' : responseData
27 | }
28 |
29 | json_responseBody = json.dumps(responseBody)
30 |
31 | print("Response body:")
32 | print(json_responseBody)
33 |
34 | headers = {
35 | 'content-type' : '',
36 | 'content-length' : str(len(json_responseBody))
37 | }
38 |
39 | try:
40 | response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody)
41 | print("Status code:", response.status)
42 |
43 | except Exception as e:
44 |
45 | print("send(..) failed executing http.request(..):", e)
--------------------------------------------------------------------------------
/src/lambda/ddbinit/lambda_function.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import os
5 | import boto3
6 | import cfnresponse
7 |
8 | TABLE_NAME = os.environ['TABLE_NAME']
9 |
10 | def lambda_handler(event, context):
11 | if event['RequestType'] != "Create":
12 | responseData = {}
13 | cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
14 | return
15 |
16 | dynamodb = boto3.client('dynamodb')
17 |
18 | # putting mock data into DynamoDB
19 | response = dynamodb.batch_write_item(
20 | RequestItems={
21 | TABLE_NAME : [
22 | {
23 | "PutRequest": {
24 | "Item": {"order_id": {"S":"6472445C25D7"}, "pickup": {"S":"SFO"}, "dropoff": {"S":"SJC"}}
25 | }
26 | },
27 | {
28 | "PutRequest": {
29 | "Item": { "order_id": {"S":"EDD052166486"},"pickup": {"S":"IAH"},"dropoff": {"S":"IAH"}}
30 | }
31 | },
32 | {
33 | "PutRequest": {
34 | "Item": {
35 | "order_id": {"S":"323E64AF67AB"},"pickup": {"S":"SEA"},"dropoff": {"S":"SFO"} }
36 | }
37 | },
38 | ]
39 | },
40 | )
41 |
42 | responseData = {}
43 | cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
44 |
45 | return
--------------------------------------------------------------------------------
/src/lambda/guardduty_helper/cfnresponse.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | from __future__ import print_function
5 | import urllib3
6 | import json
7 |
8 | SUCCESS = "SUCCESS"
9 | FAILED = "FAILED"
10 |
11 | http = urllib3.PoolManager()
12 |
13 | def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None):
14 | responseUrl = event['ResponseURL']
15 |
16 | print(responseUrl)
17 |
18 | responseBody = {
19 | 'Status' : responseStatus,
20 | 'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name),
21 | 'PhysicalResourceId' : physicalResourceId or context.log_stream_name,
22 | 'StackId' : event['StackId'],
23 | 'RequestId' : event['RequestId'],
24 | 'LogicalResourceId' : event['LogicalResourceId'],
25 | 'NoEcho' : noEcho,
26 | 'Data' : responseData
27 | }
28 |
29 | json_responseBody = json.dumps(responseBody)
30 |
31 | print("Response body:")
32 | print(json_responseBody)
33 |
34 | headers = {
35 | 'content-type' : '',
36 | 'content-length' : str(len(json_responseBody))
37 | }
38 |
39 | try:
40 | response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody)
41 | print("Status code:", response.status)
42 |
43 | except Exception as e:
44 |
45 | print("send(..) failed executing http.request(..):", e)
--------------------------------------------------------------------------------
/src/lambda/guardduty_helper/lambda_function.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import os
5 | import boto3
6 | import cfnresponse
7 |
8 | region = os.environ["region"]
9 |
10 | gd_client = boto3.client('guardduty',region_name=region)
11 |
12 | def lambda_handler(event, context):
13 |
14 | if event['RequestType'] != "Create":
15 | responseData = {}
16 | cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
17 | return
18 |
19 | detector=gd_client.list_detectors()
20 |
21 | if len(detector['DetectorIds']) <= 0:
22 | print('GuardDuty Detector does not exist in Region ' + region)
23 | print('Creating Detector in ' + region + ' ...')
24 | gd_client.create_detector(Enable=True)
25 | detector=gd_client.list_detectors()
26 |
27 |
28 | detector_id = detector['DetectorIds'][0]
29 | print('Detector exists in Region ' + region + ' Detector Id: ' + detector_id)
30 | for i in range(8):
31 | print('Creating sample finding ...')
32 | response = gd_client.create_sample_findings(
33 | DetectorId=detector_id,
34 | FindingTypes=['PrivilegeEscalation:IAMUser/AnomalousBehavior']
35 | )
36 |
37 | responseData = {}
38 | cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
39 |
40 | return
--------------------------------------------------------------------------------
/src/lambda/layer/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/zerotrust_service2service_workshop/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-zerotrust-service2service-workshop/cdeef8df51c9c2747b50be71d2365ea5ab277af9/zerotrust_service2service_workshop/__init__.py
--------------------------------------------------------------------------------
/zerotrust_service2service_workshop/service_a_stack.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import yaml
5 | from aws_cdk import core as cdk
6 |
7 | from aws_cdk import (
8 | aws_lambda as lambda_,
9 | aws_ec2 as ec2_,
10 | aws_iam as iam_,
11 | aws_secretsmanager as secretsmanager_,
12 | aws_ssm as ssm_,
13 | aws_events as events_,
14 | aws_events_targets as targets_,
15 | aws_s3 as s3_,
16 | aws_logs as logs_,
17 | )
18 |
19 | with open("./src/ec2/user_data.sh") as f:
20 | ec2_user_data = f.read()
21 |
22 | class ServiceAStack(cdk.Stack):
23 |
24 | def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
25 | super().__init__(scope, construct_id, **kwargs)
26 |
27 | with open("./config.yml","r") as f:
28 | configs = yaml.safe_load(f)
29 |
30 | if configs["dev_mode"]:
31 | pass
32 | else:
33 | # AWS Event Engine stuff - the value for these parameters will be available at an event runtime
34 | assets_bucket = cdk.CfnParameter(self,"EEAssetsBucket",type="String", description="Region-specific assets S3 bucket name.").value_as_string
35 | assets_prefix = cdk.CfnParameter(self,"EEAssetsKeyPrefix",type="String", description="S3 key prefix where this modules assets are stored. (e.g. modules/my_module/v1/)").value_as_string
36 |
37 | code_bucket = s3_.Bucket.from_bucket_name(self,"CodeBucket",
38 | bucket_name=assets_bucket
39 | )
40 |
41 | # IAM Resources - potentially move to a separate file
42 | main_instance_role = iam_.Role(self,"ServiceAInstanceRole",
43 | assumed_by=iam_.ServicePrincipal("ec2.amazonaws.com"),
44 | description="Allows EC2 instances to call AWS services on your behalf.",
45 | managed_policies=[
46 | iam_.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore"),
47 | # In prod, this should be limited to the intended API resource only
48 | iam_.ManagedPolicy.from_aws_managed_policy_name("AmazonAPIGatewayInvokeFullAccess")
49 | ]
50 | )
51 |
52 | lambda_role = iam_.Role(self,"ServiceALambdaRole",
53 | assumed_by=iam_.ServicePrincipal("lambda.amazonaws.com"),
54 | description="Allows Lambda functions to call AWS services on your behalf.",
55 | managed_policies=[
56 | iam_.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaVPCAccessExecutionRole"),
57 | ]
58 | )
59 |
60 | # Getting API secret's ARN, by its name, from parameter store.
61 | service_b_secret_arn = ssm_.StringParameter.value_for_string_parameter(self,
62 | f'{configs["params_path"]}service-b-api-secret-arn'
63 | )
64 |
65 | # Importing the secret so can use high-level grant function of CDK.
66 | # Alternative would be adding specific permissions to the roles via IAM construct - better for multi-account.
67 | service_b_secret = secretsmanager_.Secret.from_secret_complete_arn(self,"APISecret",
68 | secret_complete_arn=service_b_secret_arn
69 | )
70 |
71 | service_b_secret.grant_read(main_instance_role)
72 | service_b_secret.grant_read(lambda_role)
73 |
74 | lambda_role.add_to_policy(iam_.PolicyStatement(
75 | effect=iam_.Effect.ALLOW,
76 | actions=["ssm:GetParameter*"],
77 | resources=[
78 | cdk.Arn.format(cdk.ArnComponents(resource='parameter',service='ssm'),self)
79 | +configs["params_path"]+"*"
80 | ]
81 | ))
82 |
83 | # Network Resources - potentially move to a separate file
84 | main_vpc = ec2_.Vpc(self,"ServiceAVPC",
85 | max_azs=2,
86 | cidr=configs["main_vpc_cidr"],
87 | subnet_configuration=[
88 | ec2_.SubnetConfiguration(
89 | subnet_type=ec2_.SubnetType.PRIVATE,
90 | name="Private",
91 | cidr_mask=24
92 | ),
93 | # to allow instances dowload pip and workshop files (IGW,NATGW)
94 | ec2_.SubnetConfiguration(
95 | subnet_type=ec2_.SubnetType.PUBLIC,
96 | name="Public",
97 | cidr_mask=24
98 | )
99 | ],
100 | # set to 1 only for workshop purpose. Delete the line to get one per AZ.
101 | nat_gateways=1
102 | )
103 |
104 | vpc2 = ec2_.Vpc(self,"OtherVPC",
105 | max_azs=1,
106 | cidr=configs["other_vpc_cidr"],
107 | subnet_configuration=[
108 | ec2_.SubnetConfiguration(
109 | subnet_type=ec2_.SubnetType.PRIVATE,
110 | name="Private",
111 | cidr_mask=26
112 | ),
113 | ec2_.SubnetConfiguration(
114 | subnet_type=ec2_.SubnetType.PUBLIC,
115 | name="Public",
116 | cidr_mask=26
117 | )
118 | ],
119 | nat_gateways=1
120 | )
121 |
122 | custom_default_secgroup = ec2_.SecurityGroup(self,"MainVPCSecurityGroup",
123 | security_group_name="MainVPCSecurityGroup",
124 | vpc=main_vpc
125 | )
126 |
127 | main_instance_secgroup = ec2_.SecurityGroup(self,"ServiceASecurityGroup",
128 | security_group_name="ServiceASecurityGroup",
129 | vpc=main_vpc,
130 | )
131 |
132 | vpce_apigw_secgroup = ec2_.SecurityGroup(self,"APIGWVPCEndpointSecurityGroup",
133 | security_group_name="APIGWVPCEndpointSecurityGroup",
134 | vpc=main_vpc,
135 | )
136 |
137 | cdk.Tags.of(vpce_apigw_secgroup).add("Name","APIGWVPCEndpointSecurityGroup")
138 |
139 | vpc_endpoint_apigw = main_vpc.add_interface_endpoint("APIGWVPCEndpoint",
140 | service=ec2_.InterfaceVpcEndpointAwsService.APIGATEWAY,
141 | security_groups=[vpce_apigw_secgroup]
142 | )
143 |
144 | vpc_endpoint_ssm = main_vpc.add_interface_endpoint("SSMVPCEndpoint",
145 | service=ec2_.InterfaceVpcEndpointAwsService.SSM
146 | )
147 |
148 | vpc_endpoint_ssmmsg = main_vpc.add_interface_endpoint("SSMMSGVPCEndpoint",
149 | service=ec2_.InterfaceVpcEndpointAwsService.SSM_MESSAGES
150 | )
151 |
152 | vpc2_endpoint_apigw = vpc2.add_interface_endpoint("APIGWVPC2Endpoint",
153 | service=ec2_.InterfaceVpcEndpointAwsService.APIGATEWAY
154 | )
155 |
156 | vpc2_endpoint_ssm = vpc2.add_interface_endpoint("SSMVPC2Endpoint",
157 | service=ec2_.InterfaceVpcEndpointAwsService.SSM
158 | )
159 |
160 | vpc2_endpoint_ssmmsg = vpc2.add_interface_endpoint("SSMMSGVPC2Endpoint",
161 | service=ec2_.InterfaceVpcEndpointAwsService.SSM_MESSAGES
162 | )
163 |
164 | # this is a workaround to remove the unintended auto-added tag (VPC name) to vpc endpoint children (security groups)
165 | for vpce in [vpc_endpoint_apigw, vpc_endpoint_ssm, vpc_endpoint_ssmmsg, vpc2_endpoint_apigw, vpc2_endpoint_ssm, vpc2_endpoint_ssmmsg]:
166 | cdk.Tags.of(vpce).remove("Name")
167 |
168 | # Compute Resources - potentially move to a separate file
169 | lambda_layer = lambda_.LayerVersion(self,"WorkshopLayer",
170 |
171 | code=(lambda_.Code.from_asset("./src/lambda/layer") if configs["dev_mode"] else lambda_.Code.from_bucket(code_bucket,f"{assets_prefix}lambda/layer/lambda-code.zip")),
172 |
173 | compatible_runtimes=[lambda_.Runtime.PYTHON_3_8],
174 | description="The layer containing external packages used in this workshop.",
175 | )
176 |
177 | caller1_lambda = lambda_.Function(self,"CallerOne",
178 | runtime=lambda_.Runtime.PYTHON_3_8,
179 |
180 | code=(lambda_.Code.from_asset("./src/lambda/caller_nosigv4") if configs["dev_mode"] else lambda_.Code.from_bucket(code_bucket,f"{assets_prefix}lambda/caller_nosigv4/lambda-code.zip")),
181 |
182 | handler="lambda_function.lambda_handler",
183 | layers=[lambda_layer],
184 | timeout=cdk.Duration.seconds(configs["lambda_timeout"]),
185 | vpc=main_vpc,
186 | vpc_subnets=ec2_.SubnetSelection(subnets=[main_vpc.private_subnets[1]]),
187 | security_groups=[custom_default_secgroup],
188 | role=lambda_role,
189 | environment= {
190 | "api_resource":configs["api_resource"],
191 | "api_region": cdk.Stack.of(self).region,
192 | "api_id_parameter":f'{configs["params_path"]}service-b-api-id',
193 | "api_secret_parameter":f'{configs["params_path"]}service-b-api-secret-arn'
194 | }
195 | )
196 |
197 | caller2_lambda = lambda_.Function(self,"CallerTwo",
198 | runtime=lambda_.Runtime.PYTHON_3_8,
199 |
200 | code=(lambda_.Code.from_asset("./src/lambda/caller_nosigv4") if configs["dev_mode"] else lambda_.Code.from_bucket(code_bucket,f"{assets_prefix}lambda/caller_nosigv4/lambda-code.zip")),
201 |
202 | handler="lambda_function.lambda_handler",
203 | layers=[lambda_layer],
204 | timeout=cdk.Duration.seconds(configs["lambda_timeout"]),
205 | vpc=main_vpc,
206 | vpc_subnets=ec2_.SubnetSelection(subnets=[main_vpc.private_subnets[0]]),
207 | security_groups=[custom_default_secgroup],
208 | role=lambda_role,
209 | environment= {
210 | "api_resource":configs["api_resource"],
211 | "api_region": cdk.Stack.of(self).region,
212 | "api_id_parameter":f'{configs["params_path"]}service-b-api-id',
213 | "api_secret_parameter":f'{configs["params_path"]}service-b-api-secret-arn'
214 | },
215 | )
216 |
217 | caller3_lambda = lambda_.Function(self,"CallerThree",
218 | runtime=lambda_.Runtime.PYTHON_3_8,
219 |
220 | code=(lambda_.Code.from_asset("./src/lambda/caller_sigv4") if configs["dev_mode"] else lambda_.Code.from_bucket(code_bucket,f"{assets_prefix}lambda/caller_sigv4/lambda-code.zip")),
221 |
222 | handler="lambda_function.lambda_handler",
223 | layers=[lambda_layer],
224 | timeout=cdk.Duration.seconds(configs["lambda_timeout"]),
225 | vpc=main_vpc,
226 | vpc_subnets=ec2_.SubnetSelection(subnets=[main_vpc.private_subnets[0]]),
227 | security_groups=[custom_default_secgroup],
228 | role=lambda_role,
229 | environment= {
230 | "api_resource":configs["api_resource"],
231 | "api_region": cdk.Stack.of(self).region,
232 | "api_id_parameter":f'{configs["params_path"]}service-b-api-id',
233 | "api_secret_parameter":f'{configs["params_path"]}service-b-api-secret-arn'
234 | }
235 | )
236 |
237 | gd_lambda = lambda_.Function(self,"GuardDutyHelper",
238 | runtime=lambda_.Runtime.PYTHON_3_8,
239 |
240 | code=(lambda_.Code.from_asset("./src/lambda/guardduty_helper") if configs["dev_mode"] else lambda_.Code.from_bucket(code_bucket,f"{assets_prefix}lambda/guardduty_helper/lambda-code.zip")),
241 |
242 | timeout=cdk.Duration.seconds(configs["lambda_timeout"]),
243 | handler="lambda_function.lambda_handler",
244 | environment= {
245 | "region": cdk.Stack.of(self).region
246 | },
247 | )
248 | gd_lambda.role.add_to_policy(iam_.PolicyStatement(
249 | resources=["*"],
250 | actions=["guardduty:CreateDetector","guardduty:CreateSampleFindings","guardduty:ListDetectors"]
251 | ))
252 | gd_lambda.role.add_to_policy(iam_.PolicyStatement(
253 | actions=["iam:CreateServiceLinkedRole"],
254 | resources=["arn:aws:iam::*:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty*"],
255 | conditions={"StringLike": {"iam:AWSServiceName": "guardduty.amazonaws.com"}}
256 |
257 | ))
258 |
259 | # NICE to have: alternative to custom resource, to reduce stack creation time
260 | gd_init = cdk.CustomResource(self,"GDInit",
261 | service_token=gd_lambda.function_arn
262 | )
263 |
264 | # creating log groups for lambda explicitly so that log groups get deleted after deleing CDK/CFN stack
265 | for i,lmbd in enumerate([caller1_lambda, caller2_lambda, caller3_lambda, gd_lambda]):
266 | logs_.LogGroup(self,f'LogGroup{i}',
267 | log_group_name=f'/aws/lambda/{lmbd.function_name}',
268 | removal_policy=cdk.RemovalPolicy.DESTROY
269 | )
270 |
271 | # Scheduling Lambdas
272 | one_minute_rule = events_.Rule(self,"ScheduleRuleOne",
273 | description="Scheduler running every minute",
274 | schedule=events_.Schedule.rate(cdk.Duration.minutes(1)),
275 | targets=[
276 | targets_.LambdaFunction(caller1_lambda),
277 | targets_.LambdaFunction(caller2_lambda),
278 | targets_.LambdaFunction(caller3_lambda),
279 | ]
280 | )
281 |
282 | # EC2 stuff
283 | amzn_linux_ami = ec2_.MachineImage.latest_amazon_linux(
284 | generation=ec2_.AmazonLinuxGeneration.AMAZON_LINUX_2,
285 | edition=ec2_.AmazonLinuxEdition.STANDARD,
286 | virtualization=ec2_.AmazonLinuxVirt.HVM,
287 | storage=ec2_.AmazonLinuxStorage.GENERAL_PURPOSE
288 | )
289 |
290 | # to use autoscaling group, uncomment the line below and comment the one after
291 | #main_instance = autoscaling_.AutoScalingGroup(self,"ServiceA",
292 | main_instance = ec2_.Instance(self,"ServiceAInstance",
293 | instance_type=ec2_.InstanceType(configs["instance_type"]),
294 | machine_image=amzn_linux_ami,
295 | vpc=main_vpc,
296 | vpc_subnets=ec2_.SubnetSelection(subnets=[main_vpc.private_subnets[0]]),
297 | security_group=main_instance_secgroup,
298 | role=main_instance_role,
299 | user_data=ec2_.UserData.custom(ec2_user_data)
300 | )
301 | main_instance.add_user_data(f'echo api_resource={configs["api_resource"]} >> /tmp/workshop/.env')
302 | main_instance.add_user_data(f'echo api_region={cdk.Stack.of(self).region} >> /tmp/workshop/.env')
303 | main_instance.add_user_data(f'echo api_id_parameter={configs["params_path"]}service-b-api-id >> /tmp/workshop/.env')
304 | main_instance.add_user_data(f'echo api_secret_parameter={configs["params_path"]}service-b-api-secret-arn >> /tmp/workshop/.env')
305 | main_instance.add_user_data(f'echo unwanted_callers_parameter={configs["params_path"]}service-a-unwanted-callers-list >> /tmp/workshop/.env')
306 | main_instance.add_user_data(f'echo unknown_api_id_parameter={configs["params_path"]}unknown-api-id >> /tmp/workshop/.env')
307 |
308 | other_instance = ec2_.Instance(self,"OtherInstance",
309 | instance_type=ec2_.InstanceType(configs["instance_type"]),
310 | machine_image=amzn_linux_ami,
311 | vpc=vpc2,
312 | vpc_subnets=ec2_.SubnetSelection(subnets=[vpc2.private_subnets[0]]),
313 | role=main_instance_role,
314 | user_data=ec2_.UserData.custom(ec2_user_data)
315 | )
316 | other_instance.add_user_data(f'echo api_resource={configs["api_resource"]} >> /tmp/workshop/.env')
317 | other_instance.add_user_data(f'echo api_region={cdk.Stack.of(self).region} >> /tmp/workshop/.env')
318 | other_instance.add_user_data(f'echo api_id_parameter={configs["params_path"]}service-b-api-id >> /tmp/workshop/.env')
319 | other_instance.add_user_data(f'echo api_secret_parameter={configs["params_path"]}service-b-api-secret-arn >> /tmp/workshop/.env')
320 |
321 | # This is for workshop purpose only - to enable scanner to invoke Lambdas and unwanted instance
322 | caller1_lambda.grant_invoke(main_instance_role)
323 | caller2_lambda.grant_invoke(main_instance_role)
324 | caller3_lambda.grant_invoke(main_instance_role)
325 | main_instance_role.add_to_policy(iam_.PolicyStatement(
326 | resources=["*"],
327 | actions=["ssm:SendCommand","ssm:ListCommandInvocations"]
328 | ))
329 |
330 | # Storing in Systems Manager Paramete Store
331 | ssm_.StringListParameter(self,"UnwantedCallersListParameter",
332 | parameter_name=f'{configs["params_path"]}service-a-unwanted-callers-list',
333 | string_list_value=[
334 | caller1_lambda.function_arn,
335 | caller2_lambda.function_arn,
336 | caller3_lambda.function_arn,
337 | other_instance.instance_id,
338 | ]
339 | )
340 |
341 | cdk.CfnOutput(self,"InstanceSession",
342 | value=f'https://console.aws.amazon.com/systems-manager/session-manager/{main_instance.instance_id}?region={cdk.Stack.of(self).region}'
343 | )
344 |
345 | cdk.CfnOutput(self,"InstanceRoleArn",
346 | value=main_instance_role.role_arn
347 | )
348 |
349 | cdk.CfnOutput(self,"ServiceAAccountID",
350 | value=cdk.Stack.of(self).account
351 | )
352 |
353 | cdk.CfnOutput(self,"VPCEndpointID",
354 | value=vpc_endpoint_apigw.vpc_endpoint_id
355 | )
--------------------------------------------------------------------------------
/zerotrust_service2service_workshop/service_b_stack.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import yaml
5 |
6 | from aws_cdk import core as cdk
7 |
8 | from aws_cdk import (
9 | aws_lambda as lambda_,
10 | aws_apigateway as apigw_,
11 | aws_iam as iam_,
12 | aws_logs as logs_,
13 | aws_secretsmanager as secretsmanager_,
14 | aws_ssm as ssm_,
15 | aws_dynamodb as ddb_,
16 | aws_cloudwatch as cw_,
17 | aws_s3 as s3_,
18 | )
19 |
20 | class ServiceBStack(cdk.Stack):
21 |
22 | def __init__(self, scope: cdk.Construct, construct_id: str,**kwargs) -> None:
23 | super().__init__(scope, construct_id, **kwargs)
24 |
25 | with open("./config.yml","r") as f:
26 | configs = yaml.safe_load(f)
27 |
28 | if configs["dev_mode"]:
29 | pass
30 | else:
31 | # AWS Event Engine stuff - the value for these parameters will be available at an event runtime
32 | assets_bucket = cdk.CfnParameter(self,"EEAssetsBucket",type="String", description="Region-specific assets S3 bucket name.").value_as_string
33 | assets_prefix = cdk.CfnParameter(self,"EEAssetsKeyPrefix",type="String", description="S3 key prefix where this modules assets are stored. (e.g. modules/my_module/v1/)").value_as_string
34 |
35 | code_bucket = s3_.Bucket.from_bucket_name(self,"CodeBucket",
36 | bucket_name=assets_bucket
37 | )
38 |
39 | orders_table = ddb_.Table(self,"OrdersTable",
40 | partition_key=ddb_.Attribute(
41 | name="order_id",
42 | type=ddb_.AttributeType.STRING
43 | ),
44 | removal_policy=cdk.RemovalPolicy.DESTROY
45 | )
46 |
47 | # custom resource for populating DDB table with mock data
48 | ddbinit_lambda = lambda_.Function(self,"DDBInitLambda",
49 | runtime=lambda_.Runtime.PYTHON_3_8,
50 |
51 | code=(lambda_.Code.from_asset("./src/lambda/ddbinit") if configs["dev_mode"] else lambda_.Code.from_bucket(code_bucket,f"{assets_prefix}lambda/ddbinit/lambda-code.zip")),
52 |
53 | handler="lambda_function.lambda_handler",
54 | timeout=cdk.Duration.seconds(configs["lambda_timeout"]),
55 | environment={
56 | "TABLE_NAME":orders_table.table_name
57 | }
58 | )
59 | orders_table.grant_write_data(ddbinit_lambda)
60 |
61 | # NICE to have: alternative to custom resource, to reduce stack creation time
62 | ddb_init = cdk.CustomResource(self,"DDBInit",
63 | service_token=ddbinit_lambda.function_arn,
64 | )
65 |
66 | backend_lambda = lambda_.Function(self,"BackendLambda",
67 | runtime=lambda_.Runtime.PYTHON_3_8,
68 |
69 | code=(lambda_.Code.from_asset("./src/lambda/backend") if configs["dev_mode"] else lambda_.Code.from_bucket(code_bucket,f"{assets_prefix}lambda/backend/lambda-code.zip")),
70 |
71 | handler="lambda_function.lambda_handler",
72 | timeout=cdk.Duration.seconds(configs["lambda_timeout"]),
73 | environment={
74 | "TABLE_NAME":orders_table.table_name
75 | }
76 | )
77 |
78 | orders_table.grant_read_data(backend_lambda)
79 |
80 | # creating log groups for lambda explicitly so that log groups get deleted after deleing CDK/CFN stack
81 | for i,lmbd in enumerate([backend_lambda, ddbinit_lambda]):
82 | logs_.LogGroup(self,f'LogGroup{i}',
83 | log_group_name=f'/aws/lambda/{lmbd.function_name}',
84 | removal_policy=cdk.RemovalPolicy.DESTROY
85 | )
86 |
87 | # API Gateway and its stuff
88 | secret = secretsmanager_.Secret(self,"APISecret",
89 | generate_secret_string=secretsmanager_.SecretStringGenerator(
90 | exclude_punctuation= True,
91 | )
92 | )
93 |
94 | access_log_group = logs_.LogGroup(self,"APIAccessLogs",
95 | removal_policy=cdk.RemovalPolicy.DESTROY
96 | )
97 |
98 | api = apigw_.LambdaRestApi(self,"ServiceBAPI",
99 | endpoint_configuration={
100 | "types":[apigw_.EndpointType.PRIVATE]
101 | },
102 | deploy_options={
103 | "stage_name":"api",
104 | "access_log_destination":apigw_.LogGroupLogDestination(access_log_group) ,
105 | "access_log_format": apigw_.AccessLogFormat.clf()
106 | },
107 | default_method_options={
108 | "api_key_required": True
109 | },
110 | policy= iam_.PolicyDocument.from_json(configs["api_resource_policy"]),
111 | handler=backend_lambda,
112 | proxy=False
113 | )
114 | key = api.add_api_key("ApiKey",
115 | value=secret.secret_value.to_string()
116 | )
117 | usage_plan = api.add_usage_plan("UsagePlan",
118 | api_stages=[{
119 | "api": api,
120 | "stage": api.deployment_stage
121 | }]
122 | )
123 | usage_plan.add_api_key(key)
124 |
125 | orders = api.root.add_resource("orders")
126 | get_orders = orders.add_method("GET")
127 |
128 | # For workshop purpose only - this is to determine when calls get blocked at API GW in the scanner.py
129 | api.add_gateway_response("APICustomResponse",
130 | type=apigw_.ResponseType.DEFAULT_4_XX,
131 | templates={
132 | "application/json": "{ 'message': $context.error.messageString, 'workshopmsg': 'hit-apigw'}"
133 | }
134 | )
135 |
136 | unknown_api = apigw_.RestApi(self,"UnknownAPI",
137 | endpoint_configuration={
138 | "types":[apigw_.EndpointType.PRIVATE]
139 | },
140 | deploy_options={
141 | "stage_name":"api",
142 | },
143 | policy= iam_.PolicyDocument.from_json(configs["api_resource_policy"]),
144 | )
145 | unknown_api.root.add_method("PUT",apigw_.MockIntegration(
146 | integration_responses=[apigw_.IntegrationResponse(
147 | status_code="200",
148 | response_templates={"application/json": "{'message':'SUCCESS Mock PUT'}"}
149 | )],
150 | request_templates={"application/json": "{'statusCode': 200}"},
151 | ),
152 | method_responses=[apigw_.MethodResponse(status_code="200")]
153 | )
154 |
155 | # Storing in Systems Manager Paramete Store - this enables the two template to be deployed in two different accounts.
156 | ssm_.StringParameter(self,"APIIDParameter",
157 | parameter_name=f'{configs["params_path"]}service-b-api-id',
158 | string_value=api.rest_api_id
159 | )
160 |
161 | ssm_.StringParameter(self,"APISecretNameParameter",
162 | parameter_name=f'{configs["params_path"]}service-b-api-secret-arn',
163 | string_value=secret.secret_arn
164 | )
165 |
166 | ssm_.StringParameter(self,"UnknownAPIIDParameter",
167 | parameter_name=f'{configs["params_path"]}unknown-api-id',
168 | string_value=unknown_api.rest_api_id
169 | )
170 |
171 |
172 | # CloudWatch dashboard for API GW
173 | dashboard = cw_.Dashboard(self,"APICallsDashboard",
174 | start="-PT2H",
175 | )
176 |
177 | dashboard.add_widgets(cw_.GraphWidget(
178 | title="Number of API Calls",
179 | left=[
180 | cw_.Metric(
181 | metric_name="Count",
182 | label="Total API Calls",
183 | namespace="AWS/ApiGateway",
184 | dimensions={"ApiName": api.rest_api_name},
185 | statistic="SampleCount"
186 | ),
187 | cw_.Metric(
188 | metric_name="4XXError",
189 | label="Unauthorized Calls",
190 | namespace="AWS/ApiGateway",
191 | dimensions={"ApiName": api.rest_api_name},
192 | statistic="Sum"
193 | )
194 | ],
195 | left_y_axis=cw_.YAxisProps(
196 | min= 0
197 | ),
198 | width=20,
199 | period=cdk.Duration.minutes(1),
200 | ))
201 |
202 | cdk.CfnOutput(self,"APIMethodARN",
203 | value=get_orders.method_arn
204 | )
--------------------------------------------------------------------------------