├── .gitignore ├── 01-validate-policy ├── identity-policy.json ├── resource-policy.json ├── scp.json ├── validate-identity-policy.sh ├── validate-resource-policy.sh └── validate-scp.sh ├── 02-create-access-preview ├── create-access-preview.sh ├── get-access-preview.sh ├── list-access-preview-findings.sh └── queue-policy.json ├── 03-no-iac ├── access_preview.py ├── analyze.py ├── colors.py ├── policies │ ├── identity │ │ ├── policy1.json │ │ ├── policy2.json │ │ ├── policy3.json │ │ └── trust │ │ │ └── trust-policy.json │ └── resource │ │ ├── kms │ │ └── key-policy.json │ │ ├── s3 │ │ └── bucket-policy.json │ │ ├── sns │ │ └── topic-policy.json │ │ └── sqs │ │ └── queue-policy.json ├── requirements.txt └── run-no-iac.sh ├── 04-cloudformation ├── 01-policy-validator │ ├── run-policy-validator.sh │ └── template.json └── 02-cdk │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── bin │ └── demo.ts │ ├── cdk.json │ ├── jest.config.js │ ├── lib │ └── demo-stack.ts │ ├── package.json │ ├── run-policy-validator.sh │ ├── test │ └── 02-cdk.test.ts │ └── tsconfig.json ├── 05-scps ├── policies │ ├── policy1.json │ └── policy2.json └── validate-scp.sh ├── 06-service-specific ├── validate-all-api-gateways.sh └── validate-all-sns-topics.sh ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | .idea 3 | .terraform 4 | .vscode/settings.json 5 | __pycache__ -------------------------------------------------------------------------------- /01-validate-policy/identity-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "ListObjectsInBucket", 6 | "Effect": "Allow", 7 | "Action": ["s3:ListBuckets"], 8 | "Resource": ["arn:aws:s3:::bucket-name"] 9 | }, 10 | { 11 | "Sid": "AllObjectActions", 12 | "Effect": "Allow", 13 | "Action": "s3:*Object*", 14 | "Resource": ["arn:aws:s3:::bucket-name/*"] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /01-validate-policy/resource-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2008-10-17", 3 | "Id": "__default_policy_ID", 4 | "Statement": [ 5 | { 6 | "Sid": "__default_statement_ID", 7 | "Effect": "Allow", 8 | "Principal": { 9 | "AWS": "arn:aws:iam::111122223333:role/exampleRole" 10 | }, 11 | "Action": [ 12 | "SNS:GetTopicAttributes", 13 | "SNS:SetTopicAttributes", 14 | "SNS:AddPermission", 15 | "SNS:RemovePermission", 16 | "SNS:DeleteTopic", 17 | "SNS:Subscribe", 18 | "SNS:ListSubscriptionsByTopic", 19 | "SNS:Publish" 20 | ], 21 | "Resource": "arn:aws:sns:us-east-1:111122223333:examplesnstopic.fifo", 22 | "Condition": { 23 | "StringEquals": { 24 | "AWS:SourceOwner": "111122223333" 25 | } 26 | } 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /01-validate-policy/scp.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [{ 4 | "Sid": "ProtectRoles", 5 | "Effect": "Deny", 6 | "Action": [ 7 | "iam:*Role*" 8 | ], 9 | "Resource": [ 10 | "arn:aws:iam::*:role/administrative-role" 11 | ] 12 | }] 13 | } -------------------------------------------------------------------------------- /01-validate-policy/validate-identity-policy.sh: -------------------------------------------------------------------------------- 1 | aws accessanalyzer validate-policy --policy-type IDENTITY_POLICY --policy-document file://identity-policy.json -------------------------------------------------------------------------------- /01-validate-policy/validate-resource-policy.sh: -------------------------------------------------------------------------------- 1 | aws accessanalyzer validate-policy --policy-type RESOURCE_POLICY --policy-document file://resource-policy.json -------------------------------------------------------------------------------- /01-validate-policy/validate-scp.sh: -------------------------------------------------------------------------------- 1 | aws accessanalyzer validate-policy --policy-type SERVICE_CONTROL_POLICY --policy-document file://scp.json -------------------------------------------------------------------------------- /02-create-access-preview/create-access-preview.sh: -------------------------------------------------------------------------------- 1 | 2 | ANALYZER_ARN=$(aws accessanalyzer list-analyzers --query "analyzers[?status=='ACTIVE'].arn | [0]" --output text) 3 | ACCOUNT_ID=$(aws sts get-caller-identity --output text | cut -f1 -d$'\t') 4 | echo "Access Analyzer ARN: $ANALYZER_ARN" 5 | 6 | QUEUE_POLICY=$(cat queue-policy.json | jq -c . | sed "s//${ACCOUNT_ID}/") 7 | 8 | CONFIGURATIONS=$(jq -n -c --arg account_id "$ACCOUNT_ID" --arg queue_policy "$QUEUE_POLICY" '{"arn:aws:sqs:us-east-1:\($account_id):MyQueue": {sqsQueue: {queuePolicy: $queue_policy}}}') 9 | 10 | echo $CONFIGURATIONS | jq . 11 | 12 | ACCESS_PREVIEW_ID=$(aws accessanalyzer create-access-preview --configurations $CONFIGURATIONS --analyzer-arn $ANALYZER_ARN --query id --output text) 13 | echo "Access Preview Created: $ACCESS_PREVIEW_ID" -------------------------------------------------------------------------------- /02-create-access-preview/get-access-preview.sh: -------------------------------------------------------------------------------- 1 | aws accessanalyzer get-access-preview --analyzer-arn $ANALYZER_ARN --access-preview-id $ACCESS_PREVIEW_ID -------------------------------------------------------------------------------- /02-create-access-preview/list-access-preview-findings.sh: -------------------------------------------------------------------------------- 1 | aws accessanalyzer list-access-preview-findings --analyzer-arn $ANALYZER_ARN --access-preview-id $ACCESS_PREVIEW_ID -------------------------------------------------------------------------------- /02-create-access-preview/queue-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Id": "MyQueuePolicy", 4 | "Statement": [{ 5 | "Sid":"AllowSendMessage", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "AWS": [ 9 | "111122223333" 10 | ] 11 | }, 12 | "Action": "sqs:SendMessage", 13 | "Resource": "arn:aws:sqs:us-east-1::MyQueue" 14 | }] 15 | } -------------------------------------------------------------------------------- /03-no-iac/access_preview.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import time 4 | 5 | from abc import ABC 6 | 7 | client = boto3.client('accessanalyzer') 8 | 9 | list_analyzers_response = client.list_analyzers( 10 | type='ACCOUNT' 11 | ) 12 | 13 | first_active_analyzer = next((analyzer for analyzer in list_analyzers_response['analyzers'] if analyzer['status'] == 'ACTIVE'), None) 14 | if first_active_analyzer is not None: 15 | analyzer_arn = first_active_analyzer['arn'] 16 | else: 17 | raise Exception('No active analyzers found in account. You must create an analyzer in your account to run access previews.') 18 | 19 | sts_client = boto3.client('sts') 20 | sts_client.get_caller_identity() 21 | identity = sts_client.get_caller_identity() 22 | account_id = identity['Account'] 23 | 24 | parts = identity['Arn'].split(':') 25 | partition = parts[1] 26 | 27 | region = client.meta.region_name 28 | 29 | 30 | class AccessPreview(ABC): 31 | def __init__(self): 32 | self.preview_id = None 33 | 34 | def create(self, policy): 35 | configuration = self._build_configuration(policy) 36 | response = client.create_access_preview( 37 | analyzerArn=analyzer_arn, 38 | configurations=configuration 39 | ) 40 | self.preview_id = response['id'] 41 | 42 | def get(self): 43 | number_of_attempts = 0 44 | while True: 45 | response = client.get_access_preview( 46 | accessPreviewId=self.preview_id, 47 | analyzerArn=analyzer_arn 48 | ) 49 | status = response['accessPreview']['status'] 50 | 51 | if status == 'CREATING': 52 | number_of_attempts = number_of_attempts + 1 53 | if number_of_attempts >= 30: 54 | raise Exception(f'Timed out after 1 minute waiting for access analyzer preview to create.') 55 | 56 | time.sleep(2) 57 | else: 58 | break 59 | 60 | if status == 'FAILED': 61 | reason = response["accessPreview"]["statusReason"]["code"] 62 | raise Exception(f'Failed to create access preview. Reason: {reason}') 63 | 64 | # status is created if method makes it to this point 65 | return status 66 | 67 | def list_findings(self): 68 | findings = [] 69 | paginator = client.get_paginator('list_access_preview_findings') 70 | for page in paginator.paginate(accessPreviewId=self.preview_id, analyzerArn=analyzer_arn): 71 | for finding in page['findings']: 72 | findings.append({'findingType': 'SECURITY_WARNING', 'details': finding}) 73 | 74 | return findings 75 | 76 | @staticmethod 77 | def _get_resource_from_policy(policy): 78 | resource = policy['Statement'][0].get('Resource') 79 | if resource is None: 80 | return None 81 | 82 | if isinstance(resource, list): 83 | resource = resource[0] 84 | 85 | return resource 86 | 87 | 88 | class SQSAccessPreview(AccessPreview): 89 | def _build_configuration(self, policy): 90 | resource = self._get_resource_from_policy(policy) 91 | if resource == "*": 92 | resource = f'arn:{partition}:sqs:{region}:{account_id}:ArbitraryQueueName' 93 | 94 | return { 95 | resource: { 96 | 'sqsQueue': { 97 | 'queuePolicy': json.dumps(policy) 98 | } 99 | } 100 | } 101 | 102 | 103 | class RoleTrustPolicyAccessPreview(AccessPreview): 104 | @staticmethod 105 | def _build_configuration(policy): 106 | # IAM trust policies cannot have a Resource field in their policy statements 107 | # the name chosen for the role does not matter 108 | resource = f'arn:{partition}:iam::{account_id}:role/ArbitraryName' 109 | 110 | return { 111 | resource: { 112 | 'iamRole': { 113 | 'trustPolicy': json.dumps(policy) 114 | } 115 | } 116 | } 117 | 118 | 119 | class S3BucketPolicyAccessPreview(AccessPreview): 120 | def _build_configuration(self, policy): 121 | resource = self._get_resource_from_policy(policy) 122 | if resource == '*': 123 | raise Exception('Resource "*" is not valid in an S3 bucket policy.') 124 | 125 | if resource.endswith('/*'): 126 | resource = resource[:-2] 127 | 128 | return { 129 | resource: { 130 | 's3Bucket': { 131 | 'bucketPolicy': json.dumps(policy) 132 | } 133 | } 134 | } 135 | 136 | 137 | class KmsKeyPolicyAccessPreview(AccessPreview): 138 | def _build_configuration(self, policy): 139 | resource = self._get_resource_from_policy(policy) 140 | if resource == '*': 141 | resource = f'arn:{partition}:kms:{region}:{account_id}:key/ArbitraryId' 142 | 143 | return { 144 | resource: { 145 | 'kmsKey': { 146 | 'keyPolicies': { 147 | 'default': json.dumps(policy) 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /03-no-iac/analyze.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | from access_preview import SQSAccessPreview, RoleTrustPolicyAccessPreview, S3BucketPolicyAccessPreview, \ 4 | KmsKeyPolicyAccessPreview 5 | from colors import colors 6 | 7 | import boto3 8 | import json 9 | import os 10 | import sys 11 | 12 | 13 | aa_client = boto3.client('accessanalyzer') 14 | 15 | 16 | def validate_policy(full_policy_filename, policy_document): 17 | parent_directory = os.path.basename(os.path.dirname(full_policy_filename)) 18 | if parent_directory.lower() == 'identity': 19 | policy_type = 'IDENTITY_POLICY' 20 | else: 21 | policy_type = 'RESOURCE_POLICY' 22 | 23 | validate_policy_response = aa_client.validate_policy( 24 | policyDocument=json.dumps(policy_document), 25 | policyType=policy_type 26 | ) 27 | 28 | findings = [] 29 | for finding in validate_policy_response['findings']: 30 | findings.append(finding) 31 | 32 | return findings 33 | 34 | 35 | def get_access_preview_findings(full_policy_filename, policy_document): 36 | parent_directory = os.path.basename(os.path.dirname(full_policy_filename)).lower() 37 | if parent_directory == 'trust': 38 | access_preview = RoleTrustPolicyAccessPreview() 39 | elif parent_directory == 'sqs': 40 | access_preview = SQSAccessPreview() 41 | elif parent_directory == 's3': 42 | access_preview = S3BucketPolicyAccessPreview() 43 | elif parent_directory == 'kms': 44 | access_preview = KmsKeyPolicyAccessPreview() 45 | else: 46 | return [] 47 | 48 | access_preview.create(policy_document) 49 | access_preview.get() 50 | return access_preview.list_findings() 51 | 52 | 53 | def get_count(results, finding_type): 54 | return len([finding for filename, findings in results.items() 55 | for finding in findings if finding['findingType'] == finding_type]) 56 | 57 | 58 | def validate(): 59 | this_scripts_directory = os.path.dirname(os.path.realpath(__file__)) 60 | policies_directory = os.path.join(this_scripts_directory, 'policies') 61 | 62 | results = defaultdict() 63 | 64 | print(f'{colors.OKBLUE}Starting analysis of {policies_directory}..') 65 | print() 66 | 67 | for root, dirs, files in os.walk(policies_directory, topdown=True): 68 | for file in files: 69 | full_policy_filename = os.path.join(root, file) 70 | with open(full_policy_filename, 'r') as file: 71 | policy_document = json.load(file) 72 | 73 | findings = [] 74 | findings.extend(validate_policy(file.name, policy_document)) 75 | findings.extend(get_access_preview_findings(file.name, policy_document)) 76 | 77 | results[full_policy_filename] = findings 78 | 79 | should_exit_with_non_zero_code = False 80 | 81 | for filename, findings in results.items(): 82 | print(f'{colors.OKBLUE}{filename}') 83 | for finding in findings: 84 | finding_type = finding['findingType'] 85 | if finding_type == 'ERROR' or finding_type == 'SECURITY_WARNING': 86 | should_exit_with_non_zero_code = True 87 | print(f'{colors.FAIL}{finding}') 88 | print() 89 | else: 90 | print(f'{colors.WARNING}{finding}') 91 | print() 92 | 93 | print(colors.RESET) 94 | 95 | print(f'{colors.OKBLUE}ERRORS: {get_count(results, "ERROR")}') 96 | print(f'{colors.OKBLUE}SECURITY_WARNINGS: {get_count(results, "SECURITY_WARNING")}') 97 | print(f'{colors.OKBLUE}WARNINGS: {get_count(results, "WARNING")}') 98 | print(f'{colors.OKBLUE}SUGGESTIONS: {get_count(results, "SUGGESTION")}') 99 | 100 | if should_exit_with_non_zero_code: 101 | print(f'{colors.FAIL}FAILED: ERROR or SECURITY_WARNING findings.') 102 | print(colors.RESET) 103 | sys.exit(1) 104 | 105 | 106 | if __name__ == "__main__": 107 | validate() 108 | -------------------------------------------------------------------------------- /03-no-iac/colors.py: -------------------------------------------------------------------------------- 1 | class colors: 2 | OKBLUE = '\033[94m' 3 | WARNING = '\033[93m' 4 | FAIL = '\033[91m' 5 | RESET = '\033[0m' 6 | -------------------------------------------------------------------------------- /03-no-iac/policies/identity/policy1.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "AllowPassRole", 6 | "Effect": "Allow", 7 | "Action": "iam:PassRole", 8 | "Resource": "*" 9 | }, 10 | { 11 | "Sid": "AllObjectActions", 12 | "Effect": "Allow", 13 | "Action": "s3:*Object*z", 14 | "Resource": ["arn:aws:s3:::bucket-name/*"] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /03-no-iac/policies/identity/policy2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "ListObjectsInBucket", 6 | "Effect": "Allow", 7 | "Action": ["s3:ListBuckets"], 8 | "Resource": ["arn:aws:s3:::bucket-name"] 9 | }, 10 | { 11 | "Sid": "AllowWriteObject", 12 | "Effect": "Allow", 13 | "Action": "s3:WriteObject", 14 | "Resource": ["arn:aws:s3:::bucket-name/*"] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /03-no-iac/policies/identity/policy3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Statement": [ 3 | { 4 | "Sid": "ListObjectsInBucket", 5 | "Effect": "Allow", 6 | "Action": ["s3:ListBuckets"], 7 | "Resource": ["arn:aws:s3:::bucket-name"] 8 | }, 9 | { 10 | "Sid": "AllObjectActions", 11 | "Effect": "Allow", 12 | "Action": "s3:*Object*", 13 | "Resource": ["arn:aws:s3:::bucket-name/*"] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /03-no-iac/policies/identity/trust/trust-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "AWS": "arn:aws:iam::111122223333:root" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /03-no-iac/policies/resource/kms/key-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Statement": [ 3 | { 4 | "Effect": "Allow", 5 | "Principal": { 6 | "AWS": "arn:aws:iam::111122223333:root" 7 | }, 8 | "Action": "kms:*", 9 | "Resource": "*" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /03-no-iac/policies/resource/s3/bucket-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version":"2012-10-17", 3 | "Statement":[ 4 | { 5 | "Sid":"PolicyForAllowUploadWithACL", 6 | "Effect":"Allow", 7 | "Principal":{ 8 | "AWS":"111122223333" 9 | }, 10 | "Action":"s3:PutObject", 11 | "Resource":"arn:aws:s3:::EXAMPLE-BUCKET/*", 12 | "Condition": { 13 | "StringEquals": {"s3:x-amz-acl":"bucket-owner-full-control"} 14 | } 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /03-no-iac/policies/resource/sns/topic-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Statement": [{ 3 | "Effect": "Allow", 4 | "Principal": { 5 | "AWS": "111122223333" 6 | }, 7 | "Action": ["sns:Publish"], 8 | "Resource": "arn:aws:sns:us-east-2:444455556666:MyTopic" 9 | }] 10 | } -------------------------------------------------------------------------------- /03-no-iac/policies/resource/sqs/queue-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Id": "MyQueuePolicy", 4 | "Statement": [{ 5 | "Sid":"AllowSendMessage", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "AWS": [ 9 | "111122223333" 10 | ] 11 | }, 12 | "Action": "sqs:SendMessage", 13 | "Resource": "*" 14 | }] 15 | } -------------------------------------------------------------------------------- /03-no-iac/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3>=1.18.56 -------------------------------------------------------------------------------- /03-no-iac/run-no-iac.sh: -------------------------------------------------------------------------------- 1 | 2 | python3 -m venv venv 3 | source venv/bin/activate 4 | pip install -r requirements.txt 5 | python3 -m analyze 6 | deactivate -------------------------------------------------------------------------------- /04-cloudformation/01-policy-validator/run-policy-validator.sh: -------------------------------------------------------------------------------- 1 | pip install cfn-policy-validator 2 | cfn-policy-validator validate --template-path template.json --region us-east-1 > findings.json -------------------------------------------------------------------------------- /04-cloudformation/01-policy-validator/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "MyQueue": { 4 | "Type": "AWS::SQS::Queue" 5 | }, 6 | "MyQueuePolicy": { 7 | "Type": "AWS::SQS::QueuePolicy", 8 | "Properties": { 9 | "PolicyDocument": { 10 | "Version": "2012-10-17", 11 | "Statement":[{ 12 | "Action": [ 13 | "sqs:SendMessage", 14 | "sqs:ReceiveMessages" 15 | ], 16 | "Effect": "Allow", 17 | "Resource": { 18 | "Fn::GetAtt": ["MyQueue", "Arn"] 19 | }, 20 | "Principal": { 21 | "AWS": [ 22 | "111122223333" 23 | ] 24 | } 25 | }] 26 | }, 27 | "Queues": [ 28 | { "Ref": "MyQueue" } 29 | ] 30 | } 31 | }, 32 | "MyAdministrativeRole": { 33 | "Type": "AWS::IAM::Role", 34 | "Properties": { 35 | "AssumeRolePolicyDocument": { 36 | "Statement": [ 37 | { 38 | "Effect": "Allow", 39 | "Principal": { 40 | "AWS": [ 41 | "111222333444", 42 | {"Ref": "AWS::AccountId"} 43 | ] 44 | }, 45 | "Action": [ 46 | "sts:AssumeRole" 47 | ] 48 | } 49 | ] 50 | }, 51 | "Policies": [ 52 | { 53 | "PolicyName": "root", 54 | "PolicyDocument": { 55 | "Version": "2012-10-17", 56 | "Statement": [ 57 | { 58 | "Effect": "Allow", 59 | "Action": ["s3:ListBuckets"], 60 | "Resource": ["arn:aws:s3:::bucket-name"] 61 | } 62 | ] 63 | } 64 | } 65 | ] 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project! 2 | 3 | This is a blank project for TypeScript development with CDK. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/bin/demo.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { DemoStack } from '../lib/demo-stack'; 5 | 6 | const app = new cdk.App(); 7 | new DemoStack(app, 'DemoStack', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | // env: { account: '123456789012', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); 22 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/demo.ts", 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 | "@aws-cdk/aws-lambda:recognizeVersionProps": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/lib/demo-stack.ts: -------------------------------------------------------------------------------- 1 | import * as iam from '@aws-cdk/aws-iam'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as sqs from '@aws-cdk/aws-sqs'; 4 | import { AccountPrincipal, AccountRootPrincipal, CompositePrincipal } from '@aws-cdk/aws-iam'; 5 | 6 | export class DemoStack extends cdk.Stack { 7 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 8 | super(scope, id, props); 9 | 10 | const myQueue = new sqs.Queue(this, 'MyQueue'); 11 | 12 | myQueue.addToResourcePolicy( 13 | new iam.PolicyStatement({ 14 | principals: [new iam.AnyPrincipal()], 15 | effect: iam.Effect.ALLOW, 16 | actions: ['sqs:SendMessage', 'sqs:ReceiveMessages'], 17 | resources: [myQueue.queueArn] 18 | }), 19 | ) 20 | 21 | const role = new iam.Role(this, 'MyRole', { 22 | assumedBy: new CompositePrincipal( 23 | new AccountPrincipal("111222333444"), 24 | new AccountRootPrincipal() 25 | ) 26 | }); 27 | 28 | role.addToPolicy(new iam.PolicyStatement({ 29 | effect: iam.Effect.ALLOW, 30 | actions: ['s3:ListBuckets'], 31 | resources: ['arn:aws:s3:::bucket-name'] 32 | })); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "02-cdk", 3 | "version": "0.1.0", 4 | "bin": { 5 | "02-cdk": "bin/02-cdk.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "1.108.0", 15 | "@types/jest": "^26.0.10", 16 | "@types/node": "10.17.27", 17 | "aws-cdk": "^1.126.0", 18 | "jest": "^26.4.2", 19 | "ts-jest": "^26.2.0", 20 | "ts-node": "^9.0.0", 21 | "typescript": "^3.9.10" 22 | }, 23 | "dependencies": { 24 | "@aws-cdk/aws-iam": "1.114.0", 25 | "@aws-cdk/aws-sqs": "1.114.0", 26 | "@aws-cdk/core": "1.114.0", 27 | "source-map-support": "^0.5.16" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/run-policy-validator.sh: -------------------------------------------------------------------------------- 1 | pip install cfn-policy-validator 2 | npm install 3 | cdk synth > synth.yml 4 | cfn-policy-validator validate --template-path synth.yml --region us-east-1 > findings.json -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/test/02-cdk.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as 02Cdk from '../lib/02-cdk-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new 02Cdk.02CdkStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /04-cloudformation/02-cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /05-scps/policies/policy1.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "DenyInternetGateways", 6 | "Effect": "Deny", 7 | "Action": [ 8 | "ec2:*InternetGateway*" 9 | ], 10 | "Resource": "*" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /05-scps/policies/policy2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "DenyInternetGateways", 6 | "Principal": "*", 7 | "Effect": "Deny", 8 | "Action": [ 9 | "iam:CreateRole" 10 | ], 11 | "NotResouce": "arn:aws:iam::123456789123:role/developers/*" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /05-scps/validate-scp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PAGER='cat' 4 | 5 | NUMBER_OF_ERRORS=0 6 | NUMBER_OF_WARNINGS=0 7 | 8 | for file in policies/*; do 9 | echo $file 10 | echo "Validating policy $file.." 11 | FINDINGS=$(aws accessanalyzer validate-policy --policy-document file://"$file" --policy-type SERVICE_CONTROL_POLICY) 12 | 13 | ERRORS=$(echo $FINDINGS | jq '.findings | map(select(.findingType == "ERROR" or .findingType == "SECURITY_WARNING"))') 14 | WARNINGS=$(echo $FINDINGS | jq '.findings | map(select(.findingType == "WARNING" or .findingType == "SUGGESTION"))') 15 | 16 | ERRORS_LENGTH=$(echo $ERRORS | jq '. | length') 17 | WARNINGS_LENGTH=$(echo $WARNINGS | jq '. | length') 18 | 19 | NUMBER_OF_ERRORS=$((NUMBER_OF_ERRORS + ERRORS_LENGTH)) 20 | NUMBER_OF_WARNINGS=$((NUMBER_OF_WARNINGS + WARNINGS_LENGTH)) 21 | 22 | echo $ERRORS | jq . 23 | done 24 | 25 | echo Errors: $NUMBER_OF_ERRORS, Warnings: $NUMBER_OF_WARNINGS -------------------------------------------------------------------------------- /06-service-specific/validate-all-api-gateways.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | #declare regions 4 | echo "Getting active regions for this AWS account" 5 | regions=$(aws ec2 describe-regions --query "Regions[*].RegionName" --output text) 6 | 7 | setopt SH_WORD_SPLIT #required for bash-like forloops in zsh 8 | 9 | echo "Beginning check of Rest API Gateways" 10 | echo "" 11 | echo "" 12 | 13 | export AWS_MAX_ATTEMPTS=8 #allow for aggressive retries/max attempts 14 | 15 | #iterate over regions, get a list of REST (v1) apis, and check. 16 | for region in $regions; 17 | do 18 | restapis=$(aws apigateway get-rest-apis --query "items[*].id" --output text --no-cli-pager --region $region) 19 | for restapi in $restapis; 20 | do 21 | policy=$(aws apigateway get-rest-api --rest-api-id $restapi --query "policy" --output text --no-cli-pager --region $region) 22 | echo "Checking Rest API ID $restapi in region $region..." 23 | if [[ $policy != "None" ]] #As policy on restapi is optional 24 | then 25 | policyjson=$(echo $policy | sed 's/\\//g') #remove slashes from API gateway policies so they're json 26 | output=$(aws accessanalyzer validate-policy --policy-type RESOURCE_POLICY --policy-document $policyjson --no-cli-pager --output json) 27 | else 28 | echo "No Resource Policy On Restapi $restapi" 29 | fi 30 | 31 | if [[ -z $output ]] || [[ -z $(echo $output | jq ".[][]") ]] 32 | then 33 | echo "No Findings From Access Analyzer for $restapi" 34 | else 35 | echo "findings for $restapi" 36 | echo "$output" 37 | output="" 38 | fi 39 | echo "------------------" 40 | echo "" 41 | done 42 | done -------------------------------------------------------------------------------- /06-service-specific/validate-all-sns-topics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | #declare regions 4 | echo "Getting active regions for this AWS account" 5 | regions=$(aws ec2 describe-regions --query "Regions[*].RegionName" --output text) 6 | 7 | setopt SH_WORD_SPLIT #required for bash-like forloops in zsh 8 | 9 | echo "Beginning check of SNS topics" 10 | echo "" 11 | echo "" 12 | 13 | export AWS_MAX_ATTEMPTS=8 #allow for aggressive retries/max attempts 14 | 15 | #iterate over regions, get a list of topics, and evaluate send them to access analyzer 16 | for region in $regions; 17 | do 18 | topics=$(aws sns list-topics --query "Topics[*].TopicArn" --output text --region $region --no-cli-pager) 19 | for topicArn in $topics; 20 | do 21 | policy=$(aws sns get-topic-attributes --topic-arn $topicArn --query Attributes.Policy --output text --region $region --no-cli-pager) 22 | echo "Checking topic ARN $topicArn in region $region..." 23 | output=$(aws accessanalyzer validate-policy --policy-type RESOURCE_POLICY --policy-document $policy --no-cli-pager --output json --no-cli-pager ) 24 | echo "$output" 25 | if [[ -z $(echo $output | jq ".[][]") ]] 26 | then 27 | echo "No Findings From Access Analyzer for $topicArn" 28 | fi 29 | echo "------------------" 30 | echo "" 31 | done 32 | done 33 | -------------------------------------------------------------------------------- /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 | # AWS IAM Access Analyzer Samples 2 | 3 | > :warning: **NOTE:** This repository contains example code and is intended for educational purposes __only__. This code should __not__ be used in production environments. :warning: 4 | 5 | --- 6 | 7 | Welcome! This repository contains sample code used to demo the AWS IAM Access Analyzer APIs and how you can use them to automate your policy validation workflows. 8 | 9 | ## About Access Analyzer 10 | 11 | AWS IAM Access Analyzer helps you identify the resources in your organization and accounts, such as Amazon S3 buckets or IAM roles, that are shared with an external entity. This lets you identify unintended access to your resources and data, which is a security risk. 12 | 13 | Learn more about Access Analyzer here: https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html 14 | 15 | ### Policy Validation 16 | 17 | You can validate your policies using AWS IAM Access Analyzer policy checks. Access Analyzer validates your policy against IAM policy grammar and best practices. You can view policy validation check findings that include security warnings, errors, general warnings, and suggestions for your policy. These findings provide actionable recommendations that help you author policies that are functional and conform to security best practices. 18 | 19 | You can learn more about this feature here: https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-policy-validation.html 20 | 21 | --- 22 | 23 | ## Samples Included in this Repository 24 | 25 | Before walking through any of the below samples, please ensure that you are properly authenticated into the AWS CLI and are using AWS CLI v2. 26 | 27 | Using the Access Analyzer APIs: 28 | 1. [Validate Policy API(s)](#validate-policy-apis) 29 | 1. [Access Preview API(s)](#access-preview-apis) 30 | 31 | Automating Policy Validation with Access Analyzer: 32 | 1. [Scripted, IAM Policies that haven't been defined using Infrastructure as Code](#scripted-iam-policies-that-havent-been-defined-using-infrastructure-as-code) 33 | 1. [IAM Policies Defined using CloudFormation](#iam-policies-defined-using-cloudformation) 34 | 1. [Service Control Policies (SCP)](#service-control-policies-scp) 35 | 36 | ### Prequisites 37 | Before running the samples in this repository, you'll need the following: 38 | - An AWS Account 39 | - Access to the AWS IAM Access Analyzer APIs 40 | - [AWS CLI v2](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) - please make sure that you have authenticated into the AWS CLI 41 | - Python 3.6+ 42 | - [jq](https://stedolan.github.io/jq/download/) - a command line json processor tool 43 | - zsh for api gateway/sns topics scripts 44 | 45 | ### How to Run the Sample Code 46 | 47 | #### [Validate Policy API(s)](01-validate-policy/) 48 | 49 | The ValidatePolicy API requests the validation of a policy and returns a list of findings. The findings help you identify issues and provide actionable recommendations to resolve the issue and enable you to author functional policies that meet security best practices. The ValidatePolicy API supports identity policies, service control policies, and resource policies. 50 | 51 | The ValidatePolicy API supports resource policies from resources that are not currently supported by IAM Access Analyzer, such as SNS and API Gateway. 52 | 53 | **Learn More:** 54 | - [AWS API](https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_ValidatePolicy.html) 55 | - [AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/accessanalyzer/validate-policy.html) 56 | 57 | Let's get started! Open up the directory with `cd ./01-validate-policy`. 58 | 59 | 60 | **To Validate an Identity Policy, run:** 61 | ``` 62 | . ./validate-identity-policy.sh 63 | ``` 64 | 65 | **To Validate a Service Control Policy, run:** 66 | ``` 67 | . ./validate-scp.sh 68 | ``` 69 | 70 | **To Validate a Resource Policy, run:** 71 | ``` 72 | . ./validate-resource-policy.sh 73 | ``` 74 | 75 | #### [Access Preview API(s)](02-create-access-preview/) 76 | 77 | In addition to helping you identify resources that are shared with an external entity, AWS IAM Access Analyzer also enables you to preview Access Analyzer findings before deploying resource permissions so you can validate that your policy changes grant only intended public and cross-account access to your resource. This helps you start with intended external access to your resources. 78 | 79 | You can preview and validate public and cross-account access to your Amazon S3 buckets in the Amazon S3 console. You can also use Access Analyzer APIs to preview public and cross-account access for your Amazon S3 buckets, AWS KMS keys, IAM roles, Amazon SQS queues and Secrets Manager secrets by providing proposed permissions for your resource. 80 | 81 | Gathering these findings consists of 3 API calls: 82 | 1. Create Access Preview ([AWS API](https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_CreateAccessPreview.html)) ([AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/accessanalyzer/create-access-preview.html)) - launches creation of an access preview 83 | 1. Get Access Preview ([AWS API](https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_GetAccessPreview.html)) ([AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/accessanalyzer/get-access-preview.html)) - gets status of access preview 84 | 1. List Access Preview Findings ([AWS API](https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_ListAccessPreviewFindings.html)) ([AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/accessanalyzer/list-access-preview-findings.html)) - retrieves a list of findings from the access preview 85 | 86 | Let's get started! Open up the directory with `cd ./02-create-access-preview`. 87 | 88 | In Line 13 of `queue-policy.json`, replace `` with your AWS Account ID. 89 | 90 | 91 | **To Create an Access Preview, run:** 92 | ``` 93 | . ./create-access-preview.sh 94 | ``` 95 | 96 | **To Get an Access Preview Status, run:** 97 | ``` 98 | . ./get-access-preview.sh 99 | ``` 100 | 101 | **Once the `status` from the previous command shows as `"COMPLETED"` run the following command to list the Access Preview findings:** 102 | ``` 103 | . ./list-access-preview-findings.sh 104 | ``` 105 | 106 | 107 | #### [Scripted, IAM Policies that haven't been defined using Infrastructure as Code](03-no-iac/) 108 | 109 | In this example, we place all of our policies into a folder called `policies/` and use a Python script to orchestrate the IAM Access Analyzer API calls that will validate and return findings on the policies that we have created. The script relies on a specific directory structure to classify each policy when running CreateAccessPreview. 110 | 111 | Let's get started! Open up the directory with `cd ./03-no-iac`. 112 | 113 | **To Analyze our policies and return findings, run:** 114 | ``` 115 | python analyze.py 116 | ``` 117 | 118 | After running this command, you will see a list of findings and recommendations for remediation. 119 | 120 | #### [IAM Policies Defined using CloudFormation](04-cloudformation/) 121 | 122 | **Using the IAM Policy Validator tool**: In this example, we will demonstrate how to run automated policy validation on IAM policies defined in a CloudFormation template. To do this, we use the [IAM Policy Validator for AWS CloudFormation command line tool](https://github.com/awslabs/aws-cloudformation-iam-policy-validator). You can learn more about this tool and how to use it in the blog post [Validate IAM policies in CloudFormation templates using IAM Access Analyzer](https://aws.amazon.com/blogs/security/validate-iam-policies-in-cloudformation-templates-using-iam-access-analyzer/). 123 | 124 | Let's get started! Open up the directory with `cd ./04-cloudformation`. 125 | 126 | Each of the scripts that we will use in this section installs IAM Policy Validator with the following command: 127 | ``` 128 | pip install cfn-policy-validator 129 | ``` 130 | 131 | **... with AWS CloudFormation templates**: First, we'll show you how to use the IAM Policy Validator tool with a basic **AWS CloudFormation template**. 132 | 133 | Navigate to the `04-cloudformation/01-policy-validator` folder. In this folder, we've defined a CloudFormation template in `template.json`. The `run-policy-validator.sh` script first runs the `cfn-policy-validator validate` command on the template to validate the resources defined in the template using Access Analyzer and then outputs any findings to a new `findings.json` file. 134 | 135 | From the `01-policy-validator` folder, run the following command to test the IAM Policy Validator tool on a CloudFormation template: 136 | ``` 137 | . ./run-policy-validator.sh 138 | ``` 139 | 140 | From the `01-policy-validator` folder, open the `findings.json` file to inspect the findings produced. 141 | 142 | 143 | **... with an AWS CDK app**: Next, we'll show you how to use the IAM Policy Validator tool with an **AWS CDK application**. 144 | 145 | Navigate to the `04-cloudformation/02-cdk` folder. In this folder, the `run-policy-validator.sh` script first uses `cdk synth` to generate a CloudFormation template from the CDK code and then runs the same `cfn-policy-validator validate` command to validate the resources defined in the template using Access Analyzer and output any findings to a new `findings.json` file. 146 | 147 | From the `04-cloudformation/02-cdk` folder, run the folling command to test the IAM Policy Validator tool on an AWS CDK application: 148 | ``` 149 | . ./run-policy-validator.sh 150 | ``` 151 | From the `01-policy-validator` folder, open the `findings.json` file to inspect the findings produced. 152 | 153 | 154 | #### [Service Control Policies (SCP)](05-scps/) 155 | 156 | In this example, we will demonstrate how to run automated policy validation on our SCPs for an AWS Organization. We only have the option to run the Validate Policy API here. Our policies are stored in a folder named `policies/` 157 | 158 | Let's get started! Open up the directory with `cd ./05-scps`. 159 | 160 | **To validate our SCPs, run:** 161 | ``` 162 | . ./validate.scp.sh 163 | ``` 164 | 165 | #### [Service Specific Bulk Scripts](06-service-specific/) 166 | 167 | In this example we will demonstrate how you can use the AWS cli and Access Analyzer to perform a bulk scan of resource policies in an AWS account. This script has logic to enumerate all active AWS regions in an account, get a list of AWS API Gateways and SNS topics respectively, and check them against Access Analyzers Validate Policy API for any findings. 168 | 169 | SNS and API Gateway are given as examples as Access Analyzer does not currently support scanning these resources proactively, but their resource policies can be sent to access analyzer for policy validation. 170 | 171 | To runs navigate to the directory with the scripts `cd ./06-service-specific` and run them. 172 | 173 | These scripts may generate a lot of output, so the examples below use shell output redirection to write it to a file. 174 | 175 | If you have a large amount of resources in your AWS account, ensure that you do not have pagination explicitly disabled in your AWS cli configuration or environmental variables. 176 | 177 | 178 | **To Validate all SNS topic's policies in all regions in an AWS account, run:** 179 | ``` 180 | . ./validate-all-sns-topics.sh >> sns_topic_findings.txt 181 | ``` 182 | 183 | **To Validate all API Gateways policies in all regions in an AWS account, run:** 184 | ``` 185 | . ./validate-all-api-gateways.sh >> api_gateway_findings.txt 186 | ``` 187 | 188 | ## Security 189 | 190 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 191 | 192 | ## License 193 | 194 | This library is licensed under the MIT-0 License. See the LICENSE file. 195 | 196 | --------------------------------------------------------------------------------