AWSTemplateFormatVersion: '2010-09-09'
Description: Least-privilege read-only access for SpendShrink cost and resource analysis.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: SpendShrink connection security
        Parameters:
          - ExternalId
      - Label:
          default: Billing data fidelity
        Parameters:
          - BillingDataGranularity
    ParameterLabels:
      ExternalId:
        default: SpendShrink External ID
      BillingDataGranularity:
        default: FOCUS cost and usage time granularity

Parameters:
  ExternalId:
    Type: String
    NoEcho: true
    MinLength: 32
    MaxLength: 128
    AllowedPattern: '^[A-Za-z0-9_-]+$'
    ConstraintDescription: Use the External ID generated by the SpendShrink connection wizard.
  BillingDataGranularity:
    Type: String
    Default: DAILY
    AllowedValues:
      - DAILY
      - HOURLY
    Description: DAILY minimizes storage cost. HOURLY supports intraday usage analysis and creates larger exports.

Resources:
  SpendShrinkBillingDataBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerEnforced
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      Tags:
        - Key: SpendShrinkManaged
          Value: 'true'
        - Key: SpendShrinkPurpose
          Value: billing-data-source

  SpendShrinkBillingDataBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref SpendShrinkBillingDataBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: AllowAWSDataExportsDelivery
            Effect: Allow
            Principal:
              Service: bcm-data-exports.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub '${SpendShrinkBillingDataBucket.Arn}/spendshrink-focus/*'
            Condition:
              ArnLike:
                aws:SourceArn: !Sub 'arn:${AWS::Partition}:bcm-data-exports:us-east-1:${AWS::AccountId}:export/*'
              StringEquals:
                aws:SourceAccount: !Ref AWS::AccountId

  SpendShrinkFocusExport:
    Type: AWS::BCMDataExports::Export
    DependsOn: SpendShrinkBillingDataBucketPolicy
    Properties:
      Export:
        Name: spendshrink-focus-1-2
        Description: Customer-owned FOCUS 1.2 cost and usage data for read-only SpendShrink analysis.
        DataQuery:
          QueryStatement: SELECT * FROM FOCUS_1_2_AWS
          TableConfigurations:
            FOCUS_1_2_AWS:
              TIME_GRANULARITY: !Ref BillingDataGranularity
        DestinationConfigurations:
          S3Destination:
            S3Bucket: !Ref SpendShrinkBillingDataBucket
            S3BucketOwner: !Ref AWS::AccountId
            S3Prefix: spendshrink-focus
            S3Region: !Ref AWS::Region
            S3OutputConfigurations:
              Compression: GZIP
              Format: TEXT_OR_CSV
              OutputType: CUSTOM
              Overwrite: OVERWRITE_REPORT
        RefreshCadence:
          Frequency: SYNCHRONOUS
      Tags:
        - Key: SpendShrinkManaged
          Value: 'true'
        - Key: SpendShrinkPurpose
          Value: focus-cost-data

  SpendShrinkReadOnlyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: SpendShrinkReadOnlyRole
      Description: Read-only cost, recommendation, inventory, and utilization access for SpendShrink.
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: SpendShrinkAssumeRole
            Effect: Allow
            Principal:
              AWS: arn:aws:iam::537124977734:root
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId
      Policies:
        - PolicyName: SpendShrinkCostAndResourceReadOnly
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Sid: IdentityAndOrganization
                Effect: Allow
                Action:
                  - sts:GetCallerIdentity
                  - organizations:DescribeOrganization
                  - organizations:ListAccounts
                Resource: '*'
              - Sid: BillingAndOptimization
                Effect: Allow
                Action:
                  - ce:GetAnomalies
                  - ce:GetAnomalyMonitors
                  - ce:GetCostAndUsage
                  - ce:GetCostForecast
                  - ce:GetReservationPurchaseRecommendation
                  - ce:GetSavingsPlansPurchaseRecommendation
                  - bcm-data-exports:GetExecution
                  - bcm-data-exports:GetExport
                  - bcm-data-exports:ListExecutions
                  - bcm-data-exports:ListExports
                  - cost-optimization-hub:GetRecommendation
                  - cost-optimization-hub:ListRecommendations
                  - compute-optimizer:GetAutoScalingGroupRecommendations
                  - compute-optimizer:GetEBSVolumeRecommendations
                  - compute-optimizer:GetEC2InstanceRecommendations
                  - compute-optimizer:GetECSServiceRecommendations
                  - compute-optimizer:GetEffectiveRecommendationPreferences
                  - compute-optimizer:GetEnrollmentStatus
                  - compute-optimizer:GetLambdaFunctionRecommendations
                  - compute-optimizer:GetRDSDatabaseRecommendations
                Resource: '*'
              - Sid: ComputeInventory
                Effect: Allow
                Action:
                  - ec2:DescribeAddresses
                  - ec2:DescribeInstances
                  - ec2:DescribeRegions
                  - ec2:DescribeSpotPriceHistory
                  - ec2:DescribeVolumes
                  - autoscaling:DescribeAutoScalingGroups
                  - elasticloadbalancing:DescribeInstanceHealth
                  - elasticloadbalancing:DescribeLoadBalancers
                  - elasticloadbalancing:DescribeTargetGroups
                  - elasticloadbalancing:DescribeTargetHealth
                  - rds:DescribeDBInstances
                  - rds:DescribeReservedDBInstances
                Resource: '*'
              - Sid: ContainerInventory
                Effect: Allow
                Action:
                  - eks:DescribeCluster
                  - eks:DescribeFargateProfile
                  - eks:DescribeNodegroup
                  - eks:ListClusters
                  - eks:ListFargateProfiles
                  - eks:ListNodegroups
                  - ecr:DescribeImages
                  - ecr:DescribeRepositories
                  - ecr:GetLifecyclePolicy
                Resource: '*'
              - Sid: StorageInventory
                Effect: Allow
                Action:
                  - s3:GetBucketLocation
                  - s3:GetBucketTagging
                  - s3:GetBucketVersioning
                  - s3:GetLifecycleConfiguration
                  - s3:ListAllMyBuckets
                Resource: '*'
              - Sid: ReadSpendShrinkBillingData
                Effect: Allow
                Action:
                  - s3:GetObject
                Resource: !Sub '${SpendShrinkBillingDataBucket.Arn}/spendshrink-focus/*'
              - Sid: ListSpendShrinkBillingData
                Effect: Allow
                Action:
                  - s3:ListBucket
                Resource: !GetAtt SpendShrinkBillingDataBucket.Arn
                Condition:
                  StringLike:
                    s3:prefix:
                      - spendshrink-focus
                      - spendshrink-focus/*
              - Sid: UtilizationMetrics
                Effect: Allow
                Action:
                  - cloudwatch:GetMetricData
                  - cloudwatch:GetMetricStatistics
                  - cloudwatch:ListMetrics
                Resource: '*'

Outputs:
  RoleARN:
    Description: Paste this role ARN into the SpendShrink connection wizard.
    Value: !GetAtt SpendShrinkReadOnlyRole.Arn
  PolicyVersion:
    Description: SpendShrink connection policy version.
    Value: '2026-08-25'
  FocusExportArn:
    Description: AWS Data Exports ARN discovered automatically by SpendShrink.
    Value: !GetAtt SpendShrinkFocusExport.ExportArn
  FocusExportBucket:
    Description: Customer-owned, retained S3 bucket containing FOCUS cost and usage data.
    Value: !Ref SpendShrinkBillingDataBucket
  FocusExportPrefix:
    Description: S3 prefix containing the SpendShrink FOCUS export.
    Value: spendshrink-focus
