AWSTemplateFormatVersion: '2010-09-09'
Description: Main CloudFormation template that builds shared resources and modules stacks
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: 'Deployment parameters'
        Parameters:
          - DestinationBucket
          - ManagementAccountRole
          - ManagementAccountID
          - MultiAccountRoleName
          - Schedule
          - RolePrefix
          - CFNTemplateSourceBucket
      - Label:
          default: 'Available modules'
        Parameters:
          - IncludeTAModule
          - IncludeRightsizingModule
          - IncludeCostAnomalyModule
          - IncludeInventoryCollectorModule
          - IncludeComputeOptimizerModule
          - ComputeOptimizerRegions
          - IncludeECSChargebackModule
          - IncludeRDSUtilizationModule
          - IncludeOrgDataModule
          - IncludeTransitGatewayModule
    ParameterLabels:
      DestinationBucket:
        default: 'Destination S3 bucket'
      ManagementAccountRole:
        default: 'Management account role'
      ManagementAccountID:
        default: 'Comma Delimited list of Account IDs for all Management Account IDs'
      MultiAccountRoleName:
        default: 'Multi Account Role Name'
      Schedule:
        default: "Schedule swap to cron(0 8 1 * ? *) for 1st day of the month"
      RolePrefix:
        default: "Role Prefix"
      CFNTemplateSourceBucket:
        default: "DO NOT CHANGE - A bucket that contains WA-Labs CloudFormation templates. Must be 'aws-well-architected-labs'"
      IncludeTAModule:
        default: 'Include AWS Trusted Advisor Data Collection Module'
      IncludeRightsizingModule:
        default: 'Include Rightsizing Recommendations Data Collection Module'
      IncludeCostAnomalyModule:
        default: 'Include Cost Anomalies ightsizing Recommendations Data Collection Module'
      IncludeInventoryCollectorModule:
        default: 'Include Inventory Collector Module'
      IncludeComputeOptimizerModule:
        default: 'Include AWS Compute Optimizer Data Collection Module'
      ComputeOptimizerRegions:
        default: "Comma Delimited list of AWS regions where AWS Compute Optimizer data will be collected."
      IncludeECSChargebackModule:
        default: 'Include ECS Chargeback Data Collection Module'
      IncludeRDSUtilizationModule:
        default: 'Include RDS Utilization Data Collection Module'
      IncludeOrgDataModule:
        default: 'Include AWS Organization Data Collection Module'
      IncludeBudgetsModule:
        default: 'Include AWS Budgets Collection Module'
      IncludeTransitGatewayModule:
        default: 'Include AWS TransitGateway Collection Module'
Mappings:
  RegionMap:
       eu-west-1:        {CodeBucket: aws-well-architected-labs-ireland }
       us-east-2:        {CodeBucket: aws-well-architected-labs-ohio }
       us-east-1:        {CodeBucket: aws-well-architected-labs-virginia }
       us-west-1:        {CodeBucket: aws-well-architected-labs-california }
       us-west-2:        {CodeBucket: aws-well-architected-labs-oregon }
       ap-southeast-1:   {CodeBucket: aws-well-architected-labs-singapore }
       eu-central-1:     {CodeBucket: aws-well-architected-labs-frankfurt }
       eu-west-2:        {CodeBucket: aws-well-architected-labs-london }
       eu-north-1:       {CodeBucket: aws-well-architected-labs-stockholm }
       ap-southeast-2:   {CodeBucket: aws-well-architected-labs-ap-sydney }
       ap-south-1:       {CodeBucket: aws-well-architected-labs-mumbai }
       ap-northeast-3:   {CodeBucket: aws-well-architected-labs-osaka }
       ap-northeast-2:   {CodeBucket: aws-well-architected-labs-seoul }
       ap-northeast-1:   {CodeBucket: aws-well-architected-labs-tokyo }
       ca-central-1:     {CodeBucket: aws-well-architected-labs-canada }
       eu-west-3:        {CodeBucket: aws-well-architected-labs-paris }
       sa-east-1:        {CodeBucket: aws-well-architected-labs-san-paulo }
