AWSTemplateFormatVersion: '2010-09-09'
Description: >
  plainfra by Triont — Read-Only Cross-Account Infrastructure Role.
  Every permission is listed explicitly — no broad managed policies.
  Wildcard patterns (e.g. ec2:Describe*) are used only where ALL matching
  actions are safe metadata operations. An explicit Deny block prevents
  reading data contents, secrets, logs, and code under any circumstances.

Parameters:
  PlainfraAccountId:
    Type: String
    Default: '921514166437'
    Description: plainfra AWS Account ID — do not change this value
    AllowedPattern: '[0-9]{12}'

  ExternalId:
    Type: String
    Description: >
      Unique identifier generated by plainfra during signup — acts as a
      second factor so the role cannot be assumed without it.
    MinLength: 8

  AllowBillingConsoleRead:
    Type: String
    Default: 'false'
    AllowedValues: ['true', 'false']
    Description: >
      Set to true to allow plainfra to read AWS Billing console data.
      Cost Explorer access is included regardless.

  RoleName:
    Type: String
    Default: 'plainfra-ReadOnly'
    Description: >
      IAM role name to create. Customers leave this at the default.
      Parameterised so a separate dev-access stack can coexist with the
      default role inside the triont staging account without colliding.
    AllowedPattern: '[A-Za-z0-9+=,.@_-]{1,64}'

Conditions:
  EnableBillingConsoleRead: !Equals [!Ref AllowBillingConsoleRead, 'true']

