AWSTemplateFormatVersion: '2010-09-09'
Description: CID Data Collection - Role for Linked Account v3.14.1
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: 'Deployment parameters'
      Parameters:
        - DataCollectionAccountID
        - ResourcePrefix
        - MultiAccountRoleName
    - Label:
        default: 'Available modules'
      Parameters:
        - IncludeBudgetsModule
        - IncludeECSChargebackModule
        - IncludeInventoryCollectorModule
        - IncludeRDSUtilizationModule
        - IncludeEUCUtilizationModule
        - IncludeTAModule
        - IncludeSupportCasesModule
        - IncludeTransitGatewayModule
        - IncludeServiceQuotasModule
        - IncludeResilienceHubModule
        - IncludeMarketplaceModule
    ParameterLabels:
      DataCollectionAccountID:
        default: 'Data Collection Account ID'
      MultiAccountRoleName:
        default: 'Multi Account Role Name'
      ResourcePrefix:
        default: "Role Prefix"
      IncludeTAModule:
        default: 'Include AWS Trusted Advisor Data Collection Module'
      IncludeSupportCasesModule:
        default: 'Include AWS Support Cases Data Collection Module'
      IncludeInventoryCollectorModule:
        default: 'Include Inventory Collector Module'
      IncludeECSChargebackModule:
        default: 'Include ECS Chargeback Data Collection Module'
      IncludeRDSUtilizationModule:
        default: 'Include RDS Utilization Data Collection Module'
      IncludeEUCUtilizationModule:
        default: 'Include WorkSpaces Utilization Data Collection Module'
      IncludeBudgetsModule:
        default: 'Include Budgets Collection Module'
      IncludeTransitGatewayModule:
        default: 'Include Transit Gateway Module'
      IncludeServiceQuotasModule:
        default: 'Include Service Quotas Module'
      IncludeResilienceHubModule:
        default: 'Include Resilience Hub Module'
      IncludeMarketplaceModule:
        default: 'Include Marketplace Agreements Module'

Parameters:
  DataCollectionAccountID:
    Type: String
    Description: AccountId of where the collector is deployed
  MultiAccountRoleName:
    Type: String
    Description: The name of the IAM role that will be deployed from the management account to linked accounts as a read only role. KEEP THE SAME AS WHAT IS DEPLOYED INTO MANAGEMENT ACCOUNT
    Default: "Optimization-Data-Multi-Account-Role"
  ResourcePrefix:
    Type: String
    Description: This prefix will be placed in front of all roles created. Note you may wish to add a dash at the end to make more readable e.g. prefix-
    Default: "CID-DC-"
  IncludeTAModule:
    Type: String
    Description: Collects AWS Trusted Advisor recommendations data
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeSupportCasesModule:
    Type: String
    Description: Collects AWS Support Cases data
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeInventoryCollectorModule:
    Type: String
    Description: Collects data about AMIs, EBS volumes and snapshots
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeECSChargebackModule:
    Type: String
    Description: Collects data which shows costs associated with ECS Tasks leveraging EC2 instances within a Cluster
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeRDSUtilizationModule:
    Type: String
    Description: Collects RDS CloudWatch metrics from your accounts
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeEUCUtilizationModule:
    Type: String
    Description: Collects WorkSpaces CloudWatch metrics from your accounts
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeBudgetsModule:
    Type: String
    Description: Collects budgets from your accounts
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeTransitGatewayModule:
    Type: String
    Description: Collects TransitGateway from your accounts
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeServiceQuotasModule:
    Type: String
    Description: Collects Service Quotas from your accounts
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeResilienceHubModule:
    Type: String
    Description: Collects Resilience Hub data from your accounts
    AllowedValues: ['yes', 'no']
    Default: 'no'
  IncludeMarketplaceModule:
    Type: String
    Description: Collects Marketplace Agreement data from your accounts
    AllowedValues: ['yes', 'no']
    Default: 'no'

Conditions:
  IncludeTAModulePolicy:                 !Equals [!Ref IncludeTAModule, "yes"]
  IncludeSupportCasesModulePolicy:       !Equals [!Ref IncludeSupportCasesModule, "yes"]
  IncludeInventoryCollectorModulePolicy: !Equals [!Ref IncludeInventoryCollectorModule, "yes"]
  IncludeECSChargebackModulePolicy:      !Equals [!Ref IncludeECSChargebackModule, "yes"]
  IncludeRDSUtilizationModulePolicy:     !Equals [!Ref IncludeRDSUtilizationModule, "yes"]
  IncludeEUCUtilizationModulePolicy:     !Equals [!Ref IncludeEUCUtilizationModule, "yes"]
  IncludeBudgetsModulePolicy:            !Equals [!Ref IncludeBudgetsModule, "yes"]
  IncludeTransitGatewayModulePolicy:     !Equals [!Ref IncludeTransitGatewayModule, "yes"]
  IncludeServiceQuotasModulePolicy:      !Equals [!Ref IncludeServiceQuotasModule, "yes"]
  IncludeResilienceHubModulePolicy:      !Equals [!Ref IncludeResilienceHubModule, "yes"]
  IncludeMarketplaceModulePolicy:         !Equals [!Ref IncludeMarketplaceModule, "yes"]

