From 3f58720e39cfeca8fda79b2b43864f90afc27b34 Mon Sep 17 00:00:00 2001 From: Donald E Badrak II Date: Mon, 22 Feb 2021 17:55:26 -0500 Subject: [PATCH] Initial (#1) * setup initial structure * update * add stuff for terraform-state * add base_tags * update docs * update docs * update usage * update doc * update doc * update doc * remove some variables * remove some variables * update required things * ignore common/READMD.md * fix typos * fix source * fix tag * fix keys * change local.tags to var.tags * add comment --- .gitignore | 13 +++ .pre-commit-config.yaml | 17 +++ CHANGELOG.md | 6 + README.md | 27 ++++- access-logging-bucket/version.tf | 1 + cloudtrail/version.tf | 1 + common/data.tf | 23 ++++ common/defaults.tf | 5 + common/prefixes.tf | 12 ++ common/variables.common.tf | 86 ++++++++++++++ common/version.tf | 3 + config/version.tf | 1 + gpg-key/version.tf | 1 + object-logging/version.tf | 1 + terraform-state/README.md | 101 ++++++++++++++++ terraform-state/data.tf | 1 + terraform-state/defaults.tf | 1 + terraform-state/main.tf | 175 ++++++++++++++++++++++++++++ terraform-state/outputs.tf | 25 ++++ terraform-state/prefixes.tf | 1 + terraform-state/variables.common.tf | 1 + terraform-state/variables.tf | 50 ++++++++ terraform-state/version.tf | 1 + 23 files changed, 552 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 CHANGELOG.md create mode 120000 access-logging-bucket/version.tf create mode 120000 cloudtrail/version.tf create mode 100644 common/data.tf create mode 100644 common/defaults.tf create mode 100644 common/prefixes.tf create mode 100644 common/variables.common.tf create mode 100644 common/version.tf create mode 120000 config/version.tf create mode 120000 gpg-key/version.tf create mode 120000 object-logging/version.tf create mode 100644 terraform-state/README.md create mode 120000 terraform-state/data.tf create mode 120000 terraform-state/defaults.tf create mode 100644 terraform-state/main.tf create mode 100644 terraform-state/outputs.tf create mode 120000 terraform-state/prefixes.tf create mode 120000 terraform-state/variables.common.tf create mode 100644 terraform-state/variables.tf create mode 120000 terraform-state/version.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4873d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# .tfvars files +*.tfvars + +.terraform/* +logs +common/README.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..85f5ae4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +repos: +- repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.31.0 + hooks: +# - id: terraform_validate + - id: terraform_fmt + - id: terraform_docs_replace + args: ['table'] + exclude: common/*.tf + exclude: version.tf + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.1.0 + hooks: + - id: check-symlinks + - id: detect-aws-credentials + - id: detect-private-key diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..72de437 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Versions + +* v1.0 -- 20210218 + - initial creation + - module: terraform-state + diff --git a/README.md b/README.md index 97f16ee..dc81bdb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ # aws-inf-setup -AWS Infrastructure Setup + +This contains a bunch of submodules used for setting up an AWS account, to the standard configurations we use +at Census. + +## Submodules + +### [terraform-state](terraform-state) + +This creates an S3 bucket, KMS key, and DynamoDB table for use with an AWS account. The bucket region is important +for connection to the remote state. Key ARN and ID, S3 bucket ID, and DDB tables are exportet, but they follow a standard +structure so they are not really needed. The bucket is `inf-tfstate-{account_id}`. + +This has no other dependencies, since it has to be created first. Only one is needed per account. + +### splunk-user + +### access-logging-bucket + +This sets up the S3 bucket used for access logs. One is needed per region, and the region and account are included +in the bucket names: `inf-log-{account_id}-{region}`. + +### object-logging +### cloudtrail +### config +### gpg-key + diff --git a/access-logging-bucket/version.tf b/access-logging-bucket/version.tf new file mode 120000 index 0000000..b83c5b7 --- /dev/null +++ b/access-logging-bucket/version.tf @@ -0,0 +1 @@ +../common/version.tf \ No newline at end of file diff --git a/cloudtrail/version.tf b/cloudtrail/version.tf new file mode 120000 index 0000000..b83c5b7 --- /dev/null +++ b/cloudtrail/version.tf @@ -0,0 +1 @@ +../common/version.tf \ No newline at end of file diff --git a/common/data.tf b/common/data.tf new file mode 100644 index 0000000..30905b7 --- /dev/null +++ b/common/data.tf @@ -0,0 +1,23 @@ +data "aws_caller_identity" "current" {} + +data "aws_arn" "current" { + arn = data.aws_caller_identity.current.arn +} + +data "aws_region" "current" {} + +# output "caller_account_id" { +# value = data.aws_caller_identity.current.account_id +# } +# +# output "account_caller_arn" { +# value = data.aws_caller_identity.current.arn +# } +# +# output "account_caller_arn_partition" { +# value = data.aws_arn.current.partition +# } +# +# output "account_region"name" { +# value = data.aws_region.current.name +# } diff --git a/common/defaults.tf b/common/defaults.tf new file mode 100644 index 0000000..c67478b --- /dev/null +++ b/common/defaults.tf @@ -0,0 +1,5 @@ + +locals { + _defaults = { + } +} diff --git a/common/prefixes.tf b/common/prefixes.tf new file mode 100644 index 0000000..fafcbde --- /dev/null +++ b/common/prefixes.tf @@ -0,0 +1,12 @@ +locals { + _prefixes = { + "efs" = "v-efs-" + "s3" = "v-s3-" + "ebs" = "v-ebs-" + "kms" = "k-kms-" + "role" = "r-" + "policy" = "p-" + "security-group" = "" + # "security-group" = "sg-" + } +} diff --git a/common/variables.common.tf b/common/variables.common.tf new file mode 100644 index 0000000..2bc450a --- /dev/null +++ b/common/variables.common.tf @@ -0,0 +1,86 @@ +#--- +# account info +#--- +variable "account_id" { + description = "AWS Account ID (default will pull from current user)" + type = string + default = "" +} + +variable "account_alias" { + description = "AWS Account Alias (required)" + type = string +} + +variable "override_prefixes" { + description = "Override built-in prefixes by component (efs, s3, ebs, kms, role, policy, security-group). This should be used primarily for common infrastructure things" + type = map(string) + default = {} +} + +variable "tags" { + description = "AWS Tags to apply to appropriate resources (S3, KMS). Do not include safeguard tags here, use the data_safeguard field for such things." + type = map(string) + default = {} +} + +## # s3 +## variable "bucket_name" { +## description = "AWS Bucket Name. Standard prefix will be applied here, do not include here." +## type = string +## } +## +## variable "bucket_folders" { +## description = "List of folders (keys) to create after creation of bucket. They will have object metadata provided based on metadata_tags and data_safeguard labels." +## type = list(string) +## default = [] +## } +## +## variable "kms_key_id" { +## description = "AWS KMS Key ID (one per bucket). This is currently ignored." +## type = string +## default = "" +## } +## +## variable "metadata_tags" { +## description = "AWS S3 Custom metadata (prefix x-amzn-meta- automatically included, not needed here). If data_safeguard labels are applied, they will be incorporated on any bucket objects created." +## type = map(string) +## default = {} +## } +## +## variable "access_log_bucket_prefix" { +## description = "Access log bucket prefix, to which the bucket name will be appended to make the target_prefix" +## type = string +## default = "s3" +## } +## +## variable "access_log_bucket" { +## description = "Server Access Logging Bucket ID" +## type = string +## # default = null +## } +## +## variable "allowed_cidr" { +## description = "List of allowed source IPs (NOT from within the VPC). If empty, there will be no restrictions on source IP. If provided, you must also use allowed_endpoints for access within a VPC." +## type = list(string) +## default = [] +## } +## +## variable "allowed_endpoints" { +## description = "List of allowed VPC endpoint IDs. If used, it will enable access to the bucket from the specific VPC endpoints." +## type = list(string) +## default = [] +## } +## +## variable "force_destroy" { +## description = "Sets force_destroy to allow the bucket and contents to be deleted. The deletion may take a very long time based on the number of objects. You normally want to update this to true, apply, and then destroy the resource." +## type = bool +## default = false +## } +## +## # variable "lifecycle_rules" { +## # description = "Setup lifecycle rules (in-progress, not working)" +## # type = map() +## # default = {} +## # } +## diff --git a/common/version.tf b/common/version.tf new file mode 100644 index 0000000..107272c --- /dev/null +++ b/common/version.tf @@ -0,0 +1,3 @@ +locals { + _module_version = "1.0" +} diff --git a/config/version.tf b/config/version.tf new file mode 120000 index 0000000..b83c5b7 --- /dev/null +++ b/config/version.tf @@ -0,0 +1 @@ +../common/version.tf \ No newline at end of file diff --git a/gpg-key/version.tf b/gpg-key/version.tf new file mode 120000 index 0000000..b83c5b7 --- /dev/null +++ b/gpg-key/version.tf @@ -0,0 +1 @@ +../common/version.tf \ No newline at end of file diff --git a/object-logging/version.tf b/object-logging/version.tf new file mode 120000 index 0000000..b83c5b7 --- /dev/null +++ b/object-logging/version.tf @@ -0,0 +1 @@ +../common/version.tf \ No newline at end of file diff --git a/terraform-state/README.md b/terraform-state/README.md new file mode 100644 index 0000000..e850cf5 --- /dev/null +++ b/terraform-state/README.md @@ -0,0 +1,101 @@ +# aws-inf-setup :: terraform-state + +This set up the needed components for the Terraform remote state: + +* S3 bucket +* KMS key for the bucket +* DynamoDB table for locking + +# Usage +Here is a simple example, the one most commonly expected to be used. + +```hcl +module "tfstate" { + source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//terraform-state" + + account_alias = "do2-govcloud" +} +``` + +This one can be used if you need to customize stuff, though really, the defaults are all built +for a reason, and deployment code (i.e., Ansible) will expect these defaults to be used in +variable file generation. + +```hcl +module "tfstate_full" { + source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//terraform-state" + + # required + account_alias = "do2-govcloud" + + # optional, defaults + tfstate_key_prefix = "do2-govcloud" + kms_tfstate_key = "k-kms-inf-tfstate" + tfstate_table = "tf_remote_state" + tfstate_bucket = "inf-tfstate-123456789012" + tfstate_bucket_prefix = "inf-tfstate" + tfstate_key_suffix = "terraform.tfstate" + + # this is generally not needed and not recommended + component_tags = { + "s3" = { + "SpecialTag1" = "something" + "SpecialTag2" = "somethingElse" + } + } +} +``` + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Modules + +No Modules. + +## Resources + +| Name | +|------| +| [aws_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | +| [aws_caller_identity](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | +| [aws_dynamodb_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | +| [aws_iam_policy_document](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | +| [aws_kms_alias](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | +| [aws_kms_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | +| [aws_region](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | +| [aws_s3_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | +| [aws_s3_bucket_public_access_block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| account\_alias | AWS Account Alias (required) | `string` | n/a | yes | +| account\_id | AWS Account ID (default will pull from current user) | `string` | `""` | no | +| component\_tags | Additional tags for Components (s3, kms, ddb) | `map(map(string))` |
{
"ddb": {},
"kms": {},
"s3": {}
}
| no | +| kms\_tfstate\_key | Terraform remote state KMS key alias | `string` | `"k-kms-inf-tfstate"` | no | +| override\_prefixes | Override built-in prefixes by component (efs, s3, ebs, kms, role, policy, security-group). This should be used primarily for common infrastructure things | `map(string)` | `{}` | no | +| tags | AWS Tags to apply to appropriate resources (S3, KMS). Do not include safeguard tags here, use the data\_safeguard field for such things. | `map(string)` | `{}` | no | +| tfstate\_bucket | Terraform remote state S3 bucket | `string` | `""` | no | +| tfstate\_bucket\_prefix | Terraform remote state S3 bucket prefix, prepended to the AWS account ID to make the bucket name. | `string` | `"inf-tfstate"` | no | +| tfstate\_key\_prefix | Terraform remote state S3 bucket prefix (account alias) | `string` | `""` | no | +| tfstate\_key\_suffix | Terraform remote state S3 bucket suffix | `string` | `"terraform.tfstate"` | no | +| tfstate\_region | Terraform remote state S3 bucket region | `string` | `""` | no | +| tfstate\_table | Terraform remote state table | `string` | `"tf_remote_state"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| tfstate\_bucket\_arn | Terraform state S3 bucket ARN | +| tfstate\_bucket\_id | Terraform state S3 bucket ID | +| tfstate\_dynamodb\_arn | Terraform state DynamoDB table ARN | +| tfstate\_key\_arn | inf-tfstate KMS key ARN | diff --git a/terraform-state/data.tf b/terraform-state/data.tf new file mode 120000 index 0000000..995624d --- /dev/null +++ b/terraform-state/data.tf @@ -0,0 +1 @@ +../common/data.tf \ No newline at end of file diff --git a/terraform-state/defaults.tf b/terraform-state/defaults.tf new file mode 120000 index 0000000..a5556ac --- /dev/null +++ b/terraform-state/defaults.tf @@ -0,0 +1 @@ +../common/defaults.tf \ No newline at end of file diff --git a/terraform-state/main.tf b/terraform-state/main.tf new file mode 100644 index 0000000..886b96d --- /dev/null +++ b/terraform-state/main.tf @@ -0,0 +1,175 @@ +/* +* # aws-inf-setup :: terraform-state +* +* This set up the needed components for the Terraform remote state: +* +* * S3 bucket +* * KMS key for the bucket +* * DynamoDB table for locking +* +* # Usage +* Here is a simple example, the one most commonly expected to be used. +* +* ```hcl +* module "tfstate" { +* source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//terraform-state" +* +* account_alias = "do2-govcloud" +* } +* ``` +* +* This one can be used if you need to customize stuff, though really, the defaults are all built +* for a reason, and deployment code (i.e., Ansible) will expect these defaults to be used in +* variable file generation. +* +* ```hcl +* module "tfstate_full" { +* source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//terraform-state" +* +* # required +* account_alias = "do2-govcloud" +* +* # optional, defaults +* tfstate_key_prefix = "do2-govcloud" +* kms_tfstate_key = "k-kms-inf-tfstate" +* tfstate_table = "tf_remote_state" +* tfstate_bucket = "inf-tfstate-123456789012" +* tfstate_bucket_prefix = "inf-tfstate" +* tfstate_key_suffix = "terraform.tfstate" +* +* # this is generally not needed and not recommended +* component_tags = { +* "s3" = { +* "SpecialTag1" = "something" +* "SpecialTag2" = "somethingElse" +* } +* } +* } +* ``` +*/ + +locals { + account_id = var.account_id != "" ? var.account_id : data.aws_caller_identity.current.account_id + tfstate_region = data.aws_region.current.name + + tfstate_key_arn = aws_kms_key.tfstate_key.arn + tfstate_bucket = var.tfstate_bucket != "" ? var.tfstate_bucket : format("%v-%v", var.tfstate_bucket_prefix, local.account_id) + + base_tags = { + "boc:tf_module_version" = local._module_version + "boc:created_by" = "terraform" + } +} + + +#--- +# dynamodb table +#--- +resource "aws_dynamodb_table" "tfstate" { + name = var.tfstate_table + hash_key = "LockID" + billing_mode = "PROVISIONED" + read_capacity = 1 + write_capacity = 1 + + attribute { + name = "LockID" + type = "S" + } + + server_side_encryption { + enabled = true + } + + tags = merge( + var.tags, + local.base_tags, + lookup(var.component_tags, "ddb", {}), + map("Name", var.tfstate_table), + ) +} + +# create iam policy for it, to apply to roles/groups as needed + +data "aws_iam_policy_document" "tfstate" { + statement { + sid = "TFRemoteStateList" + effect = "Allow" + resources = [aws_s3_bucket.tfstate.arn] + actions = ["s3:ListBucket"] + } + + statement { + sid = "TFRemoteState" + effect = "Allow" + resources = ["${aws_s3_bucket.tfstate.arn}/*"] + actions = ["s3:GetObject", "s3:PutObject"] + } + + statement { + sid = "TFRemoteStateDDB" + effect = "Allow" + resources = [aws_dynamodb_table.tfstate.arn] + actions = ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem"] + } +} + +#--- +# s3 +#--- +resource "aws_s3_bucket" "tfstate" { + bucket = local.tfstate_bucket + acl = "private" + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = aws_kms_key.tfstate_key.arn + sse_algorithm = "aws:kms" + } + } + } + + versioning { + enabled = true + } + + lifecycle { + prevent_destroy = true + } + + tags = merge( + var.tags, + local.base_tags, + lookup(var.component_tags, "s3", {}), + map("Name", local.tfstate_bucket), + ) +} + +resource "aws_s3_bucket_public_access_block" "tfstate" { + bucket = aws_s3_bucket.tfstate.id + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +#--- +# kms +#--- +resource "aws_kms_key" "tfstate_key" { + description = "inf-tfstate encryption key" + enable_key_rotation = true + + tags = merge( + var.tags, + local.base_tags, + lookup(var.component_tags, "kms", {}), + map("Name", var.kms_tfstate_key) + ) +} + +resource "aws_kms_alias" "tfstate_key" { + name = "alias/${var.kms_tfstate_key}" + target_key_id = aws_kms_key.tfstate_key.key_id +} diff --git a/terraform-state/outputs.tf b/terraform-state/outputs.tf new file mode 100644 index 0000000..e5a5fcf --- /dev/null +++ b/terraform-state/outputs.tf @@ -0,0 +1,25 @@ +output "tfstate_key_arn" { + description = "inf-tfstate KMS key ARN" + value = aws_kms_key.tfstate_key.arn +} + +output "tfstate_bucket_id" { + description = "Terraform state S3 bucket ID" + value = aws_s3_bucket.tfstate.id +} + +output "tfstate_bucket_arn" { + description = "Terraform state S3 bucket ARN" + value = aws_s3_bucket.tfstate.arn +} + +output "tfstate_dynamodb_arn" { + description = "Terraform state DynamoDB table ARN" + value = aws_dynamodb_table.tfstate.arn +} + +output "tfstate_region" { + description = "Terraform state region" + value = local.tfstate_region +} + diff --git a/terraform-state/prefixes.tf b/terraform-state/prefixes.tf new file mode 120000 index 0000000..7e265d5 --- /dev/null +++ b/terraform-state/prefixes.tf @@ -0,0 +1 @@ +../common/prefixes.tf \ No newline at end of file diff --git a/terraform-state/variables.common.tf b/terraform-state/variables.common.tf new file mode 120000 index 0000000..7439ed8 --- /dev/null +++ b/terraform-state/variables.common.tf @@ -0,0 +1 @@ +../common/variables.common.tf \ No newline at end of file diff --git a/terraform-state/variables.tf b/terraform-state/variables.tf new file mode 100644 index 0000000..5430c4d --- /dev/null +++ b/terraform-state/variables.tf @@ -0,0 +1,50 @@ +variable "kms_tfstate_key" { + description = "Terraform remote state KMS key alias" + type = string + default = "k-kms-inf-tfstate" +} + +variable "tfstate_table" { + description = "Terraform remote state table" + type = string + default = "tf_remote_state" +} + +variable "tfstate_bucket" { + description = "Terraform remote state S3 bucket" + type = string + # default = "inf-tfstate-{{ tf_account }}" + default = "" +} + +variable "tfstate_bucket_prefix" { + description = "Terraform remote state S3 bucket prefix, prepended to the AWS account ID to make the bucket name." + type = string + default = "inf-tfstate" +} + +variable "tfstate_key_prefix" { + description = "Terraform remote state S3 bucket prefix (account alias)" + type = string + default = "" + # default = "{{ tf_account_name | quote }}" +} + +variable "tfstate_key_suffix" { + description = "Terraform remote state S3 bucket suffix" + type = string + default = "terraform.tfstate" +} + +variable "tfstate_region" { + description = "Terraform remote state S3 bucket region" + type = string + # default = "{{ these_regions[0] | quote }}" + default = "" +} + +variable "component_tags" { + description = "Additional tags for Components (s3, kms, ddb)" + type = map(map(string)) + default = { "s3" = {}, "kms" = {}, "ddb" = {} } +} diff --git a/terraform-state/version.tf b/terraform-state/version.tf new file mode 120000 index 0000000..b83c5b7 --- /dev/null +++ b/terraform-state/version.tf @@ -0,0 +1 @@ +../common/version.tf \ No newline at end of file