From 92dc46c027ea99122693971c0f5baf12d8505f61 Mon Sep 17 00:00:00 2001 From: andrew-corbalt <130405742+andrew-corbalt@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:20:33 -0700 Subject: [PATCH] Use full role ARN and support overriding roles on a per account basis (#26) --- .github/workflows/build-and-push-dev.yml | 3 +- README.md | 9 +- go.mod | 1 + go.sum | 2 + main.go | 10 +- pkg/teams/team_map_test_duplicate.json | 12 +- pkg/teams/team_map_test_invalid_arn.json | 19 ++ pkg/teams/team_map_test_valid.json | 12 +- pkg/teams/teams.go | 17 ++ pkg/teams/teams_test.go | 18 +- scriptRunner.sh | 1 - terraform/README.md | 7 + terraform/collector/.terraform.lock.hcl | 24 +++ terraform/collector/main.tf | 178 ++++++++++++++++++ terraform/collector/team_map.json | 14 ++ terraform/collector/terraform.tfvars | 8 + terraform/collector/variables.tf | 39 ++++ .../{ => ecr}/dev/account/.terraform.lock.hcl | 0 terraform/{ => ecr}/dev/account/ecr.tf | 0 .../{ => ecr}/dev/account/github-oidc.tf | 0 .../{ => ecr}/dev/account/terraform.tfvars | 0 terraform/{ => ecr}/dev/account/variables.tf | 0 terraform/{ => ecr}/dev/account/versions.tf | 0 .../{ => ecr}/dev/init/.terraform.lock.hcl | 0 terraform/{ => ecr}/dev/init/main.tf | 0 .../{ => ecr}/dev/init/terraform.tfstate | 0 .../dev/init/terraform.tfstate.backup | 0 terraform/{ => ecr}/dev/init/versions.tf | 0 28 files changed, 345 insertions(+), 29 deletions(-) create mode 100644 pkg/teams/team_map_test_invalid_arn.json create mode 100644 terraform/README.md create mode 100644 terraform/collector/.terraform.lock.hcl create mode 100644 terraform/collector/main.tf create mode 100644 terraform/collector/team_map.json create mode 100644 terraform/collector/terraform.tfvars create mode 100644 terraform/collector/variables.tf rename terraform/{ => ecr}/dev/account/.terraform.lock.hcl (100%) rename terraform/{ => ecr}/dev/account/ecr.tf (100%) rename terraform/{ => ecr}/dev/account/github-oidc.tf (100%) rename terraform/{ => ecr}/dev/account/terraform.tfvars (100%) rename terraform/{ => ecr}/dev/account/variables.tf (100%) rename terraform/{ => ecr}/dev/account/versions.tf (100%) rename terraform/{ => ecr}/dev/init/.terraform.lock.hcl (100%) rename terraform/{ => ecr}/dev/init/main.tf (100%) rename terraform/{ => ecr}/dev/init/terraform.tfstate (100%) rename terraform/{ => ecr}/dev/init/terraform.tfstate.backup (100%) rename terraform/{ => ecr}/dev/init/versions.tf (100%) diff --git a/.github/workflows/build-and-push-dev.yml b/.github/workflows/build-and-push-dev.yml index e9d2875..2672dcd 100644 --- a/.github/workflows/build-and-push-dev.yml +++ b/.github/workflows/build-and-push-dev.yml @@ -1,6 +1,7 @@ name: build-and-push on: + workflow_dispatch: push: branches: - main @@ -31,7 +32,7 @@ jobs: run: echo "ECR_URI=${{ steps.login-ecr.outputs.registry }}/security-hub-collector" >> $GITHUB_ENV - name: Build the Docker image - run: docker build . --file Dockerfile --tag $ECR_URI:$(git rev-parse --short $GITHUB_SHA) --tag $ECR_URI:v2 + run: docker build . --file Dockerfile --tag $ECR_URI:$(git rev-parse --short $GITHUB_SHA) - name: Push docker image to Amazon ECR run: | diff --git a/README.md b/README.md index 7fc0f38..da69f40 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Description -This tool pulls findings from AWS Security Hub and outputs them for consumption by visualization tools. To use this tool, you need a cross-account role that is valid for all accounts listed in the team map provided to the tool. +This tool pulls findings from AWS Security Hub and outputs them for consumption by visualization tools. To use this tool, you need a role ARN that is valid for each account listed in the team map provided to the tool. ## Installation @@ -18,15 +18,15 @@ To display a full list of CLI options, build the application and run `security-h You will need to create a team map file with a JSON object that describes -your teams based on account numbers and environments. For example: +your teams based on account numbers, environments and role ARN which will be used to query the account. For example: ```json { "teams": [ { "accounts": [ - { "id": "000000000001", "environment": "dev" }, - { "id": "000000000011", "environment": "test" } + { "id": "000000000011", "environment": "dev", "roleArn": "arn:aws:iam::000000000011:role/CustomRole" }, + { "id": "000000000012", "environment": "test", "roleArn": "arn:aws:iam::000000000012:role/delegatedadmin/developer/AnotherCustomRole" } ], "name":"My Team" } @@ -48,7 +48,6 @@ To run the Docker image locally for testing, do the following: docker run \ -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN -e AWS_ACCESS_KEY_ID -e TEAM_MAP \ -e AWS_REGION={region} --e ASSUME_ROLE={role name} \ -e S3_BUCKET_PATH={bucket name} \ local-collector-test ``` diff --git a/go.mod b/go.mod index 6b402fe..4474f32 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/CMSGov/security-hub-collector go 1.19 require ( + github.com/aws/aws-sdk-go v1.54.17 github.com/aws/aws-sdk-go-v2 v1.17.7 github.com/aws/aws-sdk-go-v2/config v1.18.19 github.com/aws/aws-sdk-go-v2/credentials v1.13.18 diff --git a/go.sum b/go.sum index 0577cba..04befd8 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/aws/aws-sdk-go v1.54.17 h1:ZV/qwcCIhMHgsJ6iXXPVYI0s1MdLT+5LW28ClzCUPeI= +github.com/aws/aws-sdk-go v1.54.17/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.17.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= diff --git a/main.go b/main.go index cad7439..1caaaee 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" "path" "strings" @@ -23,7 +22,6 @@ import ( // Options describes the command line options available. type Options struct { - AssumeRole string `short:"a" long:"assume-role" required:"true" description:"Role name to assume when collecting across all accounts."` OutputFileName string `short:"o" long:"output" required:"false" description:"File to direct output to." default:"SecurityHub-Findings.csv"` S3Region string `short:"s" long:"s3-region" env:"AWS_REGION" required:"false" description:"AWS region to use for s3 uploads."` SecurityHubRegions []string `short:"r" long:"sechub-regions" required:"false" default:"us-east-1" default:"us-west-2" description:"AWS regions to use for Security Hub findings."` @@ -105,14 +103,8 @@ func collectFindings(secHubRegions []string) { log.Fatalf("could not parse team map file: %v", err) } - // Add a leading slash to the provided role if it doesn't already have one - formattedRole := options.AssumeRole - if !strings.HasPrefix(formattedRole, "/") { - formattedRole = "/" + formattedRole - } - for account, teamName := range accountsToTeams { - roleArn := fmt.Sprintf("arn:aws:iam::%s:role%s", account.ID, formattedRole) + roleArn := account.RoleARN for _, secHubRegion := range secHubRegions { log.Printf("getting findings for account %v in %v", account.ID, secHubRegion) diff --git a/pkg/teams/team_map_test_duplicate.json b/pkg/teams/team_map_test_duplicate.json index d2b4c58..55f75d4 100644 --- a/pkg/teams/team_map_test_duplicate.json +++ b/pkg/teams/team_map_test_duplicate.json @@ -4,11 +4,13 @@ "accounts": [ { "environment": "dev", - "id": "account 1" + "id": "account 1", + "roleArn": "arn:aws:iam::000000000011:role/CustomRole" }, { "environment": "test", - "id": "account 11" + "id": "account 11", + "roleArn": "arn:aws:iam::000000000012:role/CustomRole" } ], "name": "Test Team 1" @@ -17,11 +19,13 @@ "accounts": [ { "environment": "impl", - "id": "account 2" + "id": "account 2", + "roleArn": "arn:aws:iam::000000000013:role/CustomRole" }, { "environment": "prod", - "id": "account 11" + "id": "account 11", + "roleArn": "arn:aws:iam::000000000014:role/CustomRole" } ], "name": "Test Team 2" diff --git a/pkg/teams/team_map_test_invalid_arn.json b/pkg/teams/team_map_test_invalid_arn.json new file mode 100644 index 0000000..c19a14b --- /dev/null +++ b/pkg/teams/team_map_test_invalid_arn.json @@ -0,0 +1,19 @@ +{ + "teams": [ + { + "accounts": [ + { + "environment": "dev", + "id": "account 1", + "roleArn": "invalid:arn:format" + }, + { + "environment": "prod", + "id": "account 2", + "roleArn": "arn:aws:iam::000000000011:role/CustomRole" + } + ], + "name": "Test Team 1" + } + ] +} diff --git a/pkg/teams/team_map_test_valid.json b/pkg/teams/team_map_test_valid.json index 9b8c511..af0533a 100644 --- a/pkg/teams/team_map_test_valid.json +++ b/pkg/teams/team_map_test_valid.json @@ -4,11 +4,13 @@ "accounts": [ { "environment": "dev", - "id": "account 1" + "id": "account 1", + "roleArn": "arn:aws:iam::000000000011:role/CustomRole" }, { "environment": "test", - "id": "account 11" + "id": "account 11", + "roleArn": "arn:aws:iam::000000000012:role/CustomRole" } ], "name": "Test Team 1" @@ -17,11 +19,13 @@ "accounts": [ { "environment": "impl", - "id": "account 2" + "id": "account 2", + "roleArn": "arn:aws:iam::000000000013:role/CustomRole" }, { "environment": "prod", - "id": "account 22" + "id": "account 22", + "roleArn": "arn:aws:iam::000000000014:role/CustomRole" } ], "name": "Test Team 2" diff --git a/pkg/teams/teams.go b/pkg/teams/teams.go index 8f5d323..dc05be7 100644 --- a/pkg/teams/teams.go +++ b/pkg/teams/teams.go @@ -6,6 +6,8 @@ import ( "os" "path/filepath" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/CMSGov/security-hub-collector/pkg/helpers" ) @@ -17,6 +19,14 @@ func (e *duplicateAccountIDError) Error() string { return e.message } +type invalidRoleARNError struct { + message string +} + +func (e *invalidRoleARNError) Error() string { + return e.message +} + // Teams is a struct describing the format we expect in the JSON file // describing the team mappings type Teams struct { @@ -34,6 +44,7 @@ type Team struct { type Account struct { ID string `json:"id"` Environment string `json:"environment"` + RoleARN string `json:"roleArn"` } // ParseTeamMap takes a path to a team mapping JSON file, reads the file, and returns a Go map of Accounts to team names @@ -101,6 +112,12 @@ func (t *Teams) accountsToTeamNames() (map[Account]string, error) { message: fmt.Sprintf("duplicate account ID: %s", account.ID), } } + + if !arn.IsARN(account.RoleARN) { + return nil, &invalidRoleARNError{ + message: fmt.Sprintf("invalid role ARN for account %s: %s Input must be a valid Role ARN", account.ID, account.RoleARN), + } + } a[account] = team.Name } } diff --git a/pkg/teams/teams_test.go b/pkg/teams/teams_test.go index 9d40027..bbb446f 100644 --- a/pkg/teams/teams_test.go +++ b/pkg/teams/teams_test.go @@ -8,10 +8,10 @@ import ( ) var expectedAccountsToTeams = map[Account]string{ - {ID: "account 1", Environment: "dev"}: "Test Team 1", - {ID: "account 11", Environment: "test"}: "Test Team 1", - {ID: "account 2", Environment: "impl"}: "Test Team 2", - {ID: "account 22", Environment: "prod"}: "Test Team 2", + {ID: "account 1", Environment: "dev", RoleARN: "arn:aws:iam::000000000011:role/CustomRole"}: "Test Team 1", + {ID: "account 11", Environment: "test", RoleARN: "arn:aws:iam::000000000012:role/CustomRole"}: "Test Team 1", + {ID: "account 2", Environment: "impl", RoleARN: "arn:aws:iam::000000000013:role/CustomRole"}: "Test Team 2", + {ID: "account 22", Environment: "prod", RoleARN: "arn:aws:iam::000000000014:role/CustomRole"}: "Test Team 2", } func TestParseTeamMap(t *testing.T) { @@ -26,9 +26,17 @@ func TestParseTeamMap(t *testing.T) { // this test checks that a duplicate account ID is caught _, err = ParseTeamMap("team_map_test_duplicate.json") - fmt.Printf("err: %v", err) + fmt.Printf("err: %v\n", err) var duplicateAccountIDError *duplicateAccountIDError if err == nil || !errors.As(err, &duplicateAccountIDError) { t.Error("ERROR: didn't get expected error for duplicate account ID", err) } + + // Test invalid ARN + _, err = ParseTeamMap("team_map_test_invalid_arn.json") + fmt.Printf("err: %v\n", err) + var invalidRoleARNError *invalidRoleARNError + if err == nil || !errors.As(err, &invalidRoleARNError) { + t.Error("ERROR: didn't get expected error for invalid Role ARN", err) + } } diff --git a/scriptRunner.sh b/scriptRunner.sh index 54547cd..5564fb8 100755 --- a/scriptRunner.sh +++ b/scriptRunner.sh @@ -9,7 +9,6 @@ security-hub-collector \ -m teammap.json \ ${OUTPUT:+-o "$OUTPUT"} \ ${S3_KEY:+-k "$S3_KEY"} \ - ${ASSUME_ROLE:+-a "$ASSUME_ROLE"} \ ${S3_BUCKET_PATH:+-b "$S3_BUCKET_PATH"} echo "Task complete" diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 0000000..f761dde --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,7 @@ +## Collector + +/collector contains the actual collector application for verification of customer issues. + +## ECR + +/ecr contains the terraform that sets up the infrastructure to deploy the image and share it to customers. diff --git a/terraform/collector/.terraform.lock.hcl b/terraform/collector/.terraform.lock.hcl new file mode 100644 index 0000000..7a3abb2 --- /dev/null +++ b/terraform/collector/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.58.0" + hashes = [ + "h1:6vsFc7SmmlElqg3k0X6azrO0yarM7UPCUF4XsAYryjA=", + "zh:15e9be54a8febe8e560362b10967cb60b680ca3f78fe207d7209b76e076f59d3", + "zh:240f6899a2cec259aa2729ce031f6af2b453f90a8b59118bb2571c54acc65db8", + "zh:2b6e8e2ab1a3dce1001503dba6086a128bb2a71652b0d0b3b107db665b7d6881", + "zh:579b0ed95247a0bd8bfb3fac7fb767547dde76026c578f4f184b5743af5e32cc", + "zh:6adcd10fd12be0be9eb78a89e745a5b77ae0d8b3522cd782456a71178aad8ccb", + "zh:7f829cef82f0a02faa97d0fbe1417a40b73fc5142e883b12eebc5b71015efac9", + "zh:81977f001998c9096f7b59710996e159774a9313c1bc03db3beb81c3e016ebef", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a5d98ac6fab6e6c85164ca7dd38f94a1e44bd70c0e8354c61f7fbabf698957cd", + "zh:c27fa4fed50f6f83ca911bef04f05d635a7b7a01a89dc8fc5d66a277588f08df", + "zh:d4042bdf86ca6dc10e0cca91c4fcc592b12572d26185b3d37bbbb9e2026ac68b", + "zh:d536482cf4ace0d49a2a86c931150921649beae59337d0c02a785879fe943cf3", + "zh:e205f8243274a621fb9ef2b5e2c71e84c1670be1d23697739439f5a831fa620f", + "zh:eb76ce0c77fd76c47f57122c91c4fcf0f72c01423538ed7833eaa7eeaae2edf6", + "zh:ffe04e494af6cc7348ceb8d85f4c1d5a847a44510827b4496513c810a4d9196d", + ] +} diff --git a/terraform/collector/main.tf b/terraform/collector/main.tf new file mode 100644 index 0000000..19b0342 --- /dev/null +++ b/terraform/collector/main.tf @@ -0,0 +1,178 @@ +########## Building in us-east-1 ########## +provider "aws" { + region = "us-east-1" + allowed_account_ids = ["037370603820"] + + default_tags { + tags = { + Maintainer = "cms-macfc+archive@corbalt.com" + Owner = "cms-macfc+archive@corbalt.com" + Environment = "dev" + Application = "mac-fc-security-hub-collector" + Business = "MACBIS" + Automated = "Terraform" + stack = "dev" + } + } +} + +terraform { + required_version = "= 1.5.2" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.58.0" + } + } + + backend "s3" { + region = "us-east-1" + bucket = "security-hub-collector-dev-tfstate" + key = "app/state" + dynamodb_table = "security-hub-collector-dev-lock-table" + encrypt = true + } +} + +########## Create a test assume role ######## + +data "aws_iam_policy" "security_hub_read_only" { + name = "AWSSecurityHubReadOnlyAccess" +} + +resource "aws_iam_role" "security_hub_collector" { + name = "security-hub-collector" + path = "/delegatedadmin/developer/" + permissions_boundary = "arn:aws:iam::037370603820:policy/cms-cloud-admin/developer-boundary-policy" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + AWS = ["arn:aws:iam::037370603820:root"] + } + Action = "sts:AssumeRole" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "security_hub_read_only" { + role = aws_iam_role.security_hub_collector.name + policy_arn = data.aws_iam_policy.security_hub_read_only.arn +} + +########## Create s3 bucket for storing the collected findings ########## +resource "aws_s3_bucket" "security_hub_collector" { + bucket = var.security_hub_collector_results_bucket_name +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "security_hub_collector" { + bucket = aws_s3_bucket.security_hub_collector.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +resource "aws_s3_bucket_lifecycle_configuration" "security_hub_collector" { + bucket = aws_s3_bucket.security_hub_collector.id + + rule { + id = "security-hub-collector" + status = "Enabled" + + expiration { + days = 90 + } + } +} + +resource "aws_s3_bucket_public_access_block" "security_hub_collector" { + bucket = aws_s3_bucket.security_hub_collector.id + + # Block new public ACLs and uploading public objects + block_public_acls = true + + # Retroactively remove public access granted through public ACLs + ignore_public_acls = true + + # Block new public bucket policies + block_public_policy = true + + # Retroactively block public and cross-account access if bucket has public policies + restrict_public_buckets = true +} + +resource "aws_s3_bucket_policy" "security_hub_collector_bucket_policy" { + bucket = aws_s3_bucket.security_hub_collector.id + policy = jsonencode({ + Version = "2012-10-17" + Id = "var.security_hub_collector_results_bucket_name" + Statement = [ + { + Sid = "write-only" + Effect = "Allow" + Principal = { AWS : [module.security_hub_collector_runner.task_execution_role_arn] } + Action = ["s3:PutObject"] + Resource = [ + aws_s3_bucket.security_hub_collector.arn, + "${aws_s3_bucket.security_hub_collector.arn}/*", + ] + }, + { + Action = "s3:*" + Condition = { + Bool = { + "aws:SecureTransport" = "false", + } + } + Effect = "Deny" + Principal = "*" + Resource = [ + aws_s3_bucket.security_hub_collector.arn, + "${aws_s3_bucket.security_hub_collector.arn}/*", + ] + Sid = "AllowSSLRequestsOnly" + } + ] + }) +} + + +##########Create cloudwatch log group and ecs cluster ########## +resource "aws_cloudwatch_log_group" "aws-scanner-inspec" { + name = var.aws_cloudwatch_log_group_name +} + +resource "aws_ecs_cluster" "security_hub_collector_runner" { + name = "security-hub-collector" +} + +########## Use the securityhub collector runner module ########## +module "security_hub_collector_runner" { + source = "github.com/CMSgov/security-hub-collector-ecs-runner?ref=795330487905a32ae3bc9420c40abdd745fff327" + app_name = "security-hub" + environment = "dev" + task_name = "scheduled-collector" + repo_arn = "arn:aws:ecr:us-east-1:037370603820:repository/security-hub-collector" + repo_url = "037370603820.dkr.ecr.us-east-1.amazonaws.com/security-hub-collector" + repo_tag = "d93a473" + ecs_vpc_id = var.ecs_vpc_id + ecs_subnet_ids = var.ecs_subnet_ids + schedule_task_expression = var.schedule_task_expression + logs_cloudwatch_group_arn = aws_cloudwatch_log_group.aws-scanner-inspec.arn + ecs_cluster_arn = aws_ecs_cluster.security_hub_collector_runner.arn + output_path = var.output_path //optional + s3_results_bucket = var.security_hub_collector_results_bucket_name + s3_key = var.s3_key //optional + assign_public_ip = var.assign_public_ip + role_path = "/delegatedadmin/developer/" + permissions_boundary = "arn:aws:iam::037370603820:policy/cms-cloud-admin/developer-boundary-policy" + team_map = base64encode(file("${path.module}/team_map.json")) + scheduled_task_state = "ENABLED" #Set to DISABLED to stop scheduled execution +} diff --git a/terraform/collector/team_map.json b/terraform/collector/team_map.json new file mode 100644 index 0000000..a84f83f --- /dev/null +++ b/terraform/collector/team_map.json @@ -0,0 +1,14 @@ +{ + "teams": [ + { + "accounts": [ + { + "environment": "dev", + "id": "037370603820", + "roleArn": "arn:aws:iam::037370603820:role/delegatedadmin/developer/security-hub-collector" + } + ], + "name": "MAC-FC" + } + ] +} diff --git a/terraform/collector/terraform.tfvars b/terraform/collector/terraform.tfvars new file mode 100644 index 0000000..d333194 --- /dev/null +++ b/terraform/collector/terraform.tfvars @@ -0,0 +1,8 @@ +ecs_vpc_id = "vpc-07f4de56f6970729d" +ecs_subnet_ids = ["subnet-06bbdc0b680091dd1", "subnet-02d08271e8ac413b0"] +security_hub_collector_results_bucket_name = "securityhub-collector-results-037370603820s" +schedule_task_expression = "cron(30 11 ? * 2,4,6 *)" +output_path = "" +s3_key = "" +aws_cloudwatch_log_group_name = "security_hub_collector" +assign_public_ip = true diff --git a/terraform/collector/variables.tf b/terraform/collector/variables.tf new file mode 100644 index 0000000..fded746 --- /dev/null +++ b/terraform/collector/variables.tf @@ -0,0 +1,39 @@ +variable "ecs_vpc_id" { + description = "The ID of the VPC where the ECS tasks will run" + type = string +} + +variable "ecs_subnet_ids" { + description = "A list of subnet IDs where the ECS tasks will be placed" + type = list(string) +} + +variable "security_hub_collector_results_bucket_name" { + description = "The name of the S3 bucket where Security Hub collector results will be stored" + type = string +} + +variable "schedule_task_expression" { + description = "The schedule expression for when the ECS task should run (e.g., cron or rate expression)" + type = string +} + +variable "output_path" { + description = "The path where output files will be saved" + type = string +} + +variable "s3_key" { + description = "The S3 key (path) where files will be stored in the S3 bucket" + type = string +} + +variable "aws_cloudwatch_log_group_name" { + description = "The name of the CloudWatch log group where ECS task logs will be sent" + type = string +} + +variable "assign_public_ip" { + description = "Whether to assign a public IP address to the ECS task" + type = bool +} diff --git a/terraform/dev/account/.terraform.lock.hcl b/terraform/ecr/dev/account/.terraform.lock.hcl similarity index 100% rename from terraform/dev/account/.terraform.lock.hcl rename to terraform/ecr/dev/account/.terraform.lock.hcl diff --git a/terraform/dev/account/ecr.tf b/terraform/ecr/dev/account/ecr.tf similarity index 100% rename from terraform/dev/account/ecr.tf rename to terraform/ecr/dev/account/ecr.tf diff --git a/terraform/dev/account/github-oidc.tf b/terraform/ecr/dev/account/github-oidc.tf similarity index 100% rename from terraform/dev/account/github-oidc.tf rename to terraform/ecr/dev/account/github-oidc.tf diff --git a/terraform/dev/account/terraform.tfvars b/terraform/ecr/dev/account/terraform.tfvars similarity index 100% rename from terraform/dev/account/terraform.tfvars rename to terraform/ecr/dev/account/terraform.tfvars diff --git a/terraform/dev/account/variables.tf b/terraform/ecr/dev/account/variables.tf similarity index 100% rename from terraform/dev/account/variables.tf rename to terraform/ecr/dev/account/variables.tf diff --git a/terraform/dev/account/versions.tf b/terraform/ecr/dev/account/versions.tf similarity index 100% rename from terraform/dev/account/versions.tf rename to terraform/ecr/dev/account/versions.tf diff --git a/terraform/dev/init/.terraform.lock.hcl b/terraform/ecr/dev/init/.terraform.lock.hcl similarity index 100% rename from terraform/dev/init/.terraform.lock.hcl rename to terraform/ecr/dev/init/.terraform.lock.hcl diff --git a/terraform/dev/init/main.tf b/terraform/ecr/dev/init/main.tf similarity index 100% rename from terraform/dev/init/main.tf rename to terraform/ecr/dev/init/main.tf diff --git a/terraform/dev/init/terraform.tfstate b/terraform/ecr/dev/init/terraform.tfstate similarity index 100% rename from terraform/dev/init/terraform.tfstate rename to terraform/ecr/dev/init/terraform.tfstate diff --git a/terraform/dev/init/terraform.tfstate.backup b/terraform/ecr/dev/init/terraform.tfstate.backup similarity index 100% rename from terraform/dev/init/terraform.tfstate.backup rename to terraform/ecr/dev/init/terraform.tfstate.backup diff --git a/terraform/dev/init/versions.tf b/terraform/ecr/dev/init/versions.tf similarity index 100% rename from terraform/dev/init/versions.tf rename to terraform/ecr/dev/init/versions.tf