Parameters:
  DestinationBucket:
    Type: String
    Description: Name of the S3 Bucket that needs to be created to hold information
    AllowedPattern: (?=^.{3,63}$)(?!^(\d+\.)+\d+$)(^(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9\-])$)
    Default: costoptimizationdata
  ManagementAccountRole:
    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. DO NOT ENTER THE ROLE PREFIX HERE. KEEP THE SAME AS WHAT IS DEPLOYED INTO MANAGEMENT ACCOUNT.
    Default: Lambda-Assume-Role-Management-Account
  ManagementAccountID:
    Type: String
    AllowedPattern: ([a-z0-9\-, ]*?$)
    Description: "(Ex: 123456789,098654321,789054312) List of Payer IDs you wish to collect data for. Can just be one Accounts"
  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. DO NOT ENTER THE ROLE PREFIX HERE. KEEP THE SAME AS WHAT IS DEPLOYED INTO MANAGEMENT ACCOUNT.
    Default: "Optimization-Data-Multi-Account-Role"
  Schedule:
    Type: String
    Description: Cron job to trigger the lambda using cloudwatch event
    Default: "rate(14 days)"
  RolePrefix:
    Type: String
    Description: Keep this consistent with the prefix used for the Stacks and StackSets created as part of this lab. 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: "WA-"
  CFNTemplateSourceBucket:
    Type: String
    Description: "DO NOT CHANGE - A bucket that contains WA-Labs CloudFormation templates. Must be 'aws-well-architected-labs'"
    Default: "aws-well-architected-labs"
  IncludeTAModule:
    Type: String
    Description: Collects AWS Trusted Advisor recommendations data
    AllowedValues:
      - "yes"
      - "no"
  IncludeRightsizingModule:
    Type: String
    Description: "Collects AWS Cost Explorer Rightsizing Recommendations"
    AllowedValues:
      - "yes"
      - "no"
  IncludeCostAnomalyModule:
    Type: String
    Description: "Collects AWS Cost Explorer Cost Anomalies Recommendations"
    AllowedValues:
      - "yes"
      - "no"
  IncludeInventoryCollectorModule:
    Type: String
    Description: Collects data about AMIs, EBS volumes and snapshots  
    AllowedValues:
      - "yes"
      - "no"
  IncludeComputeOptimizerModule:
    Type: String
    Description: Collects AWS Compute Optimizer service recommendations
    AllowedValues:
      - "yes"
      - "no"
  ComputeOptimizerRegions:
    Default: ""
    Type: String
    Description: "Ex: us-east-1,us-east-2,us-west-1,us-west-2,eu-central-1,eu-west-1,eu-west-2,eu-west-3  if empty, the current region will be used. You can add regions later by updating the stack."
    AllowedPattern: ([a-z0-9\-, ]*?$)
  IncludeECSChargebackModule:
    Type: String
    Description: Collects data which shows costs associated with ECS Tasks leveraging EC2 instances within a Cluster
    AllowedValues:
      - "yes"
      - "no"
  IncludeRDSUtilizationModule:
    Type: String
    Description: Collects RDS CloudWatch metrics from your accounts
    AllowedValues:
      - "yes"
      - "no"
  IncludeOrgDataModule:
    Type: String
    Description: Collects AWS Organizations data such as account Id, account name, organization parent and specified tags
    AllowedValues:
      - "yes"
      - "no"
  IncludeBudgetsModule:
    Type: String
    Description: Collects AWS Budgets
    AllowedValues:
      - "yes"
      - "no"
  IncludeTransitGatewayModule:
    Type: String
    Description: Collects AWS TransitGateway data
    AllowedValues:
      - "yes"
      - "no"
