diff --git a/modules/github-aws-oidc/README.md b/modules/github-aws-oidc/README.md new file mode 100644 index 0000000..1042900 --- /dev/null +++ b/modules/github-aws-oidc/README.md @@ -0,0 +1,55 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | ~> 5.0 | +| [random](#requirement\_random) | >= 3.6 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | ~> 5.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_dynamodb_table.state_lock_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource | +| [aws_iam_openid_connect_provider.oidc_provider_entry](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | +| [aws_iam_role.organizations_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy.organizations_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_kms_key.encryption_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [aws_resourcegroups_group.github_foundations_rg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/resourcegroups_group) | resource | +| [aws_s3_bucket.state_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.state_bucket_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_versioning.state_bucket_versioning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [bucket\_name](#input\_bucket\_name) | The name of the s3 bucket that will store terraform state. | `string` | `"GithubFoundationState"` | no | +| [github\_repo\_owner](#input\_github\_repo\_owner) | The owner of the github foundations organizations repository. This value should be whatever github account you plan to make the repository under. | `string` | n/a | yes | +| [github\_thumbprints](#input\_github\_thumbprints) | A list of top intermediate certifact authority thumbprints to use for setting up an openid connect provider with github. Info on how to obtain thumbprints here: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html | `list(string)` | n/a | yes | +| [organizations\_repo\_name](#input\_organizations\_repo\_name) | The name of the github foundations organizations repository. Defaults to `organizations` | `string` | `"organizations"` | no | +| [organizations\_role\_name](#input\_organizations\_role\_name) | The name of the role that will be assummed by the github runner for the organizations repository. | `string` | `"GhFoundationsOrganizationsAction"` | no | +| [rg\_name](#input\_rg\_name) | The name of the AWS resource group to create for github foundation resources. | `string` | `"GithubFoundationResources"` | no | +| [tflock\_db\_billing\_mode](#input\_tflock\_db\_billing\_mode) | The billing mode to use for the dynamodb table storing lock file ids. Defaults to `PROVISIONED`. | `string` | `"PROVISIONED"` | no | +| [tflock\_db\_name](#input\_tflock\_db\_name) | The name of the dynamodb table that will store lock file ids. | `string` | `"TFLockIds"` | no | +| [tflock\_db\_read\_capacity](#input\_tflock\_db\_read\_capacity) | The read capacity to set for the dynamodb table storing lock file ids. Only required if billing mode is `PROVISIONED`. Defaults to 20. | `number` | `20` | no | +| [tflock\_db\_write\_capacity](#input\_tflock\_db\_write\_capacity) | The write capacity to set for the dynamodb table storing lock file ids. Only required if billing mode is `PROVISIONED`. Defaults to 20. | `number` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [dynamodb\_table\_name](#output\_dynamodb\_table\_name) | The name of the dynamodb table that was created to store lock file ids. | +| [organizations\_runner\_role](#output\_organizations\_runner\_role) | The ARN of the role that the github action runner should assume for the organizations repo | +| [s3\_bucket\_name](#output\_s3\_bucket\_name) | The name of the s3 bucket holding terraform state. | +| [s3\_bucket\_region](#output\_s3\_bucket\_region) | The region the s3 bucket holding terraform state was created in. | \ No newline at end of file diff --git a/modules/github-aws-oidc/oidc.tf b/modules/github-aws-oidc/oidc.tf new file mode 100644 index 0000000..ef2ae57 --- /dev/null +++ b/modules/github-aws-oidc/oidc.tf @@ -0,0 +1,97 @@ +resource "aws_iam_openid_connect_provider" "oidc_provider_entry" { + url = "https://token.actions.githubusercontent.com" + + client_id_list = [ "sts.amazonaws.com" ] + + thumbprint_list = var.github_thumbprints + + tags = local.rg_tags +} + +resource "aws_iam_role" "organizations_role" { + name = var.organizations_role_name + + assume_role_policy = jsonencode({ + "Version" = "2012-10-17", + "Statement" = [ + { + "Effect" = "Allow", + "Action" = "sts:AssumeRoleWithWebIdentity", + "Principal" = { + "Federated" = aws_iam_openid_connect_provider.oidc_provider_entry.arn + }, + "Condition" = { + "StringEquals" = { + "token.actions.githubusercontent.com:aud" = [ + "sts.amazonaws.com" + ] + }, + "StringLike" = { + "token.actions.githubusercontent.com:sub": [ + "repo:${var.github_repo_owner}/${var.organizations_repo_name}:*" + ] + } + } + } + ] +}) + + tags = local.rg_tags +} + +resource "aws_iam_role_policy" "organizations_role_policy" { + name = "organizations-tf-state-management-policy" + role = aws_iam_role.organizations_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "StateBucketFullAccess" + Action = [ + "s3:*" + ] + Effect = "Allow" + Resource = [ + aws_s3_bucket.state_bucket.arn, + "${aws_s3_bucket.sate_bucket.arn}/*" + ] + }, + { + Sid = "StateBucketDeleteDeny" + Action = [ + "s3:DeleteBucket" + ] + Effect = "Deny" + Resource = [aws_s3_bucket.state_bucket.arn] + }, + { + Sid = "AllowSecretRead" + Action = [ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:GetResourcePolicy" + + ] + Effect = "Allow" + Resource = "*" + Condition = { + StringEquals = { + "secretsmanager:ResourceTag/Purpose" = local.rg_tags["Purpose"] + } + } + }, + { + Sid = "AllowDynamoDBActionsOnLockTable" + Effect = "Allow", + Action = [ + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem" + ], + Resource = [ aws_dynamodb_table.state_lock_table.arn ] + } + ] + }) +} \ No newline at end of file diff --git a/modules/github-aws-oidc/outputs.tf b/modules/github-aws-oidc/outputs.tf new file mode 100644 index 0000000..2966bb2 --- /dev/null +++ b/modules/github-aws-oidc/outputs.tf @@ -0,0 +1,19 @@ +output "s3_bucket_name" { + description = "The name of the s3 bucket holding terraform state." + value = aws_s3_bucket.state_bucket.bucket +} + +output "s3_bucket_region" { + description = "The region the s3 bucket holding terraform state was created in." + value = aws_s3_bucket.state_bucket.region +} + +output "dynamodb_table_name" { + description = "The name of the dynamodb table that was created to store lock file ids." + value = aws_dynamodb_table.state_lock_table.name +} + +output "organizations_runner_role" { + description = "The ARN of the role that the github action runner should assume for the organizations repo" + value = aws_iam_role.organizations_role.arn +} \ No newline at end of file diff --git a/modules/github-aws-oidc/resource_group.tf b/modules/github-aws-oidc/resource_group.tf new file mode 100644 index 0000000..358f775 --- /dev/null +++ b/modules/github-aws-oidc/resource_group.tf @@ -0,0 +1,21 @@ +locals { + rg_tags = { + Purpose = "Github Foundations" + } +} + +resource "aws_resourcegroups_group" "github_foundations_rg" { + name = var.rg_name + + resource_query { + query = jsonencode({ + "ResourceTypeFilters" = [ "AWS::AllSupported" ] + "TagFilters" = [ + { + "Key"="Purpose" + "Values"=[ local.rg_tags.Purpose ] + } + ] + }) + } +} diff --git a/modules/github-aws-oidc/storage.tf b/modules/github-aws-oidc/storage.tf new file mode 100644 index 0000000..c9e34e2 --- /dev/null +++ b/modules/github-aws-oidc/storage.tf @@ -0,0 +1,43 @@ +resource "aws_kms_key" "encryption_key" { + description = "This key is used to encrypt state bucket objects" + deletion_window_in_days = 10 +} + +resource "aws_s3_bucket" "state_bucket" { + bucket = var.bucket_name + + tags = local.rg_tags +} + +resource "aws_s3_bucket_versioning" "state_bucket_versioning" { + bucket = aws_s3_bucket.state_bucket.id + versioning_configuration { + status = "Enabled" + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "state_bucket_encryption" { + bucket = aws_s3_bucket.state_bucket.id + + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = aws_kms_key.encryption_key.arn + sse_algorithm = "aws:kms" + } + } +} + +resource "aws_dynamodb_table" "state_lock_table" { + name = var.tflock_db_name + read_capacity = var.tflock_db_read_capacity + write_capacity = var.tflock_db_write_capacity + billing_mode = var.tflock_db_billing_mode + hash_key = "LockID" + + attribute { + name = "LockID" + type = "S" + } + + tags = local.rg_tags +} diff --git a/modules/github-aws-oidc/variables.tf b/modules/github-aws-oidc/variables.tf new file mode 100644 index 0000000..085fa92 --- /dev/null +++ b/modules/github-aws-oidc/variables.tf @@ -0,0 +1,65 @@ +# Resource Group Variables +variable "rg_name" { + type = string + description = "The name of the AWS resource group to create for github foundation resources." + default = "GithubFoundationResources" +} + +# Bucket Variables +variable "bucket_name" { + type = string + description = "The name of the s3 bucket that will store terraform state." + default = "GithubFoundationState" +} + +# DynamoDB Variables +variable "tflock_db_name" { + type = string + description = "The name of the dynamodb table that will store lock file ids." + default = "TFLockIds" +} + +variable "tflock_db_read_capacity" { + type = number + description = "The read capacity to set for the dynamodb table storing lock file ids. Only required if billing mode is `PROVISIONED`. Defaults to 20." + default = 20 +} + +variable "tflock_db_write_capacity" { + type = number + description = "The write capacity to set for the dynamodb table storing lock file ids. Only required if billing mode is `PROVISIONED`. Defaults to 20." +} + +variable "tflock_db_billing_mode" { + type = string + description = "The billing mode to use for the dynamodb table storing lock file ids. Defaults to `PROVISIONED`." + default = "PROVISIONED" +} + +# IAM Variables + +variable "github_thumbprints" { + type = list(string) + description = "A list of top intermediate certifact authority thumbprints to use for setting up an openid connect provider with github. Info on how to obtain thumbprints here: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html" + validation { + error_message = "The list must be a minimum length of 1 and has a maximum length of 5" + condition = length(var.github_thumbprints) >=1 && length(var.github_thumbprints) <= 5 + } +} + +variable "organizations_role_name" { + type = string + description = "The name of the role that will be assummed by the github runner for the organizations repository." + default = "GhFoundationsOrganizationsAction" +} + +variable "github_repo_owner" { + type = string + description = "The owner of the github foundations organizations repository. This value should be whatever github account you plan to make the repository under." +} + +variable "organizations_repo_name" { + type = string + description = "The name of the github foundations organizations repository. Defaults to `organizations`" + default = "organizations" +} \ No newline at end of file diff --git a/modules/github-aws-oidc/versions.tf b/modules/github-aws-oidc/versions.tf new file mode 100644 index 0000000..1135ec5 --- /dev/null +++ b/modules/github-aws-oidc/versions.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">= 1.6" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.6" # tftest + } + } +} diff --git a/modules/github-azure-oidc/README.md b/modules/github-azure-oidc/README.md index bc480f5..4dde149 100644 --- a/modules/github-azure-oidc/README.md +++ b/modules/github-azure-oidc/README.md @@ -4,7 +4,6 @@ |------|---------| | [terraform](#requirement\_terraform) | >= 1.6 | | [azurerm](#requirement\_azurerm) | >=3.0.0 | -| [google-beta](#requirement\_google-beta) | >= 3.77 | | [random](#requirement\_random) | >= 3.6 | ## Providers diff --git a/modules/github-azure-oidc/versions.tf b/modules/github-azure-oidc/versions.tf index d34e8d7..9a66b59 100644 --- a/modules/github-azure-oidc/versions.tf +++ b/modules/github-azure-oidc/versions.tf @@ -5,10 +5,6 @@ terraform { source = "hashicorp/azurerm" version = ">=3.0.0" #tftest } - google-beta = { - source = "hashicorp/google-beta" - version = ">= 3.77" # tftest - } random = { source = "hashicorp/random" version = ">= 3.6" # tftest