AWSTemplateFormatVersion: '2010-09-09'
Description: 'Transilience Managed Compliance - Full Onboarding with SSM Write & Cost Explorer'

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Transilience Configuration"
        Parameters:
          - CustomerEmail
          - CustomerName
    ParameterLabels:
      CustomerEmail:
        default: "Your Email Address"
      CustomerName:
        default: "Your Company Name"

Parameters:
  CustomerEmail:
    Type: String
    Description: Email address for notifications and account identification
    AllowedPattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
    ConstraintDescription: Must be a valid email address
  
  CustomerName:
    Type: String
    Description: Your company or organization name
    MinLength: 2
    MaxLength: 100

  TransilienceBackendUrl:
    Type: String
    Default: "https://transilience-mcs-dev--api-server-api-server.modal.run/create"
    Description: Transilience backend API endpoint (do not modify)

Resources:
  #============================================================================
  # OIDC Identity Provider for Modal.com
  #============================================================================
  ModalOIDCProvider:
    Type: AWS::IAM::OIDCProvider
    Properties:
      Url: https://oidc.modal.com
      ClientIdList:
        - oidc.modal.com
      ThumbprintList:
        - "9e99a48a9960b14926bb7f3b02e22da2b0ab7280"
      Tags:
        - Key: Purpose
          Value: Transilience-Managed-Compliance
        - Key: CreatedBy
          Value: Transilience-CloudFormation

  #============================================================================
  # IAM Role for Transilience Access
  #============================================================================
  TransilienceComplianceRole:
    Type: AWS::IAM::Role
    DependsOn: ModalOIDCProvider
    Properties:
      RoleName: TransilienceComplianceRole
      Description: Role for Transilience Managed Compliance service to access AWS resources
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: AllowModalOIDCAssumeRole
            Effect: Allow
            Principal:
              Federated: !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/oidc.modal.com"
            Action: sts:AssumeRoleWithWebIdentity
            Condition:
              StringEquals:
                "oidc.modal.com:aud": "oidc.modal.com"
              StringLike:
                "oidc.modal.com:sub":
                  - "modal:workspace_id:ac-v1CZ9TWcqA7282i3kcFYa9:*"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/SecurityAudit
        - !Ref ECRAndS3ReadPolicy
        - !Ref LogsAndVPCReadPolicy
        - !Ref SecurityServicesReadPolicy
        - !Ref CloudTrailReadPolicy
        - !Ref SSMReadPolicy
        - !Ref SSMWritePolicy
        - !Ref CostExplorerPolicy
      Tags:
        - Key: Purpose
          Value: Transilience-Managed-Compliance
        - Key: CreatedBy
          Value: Transilience-CloudFormation
        - Key: CustomerEmail
          Value: !Ref CustomerEmail

  #============================================================================
  # Managed Policy 1: ECR + S3 General Read
  #============================================================================
  ECRAndS3ReadPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Transilience-ECR-S3-Read
      Description: ECR and S3 general read access for Transilience compliance
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ECRReadAccess
            Effect: Allow
            Action:
              - ecr:GetAuthorizationToken
              - ecr:BatchCheckLayerAvailability
              - ecr:GetDownloadUrlForLayer
              - ecr:BatchGetImage
              - ecr:DescribeRepositories
              - ecr:DescribeImages
              - ecr:DescribeImageScanFindings
              - ecr:ListImages
              - ecr:ListTagsForResource
              - ecr:GetRepositoryPolicy
              - ecr:GetLifecyclePolicy
              - ecr:GetLifecyclePolicyPreview
            Resource: "*"
          - Sid: S3BucketConfigRead
            Effect: Allow
            Action:
              - s3:GetBucketLocation
              - s3:GetBucketPolicy
              - s3:GetBucketPolicyStatus
              - s3:GetBucketAcl
              - s3:GetBucketVersioning
              - s3:GetBucketTagging
              - s3:GetBucketLogging
              - s3:GetBucketPublicAccessBlock
              - s3:GetAccountPublicAccessBlock
              - s3:GetEncryptionConfiguration
              - s3:GetBucketWebsite
              - s3:GetBucketCORS
              - s3:GetLifecycleConfiguration
              - s3:GetReplicationConfiguration
              - s3:GetBucketObjectLockConfiguration
              - s3:ListBucket
              - s3:ListBucketVersions
              - s3:ListAllMyBuckets
            Resource: "*"

  #============================================================================
  # Managed Policy 2: CloudWatch Logs + VPC & Network Read
  #============================================================================
  LogsAndVPCReadPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Transilience-Logs-VPC-Read
      Description: CloudWatch Logs and VPC/Network read access for Transilience compliance
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CloudWatchLogsRead
            Effect: Allow
            Action:
              - logs:DescribeLogGroups
              - logs:DescribeLogStreams
              - logs:GetLogEvents
              - logs:FilterLogEvents
              - logs:GetLogRecord
              - logs:GetQueryResults
              - logs:DescribeQueries
              - logs:DescribeMetricFilters
              - logs:DescribeSubscriptionFilters
              - logs:ListTagsLogGroup
              - logs:GetLogGroupFields
              - logs:DescribeExportTasks
              - logs:DescribeDestinations
              - logs:DescribeResourcePolicies
            Resource: "*"
          - Sid: VPCFlowLogsAndNetworkRead
            Effect: Allow
            Action:
              - ec2:DescribeFlowLogs
              - ec2:DescribeVpcs
              - ec2:DescribeSubnets
              - ec2:DescribeNetworkInterfaces
              - ec2:DescribeSecurityGroups
              - ec2:DescribeSecurityGroupRules
              - ec2:DescribeNetworkAcls
              - ec2:DescribeRouteTables
              - ec2:DescribeVpcEndpoints
              - ec2:DescribeVpcPeeringConnections
              - ec2:DescribeNatGateways
              - ec2:DescribeInternetGateways
              - ec2:DescribeEgressOnlyInternetGateways
              - ec2:DescribeTransitGateways
              - ec2:DescribeTransitGatewayAttachments
              - ec2:DescribeInstances
              - ec2:DescribeInstanceStatus
              - ec2:DescribeTags
            Resource: "*"

  #============================================================================
  # Managed Policy 3: Security Services Read (Inspector, SecHub, GuardDuty,
  #                    Macie, Access Analyzer, Detective)
  #============================================================================
  SecurityServicesReadPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Transilience-Security-Services-Read
      Description: Security services read access for Transilience compliance
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: InspectorRead
            Effect: Allow
            Action:
              - inspector2:ListFindings
              - inspector2:GetFindingsReportStatus
              - inspector2:ListCoverage
              - inspector2:ListCoverageStatistics
              - inspector2:ListAccountPermissions
              - inspector2:BatchGetAccountStatus
              - inspector2:BatchGetFreeTrialInfo
              - inspector2:DescribeOrganizationConfiguration
              - inspector2:GetConfiguration
              - inspector2:GetDelegatedAdminAccount
              - inspector2:GetEc2DeepInspectionConfiguration
              - inspector2:GetEncryptionKey
              - inspector2:GetMember
              - inspector2:GetSbomExport
              - inspector2:ListDelegatedAdminAccounts
              - inspector2:ListFilters
              - inspector2:ListFindingAggregations
              - inspector2:ListMembers
              - inspector2:ListTagsForResource
              - inspector2:ListUsageTotals
              - inspector2:SearchVulnerabilities
            Resource: "*"
          - Sid: SecurityHubRead
            Effect: Allow
            Action:
              - securityhub:GetFindings
              - securityhub:GetInsights
              - securityhub:GetInsightResults
              - securityhub:GetEnabledStandards
              - securityhub:GetSecurityControlDefinition
              - securityhub:DescribeHub
              - securityhub:DescribeStandards
              - securityhub:DescribeStandardsControls
              - securityhub:DescribeProducts
              - securityhub:DescribeActionTargets
              - securityhub:ListEnabledProductsForImport
              - securityhub:ListInvitations
              - securityhub:ListMembers
              - securityhub:ListTagsForResource
              - securityhub:ListFindingAggregators
              - securityhub:ListSecurityControlDefinitions
              - securityhub:ListStandardsControlAssociations
              - securityhub:BatchGetSecurityControls
              - securityhub:BatchGetStandardsControlAssociations
              - securityhub:GetConfigurationPolicy
              - securityhub:GetConfigurationPolicyAssociation
              - securityhub:ListAutomationRules
              - securityhub:ListConfigurationPolicies
              - securityhub:ListConfigurationPolicyAssociations
            Resource: "*"
          - Sid: GuardDutyRead
            Effect: Allow
            Action:
              - guardduty:GetFindings
              - guardduty:ListFindings
              - guardduty:ListDetectors
              - guardduty:GetDetector
              - guardduty:GetFindingsStatistics
              - guardduty:ListMembers
              - guardduty:GetMasterAccount
              - guardduty:GetAdministratorAccount
            Resource: "*"
          - Sid: MacieRead
            Effect: Allow
            Action:
              - macie2:GetFindings
              - macie2:ListFindings
              - macie2:DescribeBuckets
              - macie2:GetFindingStatistics
              - macie2:GetMacieSession
            Resource: "*"
          - Sid: AccessAnalyzerAndDetectiveRead
            Effect: Allow
            Action:
              - access-analyzer:List*
              - access-analyzer:Get*
              - detective:List*
              - detective:Get*
              - account:GetAlternateContact
            Resource: "*"

  #============================================================================
  # Managed Policy 4: CloudTrail Read
  #============================================================================
  CloudTrailReadPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Transilience-CloudTrail-Read
      Description: CloudTrail read access for Transilience compliance
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CloudTrailRead
            Effect: Allow
            Action:
              - cloudtrail:DescribeTrails
              - cloudtrail:GetTrail
              - cloudtrail:GetTrailStatus
              - cloudtrail:LookupEvents
              - cloudtrail:GetEventSelectors
              - cloudtrail:GetInsightSelectors
              - cloudtrail:ListTrails
              - cloudtrail:ListTags
              - cloudtrail:GetChannel
              - cloudtrail:ListChannels
              - cloudtrail:ListEventDataStores
              - cloudtrail:GetEventDataStore
              - cloudtrail:ListQueries
              - cloudtrail:DescribeQuery
              - cloudtrail:GetQueryResults
            Resource: "*"

  #============================================================================
  # Managed Policy 5: SSM Read-Only
  #============================================================================
  SSMReadPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Transilience-SSM-Read
      Description: SSM read-only access for Transilience compliance
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: SSMInstanceRead
            Effect: Allow
            Action:
              - ssm:DescribeInstanceInformation
              - ssm:DescribeInstanceProperties
              - ssm:GetConnectionStatus
            Resource: "*"
          - Sid: SSMInventoryRead
            Effect: Allow
            Action:
              - ssm:GetInventory
              - ssm:GetInventorySchema
              - ssm:ListInventoryEntries
              - ssm:DescribeInventoryDeletions
            Resource: "*"
          - Sid: SSMPatchRead
            Effect: Allow
            Action:
              - ssm:DescribeInstancePatches
              - ssm:DescribeInstancePatchStates
              - ssm:DescribeInstancePatchStatesForPatchGroup
              - ssm:DescribePatchBaselines
              - ssm:DescribePatchGroups
              - ssm:DescribePatchGroupState
              - ssm:DescribePatchProperties
              - ssm:DescribeAvailablePatches
              - ssm:GetPatchBaseline
              - ssm:GetPatchBaselineForPatchGroup
              - ssm:GetDeployablePatchSnapshotForInstance
            Resource: "*"
          - Sid: SSMCommandRead
            Effect: Allow
            Action:
              - ssm:ListCommands
              - ssm:ListCommandInvocations
              - ssm:GetCommandInvocation
            Resource: "*"
          - Sid: SSMSessionRead
            Effect: Allow
            Action:
              - ssm:DescribeSessions
            Resource: "*"
          - Sid: SSMDocumentRead
            Effect: Allow
            Action:
              - ssm:ListDocuments
              - ssm:ListDocumentVersions
              - ssm:DescribeDocument
              - ssm:DescribeDocumentPermission
              - ssm:GetDocument
            Resource: "*"
          - Sid: SSMParameterRead
            Effect: Allow
            Action:
              - ssm:GetParameter
              - ssm:GetParameters
              - ssm:GetParametersByPath
              - ssm:GetParameterHistory
              - ssm:DescribeParameters
            Resource: "*"
          - Sid: SSMAssociationRead
            Effect: Allow
            Action:
              - ssm:ListAssociations
              - ssm:ListAssociationVersions
              - ssm:DescribeAssociation
              - ssm:DescribeAssociationExecutions
              - ssm:DescribeAssociationExecutionTargets
              - ssm:DescribeEffectiveInstanceAssociations
            Resource: "*"
          - Sid: SSMAutomationRead
            Effect: Allow
            Action:
              - ssm:DescribeAutomationExecutions
              - ssm:GetAutomationExecution
              - ssm:DescribeAutomationStepExecutions
            Resource: "*"
          - Sid: SSMMaintenanceWindowRead
            Effect: Allow
            Action:
              - ssm:DescribeMaintenanceWindows
              - ssm:GetMaintenanceWindow
              - ssm:DescribeMaintenanceWindowTargets
              - ssm:DescribeMaintenanceWindowTasks
              - ssm:GetMaintenanceWindowTask
              - ssm:DescribeMaintenanceWindowExecutions
              - ssm:DescribeMaintenanceWindowExecutionTasks
              - ssm:GetMaintenanceWindowExecution
              - ssm:GetMaintenanceWindowExecutionTask
              - ssm:DescribeMaintenanceWindowSchedule
            Resource: "*"
          - Sid: SSMComplianceRead
            Effect: Allow
            Action:
              - ssm:ListResourceComplianceSummaries
              - ssm:ListComplianceItems
              - ssm:ListComplianceSummaries
            Resource: "*"

  #============================================================================
  # Managed Policy 6: SSM Write (Patch, Run Command, Inventory, etc.)
  #============================================================================
  SSMWritePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Transilience-SSM-Write
      Description: SSM write access for patching, run command, inventory, and scheduling
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: SSMRunCommandWrite
            Effect: Allow
            Action:
              - ssm:SendCommand
              - ssm:CancelCommand
            Resource: "*"
          - Sid: SSMSessionWrite
            Effect: Allow
            Action:
              - ssm:StartSession
              - ssm:TerminateSession
              - ssm:ResumeSession
            Resource: "*"
          - Sid: SSMPatchWrite
            Effect: Allow
            Action:
              - ssm:CreatePatchBaseline
              - ssm:DeletePatchBaseline
              - ssm:UpdatePatchBaseline
              - ssm:RegisterPatchBaselineForPatchGroup
              - ssm:DeregisterPatchBaselineForPatchGroup
            Resource: "*"
          - Sid: SSMMaintenanceWindowWrite
            Effect: Allow
            Action:
              - ssm:CreateMaintenanceWindow
              - ssm:DeleteMaintenanceWindow
              - ssm:UpdateMaintenanceWindow
              - ssm:RegisterTargetWithMaintenanceWindow
              - ssm:DeregisterTargetFromMaintenanceWindow
              - ssm:RegisterTaskWithMaintenanceWindow
              - ssm:DeregisterTaskFromMaintenanceWindow
            Resource: "*"
          - Sid: SSMInventoryWrite
            Effect: Allow
            Action:
              - ssm:PutInventory
              - ssm:DeleteInventory
            Resource: "*"
          - Sid: SSMAssociationWrite
            Effect: Allow
            Action:
              - ssm:CreateAssociation
              - ssm:CreateAssociationBatch
              - ssm:UpdateAssociation
              - ssm:DeleteAssociation
            Resource: "*"
          - Sid: SSMDocumentWrite
            Effect: Allow
            Action:
              - ssm:CreateDocument
              - ssm:DeleteDocument
              - ssm:UpdateDocument
              - ssm:UpdateDocumentDefaultVersion
            Resource: "*"
          - Sid: SSMParameterWrite
            Effect: Allow
            Action:
              - ssm:PutParameter
              - ssm:DeleteParameter
              - ssm:DeleteParameters
              - ssm:AddTagsToResource
              - ssm:RemoveTagsFromResource
              - ssm:ListTagsForResource
            Resource: "*"
          - Sid: IAMPassRoleForSSM
            Effect: Allow
            Action:
              - iam:PassRole
            Resource: !Sub "arn:aws:iam::${AWS::AccountId}:role/TransilienceComplianceRole"
            Condition:
              StringEquals:
                "iam:PassedToService": "ssm.amazonaws.com"

  #============================================================================
  # Managed Policy 7: Cost Explorer, Budgets & Billing
  #============================================================================
  CostExplorerPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Transilience-Cost-Explorer
      Description: Cost Explorer, Budgets, and Billing read access for Transilience
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CostExplorerRead
            Effect: Allow
            Action:
              - ce:GetCostAndUsage
              - ce:GetCostAndUsageWithResources
              - ce:GetCostForecast
              - ce:GetDimensionValues
              - ce:GetTags
              - ce:GetReservationCoverage
              - ce:GetReservationPurchaseRecommendation
              - ce:GetReservationUtilization
              - ce:GetRightsizingRecommendation
              - ce:GetSavingsPlansCoverage
              - ce:GetSavingsPlansPurchaseRecommendation
              - ce:GetSavingsPlansUtilization
              - ce:GetSavingsPlansUtilizationDetails
              - ce:GetAnomalies
              - ce:GetAnomalyMonitors
              - ce:GetAnomalySubscriptions
              - ce:ListCostAllocationTags
              - ce:ListCostCategoryDefinitions
              - ce:DescribeCostCategoryDefinition
            Resource: "*"
          - Sid: CostUsageReportRead
            Effect: Allow
            Action:
              - cur:DescribeReportDefinitions
              - cur:GetClassicReport
              - cur:GetUsageReport
            Resource: "*"
          - Sid: BudgetsRead
            Effect: Allow
            Action:
              - budgets:ViewBudget
              - budgets:DescribeBudgetAction
              - budgets:DescribeBudgetActionsForAccount
              - budgets:DescribeBudgetActionsForBudget
              - budgets:DescribeBudgetPerformanceHistory
            Resource: "*"
          - Sid: BillingRead
            Effect: Allow
            Action:
              - billing:GetBillingData
              - billing:GetBillingDetails
              - billing:GetBillingNotifications
              - billing:GetBillingPreferences
              - billing:GetContractInformation
              - billing:GetCredits
              - billing:GetIAMAccessPreference
              - billing:GetSellerOfRecord
              - billing:ListBillingViews
            Resource: "*"

  #============================================================================
  # Lambda Role for CloudTrail Bucket Discovery
  #============================================================================
  CloudTrailDiscoveryLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: TransilienceCloudTrailDiscoveryRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: CloudTrailDiscovery
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - cloudtrail:DescribeTrails
                  - cloudtrail:GetTrailStatus
                  - cloudtrail:ListTrails
                Resource: "*"
      Tags:
        - Key: Purpose
          Value: Transilience-CloudTrail-Discovery

  #============================================================================
  # Lambda Function to Discover CloudTrail Buckets
  #============================================================================
  CloudTrailDiscoveryFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: TransilienceCloudTrailDiscovery
      Description: Discovers CloudTrail S3 buckets for dynamic IAM policy
      Runtime: python3.12
      Handler: index.handler
      Role: !GetAtt CloudTrailDiscoveryLambdaRole.Arn
      Timeout: 30
      MemorySize: 128
      Code:
        ZipFile: |
          import json
          import boto3
          import cfnresponse

          def handler(event, context):
              print(f"Received event: {json.dumps(event)}")
              
              if event.get('RequestType') == 'Delete':
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
                  return
              
              PLACEHOLDER_BUCKET = "transilience-no-cloudtrail-buckets-found-placeholder"
              
              try:
                  client = boto3.client('cloudtrail')
                  
                  trails = client.describe_trails(includeShadowTrails=True).get('trailList', [])
                  
                  buckets = list(set([
                      t['S3BucketName'] 
                      for t in trails 
                      if 'S3BucketName' in t and t['S3BucketName']
                  ]))
                  
                  print(f"Discovered CloudTrail buckets: {buckets}")
                  
                  if buckets:
                      bucket_arns = [f"arn:aws:s3:::{b}" for b in buckets]
                      object_arns = [f"arn:aws:s3:::{b}/*" for b in buckets]
                  else:
                      print("No CloudTrail buckets found, using placeholder ARNs")
                      buckets = [PLACEHOLDER_BUCKET]
                      bucket_arns = [f"arn:aws:s3:::{PLACEHOLDER_BUCKET}"]
                      object_arns = [f"arn:aws:s3:::{PLACEHOLDER_BUCKET}/*"]
                  
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {
                      'Buckets': ','.join(buckets),
                      'BucketArns': ','.join(bucket_arns),
                      'ObjectArns': ','.join(object_arns),
                      'BucketCount': str(len(buckets)) if buckets[0] != PLACEHOLDER_BUCKET else '0'
                  })
                      
              except Exception as e:
                  print(f"Error discovering CloudTrail buckets: {str(e)}")
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {
                      'Buckets': PLACEHOLDER_BUCKET,
                      'BucketArns': f"arn:aws:s3:::{PLACEHOLDER_BUCKET}",
                      'ObjectArns': f"arn:aws:s3:::{PLACEHOLDER_BUCKET}/*",
                      'BucketCount': '0',
                      'Error': str(e)
                  })
      Tags:
        - Key: Purpose
          Value: Transilience-CloudTrail-Discovery

  #============================================================================
  # Custom Resource to Trigger CloudTrail Bucket Discovery
  #============================================================================
  CloudTrailBucketDiscovery:
    Type: Custom::CloudTrailBucketDiscovery
    Properties:
      ServiceToken: !GetAtt CloudTrailDiscoveryFunction.Arn
      Timestamp: !Sub "${AWS::StackName}-${AWS::Region}-discovery"

  #============================================================================
  # Inline Policy for CloudTrail S3 Bucket Access (dynamic, stays inline
  # because it depends on Lambda output and is small)
  #============================================================================
  CloudTrailS3AccessPolicy:
    Type: AWS::IAM::Policy
    DependsOn: CloudTrailBucketDiscovery
    Properties:
      PolicyName: TransilienceCloudTrailS3Access
      Roles:
        - !Ref TransilienceComplianceRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CloudTrailBucketAccess
            Effect: Allow
            Action:
              - s3:GetBucketLocation
              - s3:GetBucketAcl
              - s3:ListBucket
              - s3:GetBucketPolicy
              - s3:GetBucketTagging
            Resource: !Split [",", !GetAtt CloudTrailBucketDiscovery.BucketArns]
          - Sid: CloudTrailObjectAccess
            Effect: Allow
            Action:
              - s3:GetObject
              - s3:GetObjectVersion
              - s3:GetObjectTagging
            Resource: !Split [",", !GetAtt CloudTrailBucketDiscovery.ObjectArns]

  #============================================================================
  # Lambda Execution Role for the Registration Function
  #============================================================================
  RegistrationLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: TransilienceRegistrationLambdaRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Tags:
        - Key: Purpose
          Value: Transilience-Registration

  #============================================================================
  # Lambda Function to Register with Transilience Backend
  #============================================================================
  RegistrationFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: TransilienceRegistration
      Description: Registers AWS account with Transilience backend on stack creation
      Runtime: python3.12
      Handler: index.handler
      Role: !GetAtt RegistrationLambdaRole.Arn
      Timeout: 30
      MemorySize: 128
      Environment:
        Variables:
          BACKEND_URL: !Ref TransilienceBackendUrl
      Code:
        ZipFile: |
          import json
          import urllib.request
          import urllib.error
          import os
          import cfnresponse

          def handler(event, context):
              """
              CloudFormation Custom Resource handler.
              Sends account registration data to Transilience backend.
              """
              print(f"Received event: {json.dumps(event)}")
              
              request_type = event.get('RequestType', '')
              
              if request_type == 'Delete':
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {
                      'Message': 'Delete acknowledged - no action taken'
                  })
                  return
              
              try:
                  properties = event.get('ResourceProperties', {})
                  account_id = properties.get('AccountId')
                  role_arn = properties.get('RoleArn')
                  role_name = properties.get('RoleName')
                  customer_email = properties.get('CustomerEmail')
                  customer_name = properties.get('CustomerName')
                  stack_id = event.get('StackId', '')
                  region = properties.get('Region')
                  cloudtrail_buckets = properties.get('CloudTrailBuckets', '')
                  
                  payload = {
                      'account_id': account_id,
                      'role_arn': role_arn,
                      'role_name': role_name,
                      'customer_email': customer_email,
                      'customer_name': customer_name,
                      'stack_id': stack_id,
                      'region': region,
                      'cloudtrail_buckets': cloudtrail_buckets,
                      'request_type': request_type,
                      'source': 'cloudformation-onboarding'
                  }
                  
                  print(f"Sending payload to backend: {json.dumps(payload)}")
                  
                  backend_url = os.environ.get('BACKEND_URL')
                  
                  req = urllib.request.Request(
                      backend_url,
                      data=json.dumps(payload).encode('utf-8'),
                      headers={
                          'Content-Type': 'application/json',
                          'User-Agent': 'Transilience-CloudFormation-Onboarding/1.0'
                      },
                      method='POST'
                  )
                  
                  with urllib.request.urlopen(req, timeout=25) as response:
                      response_body = response.read().decode('utf-8')
                      print(f"Backend response: {response_body}")
                      
                      cfnresponse.send(event, context, cfnresponse.SUCCESS, {
                          'Message': 'Successfully registered with Transilience',
                          'AccountId': account_id,
                          'RoleArn': role_arn
                      })
                      
              except urllib.error.HTTPError as e:
                  error_body = e.read().decode('utf-8') if e.fp else str(e)
                  print(f"HTTP Error: {e.code} - {error_body}")
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {
                      'Message': f'Role created but registration failed: {error_body}',
                      'Warning': 'Please contact support@transilience.ai'
                  })
                  
              except Exception as e:
                  print(f"Error: {str(e)}")
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {
                      'Message': f'Role created but registration encountered an error: {str(e)}',
                      'Warning': 'Please contact support@transilience.ai'
                  })
      Tags:
        - Key: Purpose
          Value: Transilience-Registration

  #============================================================================
  # Custom Resource to Trigger Registration
  #============================================================================
  AccountRegistration:
    Type: Custom::TransilienceRegistration
    DependsOn:
      - TransilienceComplianceRole
      - ModalOIDCProvider
      - CloudTrailBucketDiscovery
    Properties:
      ServiceToken: !GetAtt RegistrationFunction.Arn
      AccountId: !Ref AWS::AccountId
      RoleArn: !GetAtt TransilienceComplianceRole.Arn
      RoleName: !Ref TransilienceComplianceRole
      CustomerEmail: !Ref CustomerEmail
      CustomerName: !Ref CustomerName
      Region: !Ref AWS::Region
      CloudTrailBuckets: !GetAtt CloudTrailBucketDiscovery.Buckets
      Timestamp: !Sub "${AWS::StackName}-${AWS::Region}"

