AWSTemplateFormatVersion: "2010-09-09"
Description: >
  PostureRadar read-only scanning role. Deploy this in your own AWS account
  to let PostureRadar run read-only security posture checks: public
  exposure (S3, EBS/AMI/RDS snapshots, RDS instances), identity & access
  (stale IAM keys, missing MFA, root account risks, IAM Identity Center
  coverage), excessive privilege (AdministratorAccess attached directly
  or via group membership, roles any AWS account can assume), network
  exposure (security groups open to the internet), instance hardening
  (IMDSv1), encryption (unencrypted EBS volumes), logging (CloudTrail
  gaps), and threat detection (GuardDuty findings). This role has no
  write/delete permissions of any kind. Opened from a PostureRadar
  deploy link, this stack also registers the role with PostureRadar
  automatically (Custom::PostureRadarRegister). The RoleArn output is
  still there as a fallback.

Parameters:
  ExternalId:
    Type: String
    NoEcho: true
    MinLength: 1
    Description: >
      The unique External ID PostureRadar gave you during signup. Required
      so only your PostureRadar account (not anyone else who might guess
      this role's ARN) can assume this role -- see the "confused deputy"
      problem in AWS's cross-account access docs.
  CustomerId:
    Type: String
    Default: ""
    Description: >
      Pre-filled by PostureRadar's deploy link. Leave as-is so the stack
      can register itself; safe to leave blank if you deployed this
      template by hand.
  ActivationToken:
    Type: String
    NoEcho: true
    Default: ""
    Description: >
      Pre-filled by PostureRadar's deploy link. Leave as-is so the stack
      can register itself; safe to leave blank if you deployed this
      template by hand.

Resources:
  PostureRadarScanRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: PostureRadarScanRole
      Description: Read-only role scanned by PostureRadar (postureradar.com).
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              # PostureRadar's AWS account. Scoped to this account root +
              # the ExternalId condition below (the standard pattern for
              # third-party cross-account access), rather than a specific
              # IAM role ARN, so this trust policy doesn't need updating
              # if PostureRadar's internal role names ever change.
              AWS: "arn:aws:iam::028231500017:root"
            Action: "sts:AssumeRole"
            Condition:
              StringEquals:
                "sts:ExternalId": !Ref ExternalId
      Policies:
        - PolicyName: PostureRadarReadOnly
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "s3:ListAllMyBuckets"
                  - "s3:GetBucketAcl"
                  - "s3:GetBucketPolicyStatus"
                  - "s3:GetBucketPublicAccessBlock"
                  - "s3control:GetPublicAccessBlock"
                  - "iam:ListUsers"
                  - "iam:ListRoles"
                  - "iam:ListAccessKeys"
                  - "iam:GetLoginProfile"
                  - "iam:ListMFADevices"
                  - "iam:GetAccountSummary"
                  - "iam:ListAttachedUserPolicies"
                  - "iam:ListAttachedRolePolicies"
                  - "iam:ListGroupsForUser"
                  - "iam:ListAttachedGroupPolicies"
                  - "ec2:DescribeVolumes"
                  - "ec2:DescribeSecurityGroups"
                  - "ec2:DescribeRegions"
                  - "ec2:DescribeSnapshots"
                  - "ec2:DescribeImages"
                  - "ec2:DescribeInstances"
                  - "ec2:GetManagedPrefixListEntries"
                  - "guardduty:ListDetectors"
                  - "guardduty:ListFindings"
                  - "guardduty:GetFindings"
                  - "rds:DescribeDBInstances"
                  - "rds:DescribeDBSnapshots"
                  - "rds:DescribeDBSnapshotAttributes"
                  - "rds:DescribeDBClusterSnapshots"
                  - "rds:DescribeDBClusterSnapshotAttributes"
                  - "cloudtrail:DescribeTrails"
                  - "cloudtrail:GetTrailStatus"
                  # The boto3/service endpoint is "sso-admin", but the IAM
                  # action namespace is "sso" -- confirmed live: granting
                  # sso-admin:ListInstances first still 403'd with "no
                  # identity-based policy allows the sso:ListInstances
                  # action," which is what AWS itself expects here.
                  - "sso:ListInstances"
                  - "sts:GetCallerIdentity"
                Resource: "*"

  # Best-effort registration: the signup Lambda always reports SUCCESS
  # back to CloudFormation so a PostureRadar outage cannot roll this
  # stack back. If registration does not land, the customer can still
  # connect from checkout with their 12-digit account ID.
  # ServiceToken is invoked cross-account; posture-radar-signup's
  # resource policy must allow lambda:InvokeFunction from
  # cloudformation.amazonaws.com (statement-id cfn-custom-resource;
  # applied and converged by scripts/deploy.py).
  PostureRadarRegistration:
    Type: Custom::PostureRadarRegister
    DependsOn: PostureRadarScanRole
    Properties:
      ServiceToken: arn:aws:lambda:us-east-2:028231500017:function:posture-radar-signup
      RoleArn: !GetAtt PostureRadarScanRole.Arn
      ExternalId: !Ref ExternalId
      CustomerId: !Ref CustomerId
      ActivationToken: !Ref ActivationToken

Outputs:
  RoleArn:
    Description: Fallback if auto-register doesn't land -- paste this Role ARN on the PostureRadar checkout page, or just enter the 12-digit AWS account ID.
    Value: !GetAtt PostureRadarScanRole.Arn
  ExternalIdUsed:
    Description: The External ID this role was configured with (confirm it matches what PostureRadar gave you).
    Value: !Ref ExternalId
