|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Validate CloudFormation service role has sufficient permissions for IDP deployment |
| 4 | +""" |
| 5 | + |
| 6 | +import yaml |
| 7 | +import sys |
| 8 | +import os |
| 9 | + |
| 10 | +# Custom YAML loader that ignores CloudFormation intrinsic functions |
| 11 | +class CFNLoader(yaml.SafeLoader): |
| 12 | + pass |
| 13 | + |
| 14 | +def cfn_constructor(loader, tag_suffix, node): |
| 15 | + return None # Ignore CloudFormation functions |
| 16 | + |
| 17 | +# Register constructors for CloudFormation intrinsic functions |
| 18 | +CFNLoader.add_multi_constructor('!', cfn_constructor) |
| 19 | + |
| 20 | +def extract_aws_services_from_template(template_path): |
| 21 | + """Extract AWS services used in a CloudFormation template""" |
| 22 | + try: |
| 23 | + with open(template_path, 'r') as f: |
| 24 | + template = yaml.load(f, Loader=CFNLoader) |
| 25 | + |
| 26 | + services = set() |
| 27 | + if template and 'Resources' in template: |
| 28 | + for resource in template['Resources'].values(): |
| 29 | + if resource and 'Type' in resource: |
| 30 | + resource_type = resource['Type'] |
| 31 | + if resource_type and resource_type.startswith('AWS::'): |
| 32 | + service = resource_type.split('::')[1].lower() |
| 33 | + services.add(service) |
| 34 | + return services |
| 35 | + except Exception as e: |
| 36 | + print(f'Error parsing {template_path}: {e}') |
| 37 | + return set() |
| 38 | + |
| 39 | +def extract_permissions_from_role(role_template_path): |
| 40 | + """Extract permissions from CloudFormation service role template""" |
| 41 | + try: |
| 42 | + with open(role_template_path, 'r') as f: |
| 43 | + role_template = yaml.load(f, Loader=CFNLoader) |
| 44 | + |
| 45 | + permissions = set() |
| 46 | + if role_template and 'Resources' in role_template: |
| 47 | + for resource in role_template['Resources'].values(): |
| 48 | + if resource and resource.get('Type') == 'AWS::IAM::Role': |
| 49 | + policies = resource.get('Properties', {}).get('Policies', []) |
| 50 | + for policy in policies: |
| 51 | + statements = policy.get('PolicyDocument', {}).get('Statement', []) |
| 52 | + for statement in statements: |
| 53 | + actions = statement.get('Action', []) |
| 54 | + if isinstance(actions, str): |
| 55 | + actions = [actions] |
| 56 | + for action in actions: |
| 57 | + if '*' in action: |
| 58 | + service = action.split(':')[0] |
| 59 | + permissions.add(f'{service}:*') |
| 60 | + else: |
| 61 | + permissions.add(action) |
| 62 | + return permissions |
| 63 | + except Exception as e: |
| 64 | + print(f'Error parsing role template: {e}') |
| 65 | + return set() |
| 66 | + |
| 67 | +def main(): |
| 68 | + # Templates to check |
| 69 | + templates = [ |
| 70 | + 'template.yaml', # Main template |
| 71 | + 'patterns/pattern-1/template.yaml', |
| 72 | + 'patterns/pattern-2/template.yaml', |
| 73 | + 'patterns/pattern-3/template.yaml', |
| 74 | + 'options/bda-lending-project/template.yaml', |
| 75 | + 'options/bedrockkb/template.yaml' |
| 76 | + ] |
| 77 | + |
| 78 | + all_services = set() |
| 79 | + |
| 80 | + # Extract services from all templates |
| 81 | + for template_path in templates: |
| 82 | + if os.path.exists(template_path): |
| 83 | + services = extract_aws_services_from_template(template_path) |
| 84 | + all_services.update(services) |
| 85 | + print(f'{template_path}: {sorted(services)}') |
| 86 | + else: |
| 87 | + print(f'⚠️ Template not found: {template_path}') |
| 88 | + |
| 89 | + print(f'\nAll templates use services: {sorted(all_services)}') |
| 90 | + |
| 91 | + # Extract permissions from service role |
| 92 | + role_permissions = extract_permissions_from_role('iam-roles/cloudformation-management/IDP-Cloudformation-Service-Role.yaml') |
| 93 | + print(f'Service role has {len(role_permissions)} permissions') |
| 94 | + |
| 95 | + # Basic validation - check if role has broad permissions |
| 96 | + has_broad_permissions = any('*' in perm for perm in role_permissions) |
| 97 | + print(f'Service role has broad permissions: {has_broad_permissions}') |
| 98 | + |
| 99 | + if has_broad_permissions: |
| 100 | + print('✅ Service role appears to have sufficient permissions for deployment') |
| 101 | + return 0 |
| 102 | + else: |
| 103 | + print('⚠️ Service role may need additional permissions') |
| 104 | + return 0 # Don't fail the pipeline, just warn |
| 105 | + |
| 106 | +if __name__ == '__main__': |
| 107 | + sys.exit(main()) |
0 commit comments