#============================================================================
# Outputs
#============================================================================
Outputs:
  AccountId:
    Description: AWS Account ID that was onboarded
    Value: !Ref AWS::AccountId
    Export:
      Name: !Sub "${AWS::StackName}-AccountId"

  RoleArn:
    Description: ARN of the Transilience Compliance Role
    Value: !GetAtt TransilienceComplianceRole.Arn
    Export:
      Name: !Sub "${AWS::StackName}-RoleArn"

  RoleName:
    Description: Name of the Transilience Compliance Role
    Value: !Ref TransilienceComplianceRole
    Export:
      Name: !Sub "${AWS::StackName}-RoleName"

  OIDCProviderArn:
    Description: ARN of the Modal OIDC Provider
    Value: !GetAtt ModalOIDCProvider.Arn
    Export:
      Name: !Sub "${AWS::StackName}-OIDCProviderArn"

  CloudTrailBuckets:
    Description: Discovered CloudTrail S3 buckets
    Value: !GetAtt CloudTrailBucketDiscovery.Buckets

  CloudTrailBucketCount:
    Description: Number of CloudTrail buckets discovered
    Value: !GetAtt CloudTrailBucketDiscovery.BucketCount

  OnboardingStatus:
    Description: Status of the onboarding registration
    Value: !GetAtt AccountRegistration.Message

  PermissionsSummary:
    Description: Summary of permissions granted
    Value: "SecurityAudit, ECR, S3, CloudWatch Logs, VPC & Network, Inspector v2, Security Hub, GuardDuty, Macie, Access Analyzer, Detective, CloudTrail, CloudTrail S3 Buckets, SSM (Read+Write), Cost Explorer, Budgets, Billing"
