├── VERSION ├── .github ├── CODEOWNERS ├── dependabot.yml ├── workflows │ ├── yaml.integration.yaml │ ├── github.repo.yaml │ ├── github.pr.yaml │ └── terraform.integration.yaml ├── auto-assign.yaml ├── labeler.yaml ├── labels.yaml └── labels.common.yaml ├── examples ├── macie-account-simple │ ├── outputs.tf │ ├── versions.tf │ └── main.tf ├── config-recorder-simple │ ├── outputs.tf │ ├── versions.tf │ ├── main.tf │ └── s3.tf ├── cloudtrail-event-data-store-full │ ├── outputs.tf │ ├── versions.tf │ └── main.tf ├── cloudtrail-event-data-store-config │ ├── outputs.tf │ ├── versions.tf │ └── main.tf └── cloudtrail-event-data-store-simple │ ├── outputs.tf │ ├── versions.tf │ └── main.tf ├── modules ├── access-analyzer │ ├── migrations.tf │ ├── versions.tf │ ├── resource-group.tf │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md ├── cloudtrail-trail │ ├── migrations.tf │ ├── versions.tf │ ├── resource-group.tf │ ├── iam.tf │ ├── outputs.tf │ ├── main.tf │ └── README.md ├── macie-account │ ├── migrations.tf │ ├── versions.tf │ ├── resource-group.tf │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md ├── config-managed-rule │ ├── migrations.tf │ ├── versions.tf │ ├── resource-group.tf │ ├── get-managed-rules.sh │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md ├── config-recorder │ ├── versions.tf │ ├── migrations.tf │ ├── resource-group.tf │ ├── aggregator.tf │ ├── main.tf │ ├── outputs.tf │ └── iam.tf ├── cloudtrail-event-data-store │ ├── versions.tf │ ├── resource-group.tf │ ├── iam.tf │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md └── macie-custom-data-identifier │ ├── versions.tf │ ├── resource-group.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── README.md ├── .editorconfig ├── .pre-commit-config.yaml ├── .yamllint.yaml ├── .gitignore ├── .tflint.hcl ├── README.md └── LICENSE /VERSION: -------------------------------------------------------------------------------- 1 | 0.10.2 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @posquit0 2 | -------------------------------------------------------------------------------- /examples/macie-account-simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "account" { 2 | value = module.account 3 | } 4 | -------------------------------------------------------------------------------- /examples/config-recorder-simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "recorder" { 2 | value = module.recorder 3 | } 4 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-full/outputs.tf: -------------------------------------------------------------------------------- 1 | output "event_data_store" { 2 | value = module.event_data_store 3 | } 4 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-config/outputs.tf: -------------------------------------------------------------------------------- 1 | output "event_data_store" { 2 | value = module.event_data_store 3 | } 4 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "event_data_store" { 2 | value = module.event_data_store 3 | } 4 | -------------------------------------------------------------------------------- /modules/access-analyzer/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2023-02-01 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /modules/cloudtrail-trail/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2023-02-01 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /modules/macie-account/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2023-02-01 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /modules/config-managed-rule/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2023-02-01 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /modules/macie-account/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/access-analyzer/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/cloudtrail-trail/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/config-recorder/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/config-recorder-simple/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.6" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/macie-account-simple/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/config-managed-rule/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/cloudtrail-event-data-store/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/macie-custom-data-identifier/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-config/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-full/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-simple/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: daily 8 | 9 | - package-ecosystem: terraform 10 | directories: 11 | - /modules/* 12 | schedule: 13 | interval: weekly 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{tf,tfvars}] 13 | indent_size = 2 14 | indent_style = space 15 | 16 | [.gitignore] 17 | end_of_line = 18 | -------------------------------------------------------------------------------- /modules/config-recorder/migrations.tf: -------------------------------------------------------------------------------- 1 | # INFO: 2023-12-07 - module-defined IAM Role is optional 2 | moved { 3 | from = module.role__recorder 4 | to = module.role__recorder[0] 5 | 6 | } 7 | # 2023-02-01 8 | moved { 9 | from = aws_resourcegroups_group.this[0] 10 | to = module.resource_group[0].aws_resourcegroups_group.this 11 | } 12 | -------------------------------------------------------------------------------- /examples/macie-account-simple/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # Macie Account 8 | ################################################### 9 | 10 | module "account" { 11 | source = "../../modules/macie-account" 12 | # source = "tedilabs/security/aws//modules/macie-account" 13 | # version = "~> 0.6.0" 14 | 15 | enabled = true 16 | 17 | tags = { 18 | "project" = "terraform-aws-security-examples" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-simple/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # CloudTrail Event Data Store 8 | ################################################### 9 | 10 | module "event_data_store" { 11 | source = "../../modules/cloudtrail-event-data-store" 12 | # source = "tedilabs/security/aws//modules/cloudtrail-event-data-store" 13 | # version = "~> 0.6.0" 14 | 15 | name = "management-event" 16 | 17 | termination_protection_enabled = false 18 | 19 | tags = { 20 | "project" = "terraform-aws-security-examples" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-config/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # CloudTrail Event Data Store 8 | ################################################### 9 | 10 | module "event_data_store" { 11 | source = "../../modules/cloudtrail-event-data-store" 12 | # source = "tedilabs/security/aws//modules/cloudtrail-event-data-store" 13 | # version = "~> 0.6.0" 14 | 15 | name = "config-configuration-items" 16 | event_type = "CONFIG_CONFIGURATION_ITEMS" 17 | 18 | termination_protection_enabled = false 19 | 20 | tags = { 21 | "project" = "terraform-aws-security-examples" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/config-recorder-simple/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # Config Recorder 8 | ################################################### 9 | 10 | module "recorder" { 11 | source = "../../modules/config-recorder" 12 | # source = "tedilabs/security/aws//modules/config-recorder" 13 | # version = "~> 0.6.0" 14 | 15 | name = "test" 16 | enabled = true 17 | 18 | scope = { 19 | strategy = "ALL_WITHOUT_GLOBAL" 20 | } 21 | 22 | delivery_channels = { 23 | s3_bucket = { 24 | name = module.bucket.name 25 | } 26 | } 27 | 28 | tags = { 29 | "project" = "terraform-aws-security-examples" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/yaml.integration.yaml: -------------------------------------------------------------------------------- 1 | name: Integration - YAML 2 | 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - "**.yaml" 10 | - "**.yml" 11 | 12 | pull_request: 13 | paths: 14 | - "**.yaml" 15 | - "**.yml" 16 | 17 | workflow_dispatch: {} 18 | 19 | 20 | concurrency: 21 | group: integration-yaml-${{ github.ref }} 22 | cancel-in-progress: true 23 | 24 | 25 | jobs: 26 | lint: 27 | name: Lint (yamllint) 28 | uses: tedilabs/github-actions/.github/workflows/yaml.yamllint.yaml@main 29 | 30 | with: 31 | yamllint_version: latest 32 | yamllint_config_file: .yamllint.yaml 33 | yamllint_target_dir: ./ 34 | secrets: 35 | token: ${{ secrets.GITHUB_TOKEN }} 36 | -------------------------------------------------------------------------------- /modules/access-analyzer/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/cloudtrail-trail/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/config-recorder/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/macie-account/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/config-managed-rule/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/cloudtrail-event-data-store/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/macie-custom-data-identifier/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/github.repo.yaml: -------------------------------------------------------------------------------- 1 | name: GitHub - Repository 2 | 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - .github/labels*.yaml 10 | - .github/workflows/github.repo.yaml 11 | 12 | pull_request: 13 | paths: 14 | - .github/labels*.yaml 15 | - .github/workflows/github.repo.yaml 16 | 17 | workflow_dispatch: {} 18 | 19 | 20 | jobs: 21 | sync-labels: 22 | name: Sync Labels 23 | strategy: 24 | matrix: 25 | config_file: 26 | - .github/labels.yaml 27 | - .github/labels.common.yaml 28 | uses: tedilabs/github-actions/.github/workflows/github.repo.sync-labels.yaml@main 29 | 30 | permissions: 31 | contents: read 32 | issues: write 33 | 34 | with: 35 | config_file: ${{ matrix.config_file }} 36 | skip_delete: true 37 | dry_run: ${{ github.event_name == 'pull_request' }} 38 | secrets: 39 | token: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.github/auto-assign.yaml: -------------------------------------------------------------------------------- 1 | runOnDraft: false 2 | # A list of keywords to be skipped the process that add reviewers if pull requests include it 3 | skipKeywords: 4 | - wip 5 | 6 | # Set to true to add reviewers to pull requests 7 | addReviewers: true 8 | # A number of reviewers added to the pull request 9 | # Set 0 to add all the reviewers (default: 0) 10 | numberOfReviewers: 1 11 | useReviewGroups: true 12 | reviewGroups: 13 | owners: 14 | - posquit0 15 | maintainers: 16 | - posquit0 17 | 18 | # true: Add assignees to pull requests 19 | # 'author': Set the PR creator as the assignee. 20 | addAssignees: 'author' 21 | # A number of assignees to add to the pull request 22 | # Set to 0 to add all of the assignees. 23 | # Uses numberOfReviewers if unset. 24 | # numberOfAssignees: 2 25 | useAssigneeGroups: false 26 | # assigneeGroups: 27 | # groupA: 28 | # - assigneeA 29 | # - assigneeB 30 | # - assigneeC 31 | # groupB: 32 | # - assigneeD 33 | # - assigneeE 34 | # - assigneeF 35 | -------------------------------------------------------------------------------- /.github/labeler.yaml: -------------------------------------------------------------------------------- 1 | # Modules 2 | ":floppy_disk: access-analyzer": 3 | - changed-files: 4 | - any-glob-to-any-file: 5 | - modules/access-analyzer/**/* 6 | 7 | ":floppy_disk: cloudtrail-trail": 8 | - changed-files: 9 | - any-glob-to-any-file: 10 | - modules/cloudtrail-trail/**/* 11 | 12 | ":floppy_disk: cloudtrail-event-data-store": 13 | - changed-files: 14 | - any-glob-to-any-file: 15 | - modules/cloudtrail-event-data-store/**/* 16 | 17 | ":floppy_disk: config-recorder": 18 | - changed-files: 19 | - any-glob-to-any-file: 20 | - modules/config-recorder/**/* 21 | 22 | ":floppy_disk: config-managed-rule": 23 | - changed-files: 24 | - any-glob-to-any-file: 25 | - modules/config-managed-rule/**/* 26 | 27 | ":floppy_disk: macie-account": 28 | - changed-files: 29 | - any-glob-to-any-file: 30 | - modules/macie-account/**/* 31 | 32 | ":floppy_disk: macie-custom-data-identifier": 33 | - changed-files: 34 | - any-glob-to-any-file: 35 | - modules/macie-custom-data-identifier/**/* 36 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_install_hook_types: 2 | - pre-commit 3 | - commit-msg 4 | 5 | repos: 6 | - repo: https://github.com/antonbabenko/pre-commit-terraform 7 | rev: v1.104.0 8 | hooks: 9 | - id: terraform_fmt 10 | name: (terraform) Format .tf files with `terraform fmt` 11 | args: 12 | - --args=-diff 13 | - id: terraform_validate 14 | name: (terraform) Check with `terraform validate` 15 | args: 16 | - --hook-config=--retry-once-with-cleanup=true 17 | - --tf-init-args=-upgrade 18 | - id: terraform_tflint 19 | name: (terraform) Check with `tflint` 20 | args: 21 | - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl 22 | files: ^modules/ 23 | - id: terraform_docs 24 | name: (terraform) Generate docs with `terraform-docs` 25 | args: ["--args=--sort-by required"] 26 | 27 | - repo: https://github.com/pre-commit/pre-commit-hooks 28 | rev: v6.0.0 29 | hooks: 30 | - id: trailing-whitespace 31 | 32 | - repo: https://github.com/adrienverge/yamllint 33 | rev: v1.37.1 34 | hooks: 35 | - id: yamllint 36 | name: (yaml) Check with `yamllint` 37 | 38 | - repo: https://github.com/compilerla/conventional-pre-commit 39 | rev: v4.3.0 40 | hooks: 41 | - id: conventional-pre-commit 42 | name: (commit-message) Check conventional commit 43 | stages: [commit-msg] 44 | args: [] 45 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | yaml-files: 2 | - '*.yaml' 3 | - '*.yml' 4 | 5 | rules: 6 | braces: 7 | min-spaces-inside: 0 8 | max-spaces-inside: 1 9 | min-spaces-inside-empty: 0 10 | max-spaces-inside-empty: 0 11 | brackets: 12 | min-spaces-inside: 0 13 | max-spaces-inside: 1 14 | min-spaces-inside-empty: 0 15 | max-spaces-inside-empty: 0 16 | colons: 17 | max-spaces-before: 0 18 | max-spaces-after: 1 19 | commas: 20 | max-spaces-before: 0 21 | comments: 22 | level: warning 23 | require-starting-space: true 24 | min-spaces-from-content: 1 25 | comments-indentation: disable 26 | document-end: disable 27 | document-start: disable 28 | empty-lines: 29 | level: warning 30 | max: 2 31 | max-start: 0 32 | max-end: 1 33 | empty-values: 34 | forbid-in-block-mappings: true 35 | forbid-in-flow-mappings: true 36 | hyphens: 37 | max-spaces-after: 1 38 | indentation: 39 | spaces: consistent 40 | indent-sequences: false 41 | key-duplicates: enable 42 | key-ordering: disable 43 | line-length: disable 44 | new-line-at-end-of-file: enable 45 | # Use UNIX new line characters `\n` instead of DOS new line characters `\r\n` 46 | new-lines: 47 | type: unix 48 | octal-values: disable 49 | quoted-strings: 50 | quote-type: any 51 | required: false 52 | trailing-spaces: enable 53 | truthy: disable 54 | -------------------------------------------------------------------------------- /modules/config-managed-rule/get-managed-rules.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | curl https://a.b.cdn.console.awsstatic.com/a/v1/775YGM4M3SHLVWHBMZMAQNPI5LE3J2LYXJ5DISHSLQR4LRANT23A/managed-rules.json -o raw.json 4 | cat raw.json | jq ' 5 | [ 6 | .[] | { 7 | id: .identifier, 8 | default_name: .defaultName, 9 | description: .description, 10 | is_ready: .isReadyToUse, 11 | supported_evaluation_modes: (.supportedEvaluationModes | map(.evaluationMode)), 12 | parameters: { 13 | required: .compulsoryInputParameterDetails, 14 | optional: .optionalInputParameterDetails 15 | }, 16 | trigger_by_change: { 17 | enabled: (.sourceDetails | map(.messageType == "ConfigurationItemChangeNotification" or .messageType == "OversizedConfigurationItemChangeNotification") | any(.) ), 18 | scope: (if (.scope != null) 19 | then { 20 | resource_types: .scope.ResourceTypes 21 | } 22 | else null 23 | end), 24 | }, 25 | trigger_by_schedule: { 26 | enabled: (.sourceDetails | map(.messageType == "ScheduledNotification") | any(.) ), 27 | max_frequency: (if (.sourceDetails[].messageType == "ScheduledNotification") 28 | then ((.sourceDetails[].maximumExecutionFrequencyMinutes // 1440) / 60 | tostring + "h") 29 | else null 30 | end), 31 | }, 32 | labels: .labels 33 | } 34 | ] | INDEX(.default_name)' 35 | -------------------------------------------------------------------------------- /modules/macie-custom-data-identifier/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-security" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # Custom Data Identifier for Macie Account 20 | ################################################### 21 | 22 | # INFO: Not supported attributes 23 | # - `name_prefix` 24 | resource "aws_macie2_custom_data_identifier" "this" { 25 | region = var.region 26 | 27 | name = var.name 28 | description = var.description 29 | 30 | regex = var.regex 31 | keywords = length(var.keywords) > 0 ? var.keywords : null 32 | ignore_words = length(var.ignore_words) > 0 ? var.ignore_words : null 33 | maximum_match_distance = var.maximum_match_distance 34 | 35 | tags = merge( 36 | { 37 | "Name" = local.metadata.name 38 | }, 39 | local.module_tags, 40 | var.tags, 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/github.pr.yaml: -------------------------------------------------------------------------------- 1 | name: GitHub - Pull Request 2 | 3 | 4 | on: 5 | pull_request_target: {} 6 | 7 | 8 | jobs: 9 | auto-assign: 10 | name: Auto Assign 11 | uses: tedilabs/github-actions/.github/workflows/github.pr.auto-assign.yaml@main 12 | if: ${{ github.event_name == 'pull_request_target' }} 13 | 14 | permissions: 15 | contents: read 16 | pull-requests: write 17 | 18 | with: 19 | config_file: .github/auto-assign.yaml 20 | secrets: 21 | token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | auto-label: 24 | name: Auto Label 25 | uses: tedilabs/github-actions/.github/workflows/github.pr.auto-label.yaml@main 26 | if: ${{ github.event_name == 'pull_request_target' }} 27 | 28 | permissions: 29 | contents: read 30 | issues: write 31 | pull-requests: write 32 | 33 | with: 34 | labeler_config_file: .github/labeler.yaml 35 | labeler_dot_included: true 36 | labeler_sync_labels: true 37 | pr_size_xs_label: 'size/XS' 38 | pr_size_s_label: 'size/S' 39 | pr_size_m_label: 'size/M' 40 | pr_size_l_label: 'size/L' 41 | pr_size_xl_label: 'size/XL' 42 | secrets: 43 | token: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | welcome: 46 | name: Welcome for First Pull Request 47 | uses: tedilabs/github-actions/.github/workflows/github.issue.welcome.yaml@main 48 | if: ${{ github.event_name == 'pull_request_target' && github.event.action == 'opened' }} 49 | 50 | secrets: 51 | token: ${{ secrets.GITHUB_TOKEN }} 52 | -------------------------------------------------------------------------------- /examples/config-recorder-simple/s3.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "config" { 2 | statement { 3 | sid = "AWSConfigBucketExistenceAndPermissionsCheck" 4 | 5 | effect = "Allow" 6 | actions = [ 7 | "s3:GetBucketAcl", 8 | "s3:ListBucket", 9 | ] 10 | resources = [ 11 | module.bucket.arn, 12 | ] 13 | 14 | principals { 15 | type = "Service" 16 | identifiers = ["config.amazonaws.com"] 17 | } 18 | } 19 | statement { 20 | sid = "AWSConfigBucketDelivery" 21 | 22 | effect = "Allow" 23 | actions = [ 24 | "s3:PutObject", 25 | ] 26 | resources = [ 27 | module.bucket.arn, 28 | "${module.bucket.arn}/AWSLogs/*/Config/*", 29 | ] 30 | 31 | principals { 32 | type = "Service" 33 | identifiers = ["config.amazonaws.com"] 34 | } 35 | condition { 36 | test = "StringEquals" 37 | variable = "s3:x-amz-acl" 38 | values = ["bucket-owner-full-control"] 39 | } 40 | } 41 | } 42 | 43 | 44 | ################################################### 45 | # S3 Bucket 46 | ################################################### 47 | 48 | resource "random_string" "this" { 49 | length = 32 50 | special = false 51 | numeric = false 52 | upper = false 53 | } 54 | 55 | locals { 56 | bucket_name = random_string.this.id 57 | } 58 | 59 | module "bucket" { 60 | source = "tedilabs/data/aws//modules/s3-bucket" 61 | version = "~> 0.6.0" 62 | 63 | name = local.bucket_name 64 | force_destroy = true 65 | 66 | policy = data.aws_iam_policy_document.config.json 67 | 68 | tags = { 69 | "project" = "terraform-aws-data-examples" 70 | } 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /.github/labels.yaml: -------------------------------------------------------------------------------- 1 | # Warning 2 | - color: "ee0701" 3 | description: "Categorize to note any change that requires extra attention." 4 | name: ":warning: breaking change" 5 | - color: "ee0701" 6 | description: "Categorize bug reports." 7 | name: ":warning: bug" 8 | - color: "ee0701" 9 | description: "Categorize vulnerability reports." 10 | name: ":warning: vulnerability" 11 | 12 | # Task Types 13 | - color: "1d76db" 14 | description: "Improvements or additions to documentation." 15 | name: ":pencil2: documentation" 16 | - color: "1d76db" 17 | description: "A new feature of the project." 18 | name: ":pencil2: feature" 19 | - color: "1d76db" 20 | description: "Any dependency updates, housekeeping, or maintenance work." 21 | name: ":pencil2: maintenance" 22 | 23 | # Highlight 24 | - color: "0e8a16" 25 | description: "Good for newcomers." 26 | name: ":fire: good first issue" 27 | - color: "0e8a16" 28 | description: "Extra attention is needed." 29 | name: ":fire: help wanted" 30 | 31 | # Cancel 32 | - color: "b60205" 33 | description: "This issue or pull request already exists." 34 | name: ":pray: duplicate" 35 | - color: "b60205" 36 | description: "This issue or pull request is invalid." 37 | name: ":pray: invalid" 38 | - color: "b60205" 39 | description: "This will not be worked on." 40 | name: ":pray: wontfix" 41 | 42 | # Size 43 | - color: "cfd3d7" 44 | description: "Extra Small size issue or PR." 45 | name: "size/XS" 46 | - color: "cfd3d7" 47 | description: "Small size issue or PR." 48 | name: "size/S" 49 | - color: "cfd3d7" 50 | description: "Medium size issue or PR." 51 | name: "size/M" 52 | - color: "cfd3d7" 53 | description: "Large size issue or PR." 54 | name: "size/L" 55 | - color: "cfd3d7" 56 | description: "Extra Large size issue or PR." 57 | name: "size/XL" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OSX ### 2 | # General 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | # Icon must end with two \r 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | 29 | 30 | ### Terraform ### 31 | # Lock file 32 | .terraform.lock.hcl 33 | 34 | # Local .terraform directories 35 | **/.terraform/* 36 | 37 | # .tfstate files 38 | *.tfstate 39 | *.tfstate.* 40 | 41 | # Crash log files 42 | crash.log 43 | 44 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 45 | # .tfvars files are managed as part of configuration and so should be included in 46 | # version control. 47 | # 48 | # example.tfvars 49 | 50 | # Ignore override files as they are usually used to override resources locally and so 51 | # are not checked in 52 | override.tf 53 | override.tf.json 54 | *_override.tf 55 | *_override.tf.json 56 | 57 | # Include override files you do wish to add to version control using negated pattern 58 | # !example_override.tf 59 | 60 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 61 | # example: *tfplan* 62 | 63 | 64 | ### Vim ### 65 | # Swap 66 | [._]*.s[a-v][a-z] 67 | !*.svg # comment out if you don't need vector files 68 | [._]*.sw[a-p] 69 | [._]s[a-rt-v][a-z] 70 | [._]ss[a-gi-z] 71 | [._]sw[a-p] 72 | 73 | # Session 74 | Session.vim 75 | Sessionx.vim 76 | 77 | # Temporary 78 | .netrwhist 79 | *~ 80 | # Auto-generated tag files 81 | tags 82 | # Persistent undo 83 | [._]*.un~ 84 | -------------------------------------------------------------------------------- /.github/labels.common.yaml: -------------------------------------------------------------------------------- 1 | # Warning 2 | - color: "ee0701" 3 | description: "Categorize to note any change that requires extra attention." 4 | name: ":warning: breaking change" 5 | - color: "ee0701" 6 | description: "Categorize bug reports." 7 | name: ":warning: bug" 8 | from_name: "bug" 9 | - color: "ee0701" 10 | description: "Categorize vulnerability reports." 11 | name: ":warning: vulnerability" 12 | 13 | # Task Types 14 | - color: "1d76db" 15 | description: "Improvements or additions to documentation." 16 | name: ":pencil2: documentation" 17 | from_name: "documentation" 18 | - color: "1d76db" 19 | description: "A new feature of the project." 20 | name: ":pencil2: feature" 21 | from_name: "enhancement" 22 | - color: "1d76db" 23 | description: "Any dependency updates, housekeeping, or maintenance work." 24 | name: ":pencil2: maintenance" 25 | 26 | # Highlight 27 | - color: "0e8a16" 28 | description: "Good for newcomers." 29 | name: ":fire: good first issue" 30 | from_name: "good first issue" 31 | - color: "0e8a16" 32 | description: "Extra attention is needed." 33 | name: ":fire: help wanted" 34 | from_name: "help wanted" 35 | 36 | # Cancel 37 | - color: "b60205" 38 | description: "This issue or pull request already exists." 39 | name: ":pray: duplicate" 40 | from_name: "duplicate" 41 | - color: "b60205" 42 | description: "This issue or pull request is invalid." 43 | name: ":pray: invalid" 44 | from_name: "invalid" 45 | - color: "b60205" 46 | description: "This will not be worked on." 47 | name: ":pray: wontfix" 48 | from_name: "wontfix" 49 | 50 | # Size 51 | - color: "cfd3d7" 52 | description: "Extra Small size issue or PR." 53 | name: "size/XS" 54 | - color: "cfd3d7" 55 | description: "Small size issue or PR." 56 | name: "size/S" 57 | - color: "cfd3d7" 58 | description: "Medium size issue or PR." 59 | name: "size/M" 60 | - color: "cfd3d7" 61 | description: "Large size issue or PR." 62 | name: "size/L" 63 | - color: "cfd3d7" 64 | description: "Extra Large size issue or PR." 65 | name: "size/XL" 66 | -------------------------------------------------------------------------------- /examples/cloudtrail-event-data-store-full/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # CloudTrail Event Data Store 8 | ################################################### 9 | 10 | module "event_data_store" { 11 | source = "../../modules/cloudtrail-event-data-store" 12 | # source = "tedilabs/security/aws//modules/cloudtrail-event-data-store" 13 | # version = "~> 0.6.0" 14 | 15 | name = "management-event" 16 | 17 | level = "ACCOUNT" 18 | scope = "REGIONAL" 19 | 20 | 21 | ## Event Selector 22 | event_type = "CLOUDTRAIL_EVENTS" 23 | event_selectors = [ 24 | { 25 | category = "MANAGEMENT" 26 | scope = "READ" 27 | exclude_sources = ["kms.amazonaws.com"] 28 | }, 29 | { 30 | category = "DATA" 31 | scope = "ALL" 32 | resource_type = "AWS::S3::Object" 33 | selectors = [ 34 | { 35 | field = "resource_arn" 36 | operator = "ends_with" 37 | values = ["hello"] 38 | } 39 | ] 40 | }, 41 | { 42 | category = "DATA" 43 | scope = "WRITE" 44 | resource_type = "AWS::S3Outposts::Object" 45 | selectors = [ 46 | { 47 | field = "event_name" 48 | operator = "starts_with" 49 | values = ["Put"] 50 | } 51 | ] 52 | }, 53 | ] 54 | 55 | 56 | ## Attributes 57 | retention_in_days = 365 * 7 58 | termination_protection_enabled = false 59 | 60 | 61 | ## IAM Role 62 | import_trail_events_iam_role = { 63 | enabled = true 64 | source_s3_buckets = [ 65 | { 66 | name = "helloworld" 67 | key_prefix = "asdf/" 68 | }, 69 | { 70 | name = "foo" 71 | key_prefix = "bar/" 72 | }, 73 | { 74 | name = "demo" 75 | key_prefix = "" 76 | }, 77 | ] 78 | } 79 | 80 | tags = { 81 | "project" = "terraform-aws-security-examples" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /modules/access-analyzer/outputs.tf: -------------------------------------------------------------------------------- 1 | output "region" { 2 | description = "The AWS region this module resources resides in." 3 | value = aws_accessanalyzer_analyzer.this.region 4 | } 5 | 6 | output "name" { 7 | description = "The name of the Analyzer." 8 | value = aws_accessanalyzer_analyzer.this.analyzer_name 9 | } 10 | 11 | output "id" { 12 | description = "The ID of this Analyzer." 13 | value = aws_accessanalyzer_analyzer.this.id 14 | } 15 | 16 | output "arn" { 17 | description = "The Amazon Resource Name (ARN) of this Analyzer." 18 | value = aws_accessanalyzer_analyzer.this.arn 19 | } 20 | 21 | output "type" { 22 | description = "The finding type of Analyzer." 23 | value = var.type 24 | } 25 | 26 | output "scope" { 27 | description = "The scope of Analyzer." 28 | value = var.scope 29 | } 30 | 31 | output "internal_access_analysis" { 32 | description = "The configurations for the `INTERNAL_ACCESS` type Analyzer." 33 | value = (var.type == "INTERNAL_ACCESS" 34 | ? { 35 | rules = var.internal_access_analysis.rules 36 | } 37 | : null 38 | ) 39 | } 40 | 41 | output "unused_access_analysis" { 42 | description = "The configurations for the `UNUSED_ACCESS` type Analyzer." 43 | value = (var.type == "UNUSED_ACCESS" 44 | ? { 45 | tracking_period = one(aws_accessanalyzer_analyzer.this.configuration[0].unused_access[*].unused_access_age) 46 | rules = var.unused_access_analysis.rules 47 | } 48 | : null 49 | ) 50 | } 51 | 52 | output "archive_rules" { 53 | description = "A list of archive rules for the Analyzer." 54 | value = { 55 | for name, rule in aws_accessanalyzer_archive_rule.this : 56 | name => rule.filter 57 | } 58 | } 59 | 60 | output "resource_group" { 61 | description = "The resource group created to manage resources in this module." 62 | value = merge( 63 | { 64 | enabled = var.resource_group.enabled && var.module_tags_enabled 65 | }, 66 | (var.resource_group.enabled && var.module_tags_enabled 67 | ? { 68 | arn = module.resource_group[0].arn 69 | name = module.resource_group[0].name 70 | } 71 | : {} 72 | ) 73 | ) 74 | } 75 | -------------------------------------------------------------------------------- /modules/config-recorder/aggregator.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Authorization for Aggregators 3 | ################################################### 4 | 5 | # TODO: Not yet support `region` for AWS provider v6 6 | resource "aws_config_aggregate_authorization" "this" { 7 | for_each = { 8 | for aggregator in var.authorized_aggregators : 9 | "${aggregator.account}:${aggregator.region}" => aggregator 10 | } 11 | 12 | account_id = each.value.account 13 | authorized_aws_region = each.value.region 14 | 15 | tags = merge( 16 | local.module_tags, 17 | var.tags, 18 | each.value.tags, 19 | ) 20 | } 21 | 22 | 23 | ################################################### 24 | # Aggregators 25 | ################################################### 26 | 27 | resource "aws_config_configuration_aggregator" "account" { 28 | for_each = { 29 | for aggregation in var.account_aggregations : 30 | aggregation.name => aggregation 31 | } 32 | 33 | region = var.region 34 | 35 | name = each.key 36 | 37 | account_aggregation_source { 38 | all_regions = length(each.value.regions) < 1 39 | regions = (length(each.value.regions) < 1 40 | ? null 41 | : each.value.regions 42 | ) 43 | account_ids = each.value.accounts 44 | } 45 | 46 | tags = merge( 47 | { 48 | "Name" = each.key 49 | }, 50 | local.module_tags, 51 | var.tags, 52 | each.value.tags, 53 | ) 54 | } 55 | 56 | resource "aws_config_configuration_aggregator" "organization" { 57 | count = var.organization_aggregation.enabled ? 1 : 0 58 | 59 | region = var.region 60 | 61 | name = var.organization_aggregation.name 62 | 63 | organization_aggregation_source { 64 | all_regions = length(var.organization_aggregation.regions) < 1 65 | regions = (length(var.organization_aggregation.regions) < 1 66 | ? null 67 | : var.organization_aggregation.regions 68 | ) 69 | role_arn = (var.default_organization_aggregator_role.enabled 70 | ? module.role__aggregator[0].arn 71 | : var.organization_aggregator_role 72 | ) 73 | } 74 | 75 | tags = merge( 76 | { 77 | "Name" = var.organization_aggregation.name 78 | }, 79 | local.module_tags, 80 | var.tags, 81 | var.organization_aggregation.tags, 82 | ) 83 | } 84 | -------------------------------------------------------------------------------- /.tflint.hcl: -------------------------------------------------------------------------------- 1 | config { 2 | plugin_dir = "~/.tflint.d/plugins" 3 | 4 | format = "compact" 5 | call_module_type = "local" 6 | force = false 7 | disabled_by_default = false 8 | 9 | ignore_module = {} 10 | } 11 | 12 | 13 | ################################################### 14 | # Rule Sets - Terraform 15 | ################################################### 16 | 17 | plugin "terraform" { 18 | enabled = true 19 | preset = "recommended" 20 | } 21 | 22 | rule "terraform_comment_syntax" { 23 | enabled = true 24 | } 25 | 26 | rule "terraform_documented_variables" { 27 | enabled = true 28 | } 29 | 30 | rule "terraform_documented_outputs" { 31 | enabled = true 32 | } 33 | 34 | rule "terraform_naming_convention" { 35 | enabled = true 36 | format = "snake_case" 37 | 38 | custom_formats = { 39 | extended_snake_case = { 40 | description = "Extended snake_case Format which allows double underscore like `a__b`." 41 | regex = "^[a-z][a-z0-9]+([_]{1,2}[a-z0-9]+)*$" 42 | } 43 | } 44 | 45 | module { 46 | format = "extended_snake_case" 47 | } 48 | 49 | resource { 50 | format = "extended_snake_case" 51 | } 52 | 53 | data { 54 | format = "extended_snake_case" 55 | } 56 | 57 | locals { 58 | format = "extended_snake_case" 59 | } 60 | 61 | variable { 62 | format = "extended_snake_case" 63 | } 64 | 65 | output { 66 | format = "extended_snake_case" 67 | } 68 | 69 | check { 70 | format = "extended_snake_case" 71 | } 72 | } 73 | 74 | rule "terraform_unused_declarations" { 75 | enabled = false 76 | } 77 | 78 | rule "terraform_unused_required_providers" { 79 | enabled = true 80 | } 81 | 82 | 83 | ################################################### 84 | # Rule Sets - AWS 85 | ################################################### 86 | 87 | plugin "aws" { 88 | source = "github.com/terraform-linters/tflint-ruleset-aws" 89 | version = "0.44.0" 90 | 91 | enabled = true 92 | deep_check = false 93 | } 94 | 95 | rule "aws_iam_policy_attachment_exclusive_attachment" { 96 | enabled = true 97 | } 98 | 99 | rule "aws_iam_role_deprecated_policy_attributes" { 100 | enabled = true 101 | } 102 | 103 | rule "aws_security_group_inline_rules" { 104 | enabled = true 105 | } 106 | 107 | rule "aws_security_group_rule_deprecated" { 108 | enabled = true 109 | } 110 | -------------------------------------------------------------------------------- /modules/macie-custom-data-identifier/outputs.tf: -------------------------------------------------------------------------------- 1 | output "region" { 2 | description = "The AWS region this module resources resides in." 3 | value = aws_macie2_custom_data_identifier.this.region 4 | } 5 | 6 | output "arn" { 7 | description = "The ARN (Amazon Resource Name) for the macie custom data identifier. For example: `arn:aws:cloudfront::123456789012:distribution/EDFDVBD632BHDS5`." 8 | value = aws_macie2_custom_data_identifier.this.arn 9 | } 10 | 11 | output "id" { 12 | description = "The ID of the macie custom data identifier." 13 | value = aws_macie2_custom_data_identifier.this.id 14 | } 15 | 16 | output "name" { 17 | description = "The name of the macie custom data identifier." 18 | value = local.metadata.name 19 | } 20 | 21 | output "description" { 22 | description = "The description of the macie custom data identifier." 23 | value = aws_macie2_custom_data_identifier.this.description 24 | } 25 | 26 | output "regex" { 27 | description = "The regular expression (regex) that defines the pattern to match." 28 | value = aws_macie2_custom_data_identifier.this.regex 29 | } 30 | 31 | output "keywords" { 32 | description = "An array that lists specific character sequences (keywords), one of which must be within proximity (maximum_match_distance) of the regular expression to match." 33 | value = aws_macie2_custom_data_identifier.this.keywords 34 | } 35 | 36 | output "ignore_words" { 37 | description = "An array that lists specific character sequences (ignore words) to exclude from the results." 38 | value = aws_macie2_custom_data_identifier.this.ignore_words 39 | } 40 | 41 | output "maximum_match_distance" { 42 | description = "The maximum number of characters that can exist between text that matches the regex pattern and the character sequences specified by the keywords array." 43 | value = aws_macie2_custom_data_identifier.this.maximum_match_distance 44 | } 45 | 46 | output "created_at" { 47 | description = "The date and time, in UTC and extended RFC 3339 format, when the Amazon Macie custom data identifier was created." 48 | value = aws_macie2_custom_data_identifier.this.created_at 49 | } 50 | 51 | output "resource_group" { 52 | description = "The resource group created to manage resources in this module." 53 | value = merge( 54 | { 55 | enabled = var.resource_group.enabled && var.module_tags_enabled 56 | }, 57 | (var.resource_group.enabled && var.module_tags_enabled 58 | ? { 59 | arn = module.resource_group[0].arn 60 | name = module.resource_group[0].name 61 | } 62 | : {} 63 | ) 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /modules/cloudtrail-trail/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_partition" "this" {} 2 | data "aws_region" "this" { 3 | region = var.region 4 | } 5 | data "aws_caller_identity" "this" {} 6 | data "aws_organizations_organization" "this" {} 7 | 8 | 9 | ################################################### 10 | # IAM Role for CloudTrail 11 | ################################################### 12 | 13 | module "role" { 14 | count = var.delivery_channels.cloudwatch_log_group.enabled ? 1 : 0 15 | 16 | source = "tedilabs/account/aws//modules/iam-role" 17 | version = "~> 0.32.0" 18 | 19 | name = "cloudtrail-${local.metadata.name}" 20 | path = "/" 21 | description = "Role for the CloudTrail trail(${local.metadata.name})" 22 | 23 | trusted_service_policies = [ 24 | { 25 | services = ["cloudtrail.amazonaws.com"] 26 | } 27 | ] 28 | 29 | inline_policies = { 30 | "cloudwatch" = one(data.aws_iam_policy_document.cloudwatch[*].json) 31 | } 32 | 33 | force_detach_policies = true 34 | resource_group = { 35 | enabled = false 36 | } 37 | module_tags_enabled = false 38 | 39 | tags = merge( 40 | local.module_tags, 41 | var.tags, 42 | ) 43 | } 44 | 45 | 46 | ################################################### 47 | # IAM Policy for CloudtWatch Logs 48 | ################################################### 49 | 50 | locals { 51 | partition = data.aws_partition.this.partition 52 | region = data.aws_region.this.region 53 | account_id = data.aws_caller_identity.this.account_id 54 | org_id = data.aws_organizations_organization.this.id 55 | 56 | cloudwatch_log_group_arn = var.delivery_channels.cloudwatch_log_group.enabled ? "arn:${local.partition}:logs:${local.region}:${local.account_id}:log-group:${var.delivery_channels.cloudwatch_log_group.name}" : null 57 | 58 | } 59 | 60 | data "aws_iam_policy_document" "cloudwatch" { 61 | count = var.delivery_channels.cloudwatch_log_group.enabled ? 1 : 0 62 | 63 | statement { 64 | sid = "CreateLogStream" 65 | 66 | effect = "Allow" 67 | actions = ["logs:CreateLogStream"] 68 | resources = concat( 69 | ["${local.cloudwatch_log_group_arn}:log-stream:${local.account_id}_CloudTrail_${local.region}*"], 70 | var.level == "ORGANIZATION" ? ["${local.cloudwatch_log_group_arn}:log-stream:${local.org_id}_*"] : [] 71 | ) 72 | } 73 | 74 | statement { 75 | sid = "PutLogEvents" 76 | 77 | effect = "Allow" 78 | actions = ["logs:PutLogEvents"] 79 | resources = concat( 80 | ["${local.cloudwatch_log_group_arn}:log-stream:${local.account_id}_CloudTrail_${local.region}*"], 81 | var.level == "ORGANIZATION" ? ["${local.cloudwatch_log_group_arn}:log-stream:${local.org_id}_*"] : [] 82 | ) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /modules/cloudtrail-event-data-store/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "this" {} 2 | 3 | locals { 4 | account_id = data.aws_caller_identity.this.account_id 5 | } 6 | 7 | 8 | ################################################### 9 | # IAM Role for CloudTrail Event Data Store 10 | ################################################### 11 | 12 | module "role" { 13 | count = var.import_trail_events_iam_role.enabled ? 1 : 0 14 | 15 | source = "tedilabs/account/aws//modules/iam-role" 16 | version = "~> 0.32.0" 17 | 18 | name = "cloudtrail-event-data-store-${local.metadata.name}" 19 | path = "/" 20 | description = "Role for the CloudTrail Event Data Store (${local.metadata.name})" 21 | 22 | trusted_service_policies = [ 23 | { 24 | services = ["cloudtrail.amazonaws.com"] 25 | } 26 | ] 27 | conditions = [ 28 | { 29 | key = "aws:SourceAccount" 30 | condition = "StringEquals" 31 | values = [local.account_id] 32 | }, 33 | { 34 | key = "aws:SourceArn" 35 | condition = "StringEquals" 36 | values = [aws_cloudtrail_event_data_store.this.arn] 37 | }, 38 | ] 39 | 40 | inline_policies = { 41 | "s3" = one(data.aws_iam_policy_document.s3[*].json) 42 | } 43 | 44 | force_detach_policies = true 45 | resource_group = { 46 | enabled = false 47 | } 48 | module_tags_enabled = false 49 | 50 | tags = merge( 51 | local.module_tags, 52 | var.tags, 53 | ) 54 | } 55 | 56 | 57 | ################################################### 58 | # IAM Policy to Import CloudTrail Events 59 | ################################################### 60 | 61 | locals { 62 | default_s3_bucket = { 63 | name = "you-should-configure-source-s3-buckets-to-import" 64 | key_prefix = "" 65 | } 66 | source_s3_buckets = [ 67 | for bucket in coalescelist(var.import_trail_events_iam_role.source_s3_buckets, [local.default_s3_bucket]) : { 68 | name = bucket.name 69 | key_prefix = trim(bucket.key_prefix, "/") 70 | 71 | } 72 | ] 73 | } 74 | 75 | data "aws_iam_policy_document" "s3" { 76 | count = var.import_trail_events_iam_role.enabled ? 1 : 0 77 | 78 | statement { 79 | sid = "AWSCloudTrailImportBucketAccess" 80 | 81 | effect = "Allow" 82 | actions = ["s3:ListBucket", "s3:GetBucketAcl"] 83 | resources = [ 84 | for bucket in local.source_s3_buckets : 85 | "arn:aws:s3:::${bucket.name}" 86 | ] 87 | } 88 | 89 | statement { 90 | sid = "AWSCloudTrailImportObjectAccess" 91 | 92 | effect = "Allow" 93 | actions = ["s3:GetObject"] 94 | resources = flatten([ 95 | for bucket in local.source_s3_buckets : [ 96 | join("/", compact(["arn:aws:s3:::${bucket.name}", bucket.key_prefix])), 97 | join("/", compact(["arn:aws:s3:::${bucket.name}", bucket.key_prefix, "*"])), 98 | ] 99 | ]) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /modules/macie-account/outputs.tf: -------------------------------------------------------------------------------- 1 | output "region" { 2 | description = "The AWS region this module resources resides in." 3 | value = aws_macie2_account.this.region 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the macie account." 8 | value = aws_macie2_account.this.id 9 | } 10 | 11 | output "name" { 12 | description = "The account ID of the macie account." 13 | value = local.metadata.name 14 | } 15 | 16 | output "enabled" { 17 | description = "Whether the macie account is eanbled." 18 | value = aws_macie2_account.this.status == "ENABLED" 19 | } 20 | 21 | output "update_frequency" { 22 | description = "How often to publish updates to policy findings for the macie account." 23 | value = { 24 | for k, v in local.update_frequency : 25 | v => k 26 | }[aws_macie2_account.this.finding_publishing_frequency] 27 | } 28 | 29 | output "service_role" { 30 | description = "The Amazon Resource Name (ARN) of the service-linked role that allows Macie to monitor and analyze data in AWS resources for the account." 31 | value = aws_macie2_account.this.service_role 32 | } 33 | 34 | output "created_at" { 35 | description = "The date and time, in UTC and extended RFC 3339 format, when the Amazon Macie account was created." 36 | value = aws_macie2_account.this.created_at 37 | } 38 | 39 | output "updated_at" { 40 | description = "The date and time, in UTC and extended RFC 3339 format, of the most recent change to the status of the Macie account." 41 | value = aws_macie2_account.this.updated_at 42 | } 43 | 44 | output "member_accounts" { 45 | description = < { 51 | id = account.id 52 | arn = account.arn 53 | email = account.email 54 | type = timecmp(account.invited_at, "1999-01-01T00:00:00Z") < 0 ? "ORGANIZATION" : "INVITATION" 55 | enabled = account.status == "ENABLED" 56 | relationship_status = account.relationship_status 57 | 58 | invited_at = timecmp(account.invited_at, "1999-01-01T00:00:00Z") < 0 ? null : account.invited_at 59 | updated_at = account.updated_at 60 | } 61 | } 62 | } 63 | 64 | output "organization_config" { 65 | description = < 0 69 | ? { 70 | auto_enable = aws_macie2_organization_configuration.this[0].auto_enable 71 | } 72 | : null 73 | ) 74 | } 75 | 76 | output "discovery_result_repository" { 77 | description = <> $GITHUB_OUTPUT 56 | echo "modified=${{ steps.changed-directories.outputs.any_modified }}" >> $GITHUB_OUTPUT 57 | 58 | echo "changed_files=${{ steps.changed-files.outputs.all_changed_files }}" >> $GITHUB_OUTPUT 59 | echo "modified_files=${{ steps.changed-files.outputs.all_modified_files }}" >> $GITHUB_OUTPUT 60 | 61 | echo "changed_directories=${{ steps.changed-directories.outputs.all_changed_files }}" >> $GITHUB_OUTPUT 62 | echo "modified_directories=${{ steps.changed-directories.outputs.all_modified_files }}" >> $GITHUB_OUTPUT 63 | 64 | 65 | terraform: 66 | name: Lint (terraform) 67 | needs: 68 | - changed 69 | if: ${{ needs.changed.outputs.modified == 'true' }} 70 | uses: tedilabs/.github/.github/workflows/terraform.terraform.yaml@main 71 | 72 | strategy: 73 | matrix: 74 | path: ${{ fromJson(needs.changed.outputs.modified_directories) }} 75 | 76 | with: 77 | terraform_target_dir: ${{ matrix.path }} 78 | terraform_version: latest 79 | terraform_host: app.terraform.io 80 | secrets: 81 | gh_token: ${{ secrets.GITHUB_TOKEN }} 82 | token: ${{ secrets.GITHUB_TOKEN }} 83 | terraform_token: ${{ secrets.TERRAFORM_TOKEN }} 84 | 85 | 86 | tflint: 87 | name: Lint (tflint) 88 | needs: 89 | - changed 90 | if: ${{ needs.changed.outputs.modified == 'true' }} 91 | uses: tedilabs/.github/.github/workflows/terraform.tflint.yaml@main 92 | 93 | strategy: 94 | matrix: 95 | path: ${{ fromJson(needs.changed.outputs.modified_directories) }} 96 | 97 | with: 98 | tflint_version: latest 99 | tflint_config_file: .tflint.hcl 100 | tflint_target_dir: ${{ matrix.path }} 101 | tflint_recursive_enabled: false 102 | tflint_terraform_init_enabled: true 103 | terraform_version: latest 104 | terraform_host: app.terraform.io 105 | secrets: 106 | gh_token: ${{ secrets.GITHUB_TOKEN }} 107 | token: ${{ secrets.GITHUB_TOKEN }} 108 | terraform_token: ${{ secrets.TERRAFORM_TOKEN }} 109 | -------------------------------------------------------------------------------- /modules/cloudtrail-trail/outputs.tf: -------------------------------------------------------------------------------- 1 | output "region" { 2 | description = "The AWS region this module resources resides in." 3 | value = aws_cloudtrail.this.region 4 | } 5 | 6 | output "arn" { 7 | description = "The Amazon Resource Name (ARN) of the trail." 8 | value = aws_cloudtrail.this.arn 9 | } 10 | output "id" { 11 | description = "The ID of the trail." 12 | value = aws_cloudtrail.this.id 13 | } 14 | 15 | output "name" { 16 | description = "The name of the trail." 17 | value = aws_cloudtrail.this.name 18 | } 19 | 20 | output "enabled" { 21 | description = "Whether the trail is enabled." 22 | value = aws_cloudtrail.this.enable_logging 23 | } 24 | 25 | output "home_region" { 26 | description = "The region in which the trail was created." 27 | value = aws_cloudtrail.this.home_region 28 | } 29 | 30 | output "level" { 31 | description = "The level of the trail to decide whether the trail is an AWS Organizations trail." 32 | value = var.level 33 | } 34 | 35 | output "scope" { 36 | description = "The scope of the trail to decide whether the trail is multi-region trail." 37 | value = var.scope 38 | } 39 | 40 | output "iam_role" { 41 | description = "The IAM Role for the CloudTrail trail." 42 | value = { 43 | arn = one(module.role[*].arn) 44 | name = one(module.role[*].name) 45 | } 46 | } 47 | 48 | output "delivery_channels" { 49 | description = "The configurations for the delivery channels of the trail." 50 | value = { 51 | s3_bucket = { 52 | name = aws_cloudtrail.this.s3_bucket_name 53 | key_prefix = aws_cloudtrail.this.s3_key_prefix 54 | integrity_validation_enabled = aws_cloudtrail.this.enable_log_file_validation 55 | sse_kms_key = var.delivery_channels.s3_bucket.sse_kms_key 56 | } 57 | sns_topic = { 58 | enabled = var.delivery_channels.sns_topic.enabled 59 | name = var.delivery_channels.sns_topic.name 60 | } 61 | cloudwatch_log_group = { 62 | enabled = var.delivery_channels.cloudwatch_log_group.enabled 63 | arn = aws_cloudtrail.this.cloud_watch_logs_group_arn 64 | name = var.delivery_channels.cloudwatch_log_group.name 65 | } 66 | } 67 | } 68 | 69 | output "management_event" { 70 | description = "A selector for management events of the trail." 71 | value = var.management_event_selector 72 | } 73 | 74 | output "data_event" { 75 | description = "A list of selectors for data events of the trail." 76 | value = var.data_event_selectors 77 | } 78 | 79 | output "insight_event" { 80 | description = "A selector for insight events of the trail." 81 | value = var.insight_event_selector 82 | } 83 | 84 | output "resource_group" { 85 | description = "The resource group created to manage resources in this module." 86 | value = merge( 87 | { 88 | enabled = var.resource_group.enabled && var.module_tags_enabled 89 | }, 90 | (var.resource_group.enabled && var.module_tags_enabled 91 | ? { 92 | arn = module.resource_group[0].arn 93 | name = module.resource_group[0].name 94 | } 95 | : {} 96 | ) 97 | ) 98 | } 99 | 100 | # output "debug" { 101 | # value = { 102 | # for k, v in aws_cloudtrail.this : 103 | # k => v 104 | # if !contains(["id", "arn", "name", "enable_logging", "home_region", "s3_bucket_name", "s3_key_prefix", "enable_log_file_validation", "kms_key_id", "sns_topic_name", "cloud_watch_logs_group_arn", "tags", "tags_all", "is_multi_region_trail", "is_organization_trail", "include_global_service_events", "insight_selector", "event_selector", "advanced_event_selector"], k) 105 | # } 106 | # } 107 | -------------------------------------------------------------------------------- /modules/macie-custom-data-identifier/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "(Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region." 3 | type = string 4 | default = null 5 | nullable = true 6 | } 7 | 8 | variable "name" { 9 | description = "(Required) A name for the custom data identifier. The name can contain as many as 128 characters." 10 | type = string 11 | nullable = false 12 | } 13 | 14 | variable "description" { 15 | description = "(Optional) A description of the custom data identifier. Defaults to `Managed by Terraform.`." 16 | type = string 17 | default = "Managed by Terraform." 18 | nullable = false 19 | } 20 | 21 | variable "regex" { 22 | description = "(Required) The regular expression (regex) that defines the pattern to match. The expression can contain as many as 512 characters." 23 | type = string 24 | nullable = false 25 | } 26 | 27 | variable "keywords" { 28 | description = "(Optional) An array that lists specific character sequences (keywords), one of which must be within proximity (maximum_match_distance) of the regular expression to match. The array can contain as many as 50 keywords. Each keyword can contain 3 - 90 characters. Keywords aren't case sensitive." 29 | type = set(string) 30 | default = [] 31 | nullable = false 32 | } 33 | 34 | variable "ignore_words" { 35 | description = "(Optional) An array that lists specific character sequences (ignore words) to exclude from the results. If the text matched by the regular expression is the same as any string in this array, Amazon Macie ignores it. The array can contain as many as 10 ignore words. Each ignore word can contain 4 - 90 characters. Ignore words are case sensitive." 36 | type = set(string) 37 | default = [] 38 | nullable = false 39 | } 40 | 41 | variable "maximum_match_distance" { 42 | description = "(Optional) The maximum allowable distance between text that matches the regex pattern and the keywords. The distance can be 1 - 300 characters. Defaults to `50`." 43 | type = number 44 | default = 50 45 | nullable = false 46 | 47 | validation { 48 | condition = var.maximum_match_distance >= 1 && var.maximum_match_distance <= 300 49 | error_message = "Value for `maximum_match_distance` must be between 1 and 300." 50 | } 51 | } 52 | 53 | variable "tags" { 54 | description = "(Optional) A map of tags to add to all resources." 55 | type = map(string) 56 | default = {} 57 | nullable = false 58 | } 59 | 60 | variable "module_tags_enabled" { 61 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 62 | type = bool 63 | default = true 64 | nullable = false 65 | } 66 | 67 | 68 | ################################################### 69 | # Resource Group 70 | ################################################### 71 | 72 | variable "resource_group" { 73 | description = < account 54 | } 55 | 56 | region = aws_macie2_account.this.region 57 | 58 | account_id = each.key 59 | email = each.value.email 60 | status = each.value.enabled ? "ENABLED" : "PAUSED" 61 | 62 | 63 | ## Invitation 64 | invite = each.value.type == "INVITATION" ? true : null 65 | invitation_message = each.value.invitation.message 66 | invitation_disable_email_notification = !each.value.invitation.email_notification_enabled 67 | 68 | 69 | tags = merge( 70 | { 71 | "Name" = each.key 72 | }, 73 | local.module_tags, 74 | var.tags, 75 | try(each.value.tags, {}), 76 | ) 77 | 78 | # TODO: Bug for `email` parameter only when member is from organization 79 | # https://github.com/hashicorp/terraform-provider-aws/issues/26218 80 | lifecycle { 81 | ignore_changes = [ 82 | email, 83 | ] 84 | } 85 | } 86 | 87 | ################################################### 88 | # Organization Configurations for Macie Account 89 | ################################################### 90 | 91 | resource "aws_macie2_organization_configuration" "this" { 92 | count = anytrue([ 93 | for account in var.member_accounts : 94 | account.type == "ORGANIZATION" 95 | ]) ? 1 : 0 96 | 97 | region = aws_macie2_account.this.region 98 | 99 | auto_enable = var.organization_config.auto_enable 100 | } 101 | 102 | 103 | ################################################### 104 | # S3 Bucket for Sensitive Data Discovery Results 105 | ################################################### 106 | 107 | resource "aws_macie2_classification_export_configuration" "this" { 108 | count = var.discovery_result_repository.s3_bucket != null ? 1 : 0 109 | 110 | region = aws_macie2_account.this.region 111 | 112 | s3_destination { 113 | bucket_name = var.discovery_result_repository.s3_bucket.name 114 | key_prefix = var.discovery_result_repository.s3_bucket.key_prefix 115 | kms_key_arn = var.discovery_result_repository.s3_bucket.sse_kms_key 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-security 2 | 3 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/tedilabs/terraform-aws-security?color=blue&sort=semver&style=flat-square) 4 | ![GitHub](https://img.shields.io/github/license/tedilabs/terraform-aws-security?color=blue&style=flat-square) 5 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square)](https://github.com/pre-commit/pre-commit) 6 | 7 | Terraform module which creates security related resources on AWS. 8 | 9 | - [access-analyzer](./modules/access-analyzer) 10 | - [cloudtrail-event-data-store](./modules/cloudtrail-event-data-store) 11 | - [cloudtrail-trail](./modules/cloudtrail-trail) 12 | - [config-managed-rule](./modules/config-managed-rule) 13 | - [config-recorder](./modules/config-recorder) 14 | - [macie-account](./modules/macie-account) 15 | - [macie-custom-data-identifier](./modules/macie-custom-data-identifier) 16 | 17 | 18 | ## Target AWS Services 19 | 20 | Terraform Modules from [this package](https://github.com/tedilabs/terraform-aws-security) were written to manage the following AWS Services with Terraform. 21 | 22 | - **AWS IAM** 23 | - Access Analyzer 24 | - **AWS CloudTrail** 25 | - Event Data Store 26 | - Trail 27 | - **AWS Config** 28 | - Recorder 29 | - Rules 30 | - Managed Rules 31 | - **AWS Macie** 32 | - Account 33 | - Member Accounts 34 | - Organization Configurations 35 | - Custom Data Identifier 36 | 37 | 38 | ## Usage 39 | 40 | ### CloudTrail 41 | 42 | ```tf 43 | module "event_data_store" { 44 | source = "tedilabs/security/aws//modules/cloudtrail-event-data-store" 45 | version = "~> 0.6.0" 46 | 47 | name = "management-event" 48 | 49 | level = "ACCOUNT" 50 | scope = "REGIONAL" 51 | 52 | 53 | ## Event Selector 54 | event_type = "CLOUDTRAIL_EVENTS" 55 | event_selectors = [ 56 | { 57 | category = "MANAGEMENT" 58 | scope = "READ" 59 | exclude_sources = ["kms.amazonaws.com"] 60 | }, 61 | { 62 | category = "DATA" 63 | scope = "ALL" 64 | resource_type = "AWS::S3::Object" 65 | selectors = [ 66 | { 67 | field = "resource_arn" 68 | operator = "ends_with" 69 | values = ["hello"] 70 | } 71 | ] 72 | }, 73 | { 74 | category = "DATA" 75 | scope = "WRITE" 76 | resource_type = "AWS::S3Outposts::Object" 77 | selectors = [ 78 | { 79 | field = "event_name" 80 | operator = "starts_with" 81 | values = ["Put"] 82 | } 83 | ] 84 | }, 85 | ] 86 | 87 | 88 | ## IAM Role 89 | import_trail_events_iam_role = { 90 | enabled = true 91 | source_s3_buckets = [ 92 | { 93 | name = "helloworld" 94 | key_prefix = "asdf/" 95 | }, 96 | { 97 | name = "foo" 98 | key_prefix = "bar/" 99 | }, 100 | { 101 | name = "demo" 102 | key_prefix = "" 103 | }, 104 | ] 105 | } 106 | 107 | 108 | ## Attributes 109 | retention_in_days = 365 * 7 110 | termination_protection_enabled = false 111 | 112 | tags = { 113 | "project" = "terraform-aws-security-examples" 114 | } 115 | } 116 | ``` 117 | 118 | 119 | ## Examples 120 | 121 | ### CloudTrail 122 | 123 | - [Simple Event Data Store in CloudTrail](./examples/cloudtrail-event-data-store-simple) 124 | - [Event Data Store in CloudTrail with Config Configuration Items](./examples/cloudtrail-event-data-store-config) 125 | - [Full Event Data Store in CloudTrail](./examples/cloudtrail-event-data-store-full) 126 | 127 | ### Macie 128 | 129 | - [Simple Macie Account](./examples/macie-account-simple) 130 | 131 | 132 | ## Self Promotion 133 | 134 | Like this project? Follow the repository on [GitHub](https://github.com/tedilabs/terraform-aws-security). And if you're feeling especially charitable, follow **[posquit0](https://github.com/posquit0)** on GitHub. 135 | 136 | 137 | ## License 138 | 139 | Provided under the terms of the [Apache License](LICENSE). 140 | 141 | Copyright © 2021-2025, [Byungjin Park](https://www.posquit0.com). 142 | -------------------------------------------------------------------------------- /modules/access-analyzer/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-security" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # Access Analyzer 20 | ################################################### 21 | 22 | resource "aws_accessanalyzer_analyzer" "this" { 23 | region = var.region 24 | 25 | analyzer_name = var.name 26 | type = "${var.scope}${var.type == "EXTERNAL_ACCESS" ? "" : "_${var.type}"}" 27 | 28 | dynamic "configuration" { 29 | for_each = contains(["INTERNAL_ACCESS", "UNUSED_ACCESS"], var.type) ? ["go"] : [] 30 | 31 | content { 32 | dynamic "internal_access" { 33 | for_each = var.type == "INTERNAL_ACCESS" ? ["go"] : [] 34 | 35 | content { 36 | dynamic "analysis_rule" { 37 | for_each = length(var.internal_access_analysis.rules) > 0 ? ["go"] : [] 38 | iterator = rule 39 | 40 | content { 41 | dynamic "inclusion" { 42 | for_each = var.internal_access_analysis.rules[*].inclusion 43 | 44 | content { 45 | account_ids = (length(inclusion.value.accounts) > 0 46 | ? inclusion.value.accounts 47 | : null 48 | ) 49 | resource_arns = (length(inclusion.value.resource_arns) > 0 50 | ? inclusion.value.resource_arns 51 | : null 52 | ) 53 | resource_types = (length(inclusion.value.resource_types) > 0 54 | ? inclusion.value.resource_types 55 | : null 56 | ) 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | dynamic "unused_access" { 65 | for_each = var.type == "UNUSED_ACCESS" ? ["go"] : [] 66 | 67 | content { 68 | unused_access_age = var.unused_access_analysis.tracking_period 69 | 70 | dynamic "analysis_rule" { 71 | for_each = length(var.unused_access_analysis.rules) > 0 ? ["go"] : [] 72 | iterator = rule 73 | 74 | content { 75 | dynamic "exclusion" { 76 | for_each = var.unused_access_analysis.rules[*].exclusion 77 | 78 | content { 79 | account_ids = (length(exclusion.value.accounts) > 0 80 | ? exclusion.value.accounts 81 | : null 82 | ) 83 | resource_tags = (length(exclusion.value.resource_tags) > 0 84 | ? exclusion.value.resource_tags 85 | : null 86 | ) 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | 96 | tags = merge( 97 | { 98 | "Name" = local.metadata.name 99 | }, 100 | local.module_tags, 101 | var.tags, 102 | ) 103 | } 104 | 105 | resource "aws_accessanalyzer_archive_rule" "this" { 106 | for_each = { 107 | for rule in var.archive_rules : 108 | rule.name => rule 109 | } 110 | 111 | region = var.region 112 | 113 | analyzer_name = aws_accessanalyzer_analyzer.this.analyzer_name 114 | rule_name = each.key 115 | 116 | dynamic "filter" { 117 | for_each = each.value.filters 118 | 119 | content { 120 | criteria = filter.value.criteria 121 | 122 | contains = try(filter.value.contains, null) 123 | exists = try(filter.value.exists, null) 124 | eq = try(filter.value.eq, null) 125 | neq = try(filter.value.neq, null) 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /modules/config-recorder/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-security" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | locals { 18 | recording_groups = { 19 | "ALL" = { 20 | recording_strategy = "ALL_SUPPORTED_RESOURCE_TYPES" 21 | all_supported = true 22 | include_global_resource_types = true 23 | resource_types = null 24 | exclude_resource_types = null 25 | } 26 | "ALL_WITHOUT_GLOBAL" = { 27 | recording_strategy = "ALL_SUPPORTED_RESOURCE_TYPES" 28 | all_supported = true 29 | include_global_resource_types = false 30 | resource_types = null 31 | exclude_resource_types = null 32 | } 33 | "WHITELIST" = { 34 | recording_strategy = "INCLUSION_BY_RESOURCE_TYPES" 35 | all_supported = false 36 | include_global_resource_types = false 37 | resource_types = var.scope.resource_types 38 | exclude_resource_types = null 39 | } 40 | "BLACKLIST" = { 41 | recording_strategy = "EXCLUSION_BY_RESOURCE_TYPES" 42 | all_supported = false 43 | include_global_resource_types = false 44 | resource_types = null 45 | exclude_resource_types = var.scope.resource_types 46 | } 47 | } 48 | delivery_frequency = { 49 | "1h" = "One_Hour" 50 | "3h" = "Three_Hours" 51 | "6h" = "Six_Hours" 52 | "12h" = "Twelve_Hours" 53 | "24h" = "TwentyFour_Hours" 54 | } 55 | } 56 | 57 | 58 | ################################################### 59 | # Config Recorder 60 | ################################################### 61 | 62 | resource "aws_config_configuration_recorder" "this" { 63 | region = var.region 64 | 65 | name = var.name 66 | role_arn = (var.default_service_role.enabled 67 | ? module.role__recorder[0].arn 68 | : var.service_role 69 | ) 70 | 71 | recording_mode { 72 | recording_frequency = var.recording_frequency.mode 73 | 74 | dynamic "recording_mode_override" { 75 | for_each = var.recording_frequency.overrides 76 | iterator = override 77 | 78 | content { 79 | resource_types = override.value.resource_types 80 | recording_frequency = override.value.mode 81 | description = override.value.description 82 | } 83 | } 84 | } 85 | 86 | recording_group { 87 | recording_strategy { 88 | use_only = local.recording_groups[var.scope.strategy].recording_strategy 89 | } 90 | 91 | all_supported = local.recording_groups[var.scope.strategy].all_supported 92 | include_global_resource_types = local.recording_groups[var.scope.strategy].include_global_resource_types 93 | resource_types = local.recording_groups[var.scope.strategy].resource_types 94 | 95 | dynamic "exclusion_by_resource_types" { 96 | for_each = local.recording_groups[var.scope.strategy].exclude_resource_types != null ? ["go"] : [] 97 | 98 | content { 99 | resource_types = local.recording_groups[var.scope.strategy].exclude_resource_types 100 | } 101 | } 102 | } 103 | } 104 | 105 | resource "aws_config_configuration_recorder_status" "this" { 106 | region = var.region 107 | 108 | name = aws_config_configuration_recorder.this.name 109 | is_enabled = var.enabled 110 | 111 | depends_on = [ 112 | aws_config_delivery_channel.this, 113 | ] 114 | } 115 | 116 | resource "aws_config_retention_configuration" "this" { 117 | region = var.region 118 | 119 | retention_period_in_days = var.retention_period 120 | } 121 | 122 | 123 | ################################################### 124 | # Delivery Channel 125 | ################################################### 126 | 127 | resource "aws_config_delivery_channel" "this" { 128 | region = var.region 129 | 130 | name = aws_config_configuration_recorder.this.name 131 | 132 | s3_bucket_name = var.delivery_channels.s3_bucket.name 133 | s3_key_prefix = var.delivery_channels.s3_bucket.key_prefix 134 | s3_kms_key_arn = var.delivery_channels.s3_bucket.sse_kms_key 135 | 136 | sns_topic_arn = (var.delivery_channels.sns_topic.enabled 137 | ? var.delivery_channels.sns_topic.arn 138 | : null 139 | ) 140 | 141 | dynamic "snapshot_delivery_properties" { 142 | for_each = var.snapshot_delivery.enabled ? ["go"] : [] 143 | 144 | content { 145 | delivery_frequency = local.delivery_frequency[var.snapshot_delivery.frequency] 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /modules/cloudtrail-event-data-store/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-security" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | locals { 18 | event_categories = { 19 | "DATA" = "Data" 20 | "MANAGEMENT" = "Management" 21 | } 22 | event_scopes = { 23 | "ALL" = "All" 24 | "READ" = "ReadOnly" 25 | "WRITE" = "WriteOnly" 26 | } 27 | } 28 | 29 | 30 | ################################################### 31 | # Event Data Store on CloudTrail 32 | ################################################### 33 | 34 | resource "aws_cloudtrail_event_data_store" "this" { 35 | region = var.region 36 | 37 | name = var.name 38 | suspend = !var.enabled 39 | 40 | organization_enabled = var.level == "ORGANIZATION" 41 | multi_region_enabled = var.scope == "ALL" 42 | 43 | 44 | ## Encryption 45 | kms_key_id = var.encryption.kms_key 46 | 47 | 48 | ## Event Selector - AWS CloudTrail Events (Management) 49 | dynamic "advanced_event_selector" { 50 | for_each = var.event_type == "CLOUDTRAIL_EVENTS" && var.management_event_selector.enabled ? [var.management_event_selector] : [] 51 | iterator = selector 52 | 53 | content { 54 | name = "AWS CloudTrail Events - Management" 55 | 56 | field_selector { 57 | field = "eventCategory" 58 | equals = ["Management"] 59 | } 60 | 61 | dynamic "field_selector" { 62 | for_each = selector.value.scope != "ALL" ? ["go"] : [] 63 | 64 | content { 65 | field = "readOnly" 66 | equals = [{ 67 | "READ" = "true" 68 | "WRITE" = "false" 69 | }[selector.value.scope]] 70 | } 71 | } 72 | 73 | dynamic "field_selector" { 74 | for_each = (length(selector.value.exclude_event_sources) > 0) ? ["go"] : [] 75 | 76 | content { 77 | field = "eventSource" 78 | not_equals = selector.value.exclude_event_sources 79 | } 80 | } 81 | } 82 | } 83 | 84 | 85 | ## Event Selector - AWS CloudTrail Events (Data) 86 | dynamic "advanced_event_selector" { 87 | for_each = var.event_type == "CLOUDTRAIL_EVENTS" ? var.data_event_selectors : [] 88 | iterator = selector 89 | 90 | content { 91 | name = coalesce(selector.value.name, "AWS CloudTrail Events - Data ${selector.key}") 92 | 93 | field_selector { 94 | field = "eventCategory" 95 | equals = ["Data"] 96 | } 97 | 98 | field_selector { 99 | field = "resources.type" 100 | equals = [selector.value.resource_type] 101 | } 102 | 103 | dynamic "field_selector" { 104 | for_each = selector.value.scope != "ALL" ? ["go"] : [] 105 | 106 | content { 107 | field = "readOnly" 108 | equals = [{ 109 | "READ" = "true" 110 | "WRITE" = "false" 111 | }[selector.value.scope]] 112 | } 113 | } 114 | 115 | dynamic "field_selector" { 116 | for_each = length(selector.value.conditions) > 0 ? selector.value.conditions : [] 117 | iterator = condition 118 | 119 | content { 120 | field = condition.value.field 121 | 122 | equals = condition.value.operator == "equals" ? condition.value.values : null 123 | not_equals = condition.value.operator == "not_equals" ? condition.value.values : null 124 | starts_with = condition.value.operator == "starts_with" ? condition.value.values : null 125 | not_starts_with = condition.value.operator == "not_starts_with" ? condition.value.values : null 126 | ends_with = condition.value.operator == "ends_with" ? condition.value.values : null 127 | not_ends_with = condition.value.operator == "not_ends_with" ? condition.value.values : null 128 | } 129 | } 130 | } 131 | } 132 | 133 | ## Event Selector - AWS Config Configuration Items 134 | dynamic "advanced_event_selector" { 135 | for_each = var.event_type == "CONFIG_CONFIGURATION_ITEMS" ? ["go"] : [] 136 | 137 | content { 138 | name = "AWS Config Configuration Items" 139 | 140 | field_selector { 141 | field = "eventCategory" 142 | equals = ["ConfigurationItem"] 143 | } 144 | } 145 | } 146 | 147 | 148 | ## Attributes 149 | billing_mode = var.billing_mode 150 | retention_period = var.retention_in_days 151 | termination_protection_enabled = var.termination_protection_enabled 152 | 153 | tags = merge( 154 | { 155 | "Name" = local.metadata.name 156 | }, 157 | local.module_tags, 158 | var.tags, 159 | ) 160 | } 161 | -------------------------------------------------------------------------------- /modules/config-managed-rule/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-security" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = coalesce(var.name, local.rule.default_name) 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | locals { 18 | managed_rules = jsondecode(file("${path.module}/rules.json")) 19 | rule = local.managed_rules[var.source_rule] 20 | 21 | frequency = { 22 | "1h" = "One_Hour" 23 | "3h" = "Three_Hours" 24 | "6h" = "Six_Hours" 25 | "12h" = "Twelve_Hours" 26 | "24h" = "TwentyFour_Hours" 27 | } 28 | 29 | scope = { 30 | "ALL_CHANGES" = { 31 | resource_types = null 32 | resource_id = null 33 | tag_key = null 34 | tag_value = null 35 | } 36 | "RESOURCES" = { 37 | resource_types = concat(var.resource_types, local.rule.trigger_by_change.enabled ? local.rule.trigger_by_change.scope.resource_types : []) 38 | resource_id = var.resource_id 39 | tag_key = null 40 | tag_value = null 41 | } 42 | "TAGS" = { 43 | resource_types = null 44 | resource_id = null 45 | tag_key = var.resource_tag != null ? var.resource_tag.key : null 46 | tag_value = var.resource_tag != null ? var.resource_tag.value : null 47 | } 48 | } 49 | } 50 | 51 | 52 | ################################################### 53 | # Managed Rule for Account 54 | ################################################### 55 | 56 | resource "aws_config_config_rule" "this" { 57 | count = var.level == "ACCOUNT" ? 1 : 0 58 | 59 | region = var.region 60 | 61 | name = local.metadata.name 62 | description = coalesce(var.description, local.rule.description) 63 | 64 | source { 65 | owner = "AWS" 66 | source_identifier = local.rule.id 67 | } 68 | 69 | input_parameters = jsonencode(var.parameters) 70 | 71 | ### Trigger by configuration change 72 | dynamic "evaluation_mode" { 73 | for_each = var.evaluation_modes 74 | 75 | content { 76 | mode = evaluation_mode.value 77 | } 78 | } 79 | 80 | ## Scope: ALL_CHANGES 81 | dynamic "scope" { 82 | for_each = local.rule.trigger_by_change.enabled && var.scope == "ALL_CHANGES" ? ["go"] : [] 83 | 84 | content {} 85 | } 86 | ## Scope: RESOURCES 87 | dynamic "scope" { 88 | for_each = local.rule.trigger_by_change.enabled && var.scope == "RESOURCES" ? ["go"] : [] 89 | 90 | content { 91 | compliance_resource_types = local.scope["RESOURCES"].resource_types 92 | compliance_resource_id = local.scope["RESOURCES"].resource_id 93 | } 94 | } 95 | ## Scope: TAGS 96 | dynamic "scope" { 97 | for_each = local.rule.trigger_by_change.enabled && var.scope == "TAGS" ? ["go"] : [] 98 | 99 | content { 100 | tag_key = local.scope["TAGS"].tag_key 101 | tag_value = local.scope["TAGS"].tag_value 102 | } 103 | } 104 | 105 | ### Trigger by schedule 106 | maximum_execution_frequency = (local.rule.trigger_by_schedule.enabled 107 | ? local.frequency[coalesce(var.schedule_frequency, local.rule.trigger_by_schedule.max_frequency)] 108 | : null) 109 | 110 | tags = merge( 111 | local.module_tags, 112 | var.tags, 113 | ) 114 | } 115 | 116 | 117 | ################################################### 118 | # Managed Rule for Organization 119 | ################################################### 120 | 121 | resource "aws_config_organization_managed_rule" "this" { 122 | count = var.level == "ORGANIZATION" ? 1 : 0 123 | 124 | region = var.region 125 | 126 | name = local.metadata.name 127 | description = coalesce(var.description, local.rule.description) 128 | 129 | rule_identifier = local.rule.id 130 | 131 | input_parameters = jsonencode(var.parameters) 132 | 133 | ### Trigger by configuration change 134 | resource_types_scope = (local.rule.trigger_by_change.enabled 135 | ? local.scope[var.scope].resource_types 136 | : null) 137 | resource_id_scope = (local.rule.trigger_by_change.enabled 138 | ? local.scope[var.scope].resource_id 139 | : null) 140 | tag_key_scope = (local.rule.trigger_by_change.enabled 141 | ? local.scope[var.scope].tag_key 142 | : null) 143 | tag_value_scope = (local.rule.trigger_by_change.enabled 144 | ? local.scope[var.scope].tag_value 145 | : null) 146 | 147 | ### Trigger by schedule 148 | maximum_execution_frequency = (local.rule.trigger_by_schedule.enabled 149 | ? local.frequency[coalesce(var.schedule_frequency, local.rule.trigger_by_schedule.max_frequency)] 150 | : null) 151 | 152 | excluded_accounts = var.excluded_accounts 153 | 154 | timeouts { 155 | create = var.timeouts.create 156 | update = var.timeouts.update 157 | delete = var.timeouts.delete 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /modules/config-recorder/outputs.tf: -------------------------------------------------------------------------------- 1 | output "region" { 2 | description = "The AWS region this module resources resides in." 3 | value = aws_config_configuration_recorder.this.region 4 | } 5 | 6 | output "name" { 7 | description = "The name of the recorder." 8 | value = aws_config_configuration_recorder.this.name 9 | } 10 | 11 | output "id" { 12 | description = "The ID of the recorder." 13 | value = aws_config_configuration_recorder.this.id 14 | } 15 | 16 | output "enabled" { 17 | description = "Whether the configuration recorder is enabled." 18 | value = aws_config_configuration_recorder_status.this.is_enabled 19 | } 20 | 21 | output "retention_period" { 22 | description = "The number of days AWS Config stores historical information" 23 | value = aws_config_retention_configuration.this.retention_period_in_days 24 | } 25 | 26 | output "recording_frequency" { 27 | description = < 0) ? ["go"] : [] 105 | 106 | content { 107 | field = "eventSource" 108 | not_equals = selector.value.exclude_event_sources 109 | } 110 | } 111 | } 112 | } 113 | 114 | 115 | ## Event Selector - Data Events 116 | dynamic "advanced_event_selector" { 117 | for_each = var.data_event_selectors 118 | iterator = selector 119 | 120 | content { 121 | name = coalesce(selector.value.name, "AWS CloudTrail Data Events ${selector.key}") 122 | 123 | field_selector { 124 | field = "eventCategory" 125 | equals = ["Data"] 126 | } 127 | 128 | field_selector { 129 | field = "resources.type" 130 | equals = [selector.value.resource_type] 131 | } 132 | 133 | dynamic "field_selector" { 134 | for_each = selector.value.scope != "ALL" ? ["go"] : [] 135 | 136 | content { 137 | field = "readOnly" 138 | equals = [{ 139 | "READ" = "true" 140 | "WRITE" = "false" 141 | }[selector.value.scope]] 142 | } 143 | } 144 | 145 | dynamic "field_selector" { 146 | for_each = length(selector.value.conditions) > 0 ? selector.value.conditions : [] 147 | iterator = condition 148 | 149 | content { 150 | field = condition.value.field 151 | 152 | equals = condition.value.operator == "equals" ? condition.value.values : null 153 | not_equals = condition.value.operator == "not_equals" ? condition.value.values : null 154 | starts_with = condition.value.operator == "starts_with" ? condition.value.values : null 155 | not_starts_with = condition.value.operator == "not_starts_with" ? condition.value.values : null 156 | ends_with = condition.value.operator == "ends_with" ? condition.value.values : null 157 | not_ends_with = condition.value.operator == "not_ends_with" ? condition.value.values : null 158 | } 159 | } 160 | } 161 | } 162 | 163 | 164 | ## Event Selector - Insight Events 165 | dynamic "insight_selector" { 166 | for_each = var.insight_event_selector.enabled ? var.insight_event_selector.scopes : [] 167 | iterator = selector 168 | 169 | content { 170 | insight_type = local.insight_types[selector.value] 171 | } 172 | } 173 | 174 | tags = merge( 175 | { 176 | "Name" = local.metadata.name 177 | }, 178 | local.module_tags, 179 | var.tags, 180 | ) 181 | } 182 | -------------------------------------------------------------------------------- /modules/macie-custom-data-identifier/README.md: -------------------------------------------------------------------------------- 1 | # macie-custom-data-identifier 2 | 3 | This module creates following resources. 4 | 5 | - `aws_macie2_custom_data_identifier` 6 | 7 | 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.12 | 13 | | [aws](#requirement\_aws) | >= 6.12 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |------|---------| 19 | | [aws](#provider\_aws) | 6.13.0 | 20 | 21 | ## Modules 22 | 23 | | Name | Source | Version | 24 | |------|--------|---------| 25 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |------|------| 31 | | [aws_macie2_custom_data_identifier.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/macie2_custom_data_identifier) | resource | 32 | 33 | ## Inputs 34 | 35 | | Name | Description | Type | Default | Required | 36 | |------|-------------|------|---------|:--------:| 37 | | [name](#input\_name) | (Required) A name for the custom data identifier. The name can contain as many as 128 characters. | `string` | n/a | yes | 38 | | [regex](#input\_regex) | (Required) The regular expression (regex) that defines the pattern to match. The expression can contain as many as 512 characters. | `string` | n/a | yes | 39 | | [description](#input\_description) | (Optional) A description of the custom data identifier. Defaults to `Managed by Terraform.`. | `string` | `"Managed by Terraform."` | no | 40 | | [ignore\_words](#input\_ignore\_words) | (Optional) An array that lists specific character sequences (ignore words) to exclude from the results. If the text matched by the regular expression is the same as any string in this array, Amazon Macie ignores it. The array can contain as many as 10 ignore words. Each ignore word can contain 4 - 90 characters. Ignore words are case sensitive. | `set(string)` | `[]` | no | 41 | | [keywords](#input\_keywords) | (Optional) An array that lists specific character sequences (keywords), one of which must be within proximity (maximum\_match\_distance) of the regular expression to match. The array can contain as many as 50 keywords. Each keyword can contain 3 - 90 characters. Keywords aren't case sensitive. | `set(string)` | `[]` | no | 42 | | [maximum\_match\_distance](#input\_maximum\_match\_distance) | (Optional) The maximum allowable distance between text that matches the regex pattern and the keywords. The distance can be 1 - 300 characters. Defaults to `50`. | `number` | `50` | no | 43 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 44 | | [region](#input\_region) | (Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region. | `string` | `null` | no | 45 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 46 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 47 | 48 | ## Outputs 49 | 50 | | Name | Description | 51 | |------|-------------| 52 | | [arn](#output\_arn) | The ARN (Amazon Resource Name) for the macie custom data identifier. For example: `arn:aws:cloudfront::123456789012:distribution/EDFDVBD632BHDS5`. | 53 | | [created\_at](#output\_created\_at) | The date and time, in UTC and extended RFC 3339 format, when the Amazon Macie custom data identifier was created. | 54 | | [description](#output\_description) | The description of the macie custom data identifier. | 55 | | [id](#output\_id) | The ID of the macie custom data identifier. | 56 | | [ignore\_words](#output\_ignore\_words) | An array that lists specific character sequences (ignore words) to exclude from the results. | 57 | | [keywords](#output\_keywords) | An array that lists specific character sequences (keywords), one of which must be within proximity (maximum\_match\_distance) of the regular expression to match. | 58 | | [maximum\_match\_distance](#output\_maximum\_match\_distance) | The maximum number of characters that can exist between text that matches the regex pattern and the character sequences specified by the keywords array. | 59 | | [name](#output\_name) | The name of the macie custom data identifier. | 60 | | [regex](#output\_regex) | The regular expression (regex) that defines the pattern to match. | 61 | | [region](#output\_region) | The AWS region this module resources resides in. | 62 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 63 | 64 | -------------------------------------------------------------------------------- /modules/config-recorder/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "this" {} 2 | 3 | locals { 4 | account_id = data.aws_caller_identity.this.account_id 5 | } 6 | 7 | 8 | ################################################### 9 | # IAM Role 10 | ################################################### 11 | 12 | module "role__recorder" { 13 | count = var.default_service_role.enabled ? 1 : 0 14 | 15 | source = "tedilabs/account/aws//modules/iam-role" 16 | version = "~> 0.32.0" 17 | 18 | name = coalesce( 19 | var.default_service_role.name, 20 | "config-configuration-recorder-${local.metadata.name}", 21 | ) 22 | path = var.default_service_role.path 23 | description = var.default_service_role.description 24 | 25 | trusted_service_policies = [ 26 | { 27 | services = ["config.amazonaws.com"] 28 | }, 29 | ] 30 | 31 | policies = concat( 32 | ["arn:aws:iam::aws:policy/service-role/AWS_ConfigRole"], 33 | var.default_service_role.policies, 34 | ) 35 | inline_policies = merge( 36 | { 37 | "delivery" = data.aws_iam_policy_document.delivery.json 38 | }, 39 | var.default_service_role.inline_policies 40 | ) 41 | 42 | force_detach_policies = true 43 | resource_group = { 44 | enabled = false 45 | } 46 | module_tags_enabled = false 47 | 48 | tags = merge( 49 | local.module_tags, 50 | var.tags, 51 | ) 52 | } 53 | 54 | module "role__aggregator" { 55 | count = var.organization_aggregation.enabled && var.default_organization_aggregator_role.enabled ? 1 : 0 56 | 57 | source = "tedilabs/account/aws//modules/iam-role" 58 | version = "~> 0.32.0" 59 | 60 | name = coalesce( 61 | var.default_organization_aggregator_role.name, 62 | "config-configuration-aggregator-${local.metadata.name}", 63 | ) 64 | path = var.default_organization_aggregator_role.path 65 | description = var.default_organization_aggregator_role.description 66 | 67 | trusted_service_policies = [ 68 | { 69 | services = ["config.amazonaws.com"] 70 | }, 71 | ] 72 | 73 | policies = concat( 74 | ["arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations"], 75 | var.default_organization_aggregator_role.policies, 76 | ) 77 | inline_policies = var.default_organization_aggregator_role.inline_policies 78 | 79 | force_detach_policies = true 80 | resource_group = { 81 | enabled = false 82 | } 83 | module_tags_enabled = false 84 | 85 | tags = merge( 86 | local.module_tags, 87 | var.tags, 88 | ) 89 | } 90 | 91 | 92 | ################################################### 93 | # IAM Policies 94 | ################################################### 95 | 96 | data "aws_iam_policy_document" "delivery" { 97 | statement { 98 | sid = "DeliverToS3Bucket" 99 | 100 | effect = "Allow" 101 | actions = [ 102 | "s3:PutObject", 103 | "s3:PutObjectAcl", 104 | ] 105 | resources = [ 106 | var.delivery_channels.s3_bucket.key_prefix != null 107 | ? "arn:aws:s3:::${var.delivery_channels.s3_bucket.name}/*/AWSLogs/${local.account_id}/*" 108 | : "arn:aws:s3:::${var.delivery_channels.s3_bucket.name}/AWSLogs/${local.account_id}/*", 109 | ] 110 | 111 | condition { 112 | test = "StringEquals" 113 | variable = "s3:x-amz-acl" 114 | values = ["bucket-owner-full-control"] 115 | } 116 | } 117 | statement { 118 | sid = "CheckS3BucektPermission" 119 | 120 | effect = "Allow" 121 | actions = [ 122 | "s3:GetBucketAcl", 123 | ] 124 | resources = [ 125 | "arn:aws:s3:::${var.delivery_channels.s3_bucket.name}", 126 | ] 127 | } 128 | dynamic "statement" { 129 | for_each = var.delivery_channels.s3_bucket.sse_kms_key != null ? ["go"] : [] 130 | 131 | content { 132 | sid = "EnableS3Encryption" 133 | 134 | effect = "Allow" 135 | actions = [ 136 | "kms:Decrypt", 137 | "kms:GenerateDataKey", 138 | ] 139 | resources = [ 140 | var.delivery_channels.s3_bucket.sse_kms_key, 141 | ] 142 | } 143 | } 144 | dynamic "statement" { 145 | for_each = var.delivery_channels.sns_topic.arn != null ? ["go"] : [] 146 | 147 | content { 148 | sid = "PublishToSnsTopic" 149 | 150 | effect = "Allow" 151 | actions = [ 152 | "sns:Publish", 153 | ] 154 | resources = [ 155 | var.delivery_channels.sns_topic.arn, 156 | ] 157 | } 158 | } 159 | } 160 | 161 | data "aws_iam_policy_document" "aggregation" { 162 | statement { 163 | sid = "DeliverToS3Bucket" 164 | 165 | effect = "Allow" 166 | actions = [ 167 | "s3:PutObject", 168 | "s3:PutObjectAcl", 169 | ] 170 | resources = [ 171 | var.delivery_channels.s3_bucket.key_prefix != null && var.delivery_channels.s3_bucket.key_prefix != "" 172 | ? "arn:aws:s3:::${var.delivery_channels.s3_bucket.name}/*/AWSLogs/${local.account_id}/*" 173 | : "arn:aws:s3:::${var.delivery_channels.s3_bucket.name}/AWSLogs/${local.account_id}/*", 174 | ] 175 | 176 | condition { 177 | test = "StringEquals" 178 | variable = "s3:x-amz-acl" 179 | values = ["bucket-owner-full-control"] 180 | } 181 | } 182 | statement { 183 | sid = "CheckS3BucektPermission" 184 | 185 | effect = "Allow" 186 | actions = [ 187 | "s3:GetBucketAcl", 188 | ] 189 | resources = [ 190 | "arn:aws:s3:::${var.delivery_channels.s3_bucket.name}", 191 | ] 192 | } 193 | dynamic "statement" { 194 | for_each = var.delivery_channels.s3_bucket.sse_kms_key != null ? ["go"] : [] 195 | 196 | content { 197 | sid = "EnableS3Encryption" 198 | 199 | effect = "Allow" 200 | actions = [ 201 | "kms:Decrypt", 202 | "kms:GenerateDataKey", 203 | ] 204 | resources = [ 205 | var.delivery_channels.s3_bucket.sse_kms_key, 206 | ] 207 | } 208 | } 209 | dynamic "statement" { 210 | for_each = var.delivery_channels.sns_topic.arn != null ? ["go"] : [] 211 | 212 | content { 213 | sid = "PublishToSnsTopic" 214 | 215 | effect = "Allow" 216 | actions = [ 217 | "sns:Publish", 218 | ] 219 | resources = [ 220 | var.delivery_channels.sns_topic.arn, 221 | ] 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /modules/macie-account/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "(Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region." 3 | type = string 4 | default = null 5 | nullable = true 6 | } 7 | 8 | variable "enabled" { 9 | description = "(Optional) Whether to enable Amazon Macie and start all Macie activities for the account. Defaults to `true`. Set `false` to suspend Macie, it stops monitoring your AWS environment and does not generate new findings. The existing findings remain intact and are not affected. Delete `aws_macie2_account` resource to disable Macie, it permanently deletes all of your existing findings, classification jobs, and other Macie resources." 10 | type = bool 11 | default = true 12 | nullable = false 13 | } 14 | 15 | variable "update_frequency" { 16 | description = "(Optional) How often to publish updates to policy findings for the account. This includes publishing updates to AWS Security Hub and Amazon EventBridge (formerly called Amazon CloudWatch Events). Valid values are `15m`, `1h` or `6h`. Defaults to `15m`." 17 | type = string 18 | default = "15m" 19 | nullable = false 20 | 21 | validation { 22 | condition = contains(["15m", "1h", "6h"], var.update_frequency) 23 | error_message = "Valid values for `update_frequency` are `15m`, `1h` or `6h`." 24 | } 25 | } 26 | 27 | variable "member_accounts" { 28 | description = <= 1, 84 | var.unused_access_analysis.tracking_period <= 180 85 | ]) 86 | error_message = "Valid value for `tracking_period` is between 1 and 180." 87 | } 88 | } 89 | 90 | variable "archive_rules" { 91 | description = < 0 118 | ]) 119 | error_message = "`filters` of each item of `archive_rules` must have one or more filters." 120 | } 121 | } 122 | 123 | variable "tags" { 124 | description = "(Optional) A map of tags to add to all resources." 125 | type = map(string) 126 | default = {} 127 | nullable = false 128 | } 129 | 130 | variable "module_tags_enabled" { 131 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 132 | type = bool 133 | default = true 134 | nullable = false 135 | } 136 | 137 | 138 | ################################################### 139 | # Resource Group 140 | ################################################### 141 | 142 | variable "resource_group" { 143 | description = < 9 | ## Requirements 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [terraform](#requirement\_terraform) | >= 1.12 | 14 | | [aws](#requirement\_aws) | >= 6.12 | 15 | 16 | ## Providers 17 | 18 | | Name | Version | 19 | |------|---------| 20 | | [aws](#provider\_aws) | 6.13.0 | 21 | 22 | ## Modules 23 | 24 | | Name | Source | Version | 25 | |------|--------|---------| 26 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 27 | 28 | ## Resources 29 | 30 | | Name | Type | 31 | |------|------| 32 | | [aws_accessanalyzer_analyzer.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_analyzer) | resource | 33 | | [aws_accessanalyzer_archive_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_archive_rule) | resource | 34 | 35 | ## Inputs 36 | 37 | | Name | Description | Type | Default | Required | 38 | |------|-------------|------|---------|:--------:| 39 | | [name](#input\_name) | (Required) The name of the Analyzer. | `string` | n/a | yes | 40 | | [archive\_rules](#input\_archive\_rules) | (Optional) A list of archive rules for the AccessAnalyzer Analyzer. Each item of `archive_rules` block as defined below.
(Required) `name` - The name of archive rule.
(Required) `filters` - A list of filter criterias for the archive rule. Each item of `filters` block as defined below.
(Required) `criteria` - The filter criteria.
(Optional) `contains` - Contains comparator.
(Optional) `exists` - Exists comparator (Boolean).
(Optional) `eq` - Equal comparator.
(Optional) `neq` - Not Equal comparator. |
list(object({
name = string
filters = list(object({
criteria = string
contains = optional(list(string))
exists = optional(bool)
eq = optional(list(string))
neq = optional(list(string))
}))
}))
| `[]` | no | 41 | | [internal\_access\_analysis](#input\_internal\_access\_analysis) | (Optional) A configurations for the `INTERNAL_ACCESS` type Analyzer. `internal_access_analysis` as defined below.
(Optional) `rules` - A list of rules for internal access analyzer. Each item of `rules` block as defined below.
(Required) `inclusion` - An inclusion rule to filter findings. `inclusion` as defined below.
(Optional) `accounts` - A set of account IDs to include in the analysis. Account IDs can only be applied to the analysis rule criteria for organization-level analyzers.
(Optional) `resource_arns` - A set of resource ARNs to include in the analysis. The analyzer will only generate findings for resources that match these ARNs.
(Optional) `resource_types` - A set of resource types to include in the analysis. The analyzer will only generate findings for resources of these types |
object({
rules = optional(list(object({
inclusion = object({
accounts = optional(set(string), [])
resource_arns = optional(set(string), [])
resource_types = optional(set(string), [])
})
})), [])
})
| `{}` | no | 42 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 43 | | [region](#input\_region) | (Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region. | `string` | `null` | no | 44 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 45 | | [scope](#input\_scope) | (Optional) A scope of Analyzer. Valid values are `ACCOUNT` or `ORGANIZATION`. Defaults to `ACCOUNT`. | `string` | `"ACCOUNT"` | no | 46 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 47 | | [type](#input\_type) | (Optional) A finding type of Analyzer. Valid values are `EXTERNAL_ACCESS`, `INTERNAL_ACCESS` or `UNUSED_ACCESS`. Defaults to `EXTERNAL_ACCESS`. | `string` | `"EXTERNAL_ACCESS"` | no | 48 | | [unused\_access\_analysis](#input\_unused\_access\_analysis) | (Optional) A configurations for the `UNUSED_ACCESS` type Analyzer. `unused_access_analysis` as defined below.
(Optional) `tracking_period` - A number of days for the tracking the period. Findings will be generated for access that hasn't been used in more than the specified number of days. Defaults to `90`.
(Optional) `rules` - A list of rules for unused access analyzer. Each item of `rules` block as defined below.
(Required) `exclusion` - An exclusion rule to filter findings. `exclusion` as defined below.
(Optional) `accounts` - A set of account IDs to exclude from the analysis. Account IDs can only be applied to the analysis rule criteria for organization-level analyzers.
(Optional) `resource_tags` - A list of tag key and value pairs to exclude from the analysis. |
object({
tracking_period = optional(number, 90)
rules = optional(list(object({
exclusion = object({
accounts = optional(set(string), [])
resource_tags = optional(list(map(string)), [])
})
})), [])
})
| `{}` | no | 49 | 50 | ## Outputs 51 | 52 | | Name | Description | 53 | |------|-------------| 54 | | [archive\_rules](#output\_archive\_rules) | A list of archive rules for the Analyzer. | 55 | | [arn](#output\_arn) | The Amazon Resource Name (ARN) of this Analyzer. | 56 | | [id](#output\_id) | The ID of this Analyzer. | 57 | | [internal\_access\_analysis](#output\_internal\_access\_analysis) | The configurations for the `INTERNAL_ACCESS` type Analyzer. | 58 | | [name](#output\_name) | The name of the Analyzer. | 59 | | [region](#output\_region) | The AWS region this module resources resides in. | 60 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 61 | | [scope](#output\_scope) | The scope of Analyzer. | 62 | | [type](#output\_type) | The finding type of Analyzer. | 63 | | [unused\_access\_analysis](#output\_unused\_access\_analysis) | The configurations for the `UNUSED_ACCESS` type Analyzer. | 64 | 65 | -------------------------------------------------------------------------------- /modules/config-managed-rule/README.md: -------------------------------------------------------------------------------- 1 | # config-managed-rule 2 | 3 | This module creates following resources. 4 | 5 | - `aws_config_config_rule` (optional) 6 | - `aws_config_organization_managed_rule` (optional) 7 | 8 | ## Notes 9 | 10 | - https://console.aws.amazon.com/config/service/managedRuleTemplate?region=ap-northeast-2 11 | 12 | 13 | 14 | ## Requirements 15 | 16 | | Name | Version | 17 | |------|---------| 18 | | [terraform](#requirement\_terraform) | >= 1.12 | 19 | | [aws](#requirement\_aws) | >= 6.12 | 20 | 21 | ## Providers 22 | 23 | | Name | Version | 24 | |------|---------| 25 | | [aws](#provider\_aws) | 6.13.0 | 26 | 27 | ## Modules 28 | 29 | | Name | Source | Version | 30 | |------|--------|---------| 31 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 32 | 33 | ## Resources 34 | 35 | | Name | Type | 36 | |------|------| 37 | | [aws_config_config_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 38 | | [aws_config_organization_managed_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_organization_managed_rule) | resource | 39 | 40 | ## Inputs 41 | 42 | | Name | Description | Type | Default | Required | 43 | |------|-------------|------|---------|:--------:| 44 | | [source\_rule](#input\_source\_rule) | (Required) The identifier for AWS Config managed rule. Use the format like `root-account-mfa-enabled` instead of predefiend format like `ROOT_ACCOUNT_MFA_ENABLED`. | `string` | n/a | yes | 45 | | [description](#input\_description) | (Optional) The description of the rule. Use default description if not provided. | `string` | `null` | no | 46 | | [evaluation\_modes](#input\_evaluation\_modes) | (Optional) A set of evaluation modes to enable for the Config rule. Valid values are `DETECTIVE`, `PROACTIVE`. Default value contains only `DETECTIVE`. | `set(string)` |
[
"DETECTIVE"
]
| no | 47 | | [excluded\_accounts](#input\_excluded\_accounts) | (Optional) A list of AWS account identifiers to exclude from the rule. Only need when `level` is configured with value `ORGANIZATION`. | `list(string)` | `[]` | no | 48 | | [level](#input\_level) | (Optional) Choose to create a rule across all accounts in your Organization. Valid values are `ACCOUNT` and `ORGANIZATION`. Use `ORGANIZATION` level in Organization master account or delegated administrator accounts. | `string` | `"ACCOUNT"` | no | 49 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 50 | | [name](#input\_name) | (Optional) The name of the rule. Use default rule name if not provided. | `string` | `null` | no | 51 | | [parameters](#input\_parameters) | (Optional) A map of parameters that is passed to the AWS Config rule Lambda function. | `any` | `{}` | no | 52 | | [region](#input\_region) | (Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region. | `string` | `null` | no | 53 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 54 | | [resource\_id](#input\_resource\_id) | (Optional) The ID of the only AWS resource that you want to trigger an evaluation for the rule. If you specify this, you must specify only one resource type for `resource_types`. Only need when `scope` is configured with value `RESOURCES`. | `string` | `null` | no | 55 | | [resource\_tag](#input\_resource\_tag) | (Optional) The tag that are applied to only those AWS resources that you want you want to trigger an evaluation for the rule. You can configure with only `key` or a set of `key` and `value`. Only need when `scope` is configured with value `TAGS`. |
object({
key = string
value = optional(string)
})
| `null` | no | 56 | | [resource\_types](#input\_resource\_types) | (Optional) A list of resource types of only those AWS resources that you want to trigger an evaluation for the rule. For example, `AWS::EC2::Instance` or `AWS::CloudTrail::Trail`. Only need when `scope` is configured with value `RESOURCES`. | `list(string)` | `[]` | no | 57 | | [schedule\_frequency](#input\_schedule\_frequency) | (Optional) The frequency with which AWS Config runs evaluations for a rule. Use default value if not provided. Valid values are `1h`, `3h`, `6h`, `12h`, or `24h`. | `string` | `null` | no | 58 | | [scope](#input\_scope) | (Optional) Choose when evaluations will occur. Valid values are `ALL_CHANGES`, `RESOURCES`, or `TAGS`. | `string` | `"RESOURCES"` | no | 59 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 60 | | [timeouts](#input\_timeouts) | (Optional) How long to wait for the rules to be created/updated/deleted. |
object({
create = optional(string, "30m")
update = optional(string, "30m")
delete = optional(string, "30m")
})
| `{}` | no | 61 | 62 | ## Outputs 63 | 64 | | Name | Description | 65 | |------|-------------| 66 | | [arn](#output\_arn) | The Amazon Resource Name (ARN) of the rule. | 67 | | [description](#output\_description) | The description of the rule. | 68 | | [evaluation\_modes](#output\_evaluation\_modes) | A set of evaluation modes to enable for the Config rule. | 69 | | [excluded\_accounts](#output\_excluded\_accounts) | A list of AWS account identifiers excluded from the rule. | 70 | | [id](#output\_id) | The ID of the rule. | 71 | | [level](#output\_level) | The level of the rule. `ACOUNT` or `ORGANIZATION`. The rule is for accounts in your Organization if the value is configured with `ORGANIZATION`. | 72 | | [name](#output\_name) | The name of the rule. | 73 | | [parameters](#output\_parameters) | The parameters of the rule. | 74 | | [region](#output\_region) | The AWS region this module resources resides in. | 75 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 76 | | [source\_rule](#output\_source\_rule) | The information of the managed rule used. | 77 | | [trigger\_by\_change](#output\_trigger\_by\_change) | The information of trigger by configuration changes. | 78 | | [trigger\_by\_schedule](#output\_trigger\_by\_schedule) | The information of trigger by schedule. | 79 | 80 | -------------------------------------------------------------------------------- /modules/macie-account/README.md: -------------------------------------------------------------------------------- 1 | # macie-account 2 | 3 | This module creates following resources. 4 | 5 | - `aws_macie2_account` 6 | - `aws_macie2_organization_configuration` (optional) 7 | - `aws_macie2_member` (optional) 8 | - `aws_macie2_classification_export_configuration` (optional) 9 | 10 | 11 | ## Requirements 12 | 13 | | Name | Version | 14 | |------|---------| 15 | | [terraform](#requirement\_terraform) | >= 1.12 | 16 | | [aws](#requirement\_aws) | >= 6.12 | 17 | 18 | ## Providers 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [aws](#provider\_aws) | 6.13.0 | 23 | 24 | ## Modules 25 | 26 | | Name | Source | Version | 27 | |------|--------|---------| 28 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 29 | 30 | ## Resources 31 | 32 | | Name | Type | 33 | |------|------| 34 | | [aws_macie2_account.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/macie2_account) | resource | 35 | | [aws_macie2_classification_export_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/macie2_classification_export_configuration) | resource | 36 | | [aws_macie2_member.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/macie2_member) | resource | 37 | | [aws_macie2_organization_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/macie2_organization_configuration) | resource | 38 | | [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 39 | 40 | ## Inputs 41 | 42 | | Name | Description | Type | Default | Required | 43 | |------|-------------|------|---------|:--------:| 44 | | [discovery\_result\_repository](#input\_discovery\_result\_repository) | (Optional) The configuration for discovery result location and encryption of the macie account. A `discovery_result_repository` block as defined below.
(Optional) `s3_bucket` - A configuration for the S3 bucket in which Amazon Macie exports the data discovery results. `s3_bucket` as defined below.
(Required) `name` - The name of the S3 bucket in which Amazon Macie exports the data classification results.
(Optional) `key_prefix` - The key prefix for the specified S3 bucket.
(Required) `sse_kms_key` - The ARN of the AWS KMS key to be used to encrypt the data. |
object({
s3_bucket = optional(object({
name = string
key_prefix = optional(string, "")
sse_kms_key = string
}))
})
| `{}` | no | 45 | | [enabled](#input\_enabled) | (Optional) Whether to enable Amazon Macie and start all Macie activities for the account. Defaults to `true`. Set `false` to suspend Macie, it stops monitoring your AWS environment and does not generate new findings. The existing findings remain intact and are not affected. Delete `aws_macie2_account` resource to disable Macie, it permanently deletes all of your existing findings, classification jobs, and other Macie resources. | `bool` | `true` | no | 46 | | [member\_accounts](#input\_member\_accounts) | (Optional) A list of configurations for member accounts on the macie account. Each block of `member_accounts` as defined below.
(Required) `account_id` - The AWS account ID for the account.
(Required) `email` - The email address for the account.
(Optional) `type` - The type of the member account. Valid values are `ORGANIZATION` or `INVITATION`. Defaults to `ORGANIZATION`.
(Optional) `enabled` - Whether to enable Amazon Macie and start all Macie activities for the member account. Defaults to `true`.
(Optional) `tags` - A map of key-value pairs that specifies the tags to associate with the account in Amazon Macie.
(Optional) `invitation` - A configuration for invitation to the member account. `invitation` as defined below.
(Optional) `message` - A custom message to include in the invitation to the account.
(Optional) `email_notification_enabled` - Whether to send an email notification to the account when you invite it to become a member of your Macie account. This notification is in addition to an alert that the root user receives in AWS Personal Health Dashboard. Defaults to `false`. |
list(object({
account_id = string
email = string
type = optional(string, "ORGANIZATION")
enabled = optional(bool, true)
tags = optional(map(string), {})
invitation = optional(object({
message = optional(string, "")
email_notification_enabled = optional(bool, false)
}), {})
}))
| `[]` | no | 47 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 48 | | [organization\_config](#input\_organization\_config) | (Optional) The organization configurations for the macie account. `organization_config` as defined below.
(Optional) `auto_enable` - Whether to automatically enable Macie for new accounts in the organization. Defaults to `true`. |
object({
auto_enable = optional(bool, true)
})
| `{}` | no | 49 | | [region](#input\_region) | (Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region. | `string` | `null` | no | 50 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 51 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 52 | | [update\_frequency](#input\_update\_frequency) | (Optional) How often to publish updates to policy findings for the account. This includes publishing updates to AWS Security Hub and Amazon EventBridge (formerly called Amazon CloudWatch Events). Valid values are `15m`, `1h` or `6h`. Defaults to `15m`. | `string` | `"15m"` | no | 53 | 54 | ## Outputs 55 | 56 | | Name | Description | 57 | |------|-------------| 58 | | [created\_at](#output\_created\_at) | The date and time, in UTC and extended RFC 3339 format, when the Amazon Macie account was created. | 59 | | [discovery\_result\_repository](#output\_discovery\_result\_repository) | The configuration for discovery result location and encryption of the macie account. | 60 | | [enabled](#output\_enabled) | Whether the macie account is eanbled. | 61 | | [id](#output\_id) | The ID of the macie account. | 62 | | [member\_accounts](#output\_member\_accounts) | The list of configruations for member accounts on the macie account. | 63 | | [name](#output\_name) | The account ID of the macie account. | 64 | | [organization\_config](#output\_organization\_config) | The organization configuration for the macie account. | 65 | | [region](#output\_region) | The AWS region this module resources resides in. | 66 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 67 | | [service\_role](#output\_service\_role) | The Amazon Resource Name (ARN) of the service-linked role that allows Macie to monitor and analyze data in AWS resources for the account. | 68 | | [update\_frequency](#output\_update\_frequency) | How often to publish updates to policy findings for the macie account. | 69 | | [updated\_at](#output\_updated\_at) | The date and time, in UTC and extended RFC 3339 format, of the most recent change to the status of the Macie account. | 70 | 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /modules/cloudtrail-event-data-store/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "(Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region." 3 | type = string 4 | default = null 5 | nullable = true 6 | } 7 | 8 | variable "name" { 9 | description = "(Required) The name of the event data store." 10 | type = string 11 | nullable = false 12 | } 13 | 14 | variable "enabled" { 15 | description = "(Optional) Whether to enable ingesting new events into the event data store. If set to `false`, ingestion is suspended while maintaining the ability to query existing events. If set to `true`, ingestion is active. Defaults to `true`." 16 | type = bool 17 | default = true 18 | nullable = false 19 | } 20 | 21 | variable "level" { 22 | description = "(Optional) The level of the event data store to decide whether the event data store collects events logged for an organization in AWS Organizations. Can be created in the management account or delegated administrator account. Valid values are `ACCOUNT` and `ORGANIZATION`. Defaults to `ACCOUNT`." 23 | type = string 24 | default = "ACCOUNT" 25 | nullable = false 26 | 27 | validation { 28 | condition = contains(["ACCOUNT", "ORGANIZATION"], var.level) 29 | error_message = "The level should be one of `ACCOUNT`, `ORGANIZATION`." 30 | } 31 | } 32 | 33 | variable "scope" { 34 | description = "(Optional) The scope of the event data store to decide whether the event data store includes events from all regions, or only from the region in which the event data store is created. Supported values are `REGIONAL` or `ALL`. Defaults to `ALL`." 35 | type = string 36 | default = "ALL" 37 | nullable = false 38 | 39 | validation { 40 | condition = contains(["REGIONAL", "ALL"], var.scope) 41 | error_message = "The scope should be one of `REGIONAL`, `ALL`." 42 | } 43 | } 44 | 45 | variable "event_type" { 46 | description = "(Required) A type of event to be collected by the event data store. Valid values are `CLOUDTRAIL_EVENTS`, `CONFIG_CONFIGURATION_ITEMS`. Defaults to `CLOUDTRAIL_EVENTS`." 47 | type = string 48 | default = "CLOUDTRAIL_EVENTS" 49 | nullable = false 50 | 51 | validation { 52 | condition = contains(["CLOUDTRAIL_EVENTS", "CONFIG_CONFIGURATION_ITEMS"], var.event_type) 53 | error_message = "The event type should be one of `CLOUDTRAIL_EVENTS`, `CONFIG_CONFIGURATION_ITEMS`." 54 | } 55 | } 56 | 57 | variable "management_event_selector" { 58 | description = <= 7, 230 | ]) 231 | error_message = "The scope should be one of `REGIONAL`, `ALL`." 232 | } 233 | } 234 | 235 | variable "termination_protection_enabled" { 236 | description = "(Optional) Whether termination protection is enabled for the event data store. If termination protection is enabled, you cannot delete the event data store until termination protection is disabled. Defaults to `true`." 237 | type = bool 238 | default = true 239 | nullable = false 240 | } 241 | 242 | variable "import_trail_events_iam_role" { 243 | description = < 11 | ## Requirements 12 | 13 | | Name | Version | 14 | |------|---------| 15 | | [terraform](#requirement\_terraform) | >= 1.12 | 16 | | [aws](#requirement\_aws) | >= 6.12 | 17 | 18 | ## Providers 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [aws](#provider\_aws) | 6.13.0 | 23 | 24 | ## Modules 25 | 26 | | Name | Source | Version | 27 | |------|--------|---------| 28 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 29 | | [role](#module\_role) | tedilabs/account/aws//modules/iam-role | ~> 0.32.0 | 30 | 31 | ## Resources 32 | 33 | | Name | Type | 34 | |------|------| 35 | | [aws_cloudtrail_event_data_store.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudtrail_event_data_store) | resource | 36 | | [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 37 | | [aws_iam_policy_document.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 38 | 39 | ## Inputs 40 | 41 | | Name | Description | Type | Default | Required | 42 | |------|-------------|------|---------|:--------:| 43 | | [management\_event\_selector](#input\_management\_event\_selector) | (Optional) A configuration of management event selector to use to select the events for the event data store. Only used if `event_type` is `CLOUDTRAIL_EVENTS`. `management_event_selector` block as defined below.
(Optional) `enabled` - Whether to capture management events. Defaults to `false`.
(Optional) `scope` - The type of events to log. Valid values are `ALL`, `READ` and `WRITE`. Defaults to `ALL`.
(Optional) `exclude_event_sources` - A set of event sources to exclude. Valid values are `kms.amazonaws.com` and `rdsdata.amazonaws.com`. `management_event_selector.enabled` must be set to true to allow this. |
object({
enabled = optional(bool, false)
scope = optional(string, "ALL")
exclude_event_sources = optional(set(string), [])
})
| n/a | yes | 44 | | [name](#input\_name) | (Required) The name of the event data store. | `string` | n/a | yes | 45 | | [billing\_mode](#input\_billing\_mode) | (Optional) The billing mode for the event data store. Valid values are `EXTENDABLE_RETENTION_PRICING` and `FIXED_RETENTION_PRICING`. Defaults to `EXTENDABLE_RETENTION_PRICING`. | `string` | `"EXTENDABLE_RETENTION_PRICING"` | no | 46 | | [data\_event\_selectors](#input\_data\_event\_selectors) | (Optional) A configuration of event selectors to use to select the data events for the event data store. Each item of `data_event_selectors` block as defined below.
(Optional) `name` - A name of the advanced event selector.
(Optional) `resource_type` - A resource type to log data events to log. Valid values are one of the following:
- `AWS::DynamoDB::Table`
- `AWS::Lambda::Function`
- `AWS::S3::Object`
- `AWS::AppConfig::Configuration`
- `AWS::B2BI::Transformer`
- `AWS::Bedrock::AgentAlias`
- `AWS::Bedrock::KnowledgeBase`
- `AWS::Cassandra::Table`
- `AWS::CloudFront::KeyValueStore`
- `AWS::CloudTrail::Channel`
- `AWS::CodeWhisperer::Customization`
- `AWS::CodeWhisperer::Profile`
- `AWS::Cognito::IdentityPool`
- `AWS::DynamoDB::Stream`
- `AWS::EC2::Snapshot`
- `AWS::EMRWAL::Workspace`
- `AWS::FinSpace::Environment`
- `AWS::Glue::Table`
- `AWS::GreengrassV2::ComponentVersion`
- `AWS::GreengrassV2::Deployment`
- `AWS::GuardDuty::Detector`
- `AWS::IoT::Certificate`
- `AWS::IoT::Thing`
- `AWS::IoTSiteWise::Asset`
- `AWS::IoTSiteWise::TimeSeries`
- `AWS::IoTTwinMaker::Entity`
- `AWS::IoTTwinMaker::Workspace`
- `AWS::KendraRanking::ExecutionPlan`
- `AWS::KinesisVideo::Stream`
- `AWS::ManagedBlockchain::Network`
- `AWS::ManagedBlockchain::Node`
- `AWS::MedicalImaging::Datastore`
- `AWS::NeptuneGraph::Graph`
- `AWS::PCAConnectorAD::Connector`
- `AWS::QBusiness::Application`
- `AWS::QBusiness::DataSource`
- `AWS::QBusiness::Index`
- `AWS::QBusiness::WebExperience`
- `AWS::RDS::DBCluster`
- `AWS::S3::AccessPoint`
- `AWS::S3ObjectLambda::AccessPoint`
- `AWS::S3Outposts::Object`
- `AWS::SageMaker::Endpoint`
- `AWS::SageMaker::ExperimentTrialComponent`
- `AWS::SageMaker::FeatureGroup`
- `AWS::ServiceDiscovery::Namespace`
- `AWS::ServiceDiscovery::Service`
- `AWS::SCN::Instance`
- `AWS::SNS::PlatformEndpoint`
- `AWS::SNS::Topic`
- `AWS::SWF::Domain`
- `AWS::SQS::Queue`
- `AWS::SSMMessages::ControlChannel`
- `AWS::ThinClient::Device`
- `AWS::ThinClient::Environment`
- `AWS::Timestream::Database`
- `AWS::Timestream::Table`
- `AWS::VerifiedPermissions::PolicyStore`
(Optional) `scope` - The type of events to log. Valid values are `ALL`, `READ` and `WRITE`. Defaults to `WRITE`.
(Optional) `conditions` - A configuration of field conditions to filter events by the ARN of resource and the event name. Each item of `conditions` as defined below.
(Required) `field` - A field to compare by the field condition. Valid values are `eventName`, `eventSource`, `eventType`, `resources.ARN`, `sessionCredentialFromConsole` and `userIdentity.arn`.
(Required) `operator` - An operator of the field condition. Valid values are `equals`, `not_equals`, `starts_with`, `not_starts_with`, `ends_with`, `not_ends_with`.
(Required) `values` - A set of values of the field condition to compare. |
list(object({
name = optional(string)
resource_type = string
scope = optional(string, "WRITE")
conditions = optional(list(object({
field = string
operator = string
values = set(string)
})), [])
}))
| `[]` | no | 47 | | [enabled](#input\_enabled) | (Optional) Whether to enable ingesting new events into the event data store. If set to `false`, ingestion is suspended while maintaining the ability to query existing events. If set to `true`, ingestion is active. Defaults to `true`. | `bool` | `true` | no | 48 | | [encryption](#input\_encryption) | (Optional) A configuration to encrypt the events delivered by CloudTrail. By default, the event data store is encrypted with a KMS key that AWS owns and manages.`encryption` as defined below.
(Optional) `kms_key` - The ID of AWS KMS key to use to encrypt the events delivered by CloudtTrail. The value can be an alias name prefixed by 'alias/', a fully specified ARN to an alias, a fully specified ARN to a key, or a globally unique identifier. |
object({
kms_key = optional(string)
})
| `{}` | no | 49 | | [event\_type](#input\_event\_type) | (Required) A type of event to be collected by the event data store. Valid values are `CLOUDTRAIL_EVENTS`, `CONFIG_CONFIGURATION_ITEMS`. Defaults to `CLOUDTRAIL_EVENTS`. | `string` | `"CLOUDTRAIL_EVENTS"` | no | 50 | | [import\_trail\_events\_iam\_role](#input\_import\_trail\_events\_iam\_role) | (Optional) A configuration of IAM Role for importing CloudTrail events from S3 Bucket. `import_trail_events_iam_role` as defined below.
(Optional) `enabled` - Indicates whether you want to create IAM Role to import trail events. Defaults to `true`.
(Optional) `source_s3_buckets` - A list of source S3 buckets to import events from. Each item of `source_s3_buckets` as defined below.
(Required) `name` - A name of source S3 bucket.
(Optional) `key_prefix` - A key prefix of source S3 bucket. |
object({
enabled = optional(bool, true)
source_s3_buckets = optional(list(object({
name = string
key_prefix = optional(string, "/")
})), [])
})
| `{}` | no | 51 | | [level](#input\_level) | (Optional) The level of the event data store to decide whether the event data store collects events logged for an organization in AWS Organizations. Can be created in the management account or delegated administrator account. Valid values are `ACCOUNT` and `ORGANIZATION`. Defaults to `ACCOUNT`. | `string` | `"ACCOUNT"` | no | 52 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 53 | | [region](#input\_region) | (Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region. | `string` | `null` | no | 54 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 55 | | [retention\_in\_days](#input\_retention\_in\_days) | (Optional) The retention period of the event data store, in days. You can set a retention period of up to 2557 days. Defaults to `2555` days (7 years). | `number` | `2555` | no | 56 | | [scope](#input\_scope) | (Optional) The scope of the event data store to decide whether the event data store includes events from all regions, or only from the region in which the event data store is created. Supported values are `REGIONAL` or `ALL`. Defaults to `ALL`. | `string` | `"ALL"` | no | 57 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 58 | | [termination\_protection\_enabled](#input\_termination\_protection\_enabled) | (Optional) Whether termination protection is enabled for the event data store. If termination protection is enabled, you cannot delete the event data store until termination protection is disabled. Defaults to `true`. | `bool` | `true` | no | 59 | 60 | ## Outputs 61 | 62 | | Name | Description | 63 | |------|-------------| 64 | | [arn](#output\_arn) | The Amazon Resource Name (ARN) of the event data store. | 65 | | [billing\_mode](#output\_billing\_mode) | The billing mode for the event data store. | 66 | | [data\_event\_selectors](#output\_data\_event\_selectors) | The event selectors to use to select the data events for the event data store. | 67 | | [enabled](#output\_enabled) | Whether the event data store is enabled. | 68 | | [encryption](#output\_encryption) | The configuration for the encryption of the event data store. | 69 | | [event\_type](#output\_event\_type) | The type of event to be collected by the event data store. | 70 | | [id](#output\_id) | The ID of the event data store. | 71 | | [import\_trail\_events\_iam\_role](#output\_import\_trail\_events\_iam\_role) | A configuration of IAM Role for importing CloudTrail events from S3 Bucket. | 72 | | [level](#output\_level) | The level of the event data store to decide whether the event data store collects events logged for an organization in AWS Organizations. | 73 | | [management\_event\_selector](#output\_management\_event\_selector) | The event selector to use to select the management events for the event data store. | 74 | | [name](#output\_name) | The name of the event data store. | 75 | | [region](#output\_region) | The AWS region this module resources resides in. | 76 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 77 | | [retention\_in\_days](#output\_retention\_in\_days) | The retention period of the event data store, in days. | 78 | | [scope](#output\_scope) | The scope of the event data store to decide whether the event data store includes events from all regions, or only from the region in which the event data store is created. | 79 | | [termination\_protection\_enabled](#output\_termination\_protection\_enabled) | Whether termination protection is enabled for the event data store. | 80 | 81 | -------------------------------------------------------------------------------- /modules/cloudtrail-trail/README.md: -------------------------------------------------------------------------------- 1 | # cloudtrail-trail 2 | 3 | This module creates following resources. 4 | 5 | - `aws_cloudtrail` 6 | - `aws_iam_role` (optional) 7 | - `aws_iam_role_policy` (optional) 8 | - `aws_iam_role_policy_attachment` (optional) 9 | 10 | 11 | ## Requirements 12 | 13 | | Name | Version | 14 | |------|---------| 15 | | [terraform](#requirement\_terraform) | >= 1.12 | 16 | | [aws](#requirement\_aws) | >= 6.12 | 17 | 18 | ## Providers 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [aws](#provider\_aws) | 6.13.0 | 23 | 24 | ## Modules 25 | 26 | | Name | Source | Version | 27 | |------|--------|---------| 28 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 29 | | [role](#module\_role) | tedilabs/account/aws//modules/iam-role | ~> 0.32.0 | 30 | 31 | ## Resources 32 | 33 | | Name | Type | 34 | |------|------| 35 | | [aws_cloudtrail.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudtrail) | resource | 36 | | [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 37 | | [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudwatch_log_group) | data source | 38 | | [aws_iam_policy_document.cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 39 | | [aws_organizations_organization.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | 40 | | [aws_partition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 41 | | [aws_region.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | 42 | 43 | ## Inputs 44 | 45 | | Name | Description | Type | Default | Required | 46 | |------|-------------|------|---------|:--------:| 47 | | [delivery\_channels](#input\_delivery\_channels) | (Required) A configuration for the delivery channels of the trail. `delivery_channels` as defined below.
(Required) `s3_bucket` - A configuration for the S3 Bucket delivery channel. `s3_bucket` as defined below.
(Required) `name` - The name of the S3 bucket used to publish log files.
(Optional) `key_prefix` - The key prefix for the specified S3 bucket.
(Optional) `integrity_validation_enabled` - To determine whether a log file was modified, deleted, or unchanged after AWS CloudTrail delivered it, use CloudTrail log file integrity validation. This feature is built using industry standard algorithms: SHA-256 for hashing and SHA-256 with RSA for digital signing. Defaults to `true`.
(Optional) `sse_kms_key` - The ARN of the AWS KMS key used to encrypt objects delivered by AWS Config. Must belong to the same Region as the destination S3 bucket.
(Optional) `sns_topic` - A configuration for the SNS Topic notifications for log file delivery. CloudTrail stores multiple events in a log file. When you enable this option, Amazon SNS notifications are sent for every log file delivery to your S3 bucket, not for every event. `sns_topic` as defined below.
(Optional) `enabled` - Whether to enable the SNS Topic notifications for log file delivery. Defaults to `false`.
(Optional) `name` - The name of the SNS topic for notification of log file delivery.
(Optional) `cloudwatch_log_group` - A configuration for the log group of CloudWatch Logs to send events to CloudWatch Logs. `cloudwatch_log_group` as defined below.
(Optional) `enabled` - Whether to send CloudTrail events to CloudWatch Logs. Defaults to `false`.
(Optional) `name` - The name of the log group of CloudWatch Logs. |
object({
s3_bucket = object({
name = string
key_prefix = optional(string, "")
integrity_validation_enabled = optional(bool, true)
sse_kms_key = optional(string)
})
sns_topic = optional(object({
enabled = optional(bool, false)
name = optional(string)
}), {})
cloudwatch_log_group = optional(object({
enabled = optional(bool, false)
name = optional(string)
# iam_role = optional(string)
}), {})
})
| n/a | yes | 48 | | [management\_event\_selector](#input\_management\_event\_selector) | (Required) A configuration block for management events logging to identify API activity for individual resources, or for all current and future resources in AWS account. `management_event_selector` block as defined below.
(Required) `enabled` - Whether the trail to log management events.
(Optional) `scope` - The type of events to log. Valid values are `ALL`, `READ` and `WRITE`. Defaults to `ALL`.
(Optional) `exclude_event_sources` - A set of event sources to exclude. Valid values are `kms.amazonaws.com` and `rdsdata.amazonaws.com`. `management_event_selector.enabled` must be set to true to allow this. |
object({
enabled = bool
scope = optional(string, "ALL")
exclude_event_sources = optional(set(string), [])
})
| n/a | yes | 49 | | [name](#input\_name) | (Required) The name of the trail. The name can only contain uppercase letters, lowercase letters, numbers, periods (.), hyphens (-), and underscores (\_). | `string` | n/a | yes | 50 | | [data\_event\_selectors](#input\_data\_event\_selectors) | (Optional) A list of configurations for data events logging the resource operations performed on or within a resource. Each item of `data_event_selectors` block as defined below.
(Optional) `name` - A name of the advanced event selector.
(Optional) `resource_type` - A resource type to log data events to log. Valid values are one of the following:
- `AWS::DynamoDB::Table`
- `AWS::Lambda::Function`
- `AWS::S3::Object`
- `AWS::AppConfig::Configuration`
- `AWS::B2BI::Transformer`
- `AWS::Bedrock::AgentAlias`
- `AWS::Bedrock::KnowledgeBase`
- `AWS::Cassandra::Table`
- `AWS::CloudFront::KeyValueStore`
- `AWS::CloudTrail::Channel`
- `AWS::CodeWhisperer::Customization`
- `AWS::CodeWhisperer::Profile`
- `AWS::Cognito::IdentityPool`
- `AWS::DynamoDB::Stream`
- `AWS::EC2::Snapshot`
- `AWS::EMRWAL::Workspace`
- `AWS::FinSpace::Environment`
- `AWS::Glue::Table`
- `AWS::GreengrassV2::ComponentVersion`
- `AWS::GreengrassV2::Deployment`
- `AWS::GuardDuty::Detector`
- `AWS::IoT::Certificate`
- `AWS::IoT::Thing`
- `AWS::IoTSiteWise::Asset`
- `AWS::IoTSiteWise::TimeSeries`
- `AWS::IoTTwinMaker::Entity`
- `AWS::IoTTwinMaker::Workspace`
- `AWS::KendraRanking::ExecutionPlan`
- `AWS::KinesisVideo::Stream`
- `AWS::ManagedBlockchain::Network`
- `AWS::ManagedBlockchain::Node`
- `AWS::MedicalImaging::Datastore`
- `AWS::NeptuneGraph::Graph`
- `AWS::PCAConnectorAD::Connector`
- `AWS::QBusiness::Application`
- `AWS::QBusiness::DataSource`
- `AWS::QBusiness::Index`
- `AWS::QBusiness::WebExperience`
- `AWS::RDS::DBCluster`
- `AWS::S3::AccessPoint`
- `AWS::S3ObjectLambda::AccessPoint`
- `AWS::S3Outposts::Object`
- `AWS::SageMaker::Endpoint`
- `AWS::SageMaker::ExperimentTrialComponent`
- `AWS::SageMaker::FeatureGroup`
- `AWS::ServiceDiscovery::Namespace`
- `AWS::ServiceDiscovery::Service`
- `AWS::SCN::Instance`
- `AWS::SNS::PlatformEndpoint`
- `AWS::SNS::Topic`
- `AWS::SWF::Domain`
- `AWS::SQS::Queue`
- `AWS::SSMMessages::ControlChannel`
- `AWS::ThinClient::Device`
- `AWS::ThinClient::Environment`
- `AWS::Timestream::Database`
- `AWS::Timestream::Table`
- `AWS::VerifiedPermissions::PolicyStore`
(Optional) `scope` - The type of events to log. Valid values are `ALL`, `READ` and `WRITE`. Defaults to `WRITE`.
(Optional) `conditions` - A configuration of field conditions to filter events by the ARN of resource and the event name. Each item of `conditions` as defined below.
(Required) `field` - A field to compare by the field condition. Valid values are `eventName`, `eventSource`, `eventType`, `resources.ARN`, `sessionCredentialFromConsole` and `userIdentity.arn`.
(Required) `operator` - An operator of the field condition. Valid values are `equals`, `not_equals`, `starts_with`, `not_starts_with`, `ends_with`, `not_ends_with`.
(Required) `values` - A set of values of the field condition to compare. |
list(object({
name = optional(string)
resource_type = string
scope = optional(string, "WRITE")
conditions = optional(list(object({
field = string
operator = string
values = set(string)
})), [])
}))
| `[]` | no | 51 | | [enabled](#input\_enabled) | (Optional) Whether the trail starts the recording of AWS API calls and log file delivery. Defaults to `true`. | `bool` | `true` | no | 52 | | [insight\_event\_selector](#input\_insight\_event\_selector) | (Optional) A configuration block for insight events logging to identify unusual operational activity. `insight_event_selector` block as defined below.
(Optional) `enabled` - Whether the trail to log insight events. Defaults to `false`.
(Optional) `scopes` - A set of insight types to log on the trail. Valid values are `API_CALL_RATE` and `API_ERROR_RATE`. |
object({
enabled = optional(bool, false)
scopes = optional(set(string), [])
})
| `{}` | no | 53 | | [level](#input\_level) | (Optional) The level of the trail to decide whether the trail is an AWS Organizations trail. Organization trails log events for the master account and all member accounts. Can only be created in the organization master account. Valid values are `ACCOUNT` and `ORGANIZATION`. Use `ORGANIZATION` level in Organization master account. Defaults to `ACCOUNT`. | `string` | `"ACCOUNT"` | no | 54 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 55 | | [region](#input\_region) | (Optional) The region in which to create the module resources. If not provided, the module resources will be created in the provider's configured region. | `string` | `null` | no | 56 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 57 | | [scope](#input\_scope) | (Optional) The scope of the trail to decide whether the trail is multi-region trail. Supported values are `REGIONAL_WITH_GLOBAL`, `REGIONAL` or `ALL`. Defaults to `REGIONAL_WITH_GLOBAL`. | `string` | `"REGIONAL_WITH_GLOBAL"` | no | 58 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 59 | 60 | ## Outputs 61 | 62 | | Name | Description | 63 | |------|-------------| 64 | | [arn](#output\_arn) | The Amazon Resource Name (ARN) of the trail. | 65 | | [data\_event](#output\_data\_event) | A list of selectors for data events of the trail. | 66 | | [delivery\_channels](#output\_delivery\_channels) | The configurations for the delivery channels of the trail. | 67 | | [enabled](#output\_enabled) | Whether the trail is enabled. | 68 | | [home\_region](#output\_home\_region) | The region in which the trail was created. | 69 | | [iam\_role](#output\_iam\_role) | The IAM Role for the CloudTrail trail. | 70 | | [id](#output\_id) | The ID of the trail. | 71 | | [insight\_event](#output\_insight\_event) | A selector for insight events of the trail. | 72 | | [level](#output\_level) | The level of the trail to decide whether the trail is an AWS Organizations trail. | 73 | | [management\_event](#output\_management\_event) | A selector for management events of the trail. | 74 | | [name](#output\_name) | The name of the trail. | 75 | | [region](#output\_region) | The AWS region this module resources resides in. | 76 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 77 | | [scope](#output\_scope) | The scope of the trail to decide whether the trail is multi-region trail. | 78 | 79 | --------------------------------------------------------------------------------