From 6c93be3965e11ce67d23c9951fa81a8de608c3fe Mon Sep 17 00:00:00 2001 From: Alex Mills Date: Fri, 23 Aug 2024 20:56:40 +0000 Subject: [PATCH] reorganisation of resources --- accounts_production.tf | 45 ++++++++++++++++++++++++ main.tf | 45 ++++-------------------- modules/github_oidc/main.tf | 59 ++++++++++++++++++++++++++++++++ modules/github_oidc/variables.tf | 4 +++ modules/terraform_state/main.tf | 51 ++------------------------- sso.tf | 52 +--------------------------- sso_groups.tf | 17 +++++++++ sso_permissions.tf | 10 ++++++ sso_users.tf | 22 ++++++++++++ 9 files changed, 167 insertions(+), 138 deletions(-) create mode 100644 accounts_production.tf create mode 100644 modules/github_oidc/main.tf create mode 100644 modules/github_oidc/variables.tf create mode 100644 sso_groups.tf create mode 100644 sso_permissions.tf create mode 100644 sso_users.tf diff --git a/accounts_production.tf b/accounts_production.tf new file mode 100644 index 0000000..2f0bd9d --- /dev/null +++ b/accounts_production.tf @@ -0,0 +1,45 @@ +# Create an account +resource "aws_organizations_account" "production" { + name = "Production" + email = "alex+production@alexmills.uk" + iam_user_access_to_billing = "ALLOW" + + parent_id = aws_organizations_organizational_unit.workloads_production.id +} + +# Assume the OrganizationAccountAccessRole to jump into the sub-account, and create resources. +provider "aws" { + alias = "production" + region = "eu-west-2" + + default_tags { + tags = local.tags + } + + assume_role { + role_arn = "arn:aws:iam::${aws_organizations_account.production.id}:role/OrganizationAccountAccessRole" + } + + allowed_account_ids = [ + aws_organizations_account.production.id + ] +} + +# Create an S3 bucket and DynamoDB table to allow for Terraform State Locking +module "production_terraform_state" { + source = "./modules/terraform_state" + providers = { + aws = aws.production + } + depends_on = [aws_organizations_account.production] +} + +# Create an OIDC provider to allow GitHub actions to assume an appropriate role +module "production_github_oidc" { + source = "./modules/github_oidc" + allowed_repositories = ["alexmills-uk/*:*"] + providers = { + aws = aws.production + } + depends_on = [aws_organizations_account.production] +} diff --git a/main.tf b/main.tf index ce0ee3b..7bd0e01 100644 --- a/main.tf +++ b/main.tf @@ -1,46 +1,15 @@ -provider "aws" { - region = var.region -} - -resource "aws_organizations_account" "production" { - name = "Production" - email = "alex+production@alexmills.uk" - iam_user_access_to_billing = "ALLOW" - +locals { tags = { - Name = "Production" - Owner = "AlexMills-UK" - Role = "production" - } - - parent_id = aws_organizations_organizational_unit.workloads_production.id -} - -provider "aws" { - alias = "production" - region = "eu-west-2" - - default_tags { - tags = { Repository = "github.com/alexmills-uk/aws-organisation" - Owner = "platform-team" + Owner = "AlexMills-UK" + Role = "production" Terraform = "true" } - } - - assume_role { - role_arn = "arn:aws:iam::${aws_organizations_account.production.id}:role/OrganizationAccountAccessRole" - } - - allowed_account_ids = [ - aws_organizations_account.production.id - ] } +provider "aws" { + region = var.region -module "production_terraform_state" { - source = "./modules/terraform_state" - providers = { - aws = aws.production + default_tags { + tags = local.tags } - depends_on = [aws_organizations_account.production] } \ No newline at end of file diff --git a/modules/github_oidc/main.tf b/modules/github_oidc/main.tf new file mode 100644 index 0000000..4b9c3f2 --- /dev/null +++ b/modules/github_oidc/main.tf @@ -0,0 +1,59 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">=5.0.0" + } + } +} + +resource "aws_iam_openid_connect_provider" "github" { + url = "https://token.actions.githubusercontent.com" + + client_id_list = [ + "sts.amazonaws.com", + ] + + thumbprint_list = [ + "6938fd4d98bab03faadb97b34396831e3780aea1", + ] +} + +data "aws_iam_policy_document" "github_oidc_assume_role" { + statement { + effect = "Allow" + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"] + } + + condition { + test = "StringEquals" + variable = "token.actions.githubusercontent.com:aud" + values = ["sts.amazonaws.com"] + } + + condition { + test = "StringLike" + variable = "token.actions.githubusercontent.com:sub" + values = [ + for repository in var.allowed_repositories : "repo:${repository}" + ] + } + } +} + +resource "aws_iam_role" "github_oidc_role" { + name = "github-deployment" + path = "/github-oidc/" + assume_role_policy = data.aws_iam_policy_document.github_oidc_assume_role.json +} + +# Ideally, this the policy attached should be more tightly scoped as AdministratorAccess is quite a wide permission set. +# Alternatively, use SCPs to limit the scope of AdministratorAccess. +resource "aws_iam_role_policy_attachment" "github_oidc_role_policy" { + role = aws_iam_role.github_oidc_role.name + policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" +} diff --git a/modules/github_oidc/variables.tf b/modules/github_oidc/variables.tf new file mode 100644 index 0000000..facd40f --- /dev/null +++ b/modules/github_oidc/variables.tf @@ -0,0 +1,4 @@ +variable "allowed_repositories" { + type = list(string) + description = "A list of GitHub repositories that are allowed to assume this AWS role. Must be in the format {organisation/username}/{repository}:{branch}." +} \ No newline at end of file diff --git a/modules/terraform_state/main.tf b/modules/terraform_state/main.tf index 61c373f..7bbeab9 100644 --- a/modules/terraform_state/main.tf +++ b/modules/terraform_state/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0.0" + version = ">=5.0.0" } } } @@ -28,51 +28,4 @@ resource "aws_dynamodb_table" "this" { name = "LockID" type = "S" } -} - -resource "aws_iam_openid_connect_provider" "github" { - url = "https://token.actions.githubusercontent.com" - - client_id_list = [ - "sts.amazonaws.com", - ] - - thumbprint_list = [ - "6938fd4d98bab03faadb97b34396831e3780aea1", - ] -} - -data "aws_iam_policy_document" "github_oidc_assume_role" { - statement { - effect = "Allow" - actions = ["sts:AssumeRoleWithWebIdentity"] - - principals { - type = "Federated" - identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"] - } - - condition { - test = "StringEquals" - variable = "token.actions.githubusercontent.com:aud" - values = ["sts.amazonaws.com"] - } - - condition { - test = "StringLike" - variable = "token.actions.githubusercontent.com:sub" - values = ["repo:alexmills-uk/*:*"] - } - } -} - -resource "aws_iam_role" "github_oidc_role" { - name = "github-deployment" - path = "/github-oidc/" - assume_role_policy = data.aws_iam_policy_document.github_oidc_assume_role.json -} - -resource "aws_iam_role_policy_attachment" "github_oidc_role_policy" { - role = aws_iam_role.github_oidc_role.name - policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" -} +} \ No newline at end of file diff --git a/sso.tf b/sso.tf index 571f62e..20b898d 100644 --- a/sso.tf +++ b/sso.tf @@ -1,51 +1 @@ -data "aws_ssoadmin_instances" "this" {} - -resource "aws_identitystore_group" "administrators" { - identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] - display_name = "Administrators" - description = "Administrators of the AWS Organisation" -} - -resource "aws_identitystore_user" "alexm" { - identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] - - display_name = "Alex Mills" - user_name = "alexm" - - name { - given_name = "Alex" - family_name = "Mills" - } - - emails { - value = "alex+user@alexmills.uk" - } -} - -resource "aws_identitystore_group_membership" "alexm_administrators" { - identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] - group_id = aws_identitystore_group.administrators.group_id - member_id = aws_identitystore_user.alexm.user_id -} - -resource "aws_ssoadmin_permission_set" "admin_permissionset" { - name = "AdministratorAccess" - instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] -} - -resource "aws_ssoadmin_managed_policy_attachment" "administrator_managed_policy_attachment" { - instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] - managed_policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" - permission_set_arn = aws_ssoadmin_permission_set.admin_permissionset.arn -} - -resource "aws_ssoadmin_account_assignment" "admin_role_assignment" { - for_each = { for account in data.aws_organizations_organization.org.accounts : account.id => account } - - instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] - principal_id = aws_identitystore_group.administrators.group_id - principal_type = "GROUP" - target_type = "AWS_ACCOUNT" - target_id = each.key - permission_set_arn = aws_ssoadmin_permission_set.admin_permissionset.arn -} \ No newline at end of file +data "aws_ssoadmin_instances" "this" {} \ No newline at end of file diff --git a/sso_groups.tf b/sso_groups.tf new file mode 100644 index 0000000..c58c376 --- /dev/null +++ b/sso_groups.tf @@ -0,0 +1,17 @@ +resource "aws_identitystore_group" "administrators" { + identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] + display_name = "Administrators" + description = "Administrators of the AWS Organisation" +} + +# Assign the Administrators group to every account +resource "aws_ssoadmin_account_assignment" "admin_role_assignment" { + for_each = { for account in data.aws_organizations_organization.org.accounts : account.id => account } + + instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] + principal_id = aws_identitystore_group.administrators.group_id + principal_type = "GROUP" + target_type = "AWS_ACCOUNT" + target_id = each.key + permission_set_arn = aws_ssoadmin_permission_set.admin_permissionset.arn +} \ No newline at end of file diff --git a/sso_permissions.tf b/sso_permissions.tf new file mode 100644 index 0000000..ed171f4 --- /dev/null +++ b/sso_permissions.tf @@ -0,0 +1,10 @@ +resource "aws_ssoadmin_permission_set" "admin_permissionset" { + name = "AdministratorAccess" + instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] +} + +resource "aws_ssoadmin_managed_policy_attachment" "administrator_managed_policy_attachment" { + instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] + managed_policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" + permission_set_arn = aws_ssoadmin_permission_set.admin_permissionset.arn +} \ No newline at end of file diff --git a/sso_users.tf b/sso_users.tf new file mode 100644 index 0000000..e161dd3 --- /dev/null +++ b/sso_users.tf @@ -0,0 +1,22 @@ +resource "aws_identitystore_user" "alexm" { + identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] + + display_name = "Alex Mills" + user_name = "alexm" + + name { + given_name = "Alex" + family_name = "Mills" + } + + emails { + value = "alex+user@alexmills.uk" + } +} + +# Add the user above to the Administrators group +resource "aws_identitystore_group_membership" "alexm_administrators" { + identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] + group_id = aws_identitystore_group.administrators.group_id + member_id = aws_identitystore_user.alexm.user_id +}