From 8fe8718b0c22e77d673c6898d35cf68eeb5d7006 Mon Sep 17 00:00:00 2001 From: Marcodb Date: Wed, 15 Jan 2025 21:47:15 +0100 Subject: [PATCH 1/5] initial commit for terraform support --- data-collection/deploy/terraform/README.md | 105 ++++++++++ .../deploy/terraform/data-collection/main.tf | 50 +++++ .../terraform/data-collection/outputs.tf | 9 + .../data-collection/terraform.tfvars | 31 +++ .../terraform/data-collection/variables.tf | 176 ++++++++++++++++ .../terraform/data-collection/versions.tf | 14 ++ .../deploy/terraform/linked-accounts/main.tf | 192 ++++++++++++++++++ .../terraform/linked-accounts/outputs.tf | 4 + .../linked-accounts/terraform.tfvars | 18 ++ .../terraform/linked-accounts/variables.tf | 62 ++++++ .../terraform/linked-accounts/versions.tf | 10 + .../terraform/management-accounts/main.tf | 27 +++ .../terraform/management-accounts/outputs.tf | 4 + .../management-accounts/terraform.tfvars | 13 ++ .../management-accounts/variables.tf | 64 ++++++ .../terraform/management-accounts/versions.tf | 10 + 16 files changed, 789 insertions(+) create mode 100644 data-collection/deploy/terraform/README.md create mode 100644 data-collection/deploy/terraform/data-collection/main.tf create mode 100644 data-collection/deploy/terraform/data-collection/outputs.tf create mode 100644 data-collection/deploy/terraform/data-collection/terraform.tfvars create mode 100644 data-collection/deploy/terraform/data-collection/variables.tf create mode 100644 data-collection/deploy/terraform/data-collection/versions.tf create mode 100644 data-collection/deploy/terraform/linked-accounts/main.tf create mode 100644 data-collection/deploy/terraform/linked-accounts/outputs.tf create mode 100644 data-collection/deploy/terraform/linked-accounts/terraform.tfvars create mode 100644 data-collection/deploy/terraform/linked-accounts/variables.tf create mode 100644 data-collection/deploy/terraform/linked-accounts/versions.tf create mode 100644 data-collection/deploy/terraform/management-accounts/main.tf create mode 100644 data-collection/deploy/terraform/management-accounts/outputs.tf create mode 100644 data-collection/deploy/terraform/management-accounts/terraform.tfvars create mode 100644 data-collection/deploy/terraform/management-accounts/variables.tf create mode 100644 data-collection/deploy/terraform/management-accounts/versions.tf diff --git a/data-collection/deploy/terraform/README.md b/data-collection/deploy/terraform/README.md new file mode 100644 index 00000000..e0f73b59 --- /dev/null +++ b/data-collection/deploy/terraform/README.md @@ -0,0 +1,105 @@ +# Cloud Intelligence Dashboards - Terraform Deployment Solution + +## Overview + +This Terraform solution simplifies the deployment of data collection components for Cloud Intelligence Dashboards across AWS Organizations. It provides a streamlined approach to configure and manage data collection from multiple AWS accounts without relying on StackSets, offering better control and flexibility over the deployment process. + +The solution acts as a Terraform wrapper around AWS CloudFormation templates, providing a more manageable and maintainable infrastructure-as-code approach for enterprise environments. + +## Architecture + +The solution consists of three main Terraform modules that work together to enable comprehensive data collection: + +### 1. Management Account Module + +This module configures the necessary resources in the AWS Organizations management account: + +- Creates IAM roles with specific permissions for monitoring and data collection +- Enables required AWS services and APIs +- Configures access for specific features like AWS Backup, Compute Optimizer, and Cost Anomaly Detection + +### 2. Linked Account Role Module + +This module deploys the necessary IAM roles to each linked account for monitoring purposes: + +- Creates standardized IAM roles across member accounts +- Configures granular permissions for various data collection features +- Supports multiple optional monitoring components: + - Trusted Advisor + - Support Cases + - AWS Budgets + - Inventory Collection + - ECS Chargeback + - RDS Utilization + - Transit Gateway + - Service Quotas + + +### 3. Data Collection Module + +This module orchestrates the data collection process and deploys CloudFormation template for data collection as a wrapper + + +## Deployment Process + +### Step 1: Deploy Management Account Module + +1. Configure your AWS credentials for the management account +2. Initialize and apply the management account module: +```bash +cd management-accounts +terraform init +terraform plan +terraform apply +``` + +### Step 2: Deploy Linked Account Roles + +1. For each linked account: + - Configure AWS credentials + - Initialize and apply the linked account role module +```bash +cd linked-accounts +terraform init +terraform plan +terraform apply +``` + +### Step 3: Deploy Data Collection + +1. Use the outputs from previous modules as inputs +2. Deploy the data collection module: +```bash +cd data-collection +terraform init +terraform plan +terraform apply +``` + +## Why Not StackSets? + +This solution intentionally avoids using AWS StackSets for several reasons: +- Greater control over deployment timing and sequencing +- Simplified troubleshooting and rollback procedures +- Better integration with existing Terraform workflows +- More flexible permission management +- Enhanced visibility of deployment status per account + +## Troubleshooting + +Common issues and solutions: + +1. **Linked Account Access Issues** + - Verify IAM role trust relationships + - Check for correct resource prefixes + - Ensure AWS Organizations access is properly configured + +2. **Permission Errors** + - Review IAM role policies in both management and linked accounts + - Verify service enablement in AWS Organizations + - Check for any service quotas or limits + +3. **Data Collection Failures** + - Verify S3 bucket permissions + - Check CloudWatch Logs for execution errors + - Ensure correct role ARNs are being used \ No newline at end of file diff --git a/data-collection/deploy/terraform/data-collection/main.tf b/data-collection/deploy/terraform/data-collection/main.tf new file mode 100644 index 00000000..6ade6658 --- /dev/null +++ b/data-collection/deploy/terraform/data-collection/main.tf @@ -0,0 +1,50 @@ +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} + +locals { + template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/data-collection/deploy-data-collection.yaml" +} + +resource "aws_cloudformation_stack" "data_collection" { + name = "${var.resource_prefix}data-collection" + capabilities = [ + "CAPABILITY_NAMED_IAM", + "CAPABILITY_AUTO_EXPAND" + ] + template_url = local.template_url + + parameters = { + ResourcePrefix = var.resource_prefix + DatabaseName = var.database_name + DestinationBucket = var.destination_bucket + ManagementAccountID = var.management_account_id + ManagementAccountRole = var.management_account_role + MultiAccountRoleName = var.multi_account_role_name + Schedule = var.schedule + ScheduleFrequent = var.schedule_frequent + CFNSourceBucket = var.cfn_source_bucket + RegionsInScope = var.regions_in_scope + DatabaseName = var.database_name + DataBucketsKmsKeysArns = var.data_buckets_kms_keys_arns + IncludeTAModule = var.include_ta_module ? "yes" : "no" + IncludeRDSUtilizationModule = var.include_rds_usage_module ? "yes" : "no" + IncludeOrgDataModule = var.include_org_data_module ? "yes" : "no" + IncludeRightsizingModule = var.include_ce_rightsizing_module ? "yes" : "no" + IncludeCostAnomalyModule = var.include_cost_anomaly_module ? "yes" : "no" + IncludeSupportCasesModule = var.include_support_cases_module ? "yes" : "no" + IncludeBackupModule = var.include_backup_module ? "yes" : "no" + IncludeInventoryCollectorModule = var.include_inventory_module ? "yes" : "no" + IncludeComputeOptimizerModule = var.include_compute_optimizer_module ? "yes" : "no" + IncludeECSChargebackModule = var.include_ecs_chargeback_module ? "yes" : "no" + IncludeBudgetsModule = var.include_budgets_module ? "yes" : "no" + IncludeTransitGatewayModule = var.include_transit_gateway_module ? "yes" : "no" + IncludeAWSFeedsModule = var.include_aws_feeds_module ? "yes" : "no" + IncludeHealthEventsModule = var.include_health_events_module ? "yes" : "no" + IncludeLicenseManagerModule = var.include_license_manager_module ? "yes" : "no" + IncludeServiceQuotasModule = var.include_service_quotas_module ? "yes" : "no" + IncludeQuickSightModule = var.include_quicksight_module ? "yes" : "no" + } + + tags = var.tags +} diff --git a/data-collection/deploy/terraform/data-collection/outputs.tf b/data-collection/deploy/terraform/data-collection/outputs.tf new file mode 100644 index 00000000..48d2ea9f --- /dev/null +++ b/data-collection/deploy/terraform/data-collection/outputs.tf @@ -0,0 +1,9 @@ +output "stack_id" { + description = "The unique identifier for the stack" + value = aws_cloudformation_stack.data_collection.id +} + +output "stack_outputs" { + description = "Map of outputs from the CloudFormation stack" + value = aws_cloudformation_stack.data_collection.outputs +} diff --git a/data-collection/deploy/terraform/data-collection/terraform.tfvars b/data-collection/deploy/terraform/data-collection/terraform.tfvars new file mode 100644 index 00000000..d4b484d7 --- /dev/null +++ b/data-collection/deploy/terraform/data-collection/terraform.tfvars @@ -0,0 +1,31 @@ +# Default values for variables +resource_prefix = "CID-DC-" + +destination_bucket = "cid-data-" +management_account_id = "987654321987" # Required: Add your management account IDs as a comma separated list +management_account_role = "Lambda-Assume-Role-Management-Account" # Required: Add your management account role +multi_account_role_name = "Optimization-Data-Multi-Account-Role" # Required: Add your multi-account role name +regions_in_scope = "us-east-1,eu-west-1,eu-central-1" # Update with your desired regions +data_buckets_kms_keys_arns = "KMS_KEY_ARN" +schedule = "rate(1 day)" # Update with your desired schedule +schedule_frequent = "rate(1 day)" # Update with your desired frequent schedule +database_name = "optimization_data" +cfn_source_bucket = "aws-managed-cost-intelligence-dashboards" # Don't Change +include_ta_module = "true" +include_rds_usage_module = "true" +include_org_data_module = "true" +include_ce_rightsizing_module = "true" +include_cost_anomaly_module = "true" +include_support_cases_module = "true" +include_backup_module = "true" +include_inventory_module = "true" +include_pricing_module = "true" +include_compute_optimizer_module = "false" +include_ecs_chargeback_module = "true" +include_budgets_module = "true" +include_transit_gateway_module = "true" +include_aws_feeds_module = "true" +include_health_events_module = "true" +include_license_manager_module = "true" +include_service_quotas_module = "true" +include_quicksight_module = "true" diff --git a/data-collection/deploy/terraform/data-collection/variables.tf b/data-collection/deploy/terraform/data-collection/variables.tf new file mode 100644 index 00000000..c92bd65e --- /dev/null +++ b/data-collection/deploy/terraform/data-collection/variables.tf @@ -0,0 +1,176 @@ +variable "resource_prefix" { + description = "Prefix to be used for all resources created by this module" + type = string + default = "CID-DC-" +} + +variable "cfn_source_bucket" { + description = "Name of the S3 bucket where CloudFormation templates are stored" + type = string + default = "aws-managed-cost-intelligence-dashboards" +} + +variable "destination_bucket" { + description = "Name of the S3 bucket where data will be stored" + type = string + default = "cid-data-" +} + +variable "management_account_id" { + description = "Comma-delimited list of Account IDs for Management Account IDs" + type = string +} + +variable "management_account_role" { + description = "Management account role" + type = string + default = "Lambda-Assume-Role-Management-Account" +} + +variable "multi_account_role_name" { + description = "Multi Account Role Name" + type = string + default = "Optimization-Data-Multi-Account-Role" +} + +variable "regions_in_scope" { + description = "Comma-delimited list of AWS regions for resource data collection (e.g., \"us-east-1,eu-west-1,ap-northeast-1\")" + type = string +} + +variable "data_buckets_kms_keys_arns" { + description = "ARNs of KMS Keys for data buckets and/or Glue Catalog. Comma separated list, no spaces. Keep empty if data Buckets and Glue Catalog are not Encrypted with KMS. You can also set it to '*' to grant decrypt permission for all the keys." + type = string +} + +variable "schedule" { + description = "Cron expression for execution schedule.\")" + type = string + default = "rate(14 days)" +} + +variable "schedule_frequent" { + description = "Cron expression for more frequent executions.\")" + type = string + default = "rate(1 day)" +} + +variable "database_name" { + description = "Name of the Glue Database to be created" + type = string + default = "optimization_data" +} + +variable "include_ta_module" { + description = "Whether to include the Trusted Advisor module" + type = bool + default = true +} + +variable "include_rds_usage_module" { + description = "Whether to include the RDS Usage module" + type = bool + default = true +} + +variable "include_org_data_module" { + description = "Whether to include the Organization Data module" + type = bool + default = true +} + +variable "include_ce_rightsizing_module" { + description = "Whether to include the Cost Explorer Rightsizing module" + type = bool + default = true +} + +variable "include_cost_anomaly_module" { + description = "Whether to include the Cost Anomaly module" + type = bool + default = true +} + +variable "include_support_cases_module" { + description = "Whether to include the Support Cases module" + type = bool + default = true +} + +variable "include_backup_module" { + description = "Whether to include the Backup module" + type = bool + default = true +} + +variable "include_inventory_module" { + description = "Whether to include the Inventory module" + type = bool + default = true +} + +variable "include_pricing_module" { + description = "Whether to include the Pricing module" + type = bool + default = true +} + +variable "include_compute_optimizer_module" { + description = "Whether to include the Compute Optimizer module" + type = bool + default = true +} + +variable "include_ecs_chargeback_module" { + description = "Whether to include the ECS Chargeback module" + type = bool + default = true +} + +variable "include_budgets_module" { + description = "Whether to include the Budgets module" + type = bool + default = true +} + +variable "include_transit_gateway_module" { + description = "Whether to include the Transit Gateway module" + type = bool + default = true +} + +variable "include_aws_feeds_module" { + description = "Whether to include the AWS Feeds module" + type = bool + default = true +} + +variable "include_health_events_module" { + description = "Whether to include the Health Events module" + type = bool + default = true +} + +variable "include_license_manager_module" { + description = "Whether to include the License Manager module" + type = bool + default = true +} + +variable "include_service_quotas_module" { + description = "Whether to include the Service Quotas module" + type = bool + default = true +} + +variable "include_quicksight_module" { + description = "Whether to include the QuickSight module" + type = bool + default = true +} + +variable "tags" { + description = "Tags to be added to all resources" + type = map(string) + default = {} +} diff --git a/data-collection/deploy/terraform/data-collection/versions.tf b/data-collection/deploy/terraform/data-collection/versions.tf new file mode 100644 index 00000000..838cd8bc --- /dev/null +++ b/data-collection/deploy/terraform/data-collection/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.63" + } + } +} + +provider "aws" { + alias = "main" +} diff --git a/data-collection/deploy/terraform/linked-accounts/main.tf b/data-collection/deploy/terraform/linked-accounts/main.tf new file mode 100644 index 00000000..a2de83b2 --- /dev/null +++ b/data-collection/deploy/terraform/linked-accounts/main.tf @@ -0,0 +1,192 @@ +data "aws_region" "current" {} +data "aws_partition" "current" {} + +# Lambda execution role for data collection +resource "aws_iam_role" "lambda_role" { + name = "${var.resource_prefix}${var.multi_account_role_name}" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + AWS = "arn:${data.aws_partition.current.partition}:iam::${var.data_collection_account_id}:root" + } + } + ] + }) +} + +# Conditional policies based on features enabled +resource "aws_iam_role_policy" "ta_policy" { + count = var.allow_ta_module == "yes" ? 1 : 0 + name = "TAPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "support:DescribeTrustedAdvisorChecks", + "support:DescribeTrustedAdvisorCheckResult" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "support_cases_policy" { + count = var.allow_support_cases == "yes" ? 1 : 0 + name = "SupportCasesPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "support:DescribeCases" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "budgets_readonly_policy" { + count = var.allow_budgets_readonly == "yes" ? 1 : 0 + name = "BudgetsReadOnlyPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "budgets:ViewBudget", + "budgets:ListTagsForResource" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "inventory_collector_policy" { + count = var.allow_inventory_collection == "yes" ? 1 : 0 + name = "InventoryCollectorPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ec2:DescribeInstances", + "ec2:DescribeImages", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeVolumes", + "ec2:DescribeSnapshots", + "ec2:DescribeVpcs", + "ec2:DescribeRegions" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "ecs_chargeback_policy" { + count = var.allow_ecs_chargeback == "yes" ? 1 : 0 + name = "ECSChargebackPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ecs:ListClusters", + "ecs:ListContainerInstances", + "ecs:DescribeContainerInstances" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "rds_utilization_policy" { + count = var.allow_rds_utilization == "yes" ? 1 : 0 + name = "RDSUtilizationPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "rds:DescribeDBInstances", + "rds:DescribeDBClusters", + "cloudwatch:GetMetricData", + "cloudwatch:GetMetricStatistics", + "cloudwatch:ListMetrics" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "transit_gateway_policy" { + count = var.allow_transit_gateway == "yes" ? 1 : 0 + name = "TransitGatewayPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ec2:DescribeTransitGatewayAttachments", + "cloudwatch:Describe*", + "cloudwatch:Get*", + "cloudwatch:List*" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "service_quotas_policy" { + count = var.allow_service_quotas == "yes" ? 1 : 0 + name = "ServiceQuotasReadOnlyPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "servicequotas:ListRequestedServiceQuotaChangeHistory", + "servicequotas:GetServiceQuota", + "servicequotas:GetAWSDefaultServiceQuota" + ] + Resource = "*" + } + ] + }) +} \ No newline at end of file diff --git a/data-collection/deploy/terraform/linked-accounts/outputs.tf b/data-collection/deploy/terraform/linked-accounts/outputs.tf new file mode 100644 index 00000000..52f11c6e --- /dev/null +++ b/data-collection/deploy/terraform/linked-accounts/outputs.tf @@ -0,0 +1,4 @@ +output "lambda_role_arn" { + description = "ARN of the created Lambda execution role" + value = aws_iam_role.lambda_role.arn +} \ No newline at end of file diff --git a/data-collection/deploy/terraform/linked-accounts/terraform.tfvars b/data-collection/deploy/terraform/linked-accounts/terraform.tfvars new file mode 100644 index 00000000..2683850a --- /dev/null +++ b/data-collection/deploy/terraform/linked-accounts/terraform.tfvars @@ -0,0 +1,18 @@ +# Resource prefix for naming +resource_prefix = "CID-DC-" + +# Module permissions +allow_ta_module = "yes" +allow_support_cases = "yes" +allow_budgets_readonly = "yes" +allow_inventory_collection = "yes" +allow_ecs_chargeback = "yes" +allow_rds_utilization = "yes" +allow_transit_gateway = "yes" +allow_service_quotas = "yes" + +# Role name +multi_account_role_name = "Optimization-Data-Multi-Account-Role" + +# Data collection account ID +data_collection_account_id = "123456789123" # Replace with actual AWS account ID \ No newline at end of file diff --git a/data-collection/deploy/terraform/linked-accounts/variables.tf b/data-collection/deploy/terraform/linked-accounts/variables.tf new file mode 100644 index 00000000..0b4bda7f --- /dev/null +++ b/data-collection/deploy/terraform/linked-accounts/variables.tf @@ -0,0 +1,62 @@ +variable "resource_prefix" { + description = "Prefix for IAM resources" + type = string +} + +variable "multi_account_role_name" { + description = "Name of the multi-account IAM role" + type = string +} + +variable "data_collection_account_id" { + description = "AWS account ID where data collection occurs" + type = string +} + +variable "allow_ta_module" { + description = "Enable TA module permissions" + type = string + default = "no" +} + +variable "allow_support_cases" { + description = "Enable support cases permissions" + type = string + default = "no" +} + +variable "allow_budgets_readonly" { + description = "Enable budgets read-only permissions" + type = string + default = "no" +} + +variable "allow_inventory_collection" { + description = "Enable inventory collection permissions" + type = string + default = "no" +} + +variable "allow_ecs_chargeback" { + description = "Enable ECS chargeback permissions" + type = string + default = "no" +} + +variable "allow_rds_utilization" { + description = "Enable RDS utilization permissions" + type = string + default = "no" +} + +variable "allow_transit_gateway" { + description = "Enable Transit Gateway permissions" + type = string + default = "no" +} + +variable "allow_service_quotas" { + description = "Enable Service Quotas permissions" + type = string + default = "no" +} \ No newline at end of file diff --git a/data-collection/deploy/terraform/linked-accounts/versions.tf b/data-collection/deploy/terraform/linked-accounts/versions.tf new file mode 100644 index 00000000..331a6189 --- /dev/null +++ b/data-collection/deploy/terraform/linked-accounts/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.0" + } + } +} \ No newline at end of file diff --git a/data-collection/deploy/terraform/management-accounts/main.tf b/data-collection/deploy/terraform/management-accounts/main.tf new file mode 100644 index 00000000..5d1aab1a --- /dev/null +++ b/data-collection/deploy/terraform/management-accounts/main.tf @@ -0,0 +1,27 @@ +data "aws_region" "current" {} +data "aws_partition" "current" {} + +locals { + template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/data-collection/deploy-in-management-account.yaml" +} + +# Management account IAM role deployment +resource "aws_cloudformation_stack" "mgmt_read" { + name = "${var.resource_prefix}mgmt-read" + template_url = local.template_url + capabilities = ["CAPABILITY_NAMED_IAM"] + + parameters = { + ResourcePrefix = var.resource_prefix + ManagementAccountRole = var.management_account_role + DataCollectionAccountID = var.data_collection_account_id + IncludeBackupModule = var.backup_module + IncludeComputeOptimizerModule = var.compute_optimizer_module + IncludeCostAnomalyModule = var.cost_anomaly_module + IncludeSupportCasesModule = var.support_cases_module + IncludeHealthEventsModule = var.health_events_module + IncludeRightsizingModule = var.rightsizing_module + IncludeLicenseManagerModule = var.license_manager_module + IncludeServiceQuotasModule = var.service_quotas_module + } +} \ No newline at end of file diff --git a/data-collection/deploy/terraform/management-accounts/outputs.tf b/data-collection/deploy/terraform/management-accounts/outputs.tf new file mode 100644 index 00000000..0d818c8b --- /dev/null +++ b/data-collection/deploy/terraform/management-accounts/outputs.tf @@ -0,0 +1,4 @@ +output "mgmt_account_stack_id" { + description = "ID of the CloudFormation stack in management account" + value = aws_cloudformation_stack.mgmt_read.id +} \ No newline at end of file diff --git a/data-collection/deploy/terraform/management-accounts/terraform.tfvars b/data-collection/deploy/terraform/management-accounts/terraform.tfvars new file mode 100644 index 00000000..f7ac4bbb --- /dev/null +++ b/data-collection/deploy/terraform/management-accounts/terraform.tfvars @@ -0,0 +1,13 @@ +resource_prefix = "CID-DC-" +management_account_role = "Lambda-Assume-Role-Management-Account" +data_collection_account_id = "123456789123" + +# Module flags +backup_module = "yes" +compute_optimizer_module = "no" +cost_anomaly_module = "yes" +support_cases_module = "yes" +health_events_module = "yes" +rightsizing_module = "yes" +license_manager_module = "yes" +service_quotas_module = "yes" \ No newline at end of file diff --git a/data-collection/deploy/terraform/management-accounts/variables.tf b/data-collection/deploy/terraform/management-accounts/variables.tf new file mode 100644 index 00000000..3d2390ee --- /dev/null +++ b/data-collection/deploy/terraform/management-accounts/variables.tf @@ -0,0 +1,64 @@ +variable "resource_prefix" { + description = "Prefix for IAM resources" + type = string + default = "CID-DC-" +} + +variable "management_account_role" { + description = "Name of the management account IAM role" + type = string + default = "Lambda-Assume-Role-Management-Account" +} + +variable "data_collection_account_id" { + description = "Account ID for data collection" + type = string +} + +variable "backup_module" { + description = "Enable AWS Backup module permissions" + type = string + default = "no" +} + +variable "compute_optimizer_module" { + description = "Enable Compute Optimizer module permissions" + type = string + default = "no" +} + +variable "cost_anomaly_module" { + description = "Enable Cost Anomaly Detection module permissions" + type = string + default = "no" +} + +variable "support_cases_module" { + description = "Enable AWS Support Cases module permissions" + type = string + default = "no" +} + +variable "health_events_module" { + description = "Enable AWS Health Events module permissions" + type = string + default = "no" +} + +variable "rightsizing_module" { + description = "Enable Rightsizing module permissions" + type = string + default = "no" +} + +variable "license_manager_module" { + description = "Enable License Manager module permissions" + type = string + default = "no" +} + +variable "service_quotas_module" { + description = "Enable Service Quotas module permissions" + type = string + default = "no" +} diff --git a/data-collection/deploy/terraform/management-accounts/versions.tf b/data-collection/deploy/terraform/management-accounts/versions.tf new file mode 100644 index 00000000..331a6189 --- /dev/null +++ b/data-collection/deploy/terraform/management-accounts/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.0" + } + } +} \ No newline at end of file From 2af9b0db3f0e2e6905779c9e796db6a77afcbc82 Mon Sep 17 00:00:00 2001 From: Marcodb Date: Fri, 7 Feb 2025 12:03:07 +0100 Subject: [PATCH 2/5] Adding tags --- terraform/README.md | 115 +++++++++++ terraform/data-collection/main.tf | 50 +++++ terraform/data-collection/outputs.tf | 9 + terraform/data-collection/terraform.tfvars | 32 +++ terraform/data-collection/variables.tf | 187 +++++++++++++++++ terraform/data-collection/versions.tf | 14 ++ terraform/linked-accounts/main.tf | 192 ++++++++++++++++++ terraform/linked-accounts/outputs.tf | 4 + terraform/linked-accounts/terraform.tfvars | 18 ++ terraform/linked-accounts/variables.tf | 62 ++++++ terraform/linked-accounts/versions.tf | 10 + terraform/management-accounts/main.tf | 27 +++ terraform/management-accounts/outputs.tf | 4 + .../management-accounts/terraform.tfvars | 14 ++ terraform/management-accounts/variables.tf | 75 +++++++ terraform/management-accounts/versions.tf | 10 + 16 files changed, 823 insertions(+) create mode 100644 terraform/README.md create mode 100644 terraform/data-collection/main.tf create mode 100644 terraform/data-collection/outputs.tf create mode 100644 terraform/data-collection/terraform.tfvars create mode 100644 terraform/data-collection/variables.tf create mode 100644 terraform/data-collection/versions.tf create mode 100644 terraform/linked-accounts/main.tf create mode 100644 terraform/linked-accounts/outputs.tf create mode 100644 terraform/linked-accounts/terraform.tfvars create mode 100644 terraform/linked-accounts/variables.tf create mode 100644 terraform/linked-accounts/versions.tf create mode 100644 terraform/management-accounts/main.tf create mode 100644 terraform/management-accounts/outputs.tf create mode 100644 terraform/management-accounts/terraform.tfvars create mode 100644 terraform/management-accounts/variables.tf create mode 100644 terraform/management-accounts/versions.tf diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 00000000..dbc7b7ae --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,115 @@ +# Cloud Intelligence Dashboards - Terraform Deployment Solution + +## Overview + +This Terraform solution simplifies the deployment of data collection components for Cloud Intelligence Dashboards across AWS Organizations. It provides a streamlined approach to configure and manage data collection from multiple AWS accounts without relying on StackSets, offering better control and flexibility over the deployment process. + +The solution acts as a Terraform wrapper around AWS CloudFormation templates, providing a more manageable and maintainable infrastructure-as-code approach for enterprise environments. + +## Architecture + +The solution consists of three main Terraform modules that work together to enable comprehensive data collection: + +### 1. Management Account Module + +This module configures the necessary resources in the AWS Organizations management account: + +- Creates IAM roles with specific permissions for monitoring and data collection +- Enables required AWS services and APIs +- Configures access for specific features like AWS Backup, Compute Optimizer, and Cost Anomaly Detection + +### 2. Linked Account Role Module + +This module deploys the necessary IAM roles to each linked account for monitoring purposes: + +- Creates standardized IAM roles across member accounts +- Configures granular permissions for various data collection features +- Supports multiple optional monitoring components: + - Trusted Advisor + - Support Cases + - AWS Budgets + - Inventory Collection + - ECS Chargeback + - RDS Utilization + - Transit Gateway + - Service Quotas + + +### 3. Data Collection Module + +This module orchestrates the data collection process and deploys CloudFormation template for data collection as a wrapper + + +## Deployment Process + +### Step 1: Deploy Management Account Module + +1. Configure your AWS credentials for the management account +2. Initialize and apply the management account module: +```bash +cd management-accounts +terraform init +terraform plan +terraform apply +``` + +### Step 2: Deploy Linked Account Roles + +1. For each linked account: + - Configure AWS credentials + - Initialize and apply the linked account role module +```bash +cd linked-accounts +terraform init +terraform plan +terraform apply +``` + +### Step 3: Deploy Data Collection + +1. Use the outputs from previous modules as inputs +2. Deploy the data collection module: +```bash +cd data-collection +terraform init +terraform plan +terraform apply +``` + +## Why Not StackSets? + +This solution intentionally avoids using AWS StackSets for several reasons: +- Greater control over deployment timing and sequencing +- Simplified troubleshooting and rollback procedures +- Better integration with existing Terraform workflows +- More flexible permission management +- Enhanced visibility of deployment status per account + +## Troubleshooting + +Common issues and solutions: + +1. **Linked Account Access Issues** + - Verify IAM role trust relationships + - Check for correct resource prefixes + - Ensure AWS Organizations access is properly configured + +2. **Permission Errors** + - Review IAM role policies in both management and linked accounts + - Verify service enablement in AWS Organizations + - Check for any service quotas or limits + +3. **Data Collection Failures** + - Verify S3 bucket permissions + - Check CloudWatch Logs for execution errors + - Ensure correct role ARNs are being used + +## Contributing + +Contributions to improve the solution are welcome! Please: + +1. Fork the repository +2. Create a feature branch +3. Submit a pull request with detailed description of changes +4. Ensure all existing tests pass +5. Add new tests as needed \ No newline at end of file diff --git a/terraform/data-collection/main.tf b/terraform/data-collection/main.tf new file mode 100644 index 00000000..9bb29960 --- /dev/null +++ b/terraform/data-collection/main.tf @@ -0,0 +1,50 @@ +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} + +locals { + template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/${var.tag_version}/data-collection/deploy-data-collection.yaml" +} + +resource "aws_cloudformation_stack" "data_collection" { + name = "${var.resource_prefix}data-collection" + capabilities = [ + "CAPABILITY_NAMED_IAM", + "CAPABILITY_AUTO_EXPAND" + ] + template_url = local.template_url + + parameters = { + ResourcePrefix = var.resource_prefix + DatabaseName = var.database_name + DestinationBucket = var.destination_bucket + ManagementAccountID = var.management_account_id + ManagementAccountRole = var.management_account_role + MultiAccountRoleName = var.multi_account_role_name + Schedule = var.schedule + ScheduleFrequent = var.schedule_frequent + CFNSourceBucket = var.cfn_source_bucket + RegionsInScope = var.regions_in_scope + DatabaseName = var.database_name + DataBucketsKmsKeysArns = var.data_buckets_kms_keys_arns + IncludeTAModule = var.include_ta_module ? "yes" : "no" + IncludeRDSUtilizationModule = var.include_rds_usage_module ? "yes" : "no" + IncludeOrgDataModule = var.include_org_data_module ? "yes" : "no" + IncludeRightsizingModule = var.include_ce_rightsizing_module ? "yes" : "no" + IncludeCostAnomalyModule = var.include_cost_anomaly_module ? "yes" : "no" + IncludeSupportCasesModule = var.include_support_cases_module ? "yes" : "no" + IncludeBackupModule = var.include_backup_module ? "yes" : "no" + IncludeInventoryCollectorModule = var.include_inventory_module ? "yes" : "no" + IncludeComputeOptimizerModule = var.include_compute_optimizer_module ? "yes" : "no" + IncludeECSChargebackModule = var.include_ecs_chargeback_module ? "yes" : "no" + IncludeBudgetsModule = var.include_budgets_module ? "yes" : "no" + IncludeTransitGatewayModule = var.include_transit_gateway_module ? "yes" : "no" + IncludeAWSFeedsModule = var.include_aws_feeds_module ? "yes" : "no" + IncludeHealthEventsModule = var.include_health_events_module ? "yes" : "no" + IncludeLicenseManagerModule = var.include_license_manager_module ? "yes" : "no" + IncludeServiceQuotasModule = var.include_service_quotas_module ? "yes" : "no" + IncludeQuickSightModule = var.include_quicksight_module ? "yes" : "no" + } + + tags = var.tags +} diff --git a/terraform/data-collection/outputs.tf b/terraform/data-collection/outputs.tf new file mode 100644 index 00000000..48d2ea9f --- /dev/null +++ b/terraform/data-collection/outputs.tf @@ -0,0 +1,9 @@ +output "stack_id" { + description = "The unique identifier for the stack" + value = aws_cloudformation_stack.data_collection.id +} + +output "stack_outputs" { + description = "Map of outputs from the CloudFormation stack" + value = aws_cloudformation_stack.data_collection.outputs +} diff --git a/terraform/data-collection/terraform.tfvars b/terraform/data-collection/terraform.tfvars new file mode 100644 index 00000000..3c2099ea --- /dev/null +++ b/terraform/data-collection/terraform.tfvars @@ -0,0 +1,32 @@ +# Default values for variables +resource_prefix = "CID-DC-" +tag_version = "3.6.2" + +destination_bucket = "cid-data-" +management_account_id = "987654321987" # Required: Add your management account IDs as a comma separated list +management_account_role = "Lambda-Assume-Role-Management-Account" # Required: Add your management account role +multi_account_role_name = "Optimization-Data-Multi-Account-Role" # Required: Add your multi-account role name +regions_in_scope = "us-east-1,eu-west-1,eu-central-1" # Update with your desired regions +data_buckets_kms_keys_arns = "KMS_KEY_ARN" +schedule = "rate(1 day)" # Update with your desired schedule +schedule_frequent = "rate(1 day)" # Update with your desired frequent schedule +database_name = "optimization_data" +cfn_source_bucket = "aws-managed-cost-intelligence-dashboards" # Don't Change +include_ta_module = "true" +include_rds_usage_module = "true" +include_org_data_module = "true" +include_ce_rightsizing_module = "true" +include_cost_anomaly_module = "true" +include_support_cases_module = "true" +include_backup_module = "true" +include_inventory_module = "true" +include_pricing_module = "true" +include_compute_optimizer_module = "false" +include_ecs_chargeback_module = "true" +include_budgets_module = "true" +include_transit_gateway_module = "true" +include_aws_feeds_module = "true" +include_health_events_module = "true" +include_license_manager_module = "true" +include_service_quotas_module = "true" +include_quicksight_module = "true" diff --git a/terraform/data-collection/variables.tf b/terraform/data-collection/variables.tf new file mode 100644 index 00000000..c33c2d41 --- /dev/null +++ b/terraform/data-collection/variables.tf @@ -0,0 +1,187 @@ +variable "resource_prefix" { + description = "Prefix to be used for all resources created by this module" + type = string + default = "CID-DC-" +} + +variable "cfn_source_bucket" { + description = "Name of the S3 bucket where CloudFormation templates are stored" + type = string + default = "aws-managed-cost-intelligence-dashboards" +} + +variable "destination_bucket" { + description = "Name of the S3 bucket where data will be stored" + type = string + default = "cid-data-" +} + +variable "management_account_id" { + description = "Comma-delimited list of Account IDs for Management Account IDs" + type = string +} + +variable "management_account_role" { + description = "Management account role" + type = string + default = "Lambda-Assume-Role-Management-Account" +} + +variable "multi_account_role_name" { + description = "Multi Account Role Name" + type = string + default = "Optimization-Data-Multi-Account-Role" +} + +variable "regions_in_scope" { + description = "Comma-delimited list of AWS regions for resource data collection (e.g., \"us-east-1,eu-west-1,ap-northeast-1\")" + type = string +} + +variable "data_buckets_kms_keys_arns" { + description = "ARNs of KMS Keys for data buckets and/or Glue Catalog. Comma separated list, no spaces. Keep empty if data Buckets and Glue Catalog are not Encrypted with KMS. You can also set it to '*' to grant decrypt permission for all the keys." + type = string +} + +variable "schedule" { + description = "Cron expression for execution schedule.\")" + type = string + default = "rate(14 days)" +} + +variable "schedule_frequent" { + description = "Cron expression for more frequent executions.\")" + type = string + default = "rate(1 day)" +} + +variable "database_name" { + description = "Name of the Glue Database to be created" + type = string + default = "optimization_data" +} + +variable "include_ta_module" { + description = "Whether to include the Trusted Advisor module" + type = bool + default = true +} + +variable "include_rds_usage_module" { + description = "Whether to include the RDS Usage module" + type = bool + default = true +} + +variable "include_org_data_module" { + description = "Whether to include the Organization Data module" + type = bool + default = true +} + +variable "include_ce_rightsizing_module" { + description = "Whether to include the Cost Explorer Rightsizing module" + type = bool + default = true +} + +variable "include_cost_anomaly_module" { + description = "Whether to include the Cost Anomaly module" + type = bool + default = true +} + +variable "include_support_cases_module" { + description = "Whether to include the Support Cases module" + type = bool + default = true +} + +variable "include_backup_module" { + description = "Whether to include the Backup module" + type = bool + default = true +} + +variable "include_inventory_module" { + description = "Whether to include the Inventory module" + type = bool + default = true +} + +variable "include_pricing_module" { + description = "Whether to include the Pricing module" + type = bool + default = true +} + +variable "include_compute_optimizer_module" { + description = "Whether to include the Compute Optimizer module" + type = bool + default = true +} + +variable "include_ecs_chargeback_module" { + description = "Whether to include the ECS Chargeback module" + type = bool + default = true +} + +variable "include_budgets_module" { + description = "Whether to include the Budgets module" + type = bool + default = true +} + +variable "include_transit_gateway_module" { + description = "Whether to include the Transit Gateway module" + type = bool + default = true +} + +variable "include_aws_feeds_module" { + description = "Whether to include the AWS Feeds module" + type = bool + default = true +} + +variable "include_health_events_module" { + description = "Whether to include the Health Events module" + type = bool + default = true +} + +variable "include_license_manager_module" { + description = "Whether to include the License Manager module" + type = bool + default = true +} + +variable "include_service_quotas_module" { + description = "Whether to include the Service Quotas module" + type = bool + default = true +} + +variable "include_quicksight_module" { + description = "Whether to include the QuickSight module" + type = bool + default = true +} + +variable "tags" { + description = "Tags to be added to all resources" + type = map(string) + default = {} +} + +variable "tag_version" { + description = "GitHub tag version using for the deployment (e.g. 4.0.7)" + type = string + default = "" +} + +validation { + condition = var.tag_version == "" || can(regex("^\\d+\\.\\d+\\.\\d+$", var.tag_version)) + error_message = "The tag_version must be in the format X.Y.Z where X, Y, and Z are digits (e.g., 4.0.7)" +} \ No newline at end of file diff --git a/terraform/data-collection/versions.tf b/terraform/data-collection/versions.tf new file mode 100644 index 00000000..838cd8bc --- /dev/null +++ b/terraform/data-collection/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.63" + } + } +} + +provider "aws" { + alias = "main" +} diff --git a/terraform/linked-accounts/main.tf b/terraform/linked-accounts/main.tf new file mode 100644 index 00000000..a2de83b2 --- /dev/null +++ b/terraform/linked-accounts/main.tf @@ -0,0 +1,192 @@ +data "aws_region" "current" {} +data "aws_partition" "current" {} + +# Lambda execution role for data collection +resource "aws_iam_role" "lambda_role" { + name = "${var.resource_prefix}${var.multi_account_role_name}" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + AWS = "arn:${data.aws_partition.current.partition}:iam::${var.data_collection_account_id}:root" + } + } + ] + }) +} + +# Conditional policies based on features enabled +resource "aws_iam_role_policy" "ta_policy" { + count = var.allow_ta_module == "yes" ? 1 : 0 + name = "TAPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "support:DescribeTrustedAdvisorChecks", + "support:DescribeTrustedAdvisorCheckResult" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "support_cases_policy" { + count = var.allow_support_cases == "yes" ? 1 : 0 + name = "SupportCasesPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "support:DescribeCases" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "budgets_readonly_policy" { + count = var.allow_budgets_readonly == "yes" ? 1 : 0 + name = "BudgetsReadOnlyPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "budgets:ViewBudget", + "budgets:ListTagsForResource" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "inventory_collector_policy" { + count = var.allow_inventory_collection == "yes" ? 1 : 0 + name = "InventoryCollectorPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ec2:DescribeInstances", + "ec2:DescribeImages", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeVolumes", + "ec2:DescribeSnapshots", + "ec2:DescribeVpcs", + "ec2:DescribeRegions" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "ecs_chargeback_policy" { + count = var.allow_ecs_chargeback == "yes" ? 1 : 0 + name = "ECSChargebackPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ecs:ListClusters", + "ecs:ListContainerInstances", + "ecs:DescribeContainerInstances" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "rds_utilization_policy" { + count = var.allow_rds_utilization == "yes" ? 1 : 0 + name = "RDSUtilizationPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "rds:DescribeDBInstances", + "rds:DescribeDBClusters", + "cloudwatch:GetMetricData", + "cloudwatch:GetMetricStatistics", + "cloudwatch:ListMetrics" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "transit_gateway_policy" { + count = var.allow_transit_gateway == "yes" ? 1 : 0 + name = "TransitGatewayPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ec2:DescribeTransitGatewayAttachments", + "cloudwatch:Describe*", + "cloudwatch:Get*", + "cloudwatch:List*" + ] + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy" "service_quotas_policy" { + count = var.allow_service_quotas == "yes" ? 1 : 0 + name = "ServiceQuotasReadOnlyPolicy" + role = aws_iam_role.lambda_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "servicequotas:ListRequestedServiceQuotaChangeHistory", + "servicequotas:GetServiceQuota", + "servicequotas:GetAWSDefaultServiceQuota" + ] + Resource = "*" + } + ] + }) +} \ No newline at end of file diff --git a/terraform/linked-accounts/outputs.tf b/terraform/linked-accounts/outputs.tf new file mode 100644 index 00000000..52f11c6e --- /dev/null +++ b/terraform/linked-accounts/outputs.tf @@ -0,0 +1,4 @@ +output "lambda_role_arn" { + description = "ARN of the created Lambda execution role" + value = aws_iam_role.lambda_role.arn +} \ No newline at end of file diff --git a/terraform/linked-accounts/terraform.tfvars b/terraform/linked-accounts/terraform.tfvars new file mode 100644 index 00000000..2683850a --- /dev/null +++ b/terraform/linked-accounts/terraform.tfvars @@ -0,0 +1,18 @@ +# Resource prefix for naming +resource_prefix = "CID-DC-" + +# Module permissions +allow_ta_module = "yes" +allow_support_cases = "yes" +allow_budgets_readonly = "yes" +allow_inventory_collection = "yes" +allow_ecs_chargeback = "yes" +allow_rds_utilization = "yes" +allow_transit_gateway = "yes" +allow_service_quotas = "yes" + +# Role name +multi_account_role_name = "Optimization-Data-Multi-Account-Role" + +# Data collection account ID +data_collection_account_id = "123456789123" # Replace with actual AWS account ID \ No newline at end of file diff --git a/terraform/linked-accounts/variables.tf b/terraform/linked-accounts/variables.tf new file mode 100644 index 00000000..0b4bda7f --- /dev/null +++ b/terraform/linked-accounts/variables.tf @@ -0,0 +1,62 @@ +variable "resource_prefix" { + description = "Prefix for IAM resources" + type = string +} + +variable "multi_account_role_name" { + description = "Name of the multi-account IAM role" + type = string +} + +variable "data_collection_account_id" { + description = "AWS account ID where data collection occurs" + type = string +} + +variable "allow_ta_module" { + description = "Enable TA module permissions" + type = string + default = "no" +} + +variable "allow_support_cases" { + description = "Enable support cases permissions" + type = string + default = "no" +} + +variable "allow_budgets_readonly" { + description = "Enable budgets read-only permissions" + type = string + default = "no" +} + +variable "allow_inventory_collection" { + description = "Enable inventory collection permissions" + type = string + default = "no" +} + +variable "allow_ecs_chargeback" { + description = "Enable ECS chargeback permissions" + type = string + default = "no" +} + +variable "allow_rds_utilization" { + description = "Enable RDS utilization permissions" + type = string + default = "no" +} + +variable "allow_transit_gateway" { + description = "Enable Transit Gateway permissions" + type = string + default = "no" +} + +variable "allow_service_quotas" { + description = "Enable Service Quotas permissions" + type = string + default = "no" +} \ No newline at end of file diff --git a/terraform/linked-accounts/versions.tf b/terraform/linked-accounts/versions.tf new file mode 100644 index 00000000..331a6189 --- /dev/null +++ b/terraform/linked-accounts/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.0" + } + } +} \ No newline at end of file diff --git a/terraform/management-accounts/main.tf b/terraform/management-accounts/main.tf new file mode 100644 index 00000000..25cdf273 --- /dev/null +++ b/terraform/management-accounts/main.tf @@ -0,0 +1,27 @@ +data "aws_region" "current" {} +data "aws_partition" "current" {} + +locals { + template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/${var.tag_version}/data-collection/deploy-in-management-account.yaml" +} + +# Management account IAM role deployment +resource "aws_cloudformation_stack" "mgmt_read" { + name = "${var.resource_prefix}mgmt-read" + template_url = local.template_url + capabilities = ["CAPABILITY_NAMED_IAM"] + + parameters = { + ResourcePrefix = var.resource_prefix + ManagementAccountRole = var.management_account_role + DataCollectionAccountID = var.data_collection_account_id + IncludeBackupModule = var.backup_module + IncludeComputeOptimizerModule = var.compute_optimizer_module + IncludeCostAnomalyModule = var.cost_anomaly_module + IncludeSupportCasesModule = var.support_cases_module + IncludeHealthEventsModule = var.health_events_module + IncludeRightsizingModule = var.rightsizing_module + IncludeLicenseManagerModule = var.license_manager_module + IncludeServiceQuotasModule = var.service_quotas_module + } +} \ No newline at end of file diff --git a/terraform/management-accounts/outputs.tf b/terraform/management-accounts/outputs.tf new file mode 100644 index 00000000..0d818c8b --- /dev/null +++ b/terraform/management-accounts/outputs.tf @@ -0,0 +1,4 @@ +output "mgmt_account_stack_id" { + description = "ID of the CloudFormation stack in management account" + value = aws_cloudformation_stack.mgmt_read.id +} \ No newline at end of file diff --git a/terraform/management-accounts/terraform.tfvars b/terraform/management-accounts/terraform.tfvars new file mode 100644 index 00000000..606920e8 --- /dev/null +++ b/terraform/management-accounts/terraform.tfvars @@ -0,0 +1,14 @@ +resource_prefix = "CID-DC-" +management_account_role = "Lambda-Assume-Role-Management-Account" +data_collection_account_id = "123456789123" +tag_version = "3.6.2" + +# Module flags +backup_module = "yes" +compute_optimizer_module = "no" +cost_anomaly_module = "yes" +support_cases_module = "yes" +health_events_module = "yes" +rightsizing_module = "yes" +license_manager_module = "yes" +service_quotas_module = "yes" \ No newline at end of file diff --git a/terraform/management-accounts/variables.tf b/terraform/management-accounts/variables.tf new file mode 100644 index 00000000..b9386f40 --- /dev/null +++ b/terraform/management-accounts/variables.tf @@ -0,0 +1,75 @@ +variable "resource_prefix" { + description = "Prefix for IAM resources" + type = string + default = "CID-DC-" +} + +variable "management_account_role" { + description = "Name of the management account IAM role" + type = string + default = "Lambda-Assume-Role-Management-Account" +} + +variable "data_collection_account_id" { + description = "Account ID for data collection" + type = string +} + +variable "backup_module" { + description = "Enable AWS Backup module permissions" + type = string + default = "no" +} + +variable "compute_optimizer_module" { + description = "Enable Compute Optimizer module permissions" + type = string + default = "no" +} + +variable "cost_anomaly_module" { + description = "Enable Cost Anomaly Detection module permissions" + type = string + default = "no" +} + +variable "support_cases_module" { + description = "Enable AWS Support Cases module permissions" + type = string + default = "no" +} + +variable "health_events_module" { + description = "Enable AWS Health Events module permissions" + type = string + default = "no" +} + +variable "rightsizing_module" { + description = "Enable Rightsizing module permissions" + type = string + default = "no" +} + +variable "license_manager_module" { + description = "Enable License Manager module permissions" + type = string + default = "no" +} + +variable "service_quotas_module" { + description = "Enable Service Quotas module permissions" + type = string + default = "no" +} + +variable "tag_version" { + description = "GitHub tag version using for the deployment (e.g. 4.0.7)" + type = string + default = "" +} + +validation { + condition = var.tag_version == "" || can(regex("^\\d+\\.\\d+\\.\\d+$", var.tag_version)) + error_message = "The tag_version must be in the format X.Y.Z where X, Y, and Z are digits (e.g., 4.0.7)" +} \ No newline at end of file diff --git a/terraform/management-accounts/versions.tf b/terraform/management-accounts/versions.tf new file mode 100644 index 00000000..331a6189 --- /dev/null +++ b/terraform/management-accounts/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.13.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.0" + } + } +} \ No newline at end of file From d13a34d833765802cd702fef411c4dd462be1a39 Mon Sep 17 00:00:00 2001 From: Marcodb Date: Fri, 7 Feb 2025 13:53:04 +0100 Subject: [PATCH 3/5] Update readme.md --- terraform/README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/terraform/README.md b/terraform/README.md index dbc7b7ae..5186b894 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -24,16 +24,6 @@ This module deploys the necessary IAM roles to each linked account for monitorin - Creates standardized IAM roles across member accounts - Configures granular permissions for various data collection features -- Supports multiple optional monitoring components: - - Trusted Advisor - - Support Cases - - AWS Budgets - - Inventory Collection - - ECS Chargeback - - RDS Utilization - - Transit Gateway - - Service Quotas - ### 3. Data Collection Module From 8c5070765e5810607507273aa766362d23caa3b8 Mon Sep 17 00:00:00 2001 From: Marcodb Date: Fri, 7 Feb 2025 13:58:28 +0100 Subject: [PATCH 4/5] Update readme with Version Lock --- terraform/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/terraform/README.md b/terraform/README.md index 5186b894..0f693382 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -29,6 +29,15 @@ This module deploys the necessary IAM roles to each linked account for monitorin This module orchestrates the data collection process and deploys CloudFormation template for data collection as a wrapper +## Version Locking + +For production deployments, you should lock module versions to a release tag to better control when and what updates are made. +Use "*tag_version*" variable to select a release tag. +```bash +tag_version = "3.6.2" +``` + +For a complete list of release tags, visit [https://github.com/awslabs/cid-data-collection-framework/tags]https://github.com/awslabs/cid-data-collection-framework/tags. ## Deployment Process From cabc055e8b6a634ca0cc678765531cdb49ca18a1 Mon Sep 17 00:00:00 2001 From: Marcodb Date: Fri, 7 Feb 2025 14:07:54 +0100 Subject: [PATCH 5/5] Removed duplicated directory --- data-collection/deploy/terraform/README.md | 31 ++- .../deploy/terraform/data-collection/main.tf | 2 +- .../data-collection/terraform.tfvars | 1 + .../terraform/data-collection/variables.tf | 11 + .../terraform/management-accounts/main.tf | 2 +- .../management-accounts/terraform.tfvars | 1 + .../management-accounts/variables.tf | 11 + terraform/README.md | 114 ----------- terraform/data-collection/main.tf | 50 ----- terraform/data-collection/outputs.tf | 9 - terraform/data-collection/terraform.tfvars | 32 --- terraform/data-collection/variables.tf | 187 ----------------- terraform/data-collection/versions.tf | 14 -- terraform/linked-accounts/main.tf | 192 ------------------ terraform/linked-accounts/outputs.tf | 4 - terraform/linked-accounts/terraform.tfvars | 18 -- terraform/linked-accounts/variables.tf | 62 ------ terraform/linked-accounts/versions.tf | 10 - terraform/management-accounts/main.tf | 27 --- terraform/management-accounts/outputs.tf | 4 - .../management-accounts/terraform.tfvars | 14 -- terraform/management-accounts/variables.tf | 75 ------- terraform/management-accounts/versions.tf | 10 - 23 files changed, 46 insertions(+), 835 deletions(-) delete mode 100644 terraform/README.md delete mode 100644 terraform/data-collection/main.tf delete mode 100644 terraform/data-collection/outputs.tf delete mode 100644 terraform/data-collection/terraform.tfvars delete mode 100644 terraform/data-collection/variables.tf delete mode 100644 terraform/data-collection/versions.tf delete mode 100644 terraform/linked-accounts/main.tf delete mode 100644 terraform/linked-accounts/outputs.tf delete mode 100644 terraform/linked-accounts/terraform.tfvars delete mode 100644 terraform/linked-accounts/variables.tf delete mode 100644 terraform/linked-accounts/versions.tf delete mode 100644 terraform/management-accounts/main.tf delete mode 100644 terraform/management-accounts/outputs.tf delete mode 100644 terraform/management-accounts/terraform.tfvars delete mode 100644 terraform/management-accounts/variables.tf delete mode 100644 terraform/management-accounts/versions.tf diff --git a/data-collection/deploy/terraform/README.md b/data-collection/deploy/terraform/README.md index e0f73b59..0f693382 100644 --- a/data-collection/deploy/terraform/README.md +++ b/data-collection/deploy/terraform/README.md @@ -24,21 +24,20 @@ This module deploys the necessary IAM roles to each linked account for monitorin - Creates standardized IAM roles across member accounts - Configures granular permissions for various data collection features -- Supports multiple optional monitoring components: - - Trusted Advisor - - Support Cases - - AWS Budgets - - Inventory Collection - - ECS Chargeback - - RDS Utilization - - Transit Gateway - - Service Quotas - ### 3. Data Collection Module This module orchestrates the data collection process and deploys CloudFormation template for data collection as a wrapper +## Version Locking + +For production deployments, you should lock module versions to a release tag to better control when and what updates are made. +Use "*tag_version*" variable to select a release tag. +```bash +tag_version = "3.6.2" +``` + +For a complete list of release tags, visit [https://github.com/awslabs/cid-data-collection-framework/tags]https://github.com/awslabs/cid-data-collection-framework/tags. ## Deployment Process @@ -102,4 +101,14 @@ Common issues and solutions: 3. **Data Collection Failures** - Verify S3 bucket permissions - Check CloudWatch Logs for execution errors - - Ensure correct role ARNs are being used \ No newline at end of file + - Ensure correct role ARNs are being used + +## Contributing + +Contributions to improve the solution are welcome! Please: + +1. Fork the repository +2. Create a feature branch +3. Submit a pull request with detailed description of changes +4. Ensure all existing tests pass +5. Add new tests as needed \ No newline at end of file diff --git a/data-collection/deploy/terraform/data-collection/main.tf b/data-collection/deploy/terraform/data-collection/main.tf index 6ade6658..9bb29960 100644 --- a/data-collection/deploy/terraform/data-collection/main.tf +++ b/data-collection/deploy/terraform/data-collection/main.tf @@ -3,7 +3,7 @@ data "aws_caller_identity" "current" {} data "aws_partition" "current" {} locals { - template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/data-collection/deploy-data-collection.yaml" + template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/${var.tag_version}/data-collection/deploy-data-collection.yaml" } resource "aws_cloudformation_stack" "data_collection" { diff --git a/data-collection/deploy/terraform/data-collection/terraform.tfvars b/data-collection/deploy/terraform/data-collection/terraform.tfvars index d4b484d7..3c2099ea 100644 --- a/data-collection/deploy/terraform/data-collection/terraform.tfvars +++ b/data-collection/deploy/terraform/data-collection/terraform.tfvars @@ -1,5 +1,6 @@ # Default values for variables resource_prefix = "CID-DC-" +tag_version = "3.6.2" destination_bucket = "cid-data-" management_account_id = "987654321987" # Required: Add your management account IDs as a comma separated list diff --git a/data-collection/deploy/terraform/data-collection/variables.tf b/data-collection/deploy/terraform/data-collection/variables.tf index c92bd65e..c33c2d41 100644 --- a/data-collection/deploy/terraform/data-collection/variables.tf +++ b/data-collection/deploy/terraform/data-collection/variables.tf @@ -174,3 +174,14 @@ variable "tags" { type = map(string) default = {} } + +variable "tag_version" { + description = "GitHub tag version using for the deployment (e.g. 4.0.7)" + type = string + default = "" +} + +validation { + condition = var.tag_version == "" || can(regex("^\\d+\\.\\d+\\.\\d+$", var.tag_version)) + error_message = "The tag_version must be in the format X.Y.Z where X, Y, and Z are digits (e.g., 4.0.7)" +} \ No newline at end of file diff --git a/data-collection/deploy/terraform/management-accounts/main.tf b/data-collection/deploy/terraform/management-accounts/main.tf index 5d1aab1a..25cdf273 100644 --- a/data-collection/deploy/terraform/management-accounts/main.tf +++ b/data-collection/deploy/terraform/management-accounts/main.tf @@ -2,7 +2,7 @@ data "aws_region" "current" {} data "aws_partition" "current" {} locals { - template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/data-collection/deploy-in-management-account.yaml" + template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/${var.tag_version}/data-collection/deploy-in-management-account.yaml" } # Management account IAM role deployment diff --git a/data-collection/deploy/terraform/management-accounts/terraform.tfvars b/data-collection/deploy/terraform/management-accounts/terraform.tfvars index f7ac4bbb..606920e8 100644 --- a/data-collection/deploy/terraform/management-accounts/terraform.tfvars +++ b/data-collection/deploy/terraform/management-accounts/terraform.tfvars @@ -1,6 +1,7 @@ resource_prefix = "CID-DC-" management_account_role = "Lambda-Assume-Role-Management-Account" data_collection_account_id = "123456789123" +tag_version = "3.6.2" # Module flags backup_module = "yes" diff --git a/data-collection/deploy/terraform/management-accounts/variables.tf b/data-collection/deploy/terraform/management-accounts/variables.tf index 3d2390ee..b9386f40 100644 --- a/data-collection/deploy/terraform/management-accounts/variables.tf +++ b/data-collection/deploy/terraform/management-accounts/variables.tf @@ -62,3 +62,14 @@ variable "service_quotas_module" { type = string default = "no" } + +variable "tag_version" { + description = "GitHub tag version using for the deployment (e.g. 4.0.7)" + type = string + default = "" +} + +validation { + condition = var.tag_version == "" || can(regex("^\\d+\\.\\d+\\.\\d+$", var.tag_version)) + error_message = "The tag_version must be in the format X.Y.Z where X, Y, and Z are digits (e.g., 4.0.7)" +} \ No newline at end of file diff --git a/terraform/README.md b/terraform/README.md deleted file mode 100644 index 0f693382..00000000 --- a/terraform/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Cloud Intelligence Dashboards - Terraform Deployment Solution - -## Overview - -This Terraform solution simplifies the deployment of data collection components for Cloud Intelligence Dashboards across AWS Organizations. It provides a streamlined approach to configure and manage data collection from multiple AWS accounts without relying on StackSets, offering better control and flexibility over the deployment process. - -The solution acts as a Terraform wrapper around AWS CloudFormation templates, providing a more manageable and maintainable infrastructure-as-code approach for enterprise environments. - -## Architecture - -The solution consists of three main Terraform modules that work together to enable comprehensive data collection: - -### 1. Management Account Module - -This module configures the necessary resources in the AWS Organizations management account: - -- Creates IAM roles with specific permissions for monitoring and data collection -- Enables required AWS services and APIs -- Configures access for specific features like AWS Backup, Compute Optimizer, and Cost Anomaly Detection - -### 2. Linked Account Role Module - -This module deploys the necessary IAM roles to each linked account for monitoring purposes: - -- Creates standardized IAM roles across member accounts -- Configures granular permissions for various data collection features - -### 3. Data Collection Module - -This module orchestrates the data collection process and deploys CloudFormation template for data collection as a wrapper - -## Version Locking - -For production deployments, you should lock module versions to a release tag to better control when and what updates are made. -Use "*tag_version*" variable to select a release tag. -```bash -tag_version = "3.6.2" -``` - -For a complete list of release tags, visit [https://github.com/awslabs/cid-data-collection-framework/tags]https://github.com/awslabs/cid-data-collection-framework/tags. - -## Deployment Process - -### Step 1: Deploy Management Account Module - -1. Configure your AWS credentials for the management account -2. Initialize and apply the management account module: -```bash -cd management-accounts -terraform init -terraform plan -terraform apply -``` - -### Step 2: Deploy Linked Account Roles - -1. For each linked account: - - Configure AWS credentials - - Initialize and apply the linked account role module -```bash -cd linked-accounts -terraform init -terraform plan -terraform apply -``` - -### Step 3: Deploy Data Collection - -1. Use the outputs from previous modules as inputs -2. Deploy the data collection module: -```bash -cd data-collection -terraform init -terraform plan -terraform apply -``` - -## Why Not StackSets? - -This solution intentionally avoids using AWS StackSets for several reasons: -- Greater control over deployment timing and sequencing -- Simplified troubleshooting and rollback procedures -- Better integration with existing Terraform workflows -- More flexible permission management -- Enhanced visibility of deployment status per account - -## Troubleshooting - -Common issues and solutions: - -1. **Linked Account Access Issues** - - Verify IAM role trust relationships - - Check for correct resource prefixes - - Ensure AWS Organizations access is properly configured - -2. **Permission Errors** - - Review IAM role policies in both management and linked accounts - - Verify service enablement in AWS Organizations - - Check for any service quotas or limits - -3. **Data Collection Failures** - - Verify S3 bucket permissions - - Check CloudWatch Logs for execution errors - - Ensure correct role ARNs are being used - -## Contributing - -Contributions to improve the solution are welcome! Please: - -1. Fork the repository -2. Create a feature branch -3. Submit a pull request with detailed description of changes -4. Ensure all existing tests pass -5. Add new tests as needed \ No newline at end of file diff --git a/terraform/data-collection/main.tf b/terraform/data-collection/main.tf deleted file mode 100644 index 9bb29960..00000000 --- a/terraform/data-collection/main.tf +++ /dev/null @@ -1,50 +0,0 @@ -data "aws_region" "current" {} -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/${var.tag_version}/data-collection/deploy-data-collection.yaml" -} - -resource "aws_cloudformation_stack" "data_collection" { - name = "${var.resource_prefix}data-collection" - capabilities = [ - "CAPABILITY_NAMED_IAM", - "CAPABILITY_AUTO_EXPAND" - ] - template_url = local.template_url - - parameters = { - ResourcePrefix = var.resource_prefix - DatabaseName = var.database_name - DestinationBucket = var.destination_bucket - ManagementAccountID = var.management_account_id - ManagementAccountRole = var.management_account_role - MultiAccountRoleName = var.multi_account_role_name - Schedule = var.schedule - ScheduleFrequent = var.schedule_frequent - CFNSourceBucket = var.cfn_source_bucket - RegionsInScope = var.regions_in_scope - DatabaseName = var.database_name - DataBucketsKmsKeysArns = var.data_buckets_kms_keys_arns - IncludeTAModule = var.include_ta_module ? "yes" : "no" - IncludeRDSUtilizationModule = var.include_rds_usage_module ? "yes" : "no" - IncludeOrgDataModule = var.include_org_data_module ? "yes" : "no" - IncludeRightsizingModule = var.include_ce_rightsizing_module ? "yes" : "no" - IncludeCostAnomalyModule = var.include_cost_anomaly_module ? "yes" : "no" - IncludeSupportCasesModule = var.include_support_cases_module ? "yes" : "no" - IncludeBackupModule = var.include_backup_module ? "yes" : "no" - IncludeInventoryCollectorModule = var.include_inventory_module ? "yes" : "no" - IncludeComputeOptimizerModule = var.include_compute_optimizer_module ? "yes" : "no" - IncludeECSChargebackModule = var.include_ecs_chargeback_module ? "yes" : "no" - IncludeBudgetsModule = var.include_budgets_module ? "yes" : "no" - IncludeTransitGatewayModule = var.include_transit_gateway_module ? "yes" : "no" - IncludeAWSFeedsModule = var.include_aws_feeds_module ? "yes" : "no" - IncludeHealthEventsModule = var.include_health_events_module ? "yes" : "no" - IncludeLicenseManagerModule = var.include_license_manager_module ? "yes" : "no" - IncludeServiceQuotasModule = var.include_service_quotas_module ? "yes" : "no" - IncludeQuickSightModule = var.include_quicksight_module ? "yes" : "no" - } - - tags = var.tags -} diff --git a/terraform/data-collection/outputs.tf b/terraform/data-collection/outputs.tf deleted file mode 100644 index 48d2ea9f..00000000 --- a/terraform/data-collection/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "stack_id" { - description = "The unique identifier for the stack" - value = aws_cloudformation_stack.data_collection.id -} - -output "stack_outputs" { - description = "Map of outputs from the CloudFormation stack" - value = aws_cloudformation_stack.data_collection.outputs -} diff --git a/terraform/data-collection/terraform.tfvars b/terraform/data-collection/terraform.tfvars deleted file mode 100644 index 3c2099ea..00000000 --- a/terraform/data-collection/terraform.tfvars +++ /dev/null @@ -1,32 +0,0 @@ -# Default values for variables -resource_prefix = "CID-DC-" -tag_version = "3.6.2" - -destination_bucket = "cid-data-" -management_account_id = "987654321987" # Required: Add your management account IDs as a comma separated list -management_account_role = "Lambda-Assume-Role-Management-Account" # Required: Add your management account role -multi_account_role_name = "Optimization-Data-Multi-Account-Role" # Required: Add your multi-account role name -regions_in_scope = "us-east-1,eu-west-1,eu-central-1" # Update with your desired regions -data_buckets_kms_keys_arns = "KMS_KEY_ARN" -schedule = "rate(1 day)" # Update with your desired schedule -schedule_frequent = "rate(1 day)" # Update with your desired frequent schedule -database_name = "optimization_data" -cfn_source_bucket = "aws-managed-cost-intelligence-dashboards" # Don't Change -include_ta_module = "true" -include_rds_usage_module = "true" -include_org_data_module = "true" -include_ce_rightsizing_module = "true" -include_cost_anomaly_module = "true" -include_support_cases_module = "true" -include_backup_module = "true" -include_inventory_module = "true" -include_pricing_module = "true" -include_compute_optimizer_module = "false" -include_ecs_chargeback_module = "true" -include_budgets_module = "true" -include_transit_gateway_module = "true" -include_aws_feeds_module = "true" -include_health_events_module = "true" -include_license_manager_module = "true" -include_service_quotas_module = "true" -include_quicksight_module = "true" diff --git a/terraform/data-collection/variables.tf b/terraform/data-collection/variables.tf deleted file mode 100644 index c33c2d41..00000000 --- a/terraform/data-collection/variables.tf +++ /dev/null @@ -1,187 +0,0 @@ -variable "resource_prefix" { - description = "Prefix to be used for all resources created by this module" - type = string - default = "CID-DC-" -} - -variable "cfn_source_bucket" { - description = "Name of the S3 bucket where CloudFormation templates are stored" - type = string - default = "aws-managed-cost-intelligence-dashboards" -} - -variable "destination_bucket" { - description = "Name of the S3 bucket where data will be stored" - type = string - default = "cid-data-" -} - -variable "management_account_id" { - description = "Comma-delimited list of Account IDs for Management Account IDs" - type = string -} - -variable "management_account_role" { - description = "Management account role" - type = string - default = "Lambda-Assume-Role-Management-Account" -} - -variable "multi_account_role_name" { - description = "Multi Account Role Name" - type = string - default = "Optimization-Data-Multi-Account-Role" -} - -variable "regions_in_scope" { - description = "Comma-delimited list of AWS regions for resource data collection (e.g., \"us-east-1,eu-west-1,ap-northeast-1\")" - type = string -} - -variable "data_buckets_kms_keys_arns" { - description = "ARNs of KMS Keys for data buckets and/or Glue Catalog. Comma separated list, no spaces. Keep empty if data Buckets and Glue Catalog are not Encrypted with KMS. You can also set it to '*' to grant decrypt permission for all the keys." - type = string -} - -variable "schedule" { - description = "Cron expression for execution schedule.\")" - type = string - default = "rate(14 days)" -} - -variable "schedule_frequent" { - description = "Cron expression for more frequent executions.\")" - type = string - default = "rate(1 day)" -} - -variable "database_name" { - description = "Name of the Glue Database to be created" - type = string - default = "optimization_data" -} - -variable "include_ta_module" { - description = "Whether to include the Trusted Advisor module" - type = bool - default = true -} - -variable "include_rds_usage_module" { - description = "Whether to include the RDS Usage module" - type = bool - default = true -} - -variable "include_org_data_module" { - description = "Whether to include the Organization Data module" - type = bool - default = true -} - -variable "include_ce_rightsizing_module" { - description = "Whether to include the Cost Explorer Rightsizing module" - type = bool - default = true -} - -variable "include_cost_anomaly_module" { - description = "Whether to include the Cost Anomaly module" - type = bool - default = true -} - -variable "include_support_cases_module" { - description = "Whether to include the Support Cases module" - type = bool - default = true -} - -variable "include_backup_module" { - description = "Whether to include the Backup module" - type = bool - default = true -} - -variable "include_inventory_module" { - description = "Whether to include the Inventory module" - type = bool - default = true -} - -variable "include_pricing_module" { - description = "Whether to include the Pricing module" - type = bool - default = true -} - -variable "include_compute_optimizer_module" { - description = "Whether to include the Compute Optimizer module" - type = bool - default = true -} - -variable "include_ecs_chargeback_module" { - description = "Whether to include the ECS Chargeback module" - type = bool - default = true -} - -variable "include_budgets_module" { - description = "Whether to include the Budgets module" - type = bool - default = true -} - -variable "include_transit_gateway_module" { - description = "Whether to include the Transit Gateway module" - type = bool - default = true -} - -variable "include_aws_feeds_module" { - description = "Whether to include the AWS Feeds module" - type = bool - default = true -} - -variable "include_health_events_module" { - description = "Whether to include the Health Events module" - type = bool - default = true -} - -variable "include_license_manager_module" { - description = "Whether to include the License Manager module" - type = bool - default = true -} - -variable "include_service_quotas_module" { - description = "Whether to include the Service Quotas module" - type = bool - default = true -} - -variable "include_quicksight_module" { - description = "Whether to include the QuickSight module" - type = bool - default = true -} - -variable "tags" { - description = "Tags to be added to all resources" - type = map(string) - default = {} -} - -variable "tag_version" { - description = "GitHub tag version using for the deployment (e.g. 4.0.7)" - type = string - default = "" -} - -validation { - condition = var.tag_version == "" || can(regex("^\\d+\\.\\d+\\.\\d+$", var.tag_version)) - error_message = "The tag_version must be in the format X.Y.Z where X, Y, and Z are digits (e.g., 4.0.7)" -} \ No newline at end of file diff --git a/terraform/data-collection/versions.tf b/terraform/data-collection/versions.tf deleted file mode 100644 index 838cd8bc..00000000 --- a/terraform/data-collection/versions.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_version = ">= 0.13.1" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 3.63" - } - } -} - -provider "aws" { - alias = "main" -} diff --git a/terraform/linked-accounts/main.tf b/terraform/linked-accounts/main.tf deleted file mode 100644 index a2de83b2..00000000 --- a/terraform/linked-accounts/main.tf +++ /dev/null @@ -1,192 +0,0 @@ -data "aws_region" "current" {} -data "aws_partition" "current" {} - -# Lambda execution role for data collection -resource "aws_iam_role" "lambda_role" { - name = "${var.resource_prefix}${var.multi_account_role_name}" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - AWS = "arn:${data.aws_partition.current.partition}:iam::${var.data_collection_account_id}:root" - } - } - ] - }) -} - -# Conditional policies based on features enabled -resource "aws_iam_role_policy" "ta_policy" { - count = var.allow_ta_module == "yes" ? 1 : 0 - name = "TAPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "support:DescribeTrustedAdvisorChecks", - "support:DescribeTrustedAdvisorCheckResult" - ] - Resource = "*" - } - ] - }) -} - -resource "aws_iam_role_policy" "support_cases_policy" { - count = var.allow_support_cases == "yes" ? 1 : 0 - name = "SupportCasesPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "support:DescribeCases" - ] - Resource = "*" - } - ] - }) -} - -resource "aws_iam_role_policy" "budgets_readonly_policy" { - count = var.allow_budgets_readonly == "yes" ? 1 : 0 - name = "BudgetsReadOnlyPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "budgets:ViewBudget", - "budgets:ListTagsForResource" - ] - Resource = "*" - } - ] - }) -} - -resource "aws_iam_role_policy" "inventory_collector_policy" { - count = var.allow_inventory_collection == "yes" ? 1 : 0 - name = "InventoryCollectorPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "ec2:DescribeInstances", - "ec2:DescribeImages", - "ec2:DescribeNetworkInterfaces", - "ec2:DescribeVolumes", - "ec2:DescribeSnapshots", - "ec2:DescribeVpcs", - "ec2:DescribeRegions" - ] - Resource = "*" - } - ] - }) -} - -resource "aws_iam_role_policy" "ecs_chargeback_policy" { - count = var.allow_ecs_chargeback == "yes" ? 1 : 0 - name = "ECSChargebackPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "ecs:ListClusters", - "ecs:ListContainerInstances", - "ecs:DescribeContainerInstances" - ] - Resource = "*" - } - ] - }) -} - -resource "aws_iam_role_policy" "rds_utilization_policy" { - count = var.allow_rds_utilization == "yes" ? 1 : 0 - name = "RDSUtilizationPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "rds:DescribeDBInstances", - "rds:DescribeDBClusters", - "cloudwatch:GetMetricData", - "cloudwatch:GetMetricStatistics", - "cloudwatch:ListMetrics" - ] - Resource = "*" - } - ] - }) -} - -resource "aws_iam_role_policy" "transit_gateway_policy" { - count = var.allow_transit_gateway == "yes" ? 1 : 0 - name = "TransitGatewayPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "ec2:DescribeTransitGatewayAttachments", - "cloudwatch:Describe*", - "cloudwatch:Get*", - "cloudwatch:List*" - ] - Resource = "*" - } - ] - }) -} - -resource "aws_iam_role_policy" "service_quotas_policy" { - count = var.allow_service_quotas == "yes" ? 1 : 0 - name = "ServiceQuotasReadOnlyPolicy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "servicequotas:ListRequestedServiceQuotaChangeHistory", - "servicequotas:GetServiceQuota", - "servicequotas:GetAWSDefaultServiceQuota" - ] - Resource = "*" - } - ] - }) -} \ No newline at end of file diff --git a/terraform/linked-accounts/outputs.tf b/terraform/linked-accounts/outputs.tf deleted file mode 100644 index 52f11c6e..00000000 --- a/terraform/linked-accounts/outputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "lambda_role_arn" { - description = "ARN of the created Lambda execution role" - value = aws_iam_role.lambda_role.arn -} \ No newline at end of file diff --git a/terraform/linked-accounts/terraform.tfvars b/terraform/linked-accounts/terraform.tfvars deleted file mode 100644 index 2683850a..00000000 --- a/terraform/linked-accounts/terraform.tfvars +++ /dev/null @@ -1,18 +0,0 @@ -# Resource prefix for naming -resource_prefix = "CID-DC-" - -# Module permissions -allow_ta_module = "yes" -allow_support_cases = "yes" -allow_budgets_readonly = "yes" -allow_inventory_collection = "yes" -allow_ecs_chargeback = "yes" -allow_rds_utilization = "yes" -allow_transit_gateway = "yes" -allow_service_quotas = "yes" - -# Role name -multi_account_role_name = "Optimization-Data-Multi-Account-Role" - -# Data collection account ID -data_collection_account_id = "123456789123" # Replace with actual AWS account ID \ No newline at end of file diff --git a/terraform/linked-accounts/variables.tf b/terraform/linked-accounts/variables.tf deleted file mode 100644 index 0b4bda7f..00000000 --- a/terraform/linked-accounts/variables.tf +++ /dev/null @@ -1,62 +0,0 @@ -variable "resource_prefix" { - description = "Prefix for IAM resources" - type = string -} - -variable "multi_account_role_name" { - description = "Name of the multi-account IAM role" - type = string -} - -variable "data_collection_account_id" { - description = "AWS account ID where data collection occurs" - type = string -} - -variable "allow_ta_module" { - description = "Enable TA module permissions" - type = string - default = "no" -} - -variable "allow_support_cases" { - description = "Enable support cases permissions" - type = string - default = "no" -} - -variable "allow_budgets_readonly" { - description = "Enable budgets read-only permissions" - type = string - default = "no" -} - -variable "allow_inventory_collection" { - description = "Enable inventory collection permissions" - type = string - default = "no" -} - -variable "allow_ecs_chargeback" { - description = "Enable ECS chargeback permissions" - type = string - default = "no" -} - -variable "allow_rds_utilization" { - description = "Enable RDS utilization permissions" - type = string - default = "no" -} - -variable "allow_transit_gateway" { - description = "Enable Transit Gateway permissions" - type = string - default = "no" -} - -variable "allow_service_quotas" { - description = "Enable Service Quotas permissions" - type = string - default = "no" -} \ No newline at end of file diff --git a/terraform/linked-accounts/versions.tf b/terraform/linked-accounts/versions.tf deleted file mode 100644 index 331a6189..00000000 --- a/terraform/linked-accounts/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 0.13.1" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 3.0" - } - } -} \ No newline at end of file diff --git a/terraform/management-accounts/main.tf b/terraform/management-accounts/main.tf deleted file mode 100644 index 25cdf273..00000000 --- a/terraform/management-accounts/main.tf +++ /dev/null @@ -1,27 +0,0 @@ -data "aws_region" "current" {} -data "aws_partition" "current" {} - -locals { - template_url = "https://aws-managed-cost-intelligence-dashboards-${data.aws_region.current.name}.s3.amazonaws.com/cfn/${var.tag_version}/data-collection/deploy-in-management-account.yaml" -} - -# Management account IAM role deployment -resource "aws_cloudformation_stack" "mgmt_read" { - name = "${var.resource_prefix}mgmt-read" - template_url = local.template_url - capabilities = ["CAPABILITY_NAMED_IAM"] - - parameters = { - ResourcePrefix = var.resource_prefix - ManagementAccountRole = var.management_account_role - DataCollectionAccountID = var.data_collection_account_id - IncludeBackupModule = var.backup_module - IncludeComputeOptimizerModule = var.compute_optimizer_module - IncludeCostAnomalyModule = var.cost_anomaly_module - IncludeSupportCasesModule = var.support_cases_module - IncludeHealthEventsModule = var.health_events_module - IncludeRightsizingModule = var.rightsizing_module - IncludeLicenseManagerModule = var.license_manager_module - IncludeServiceQuotasModule = var.service_quotas_module - } -} \ No newline at end of file diff --git a/terraform/management-accounts/outputs.tf b/terraform/management-accounts/outputs.tf deleted file mode 100644 index 0d818c8b..00000000 --- a/terraform/management-accounts/outputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "mgmt_account_stack_id" { - description = "ID of the CloudFormation stack in management account" - value = aws_cloudformation_stack.mgmt_read.id -} \ No newline at end of file diff --git a/terraform/management-accounts/terraform.tfvars b/terraform/management-accounts/terraform.tfvars deleted file mode 100644 index 606920e8..00000000 --- a/terraform/management-accounts/terraform.tfvars +++ /dev/null @@ -1,14 +0,0 @@ -resource_prefix = "CID-DC-" -management_account_role = "Lambda-Assume-Role-Management-Account" -data_collection_account_id = "123456789123" -tag_version = "3.6.2" - -# Module flags -backup_module = "yes" -compute_optimizer_module = "no" -cost_anomaly_module = "yes" -support_cases_module = "yes" -health_events_module = "yes" -rightsizing_module = "yes" -license_manager_module = "yes" -service_quotas_module = "yes" \ No newline at end of file diff --git a/terraform/management-accounts/variables.tf b/terraform/management-accounts/variables.tf deleted file mode 100644 index b9386f40..00000000 --- a/terraform/management-accounts/variables.tf +++ /dev/null @@ -1,75 +0,0 @@ -variable "resource_prefix" { - description = "Prefix for IAM resources" - type = string - default = "CID-DC-" -} - -variable "management_account_role" { - description = "Name of the management account IAM role" - type = string - default = "Lambda-Assume-Role-Management-Account" -} - -variable "data_collection_account_id" { - description = "Account ID for data collection" - type = string -} - -variable "backup_module" { - description = "Enable AWS Backup module permissions" - type = string - default = "no" -} - -variable "compute_optimizer_module" { - description = "Enable Compute Optimizer module permissions" - type = string - default = "no" -} - -variable "cost_anomaly_module" { - description = "Enable Cost Anomaly Detection module permissions" - type = string - default = "no" -} - -variable "support_cases_module" { - description = "Enable AWS Support Cases module permissions" - type = string - default = "no" -} - -variable "health_events_module" { - description = "Enable AWS Health Events module permissions" - type = string - default = "no" -} - -variable "rightsizing_module" { - description = "Enable Rightsizing module permissions" - type = string - default = "no" -} - -variable "license_manager_module" { - description = "Enable License Manager module permissions" - type = string - default = "no" -} - -variable "service_quotas_module" { - description = "Enable Service Quotas module permissions" - type = string - default = "no" -} - -variable "tag_version" { - description = "GitHub tag version using for the deployment (e.g. 4.0.7)" - type = string - default = "" -} - -validation { - condition = var.tag_version == "" || can(regex("^\\d+\\.\\d+\\.\\d+$", var.tag_version)) - error_message = "The tag_version must be in the format X.Y.Z where X, Y, and Z are digits (e.g., 4.0.7)" -} \ No newline at end of file diff --git a/terraform/management-accounts/versions.tf b/terraform/management-accounts/versions.tf deleted file mode 100644 index 331a6189..00000000 --- a/terraform/management-accounts/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 0.13.1" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 3.0" - } - } -} \ No newline at end of file