AWSTemplateFormatVersion: "2010-09-09"
Description: >
  SynCloud scanner access for full AWS assessment. Creates SyndevScannerRole,
  which trusts the SynCloud AWS account (307987836206) to assume it only with
  your tenant-specific ExternalId. SynCloud can read cloud configuration,
  topology, metrics, billing, usage, security posture, compliance posture, and
  migration-readiness metadata. SynCloud cannot mutate resources. Direct
  business-data reads are explicitly denied below, while configuration reads
  remain available because they are required for assessment quality.

Parameters:
  ExternalId:
    Type: String
    NoEcho: true
    MinLength: 8
    Description: >
      The tenant ExternalId issued by SynCloud (for example syndev-<uuid>).
      This shared secret locks the role to your SynCloud tenant.

  SynCloudAccountId:
    Type: String
    Default: "307987836206"
    AllowedPattern: "^[0-9]{12}$"
    Description: SynCloud's AWS account allowed to assume the role. Do not change.

Resources:
  SyndevScannerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: SyndevScannerRole
      Description: Read access for the SynCloud scanner assessment pipeline.
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub "arn:aws:iam::${SynCloudAccountId}:root"
            Action: "sts:AssumeRole"
            Condition:
              StringEquals:
                "sts:ExternalId": !Ref ExternalId

      # Broad AWS-managed read policies. ReadOnlyAccess is the main primitive:
      # AWS maintains coverage as new services and read APIs launch.
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/ReadOnlyAccess"
        - "arn:aws:iam::aws:policy/AWSBillingReadOnlyAccess"
        - "arn:aws:iam::aws:policy/ServiceQuotasReadOnlyAccess"
        - "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"

      Policies:
        - PolicyName: SyndevAssessmentResidualRead
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              # Direct customer data reads are not needed for cloud assessment.
              # Keep configuration reads available: Lambda environment config,
              # ECS task definitions, CodeBuild project config, launch template
              # user data, and similar surfaces are assessment inputs.
              - Sid: DenyDirectCustomerDataReads
                Effect: Deny
                Action:
                  # S3 object contents. Bucket config, inventory, lifecycle,
                  # object metadata, and object listings remain available.
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetObjectTorrent"
                  - "s3:GetObjectVersionTorrent"
                  - "s3-object-lambda:GetObject"
                  - "s3-object-lambda:GetObjectVersion"
                  # S3 Select — SQL queries against object content.
                  - "s3:SelectObjectContent"
                  # Glacier archive payloads.
                  - "glacier:GetJobOutput"
                  # DynamoDB table rows and stream records.
                  - "dynamodb:BatchGetItem"
                  - "dynamodb:GetItem"
                  - "dynamodb:GetRecords"
                  - "dynamodb:GetShardIterator"
                  - "dynamodb:PartiQLSelect"
                  - "dynamodb:Query"
                  - "dynamodb:Scan"
                  # Queue/stream message payloads.
                  - "sqs:ReceiveMessage"
                  - "kinesis:GetRecords"
                  - "kinesis:GetShardIterator"
                  - "kinesis:SubscribeToShard"
                  # Query result payloads.
                  - "athena:GetQueryResults"
                  - "athena:GetQueryResultsStream"
                  # Code/package artifact downloads.
                  - "codeartifact:GetPackageVersionAsset"
                  - "codeartifact:ReadFromRepository"
                  - "codecommit:GetBlob"
                  - "codecommit:GetFile"
                  - "codecommit:GitPull"
                  - "ecr:GetDownloadUrlForLayer"
                  - "lambda:GetFunction"
                  - "lambda:GetLayerVersion"
                  # SSM parameter values. SSM document/config metadata remains
                  # available through ReadOnlyAccess.
                  - "ssm:GetParameter"
                  - "ssm:GetParameterHistory"
                  - "ssm:GetParameters"
                  - "ssm:GetParametersByPath"
                Resource: "*"

              # Keep the scoped Tier-3 in-DB onboarding secret path usable,
              # but deny all other Secrets Manager value reads.
              - Sid: DenyUnscopedSecretValueReads
                Effect: Deny
                Action:
                  - "secretsmanager:GetSecretValue"
                NotResource: "arn:aws:secretsmanager:*:*:secret:syndev/indb_collector/*"

              - Sid: DenyBatchSecretValueReads
                Effect: Deny
                Action:
                  - "secretsmanager:BatchGetSecretValue"
                Resource: "*"

              # Defense in depth. These are not needed for assessment and are
              # not expected from the managed read policies, but explicit Deny
              # keeps the customer boundary clear if policies change later.
              - Sid: DenyKmsPlaintextMaterial
                Effect: Deny
                Action:
                  - "kms:Decrypt"
                  - "kms:GenerateDataKey"
                  - "kms:GenerateDataKeyPair"
                  - "kms:GenerateDataKeyPairWithoutPlaintext"
                  - "kms:GenerateDataKeyWithoutPlaintext"
                  - "kms:ReEncryptFrom"
                  - "kms:ReEncryptTo"
                Resource: "*"

              # Read-shaped residuals and explicit coverage anchors. Some are
              # currently included in ReadOnlyAccess; keeping them here makes
              # customer review deterministic if AWS moves managed policy
              # coverage around.
              # Trusted Advisor + Support read-only. AWSSupportAccess was removed
              # because it includes support:CreateCase and other write actions.
              # These explicit actions cover everything needed for assessment
              # without the ability to open or modify support cases.
              - Sid: SupportAndTrustedAdvisorRead
                Effect: Allow
                Action:
                  - "support:DescribeCases"
                  - "support:DescribeServices"
                  - "support:DescribeSeverityLevels"
                  - "support:DescribeTrustedAdvisorCheckRefreshStatuses"
                  - "support:DescribeTrustedAdvisorCheckResult"
                  - "support:DescribeTrustedAdvisorChecks"
                  - "support:DescribeTrustedAdvisorCheckSummaries"
                  - "trustedadvisor:DescribeAccount"
                  - "trustedadvisor:DescribeAccountAccess"
                  - "trustedadvisor:DescribeCheckItems"
                  - "trustedadvisor:DescribeCheckRefreshStatuses"
                  - "trustedadvisor:DescribeCheckSummaries"
                  - "trustedadvisor:DescribeChecks"
                  - "trustedadvisor:DescribeNotificationPreferences"
                  - "trustedadvisor:DescribeOrganization"
                  - "trustedadvisor:DescribeOrganizationAccounts"
                  - "trustedadvisor:DescribeRecommendation"
                  - "trustedadvisor:DescribeRecommendationResources"
                  - "trustedadvisor:ListChecks"
                  - "trustedadvisor:ListOrganizationRecommendationAccounts"
                  - "trustedadvisor:ListOrganizationRecommendationResources"
                  - "trustedadvisor:ListOrganizationRecommendations"
                  - "trustedadvisor:ListRecommendationResources"
                  - "trustedadvisor:ListRecommendations"
                Resource: "*"

              - Sid: CloudWatchLogsInsightsQueries
                Effect: Allow
                Action:
                  - "logs:StartQuery"
                  - "logs:StopQuery"
                  - "logs:GetQueryResults"
                Resource: "*"

              - Sid: IAMServiceLastAccessed
                Effect: Allow
                Action:
                  - "iam:GenerateServiceLastAccessedDetails"
                  - "iam:GetServiceLastAccessedDetails"
                  - "iam:GetServiceLastAccessedDetailsWithEntities"
                Resource: "*"

              - Sid: AccessAnalyzerGeneratedPolicies
                Effect: Allow
                Action:
                  - "access-analyzer:GetGeneratedPolicy"
                  - "access-analyzer:ListPolicyGenerations"
                Resource: "*"

              - Sid: ResourceExplorerReads
                Effect: Allow
                Action:
                  - "resource-explorer-2:BatchGetView"
                  - "resource-explorer-2:GetDefaultView"
                  - "resource-explorer-2:GetIndex"
                  - "resource-explorer-2:GetView"
                  - "resource-explorer-2:ListIndexes"
                  - "resource-explorer-2:ListViews"
                  - "resource-explorer-2:Search"
                Resource: "*"

              - Sid: AccountContactReads
                Effect: Allow
                Action:
                  - "account:GetAccountInformation"
                  - "account:GetAlternateContact"
                  - "account:GetContactInformation"
                  - "account:GetPrimaryEmail"
                  - "account:ListRegions"
                Resource: "*"

              # Migration-readiness services. These are policy-side enabled
              # even where current scanner code has not consumed them yet.
              - Sid: MigrationReadinessReads
                Effect: Allow
                Action:
                  - "application-transformation:Get*"
                  - "application-transformation:List*"
                  - "discovery:Describe*"
                  - "discovery:Get*"
                  - "discovery:List*"
                  - "drs:Describe*"
                  - "drs:Get*"
                  - "drs:List*"
                  - "mgh:Describe*"
                  - "mgh:Get*"
                  - "mgh:List*"
                  - "mgn:Describe*"
                  - "mgn:Get*"
                  - "mgn:List*"
                  - "mgn:VerifyClientRoleForMgn"
                  - "migrationhub-orchestrator:Get*"
                  - "migrationhub-orchestrator:List*"
                  - "migrationhub-strategy:Get*"
                  - "migrationhub-strategy:List*"
                Resource: "*"

              # Scoped to the one namespace SynCloud creates for the optional
              # Tier-3 in-DB collector. No other customer secret is readable.
              - Sid: Tier3InDbCollectorSecretsRead
                Effect: Allow
                Action:
                  - "secretsmanager:GetSecretValue"
                Resource: "arn:aws:secretsmanager:*:*:secret:syndev/indb_collector/*"

Outputs:
  RoleArn:
    Description: Paste this back into SynCloud to finish connecting.
    Value: !GetAtt SyndevScannerRole.Arn

  ConnectedExternalId:
    Description: The ExternalId this role was locked to.
    Value: !Ref ExternalId