Outputs:
  LambdaRole:
    Description: For lambda to assume in cost account
    Value: !GetAtt LambdaRole.Arn
Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${ResourcePrefix}${MultiAccountRoleName}"
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              AWS:
                - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:root"
            Condition:
              ForAnyValue:ArnEquals:
                aws:PrincipalArn:
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}budgets-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}ecs-chargeback-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}inventory-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}workspaces-metrics-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}rds-usage-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}transit-gateway-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}trusted-advisor-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}support-cases-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}service-quotas-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}resilience-hub-LambdaRole"
                  - !Sub "arn:${AWS::Partition}:iam::${DataCollectionAccountID}:role/${ResourcePrefix}marketplace-LambdaRole"
      Path: /
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W28
            reason: "Role must have an explicit RoleName for traceability"
  # Trusted Advisor policy
  TAPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeTAModulePolicy
    Properties:
      PolicyName: TAPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action:
              - "support:DescribeTrustedAdvisorChecks"
              - "support:DescribeTrustedAdvisorCheckResult"
              - "trustedadvisor:ListRecommendations"
              - "trustedadvisor:ListRecommendationResources"
              - "trustedadvisor:GetRecommendation"
              - "trustedadvisor:ListChecks"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # Support Cases policy
  SupportCasesPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeSupportCasesModulePolicy
    Properties:
      PolicyName: SupportCasesPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action:
              - "support:DescribeCases"
              - "support:DescribeCommunications"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # Budgets policy
  BudgetsReadOnlyPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeBudgetsModulePolicy
    Properties:
      PolicyName: BudgetsReadOnlyPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action:
              - "budgets:ViewBudget"
              - "budgets:ListTagsForResource"
            Resource: !Sub "arn:${AWS::Partition}:budgets::${AWS::AccountId}:budget/*"
      Roles:
        - Ref: LambdaRole
  # Inventory Collector policy - Split into resource-specific and global permissions
  InventoryCollectorPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeInventoryCollectorModulePolicy
    Properties:
      PolicyName: InventoryCollectorPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # Actions that support resource-level permissions
          - Effect: "Allow"
            Action:
              - "rds:DescribeDBClusters"
            Resource: !Sub "arn:${AWS::Partition}:rds:*:${AWS::AccountId}:cluster:*"
          - Effect: "Allow"
            Action:
              - "rds:DescribeDBInstances"
            Resource: !Sub "arn:${AWS::Partition}:rds:*:${AWS::AccountId}:db:*"
          - Effect: "Allow"
            Action:
              - "rds:DescribeDBSnapshots"
            Resource:
              - !Sub "arn:${AWS::Partition}:rds:*:${AWS::AccountId}:snapshot:*"
              - !Sub "arn:${AWS::Partition}:rds:*:${AWS::AccountId}:db:*"
          - Effect: "Allow"
            Action:
              - "es:DescribeDomain"
              - "es:DescribeElasticsearchDomains"
            Resource: !Sub "arn:${AWS::Partition}:es:*:${AWS::AccountId}:domain/*"
          - Effect: "Allow"
            Action:
              - "elasticache:DescribeCacheClusters"
            Resource: !Sub "arn:${AWS::Partition}:elasticache:*:${AWS::AccountId}:cluster:*"
          - Effect: "Allow"
            Action:
              - "eks:DescribeCluster"
            Resource: !Sub "arn:${AWS::Partition}:eks:*:${AWS::AccountId}:cluster/*"
          - Effect: "Allow"
            Action:
              - "eks:DescribeNodegroup"
            Resource: !Sub "arn:${AWS::Partition}:eks:*:${AWS::AccountId}:nodegroup/*/*/*"
          # Actions that require wildcard resources with account restriction
          - Effect: "Allow"
            Action:
              - "ec2:DescribeImages"
              - "ec2:DescribeVolumes"
              - "ec2:DescribeSnapshots"
              - "ec2:DescribeInstances"
              - "ec2:DescribeVpcs"
              - "ec2:DescribeRegions"
              - "ec2:DescribeNetworkInterfaces"
              - "es:ListDomainNames"
              - "eks:ListClusters"
              - "eks:ListNodegroups"
              - "lambda:ListFunctions"
              - "workspaces:DescribeWorkspaces"
              - "workspaces:DescribeWorkspaceDirectories"
              - "workspaces:DescribeWorkspacesConnectionStatus"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # ECS Chargeback policy - Split into resource-specific and global permissions
  ECSChargebackPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeECSChargebackModulePolicy
    Properties:
      PolicyName: ECSChargebackPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # Actions that support resource-level permissions
          - Effect: "Allow"
            Action:
              - "ecs:DescribeServices"
            Resource: !Sub "arn:${AWS::Partition}:ecs:*:${AWS::AccountId}:service/*"
          # Actions that don't support resource-level permissions
          - Effect: "Allow"
            Action:
              - "ecs:ListServices"
              - "ecs:ListClusters"
              - "ec2:DescribeRegions"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # RDS Utilization policy - Split into resource-specific and global permissions
  RDSUtilizationPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeRDSUtilizationModulePolicy
    Properties:
      PolicyName: RDSUtilizationPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # Actions that support resource-level permissions
          - Effect: "Allow"
            Action:
              - "rds:DescribeDBInstances"
            Resource: !Sub "arn:${AWS::Partition}:rds:*:${AWS::AccountId}:db:*"
          # Actions that don't support resource-level permissions
          - Effect: "Allow"
            Action:
              - "ec2:DescribeRegions"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
          - Effect: "Allow"
            Action:
              - "cloudwatch:GetMetricStatistics"
              - "cloudwatch:GetMetricData"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # EUC Utilization policy
  EUCUtilizationPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeEUCUtilizationModulePolicy
    Properties:
      PolicyName: EUCUtilizationPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # Actions that don't support resource-level permissions
          - Effect: "Allow"
            Action:
              - "workspaces:DescribeWorkspaces"
              - "ec2:DescribeRegions"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
          - Effect: "Allow"
            Action:
              - "cloudwatch:GetMetricStatistics"
              - "cloudwatch:GetMetricData"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # Transit Gateway policy
  TransitGatewayPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeTransitGatewayModulePolicy
    Properties:
      PolicyName: TransitGatewayPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action:
              - "ec2:DescribeTransitGatewayAttachments"
            Resource: '*' # Wildcard required as actions do not support resource-level permissions
          - Effect: "Allow"
            Action:
              - "cloudwatch:GetMetricStatistics"
              - "cloudwatch:GetMetricData"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # Service Quotas policy
  ServiceQuotasReadOnlyPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeServiceQuotasModulePolicy
    Properties:
      PolicyName: ServiceQuotasReadOnlyPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action:
              - "servicequotas:ListRequestedServiceQuotaChangeHistory"
              - "servicequotas:GetServiceQuota"
              - "servicequotas:GetAWSDefaultServiceQuota"
              - "servicequotas:ListServices"
              - "rds:DescribeAccountAttributes"
              - "elasticloadbalancing:DescribeAccountLimits"
              - "dynamodb:DescribeLimits"
              - "kinesis:DescribeLimits"
              - "cloudformation:DescribeAccountLimits"
              - "autoscaling:DescribeAccountLimits"
              - "route53:GetAccountLimit"
              - "datapipeline:GetAccountLimits"
            Resource: "*" # Wildcard required as actions do not support resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # Resilience Hub policy
  ResilienceHubPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeResilienceHubModulePolicy
    Properties:
      PolicyName: ResilienceHubPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: ResilienceHubRestricted
            Effect: Allow
            Action:
              - "resiliencehub:DescribeAppAssessment"
              - "resiliencehub:ListSopRecommendations"
              - "resiliencehub:ListAppComponentRecommendations"
              - "resiliencehub:ListAlarmRecommendations"
            Resource: !Sub "arn:${AWS::Partition}:resiliencehub:*:${AWS::AccountId}:app/*"
          - Sid: ResilienceHubNonRestricted
            Effect: Allow
            Action:
              - "resiliencehub:ListApps"
              - "resiliencehub:DescribeApp"
              - "resiliencehub:ListAppAssessments"
              - "resiliencehub:ListAppAssessmentComplianceDrifts"
              - "resiliencehub:ListTestRecommendations"
              - "resiliencehub:DescribeResiliencyPolicy"
              - "resiliencehub:ListAppVersionResources"
            Resource: "*" # Wildcard required as actions do not support   resource-level permissions
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of resources"
  # Marketplace Agreements policy
  AgreementsPolicy:
    Type: 'AWS::IAM::Policy'
    Condition: IncludeMarketplaceModulePolicy
    Properties:
      PolicyName: AgreementsPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "organizations:ListAccounts"
              - "organizations:DescribeOrganization"
            Resource: "*"
          - Effect: Allow
            Action:
              - "aws-marketplace:DescribeAgreement"
              - "aws-marketplace:DescribeEntity"
              - "aws-marketplace:GetAgreementTerms"
              - "aws-marketplace:GetProduct"
              - "aws-marketplace:ListAgreementCharges"
              - "aws-marketplace:ListEntities"
              - "aws-marketplace:SearchAgreements"
              - "aws-marketplace:SearchEntities"
              - "marketplace-agreement:DescribeAgreement"
              - "marketplace-agreement:GetAgreementTerms"
              - "marketplace-agreement:ListAgreementCharges"
              - "marketplace-agreement:SearchAgreements"
            Resource: "*"
      Roles:
        - Ref: LambdaRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W12
            reason: "Policy is used for scanning of a wide range of marketplace resources"
          - id: W3037
            reason: "GetProduct and SearchEntities are beta marketplace APIs not yet in cfn-lint validation"
      cfn-lint:
        config:
          ignore_checks:
            - W3037  # Beta marketplace APIs (GetProduct, SearchEntities, marketplace-agreement service) not yet in cfn-lint validation