Outputs:
  S3Bucket:
    Description: Name of S3 Bucket which will store the AWS Cost Explorer Rightsizing recommendations
    Value:
      Ref: S3Bucket
  S3BucketARN:
    Description: ARN of S3 Bucket which will store the AWS Cost Explorer Rightsizing recommendations
    Value:
      Fn::GetAtt:
        - S3Bucket
        - Arn
  RoleARN:
    Description: "The arn of the IAM role that deployed in the management account which can retrieve AWS Organization data"
    Value: !Sub "arn:aws:iam::${ManagementAccountID}:role/${ManagementAccountRole}"
  TaskQueuesUrl:
    Description: "SQS topics created for deployed modules"
    Value:
      Fn::Join:
        - ','
        - - !If [DeployTAModule, !Sub "${TrustedAdvisorModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
          - !If [DeployInventoryCollectorModule, !Sub "${InventoryCollectorModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
          - !If [DeployEcsChargebackModule, !Sub "${EcsChargebackModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
          - !If [DeployRDSUtilizationModule, !Sub "${RDSUtilizationModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
          - !If [DeployBudgetsModule, !Sub "${BudgetsModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
          - !If [DeployTransitGatewayModule, !Sub "${TransitGatewayModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
Conditions:
  DeployTAModule: !Equals
    - !Ref IncludeTAModule
    - "yes"
  DeployRightsizingModule: !Equals
    - !Ref IncludeRightsizingModule
    - "yes"
  DeployCostAnomalyModule: !Equals
    - !Ref IncludeCostAnomalyModule
    - "yes"
  DeployInventoryCollectorModule: !Equals
    - !Ref IncludeInventoryCollectorModule
    - "yes"
  DeployComputeOptimizerModule: !Equals
    - !Ref IncludeComputeOptimizerModule
    - "yes"
  DeployEcsChargebackModule: !Equals
    - !Ref IncludeECSChargebackModule
    - "yes"
  DeployRDSUtilizationModule: !Equals
    - !Ref IncludeRDSUtilizationModule
    - "yes"
  DeployOrgDataModule: !Equals
    - !Ref IncludeOrgDataModule
    - "yes"
  DeployBudgetsModule: !Equals
    - !Ref IncludeBudgetsModule
    - "yes"
  DeployTransitGatewayModule: !Equals
    - !Ref IncludeTransitGatewayModule
    - "yes"
  DeployPricingModule: !Or
    - !Condition DeployInventoryCollectorModule
    - !Condition DeployRDSUtilizationModule
  DeployAccountCollector: !Or
    - !Condition DeployTAModule
    - !Condition DeployInventoryCollectorModule
    - !Condition DeployRDSUtilizationModule
    - !Condition DeployEcsChargebackModule
    - !Condition DeployBudgetsModule
    - !Condition DeployRDSUtilizationModule
    - !Condition DeployTransitGatewayModule
  ComputeOptimizerRegionsIsEmpty: !Equals
    - !Join [ '', !Split [ ' ', !Ref ComputeOptimizerRegions  ] ] # remove spaces
    - ""
  ProdCFNTemplateUsed: !Equals [ !Ref CFNTemplateSourceBucket,  'aws-well-architected-labs' ]
#Reusable Resources:
Resources:
  S3CrawlerQue:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 300
      ReceiveMessageWaitTimeSeconds: 20
      DelaySeconds: 2
      QueueName: S3CrawlerQue
  CrawlerSQSPolicy:
    DependsOn:
      - S3CrawlerQue
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - Ref: S3CrawlerQue
      PolicyDocument:
        Id: SQSPolicy
        Statement:
        - Sid: SQSEventPolicy
          Effect: Allow
          Principal: {"Service": "s3.amazonaws.com"}
          Action: sqs:SendMessage
          Resource: !GetAtt S3CrawlerQue.Arn
          Condition:
            ArnLike:
              aws:SourceArn: !Sub "arn:aws:s3:::${DestinationBucket}${AWS::AccountId}"
            StringEquals:
                'aws:SourceAccount': !Sub '${AWS::AccountId}'
  S3Bucket:
    DependsOn:
      - CrawlerSQSPolicy
      - S3CrawlerQue
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName:
        !Sub "${DestinationBucket}${AWS::AccountId}"
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
            BlockPublicAcls : true
            BlockPublicPolicy : true
            IgnorePublicAcls : true
            RestrictPublicBuckets : true
      NotificationConfiguration:
        QueueConfigurations:
          - Event: s3:ObjectCreated:*
            Queue: !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:S3CrawlerQue"
            Filter:
              S3Key:
                Rules:
                - Name: prefix
                  Value: Compute_Optimizer/
  GlueRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${RolePrefix}AWS-OPTICS-Glue-Crawler"
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - glue.amazonaws.com
        Version: 2012-10-17
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole
      Path: /
      Policies:
        - PolicyName: "Put-S3"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "s3:PutObject"
                  - "s3:GetObject"
                Resource: !Join
                          - ''
                          - - !GetAtt S3Bucket.Arn
                            - '*'
  LambdaAnalyticsRole: #Execution role for the custom resource
    Type: AWS::IAM::Role
    Properties:
      Path:
        Fn::Sub: /${RolePrefix}/
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
  LambdaAnalytics:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.9
      FunctionName: !Sub ${RolePrefix}DataCollectionLab-Analytics
      Handler: index.lambda_handler
      MemorySize: 128
      Role: !GetAtt LambdaAnalyticsRole.Arn
      Timeout: 15
      Environment:
        Variables:
          WA_ANALYTICS_ENDPOINT: https://okakvoavfg.execute-api.eu-west-1.amazonaws.com/
      Code:
        ZipFile: |
          import os
          import json
          import uuid
          import urllib3
          import boto3
 
          endpoint = os.environ['WA_ANALYTICS_ENDPOINT']
          account_id = boto3.client("sts").get_caller_identity()["Account"]
 
          def lambda_handler(event, context):
              print(json.dumps(event))
              try:
                  if event['RequestType'].upper() not in ['CREATE', 'UPDATE', 'UPDATE']:
                      raise Exception(f"Unknown RequestType {event['RequestType']}")
                  action = event['RequestType'].upper()
                  name = event['ResourceProperties']['Name']
                  method = {'CREATE':'PUT', 'UPDATE': 'PATCH', 'DELETE': 'DELETE'}.get(action)
                  via_key = {'CREATE':'created_via', 'UPDATE': 'updated_via', 'DELETE': 'deleted_via'}.get(action)
                  payload = {'dashboard_id': 'data-collection-lab/' + name, 'account_id': account_id, via_key: 'CFN'}
                  r =  urllib3.PoolManager().request(method, endpoint, body=json.dumps(payload).encode('utf-8'), headers={'Content-Type': 'application/json'})
                  if r.status != 200:
                      raise Exception(f"There has been an issue logging action, server did not respond with a 200 response, actual status: {r.status}, response data {r.data.decode('utf-8')}. This issue will be ignored")
                  res, reason = 'SUCCESS', 'success'
              except Exception as exc:
                  res, reason = 'SUCCESS', f"{exc} . This issue will be ignored"
              body = {
                  'Status': res,
                  'Reason': reason,
                  'PhysicalResourceId': event.get('PhysicalResourceId', str(uuid.uuid1())),
                  'StackId': event.get('StackId'),
                  'RequestId': event.get('RequestId'),
                  'LogicalResourceId': event.get('LogicalResourceId'),
                  'NoEcho': False,
                  'Data':  {'Reason': reason},
              }
              json_body=json.dumps(body)
              print(json_body)
              url = event.get('ResponseURL')
              if not url: return
              try:
                  response = urllib3.PoolManager().request('PUT', url, body=json_body, headers={'content-type' : '', 'content-length' : str(len(json_body))}, retries=False)
                  print(f"Status code: {response}")
              except Exception as exc:
                  print("Failed sending PUT to CFN: " + str(exc))
  TrustedAdvisorModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployTAModule
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-trusted-advisor.yaml"
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        GlueRoleARN: !GetAtt GlueRole.Arn
        MultiAccountRoleName: !Sub "${RolePrefix}${MultiAccountRoleName}"
        CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNTemplateSourceBucket ]
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
  RightsizeModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployRightsizingModule
    Properties:
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        MultiAccountRoleName: !Sub "${RolePrefix}${MultiAccountRoleName}"
        ManagementRoleName: !Sub "${RolePrefix}${ManagementAccountRole}"
        ManagementAccountID: !Ref ManagementAccountID
        GlueRoleARN: !GetAtt GlueRole.Arn
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-cost-explorer-rightsizing.yaml"
  CostAnomalyModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployCostAnomalyModule
    Properties:
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        ManagementRoleName: !Sub "${RolePrefix}${ManagementAccountRole}"
        ManagementAccountID: !Ref ManagementAccountID
        GlueRoleARN: !GetAtt GlueRole.Arn
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-cost-explorer-cost-anomaly.yaml"
  InventoryCollectorModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployInventoryCollectorModule
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-inventory.yaml"
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        GlueRoleARN: !GetAtt GlueRole.Arn
        MultiAccountRoleName: !Sub "${RolePrefix}${MultiAccountRoleName}"
        CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNTemplateSourceBucket ]
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
  PricingModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployPricingModule
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-pricing.yaml"
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        RolePrefix: !Ref RolePrefix
        CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNTemplateSourceBucket ]
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
  ComputeOptimizerModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployComputeOptimizerModule
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-compute-optimizer.yaml"
      Parameters:
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        DestinationBucket: !Ref S3Bucket
        GlueRoleARN: !GetAtt GlueRole.Arn
        ManagementRoleName: !Sub "${RolePrefix}${ManagementAccountRole}"
        ManagementAccountID: !Ref ManagementAccountID
        S3CrawlerQue: !GetAtt S3CrawlerQue.Arn
        Schedule: !Ref Schedule
        RolePrefix: !Ref RolePrefix
        BucketPrefix:  !Ref DestinationBucket
        DeployRegions:
          Fn::If:
            - ComputeOptimizerRegionsIsEmpty
            - !Sub "${AWS::Region}"
            - !Join [ '', !Split [ ' ', !Ref ComputeOptimizerRegions  ] ] # remove spaces
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
  EcsChargebackModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployEcsChargebackModule
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-ecs-chargeback.yaml"
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        GlueRoleArn: !GetAtt GlueRole.Arn
        MultiAccountRoleName: !Sub "${RolePrefix}${MultiAccountRoleName}"
        CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNTemplateSourceBucket ]
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
  RDSUtilizationModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployRDSUtilizationModule
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-rds-usage.yaml"
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        GlueRoleArn: !GetAtt GlueRole.Arn
        MultiAccountRoleName: !Sub "${RolePrefix}${MultiAccountRoleName}"
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
  OrgDataModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployOrgDataModule
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-organization.yaml"
      Parameters:
        DestinationBucket: !Ref S3Bucket
        GlueRoleARN: !GetAtt GlueRole.Arn
        Schedule: !Ref Schedule
        ManagementRoleName: !Sub "${RolePrefix}${ManagementAccountRole}"
        ManagementAccountID: !Ref ManagementAccountID
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
  BudgetsModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployBudgetsModule
    Properties:
      Parameters:
          DestinationBucket: !Ref S3Bucket
          DestinationBucketARN: !GetAtt S3Bucket.Arn
          GlueRoleArn: !GetAtt GlueRole.Arn
          MultiAccountRoleName: !Sub "${RolePrefix}${MultiAccountRoleName}"
          RolePrefix: !Ref RolePrefix
          LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-budgets.yaml"
  TransitGatewayModule:
    Type: AWS::CloudFormation::Stack
    Condition: DeployTransitGatewayModule
    Properties:
      Parameters:
        DestinationBucket: !Ref S3Bucket
        DestinationBucketARN: !GetAtt S3Bucket.Arn
        GlueRoleArn: !GetAtt GlueRole.Arn
        MultiAccountRoleName: !Sub "${RolePrefix}${MultiAccountRoleName}"
        RolePrefix: !Ref RolePrefix
        LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/module-transit-gateway.yaml"
  AccountCollector:
    Type: AWS::CloudFormation::Stack
    Condition: DeployAccountCollector
    Properties:
      TemplateURL: !Sub "https://${CFNTemplateSourceBucket}.s3.amazonaws.com/Cost/Labs/300_Optimization_Data_Collection/account-collector.yaml"
      Parameters:
        RoleARN: !Sub "arn:aws:iam::${ManagementAccountID}:role/${RolePrefix}${ManagementAccountRole}"
        ManagementRoleName: !Sub "${RolePrefix}${ManagementAccountRole}"
        ManagementAccountID: !Ref ManagementAccountID
        RolePrefix: !Ref RolePrefix
        Schedule: !Ref Schedule
        TaskQueuesUrl:
          Fn::Join:
            - ','
            - - !If [DeployTAModule, !Sub "${TrustedAdvisorModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
              - !If [DeployInventoryCollectorModule, !Sub "${InventoryCollectorModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
              - !If [DeployEcsChargebackModule, !Sub "${EcsChargebackModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
              - !If [DeployRDSUtilizationModule, !Sub "${RDSUtilizationModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
              - !If [DeployBudgetsModule, !Sub "${BudgetsModule.Outputs.SQSUrl}", Ref: AWS::NoValue]
              - !If [DeployTransitGatewayModule, !Sub "${TransitGatewayModule.Outputs.SQSUrl}", Ref: AWS::NoValue]