AWSTemplateFormatVersion: '2010-09-09'
Description: >
  Cost Coach cross-account IAM role. Customers deploy this stack in their
  AWS payer/management account to grant Cost Coach read-only billing access
  via STS AssumeRole with External ID (confused deputy protection).

Metadata:
  TemplateVersion: '3.0.0'
  Description: >
    Provisions a read-only IAM role for Cost Coach to access billing data
    (Cost Explorer, Budgets, Resource Tagging) in the customer's account.
    The role uses an External ID condition to prevent confused deputy attacks.
  Author: Cost Coach
  License: MIT

Parameters:
  ExternalId:
    Type: String
    MinLength: 20
    AllowedPattern: '[a-zA-Z0-9-]+'
    ConstraintDescription: >
      External ID must be at least 20 characters and contain only
      alphanumeric characters and hyphens.
    Description: >
      Unique identifier provided by Cost Coach during onboarding.
      This value is displayed in your Cost Coach dashboard after
      account connection. Keep it secure.

  CostCoachAccountId:
    Type: String
    Default: '816069129666'
    Description: >
      The AWS account ID of the Cost Coach service. This is the account
      that will assume the role. Do not change unless instructed.

  EnableEC2Scan:
    Type: String
    Default: 'false'
    AllowedValues: ['true', 'false']
    Description: >
      Enable idle resource scanning (EC2, EBS, EIP, ELB).
      Adds ec2:Describe* and elasticloadbalancing:Describe* permissions.

  EnableComputeOptimizer:
    Type: String
    Default: 'false'
    AllowedValues: ['true', 'false']
    Description: >
      Enable Compute Optimizer rightsizing recommendations.
      Adds compute-optimizer:Get* permissions.

Conditions:
  EC2ScanEnabled: !Equals [!Ref EnableEC2Scan, 'true']
  ComputeOptimizerEnabled: !Equals [!Ref EnableComputeOptimizer, 'true']

Resources:
  CostCoachRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'CostCoach-${AWS::StackName}'
      Description: >
        Read-only billing access for Cost Coach cost optimization service.
        This role grants access to Cost Explorer, Budgets, and Resource
        Tagging APIs only. No write permissions are included.
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${CostCoachAccountId}:root'
            Action: 'sts:AssumeRole'
            Condition:
              StringEquals:
                'sts:ExternalId': !Ref ExternalId
      Policies:
        - PolicyName: CostCoachBillingReadOnly
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              # Cost Explorer read-only actions (no wildcards)
              - Sid: CostExplorerReadOnly
                Effect: Allow
                Action:
                  - 'ce:GetCostAndUsage'
                  - 'ce:GetCostAndUsageWithResources'
                  - 'ce:GetCostForecast'
                  - 'ce:GetCostCategories'
                  - 'ce:GetDimensionValues'
                  - 'ce:GetAnomalies'
                  - 'ce:ListCostAllocationTags'
                  - 'ce:ListCostCategoryDefinitions'
                  - 'ce:DescribeCostCategoryDefinition'
                  - 'ce:GetReservationCoverage'
                  - 'ce:GetReservationUtilization'
                  - 'ce:GetRightsizingRecommendation'
                  - 'ce:GetSavingsPlansCoverage'
                  - 'ce:GetSavingsPlansUtilization'
                  - 'ce:GetSavingsPlansPurchaseRecommendation'
                  - 'ce:GetReservationPurchaseRecommendation'
                Resource: '*'

              # Budgets read-only actions
              - Sid: BudgetsReadOnly
                Effect: Allow
                Action:
                  - 'budgets:ViewBudget'
                  - 'budgets:DescribeBudget'
                  - 'budgets:DescribeBudgets'
                Resource: '*'

              # Resource Tagging read-only actions
              - Sid: ResourceTaggingReadOnly
                Effect: Allow
                Action:
                  - 'tag:GetTagKeys'
                  - 'tag:GetTagValues'
                  - 'tag:GetResources'
                Resource: '*'

              # Phase 10 (v1.1 Shared Savings v2): measurement read perms — Implements
              # PERM-01..04. Read-only Describe/Get/List actions only; no writes.
              # ec2:DescribeVolumes is duplicate-listed here AND in conditional
              # EC2ScanPolicy by design (IAM action union is harmless; v1.1 needs
              # storage measurement always-on per MEAS-04 even when
              # EnableEC2Scan='false'). See CONTEXT.md D-08 + RESEARCH Pitfall 1.
              # Counting note: 10 actions in this statement; 9 are newly granted
              # relative to v2.0.0 (DescribeVolumes is the duplicate-listed one).
              - Sid: Phase10MeasurementReadOnly
                Effect: Allow
                Action:
                  # CloudWatch — vCPU-hours utilization-aware (PERM-01)
                  - 'cloudwatch:GetMetricStatistics'
                  - 'cloudwatch:ListMetrics'
                  # EC2 — instance-type catalog + storage volumes (PERM-02)
                  - 'ec2:DescribeInstanceTypes'
                  - 'ec2:DescribeVolumes'
                  # RDS / Redshift / ElastiCache — instance + storage inventory (PERM-03)
                  - 'rds:DescribeDBInstances'
                  - 'rds:DescribeDBClusters'
                  - 'redshift:DescribeClusters'
                  - 'elasticache:DescribeCacheClusters'
                  - 'elasticache:DescribeReplicationGroups'
                  # S3 — bucket enumeration for storage measurement (PERM-04)
                  - 's3:ListAllMyBuckets'
                Resource: '*'

  EC2ScanPolicy:
    Type: AWS::IAM::Policy
    Condition: EC2ScanEnabled
    Properties:
      PolicyName: CostCoachEC2Scan
      Roles: [!Ref CostCoachRole]
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: EC2ScanReadOnly
            Effect: Allow
            Action:
              - 'ec2:DescribeVolumes'
              - 'ec2:DescribeAddresses'
              - 'ec2:DescribeInstances'
              - 'ec2:DescribeSnapshots'
              - 'elasticloadbalancing:DescribeLoadBalancers'
              - 'elasticloadbalancing:DescribeTargetGroups'
              - 'elasticloadbalancing:DescribeTargetHealth'
            Resource: '*'

  ComputeOptimizerPolicy:
    Type: AWS::IAM::Policy
    Condition: ComputeOptimizerEnabled
    Properties:
      PolicyName: CostCoachComputeOptimizer
      Roles: [!Ref CostCoachRole]
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ComputeOptimizerReadOnly
            Effect: Allow
            Action:
              - 'compute-optimizer:GetEC2InstanceRecommendations'
              - 'compute-optimizer:GetEnrollmentStatus'
            Resource: '*'

Outputs:
  RoleArn:
    Description: >
      IAM Role ARN to enter in your Cost Coach dashboard.
      Copy this value and paste it during account connection.
    Value: !GetAtt CostCoachRole.Arn
    Export:
      Name: !Sub '${AWS::StackName}-RoleArn'

  ExternalId:
    Description: >
      External ID used for this connection. Keep this value secure.
      It is required for Cost Coach to assume the role.
    Value: !Ref ExternalId
