├── .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 | ) --------------------------------------------------------------------------------