Resources:
  PlainfraReadOnlyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref RoleName
      Description: >
        Read-only infrastructure discovery for plainfra by Triont.
        All permissions explicitly listed. No managed policies attached.
      MaxSessionDuration: 3600

      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: AllowPlainfraAssumeWithExternalId
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${PlainfraAccountId}:root'
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId

      Policies:

        # ── ALLOW: Infrastructure Metadata Discovery ──────────────────────
        # Wildcard Describe*/List*/Get* are used only for services where
        # every matching action returns infrastructure metadata — never
        # application data. The Deny policy below overrides any wildcards
        # that could touch actual data (s3:GetObject, lambda:GetFunction,
        # cloudformation:GetTemplate, etc.).
        - PolicyName: PlainfraInfrastructureDiscovery
          PolicyDocument:
            Version: '2012-10-17'
            Statement:

              # Compute, containers, serverless
              - Sid: ComputeContainersAndServerless
                Effect: Allow
                Action:
                  - ec2:Describe*
                  - elasticloadbalancing:Describe*
                  - autoscaling:Describe*
                  - application-autoscaling:Describe*
                  - ecs:Describe*
                  - ecs:List*
                  - eks:Describe*
                  - eks:List*
                  - ecr:Describe*
                  - ecr:List*
                  - ecr:GetRepositoryPolicy
                  - ecr:GetLifecyclePolicy
                  - ecr:GetRegistryPolicy
                  - lambda:List*
                  - lambda:Get*
                  - states:Describe*
                  - states:List*
                  - apprunner:Describe*
                  - apprunner:List*
                Resource: "*"

              # Storage and databases
              - Sid: DataStorageAndDatabases
                Effect: Allow
                Action:
                  - s3:Get*
                  - s3:List*
                  - elasticfilesystem:Describe*
                  - elasticfilesystem:List*
                  - backup:Describe*
                  - backup:Get*
                  - backup:List*
                  - rds:Describe*
                  - rds:List*
                  - dynamodb:Describe*
                  - dynamodb:List*
                  - elasticache:Describe*
                  - elasticache:List*
                  - redshift:Describe*
                Resource: "*"

              # Identity, security, compliance, secrets/encryption metadata
              - Sid: SecurityIdentityAndCompliance
                Effect: Allow
                Action:
                  - iam:Get*
                  - iam:List*
                  - iam:GenerateCredentialReport
                  - iam:GenerateServiceLastAccessedDetails
                  - cloudtrail:Describe*
                  - cloudtrail:Get*
                  - cloudtrail:List*
                  - cloudtrail:LookupEvents
                  - config:Describe*
                  - config:Get*
                  - config:List*
                  - guardduty:Describe*
                  - guardduty:Get*
                  - guardduty:List*
                  - securityhub:Describe*
                  - securityhub:Get*
                  - securityhub:List*
                  - access-analyzer:Get*
                  - access-analyzer:List*
                  - secretsmanager:ListSecrets
                  - secretsmanager:DescribeSecret
                  - secretsmanager:ListSecretVersionIds
                  - secretsmanager:GetResourcePolicy
                  - ssm:Describe*
                  - ssm:List*
                  - ssm:GetInventory
                  - ssm:GetInventorySchema
                  - kms:Describe*
                  - kms:Get*
                  - kms:List*
                Resource: "*"

              # Networking, CDN, observability, messaging, cost, governance
              - Sid: NetworkingObservabilityAndGovernance
                Effect: Allow
                Action:
                  - cloudfront:Get*
                  - cloudfront:List*
                  - cloudfront:DescribeFunction
                  - route53:Get*
                  - route53:List*
                  - route53domains:Get*
                  - route53domains:List*
                  - route53resolver:Get*
                  - route53resolver:List*
                  - acm:Describe*
                  - acm:Get*
                  - acm:List*
                  - wafv2:Get*
                  - wafv2:List*
                  - apigateway:GET
                  - cloudwatch:Describe*
                  - cloudwatch:Get*
                  - cloudwatch:List*
                  - logs:Describe*
                  - logs:List*
                  - xray:Get*
                  - xray:List*
                  - sns:Get*
                  - sns:List*
                  - sqs:Get*
                  - sqs:List*
                  - events:Describe*
                  - events:List*
                  - scheduler:Get*
                  - scheduler:List*
                  - kinesis:Describe*
                  - kinesis:List*
                  - firehose:Describe*
                  - firehose:List*
                  - cloudformation:Describe*
                  - cloudformation:List*
                  - cloudformation:Get*
                  - cloudformation:ValidateTemplate
                  - ce:Get*
                  - ce:List*
                  - cur:Describe*
                  - budgets:Describe*
                  - budgets:View*
                  - organizations:Describe*
                  - organizations:List*
                  - account:Get*
                  - account:List*
                  - support:Describe*
                  - trustedadvisor:Get*
                  - trustedadvisor:List*
                  - health:Describe*
                  - tag:Get*
                  - tag:Describe*
                Resource: "*"

        # ── DENY: Data, Secrets, Code, and Execution ──────────────────────
        # These denies override ANY Allow above. They prevent reading the
        # contents of your files, databases, logs, secrets, and code even
        # if a wildcard above technically matches the action.
        - PolicyName: PlainfraDataAccessDeny
          PolicyDocument:
            Version: '2012-10-17'
            Statement:

              # Cannot read file, record, stream, queue, or image contents
              - Sid: DenyDataContents
                Effect: Deny
                Action:
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetObjectTorrent
                  - s3:GetObjectVersionTorrent
                  - s3:SelectObjectContent
                  - dynamodb:GetItem
                  - dynamodb:BatchGetItem
                  - dynamodb:Query
                  - dynamodb:Scan
                  - dynamodb:GetRecords
                  - dynamodb:PartiQLSelect
                  - kinesis:GetRecords
                  - kinesis:GetShardIterator
                  - kinesis:SubscribeToShard
                  - sqs:ReceiveMessage
                  - ecr:GetDownloadUrlForLayer
                  - ecr:BatchGetImage
                Resource: "*"

              # Cannot read secrets, parameters, logs, or decrypt anything
              - Sid: DenySecretsLogsAndDecryption
                Effect: Deny
                Action:
                  - secretsmanager:GetSecretValue
                  - ssm:GetParameter
                  - ssm:GetParameters
                  - ssm:GetParametersByPath
                  - ssm:GetParameterHistory
                  - logs:GetLogEvents
                  - logs:FilterLogEvents
                  - logs:StartQuery
                  - logs:StopQuery
                  - logs:GetQueryResults
                  - logs:StartLiveTail
                  - kms:Decrypt
                  - kms:GenerateDataKey
                  - kms:GenerateDataKeyPair
                  - kms:GenerateDataKeyWithoutPlaintext
                Resource: "*"

              # Cannot download source code, templates, execution data,
              # invoke functions, access consoles, or run SQL
              - Sid: DenyCodeExecutionAndConsoleAccess
                Effect: Deny
                Action:
                  - lambda:InvokeFunction
                  - lambda:InvokeFunctionUrl
                  - lambda:GetFunction
                  - cloudformation:GetTemplate
                  - states:GetExecutionHistory
                  - ec2:GetConsoleOutput
                  - ec2:GetConsoleScreenshot
                  - ec2:GetPasswordData
                  - rds-data:ExecuteStatement
                  - rds-data:BatchExecuteStatement
                  - glacier:GetJobOutput
                  - glacier:InitiateJob
                Resource: "*"

              # Cannot read API Gateway API key values
              - Sid: DenyAPIKeyValueAccess
                Effect: Deny
                Action:
                  - apigateway:GET
                Resource:
                  - !Sub 'arn:aws:apigateway:*::/apikeys'
                  - !Sub 'arn:aws:apigateway:*::/apikeys/*'

        # ── OPTIONAL: Billing Console ─────────────────────────────────────
        - !If
          - EnableBillingConsoleRead
          - PolicyName: PlainfraBillingConsoleRead
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Sid: BillingConsoleRead
                  Effect: Allow
                  Action:
                    - aws-portal:ViewBilling
                    - aws-portal:ViewUsage
                    - billing:GetBillingData
                    - billing:GetBillingDetails
                    - billing:GetBillingNotifications
                    - billing:GetBillingPreferences
                    - billing:ListBillingViews
                  Resource: "*"
          - !Ref AWS::NoValue

      Tags:
        - Key: Purpose
          Value: plainfra Infrastructure Monitoring
        - Key: ManagedBy
          Value: Triont / plainfra
        - Key: Documentation
          Value: https://plainfra.com/security.html

Outputs:
  RoleArn:
    Description: Role ARN — paste this into plainfra to complete connection
    Value: !GetAtt PlainfraReadOnlyRole.Arn

  AccountId:
    Description: This AWS Account ID
    Value: !Ref AWS::AccountId

  ExternalId:
    Description: ExternalId configured on this role
    Value: !Ref ExternalId

  BillingConsoleReadEnabled:
    Description: Whether optional billing console permissions were included
    Value: !Ref AllowBillingConsoleRead
