AWSTemplateFormatVersion: "2010-09-09"

Description: >-
  IdleLens cross-account reader role. Creates a single read-only IAM role
  that IdleLens assumes to scan this account for idle/unused resources.
  The role grants Describe*/List*/Get* actions ONLY -- it can never create,
  modify, stop, or delete anything. Deploy one stack per AWS account you
  want IdleLens to scan.

# ---------------------------------------------------------------------------
# Parameters
#
# Both values come from the IdleLens onboarding screen:
#   - TrustPrincipalArn: the IAM principal IdleLens uses to call
#     sts:AssumeRole. Today this is the consultant's local IAM user/role;
#     at M7 it becomes the hosted IdleLens service role. Passing it as a
#     parameter means the same template works for both without edits.
#   - ExternalId: a per-workspace secret generated once by IdleLens. It is
#     enforced in the trust policy Condition so a leaked role ARN alone is
#     not enough to assume the role (guards against the confused-deputy
#     problem -- see AWS IAM docs on cross-account roles + external IDs).
# ---------------------------------------------------------------------------
Parameters:
  TrustPrincipalArn:
    Type: String
    Description: >-
      ARN of the IdleLens principal allowed to assume this role
      (e.g. arn:aws:iam::<idlelens-account>:user/idlelens or a role ARN).
    AllowedPattern: "^arn:aws[a-zA-Z-]*:iam::[0-9]{12}:(user|role)/.+$"
    ConstraintDescription: must be a valid IAM user or role ARN.

  ExternalId:
    Type: String
    Description: >-
      The External ID shown on the IdleLens onboarding screen. Required on
      every AssumeRole call; do not share it outside IdleLens.
    MinLength: 8
    MaxLength: 256
    NoEcho: true

  RoleName:
    Type: String
    Description: Name for the created IAM role.
    Default: IdleLensReader

Resources:
  IdleLensReaderRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref RoleName
      Description: Read-only role assumed by IdleLens to scan for idle AWS resources.
      MaxSessionDuration: 3600
      # Trust policy: only TrustPrincipalArn may assume the role, and only
      # when it presents the matching ExternalId.
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Ref TrustPrincipalArn
            Action: "sts:AssumeRole"
            Condition:
              StringEquals:
                "sts:ExternalId": !Ref ExternalId
      Policies:
        - PolicyName: IdleLensReadOnly
          # Permissions policy: the exact Describe*/List*/Get* actions the
          # 17 IdleLens scanners + the live re-inspection step need. No
          # write, stop, or delete actions appear here -- by design.
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: IdleLensReadOnlyScan
                Effect: Allow
                Action:
                  # Identity probe run on every verify + scan.
                  - "sts:GetCallerIdentity"
                  # Cost Explorer (global service) -- powers the spend-by-service
                  # cost-context panel. Optional: IdleLens degrades gracefully
                  # when this is absent or member-account CE access is disabled.
                  - "ce:GetCostAndUsage"
                  # CloudWatch -- the evidence backbone for every metric-based finding.
                  - "cloudwatch:GetMetricStatistics"
                  - "cloudwatch:GetMetricData"
                  - "cloudwatch:ListMetrics"
                  # EC2 family: EIP, Public IPv4, EBS, snapshots, NAT GW,
                  # VPC interface endpoints, Transit Gateway attachments,
                  # stopped/idle instances, AMI lookups.
                  - "ec2:DescribeAddresses"
                  - "ec2:DescribeImages"
                  - "ec2:DescribeInstances"
                  - "ec2:DescribeNatGateways"
                  - "ec2:DescribeNetworkInterfaces"
                  - "ec2:DescribeSnapshots"
                  - "ec2:DescribeTransitGatewayAttachments"
                  - "ec2:DescribeVolumes"
                  - "ec2:DescribeVpcEndpoints"
                  - "ec2:DescribeRegions"
                  # Elastic Load Balancing v2: idle ALB/NLB detection.
                  - "elasticloadbalancing:DescribeLoadBalancers"
                  - "elasticloadbalancing:DescribeTargetGroups"
                  - "elasticloadbalancing:DescribeTargetHealth"
                  - "elasticloadbalancing:DescribeTags"
                  # AWS WAFv2: unassociated Web ACL detection, plus CloudFront
                  # association checks for CLOUDFRONT-scoped Web ACLs.
                  - "wafv2:ListWebACLs"
                  - "wafv2:GetWebACL"
                  - "wafv2:ListResourcesForWebACL"
                  - "wafv2:ListTagsForResource"
                  - "cloudfront:ListDistributionsByWebACLId"
                  # ElastiCache: node-based cluster / replication group idle detection.
                  - "elasticache:DescribeCacheClusters"
                  - "elasticache:DescribeReplicationGroups"
                  - "elasticache:ListTagsForResource"
                  # RDS: idle + stopped instance detection.
                  - "rds:DescribeDBInstances"
                  - "rds:ListTagsForResource"
                  # Lambda: idle functions + provisioned-concurrency waste.
                  - "lambda:ListFunctions"
                  - "lambda:ListEventSourceMappings"
                  - "lambda:ListProvisionedConcurrencyConfigs"
                  - "lambda:ListTags"
                  - "lambda:GetFunction"
                  - "lambda:GetPolicy"
                  # CloudWatch Logs: never-queried log-group detection.
                  - "logs:DescribeLogGroups"
                  - "logs:DescribeLogStreams"
                  - "logs:GetLogEvents"
                  - "logs:ListTagsLogGroup"
                  # ACM Private CA: idle private certificate authorities.
                  - "acm-pca:ListCertificateAuthorities"
                  - "acm-pca:DescribeCertificateAuthority"
                  - "acm-pca:ListTags"
                  # SageMaker: idle real-time endpoints.
                  - "sagemaker:ListEndpoints"
                  - "sagemaker:DescribeEndpoint"
                  - "sagemaker:DescribeEndpointConfig"
                  - "sagemaker:ListTags"
                  # OpenSearch Service: idle managed-cluster detection. The
                  # IAM prefix `es:` is inherited from the legacy Elasticsearch
                  # Service name; OpenSearch Serverless (`aoss:*`) is a
                  # different service and is out of scope.
                  - "es:ListDomainNames"
                  - "es:DescribeDomain"
                  - "es:DescribeDomains"
                  - "es:ListTags"
                  # Redshift: idle running + paused cluster detection.
                  # DescribeClusterSnapshots backs the paused-cluster managed-
                  # storage estimate (latest automated snapshot size is the
                  # only usage proxy available while a cluster is paused).
                  - "redshift:DescribeClusters"
                  - "redshift:DescribeClusterSnapshots"
                  - "redshift:DescribeTags"
                Resource: "*"

Outputs:
  RoleArn:
    Description: >-
      ARN of the IdleLensReader role. Paste this back into the IdleLens
      onboarding screen to finish connecting the account.
    Value: !GetAtt IdleLensReaderRole.Arn
