From 673040104cd13c5fdbd9f26ff56729d72cdb66d6 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 17 Sep 2020 16:47:24 -0700 Subject: [PATCH 01/17] initial commit, need check together with terraform-zero-modules/iam_users --- templates/terraform/environments/prod/main.tf | 11 +++++++++++ templates/terraform/environments/stage/main.tf | 11 +++++++++++ templates/terraform/modules/environment/main.tf | 14 ++++++++++++++ .../terraform/modules/environment/variables.tf | 10 ++++++++++ 4 files changed, 46 insertions(+) diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index 2840ef3..b6b0851 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -62,4 +62,15 @@ module "prod" { sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" + + # IAM roles and users + iam_roles = [ + ["developer", data.aws_iam_policy_document.developer_access.json], + ["operator", data.aws_iam_policy_document.developer_access.json], + ] + iam_users = [ + ["dev1", ["developer"]], + ["devops1", ["developer", "operator"]], + ["operator1", ["operator"]], + ] } diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index 8b19b1e..41b6d28 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -67,4 +67,15 @@ module "stage" { sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" + + # IAM roles and users + iam_roles = [ + ["developer", data.aws_iam_policy_document.developer_access.json], + ["operator", data.aws_iam_policy_document.developer_access.json], + ] + iam_users = [ + ["dev1", ["developer"]], + ["devops1", ["developer", "operator"]], + ["operator1", ["operator"]], + ] } diff --git a/templates/terraform/modules/environment/main.tf b/templates/terraform/modules/environment/main.tf index cc0eede..16c4709 100644 --- a/templates/terraform/modules/environment/main.tf +++ b/templates/terraform/modules/environment/main.tf @@ -47,6 +47,9 @@ module "eks" { worker_asg_min_size = var.eks_worker_asg_min_size worker_asg_max_size = var.eks_worker_asg_max_size worker_ami = var.eks_worker_ami # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html + + iam_role_arns = module.iam_users.eks_iam_role_arns + } @@ -132,3 +135,14 @@ module "sendgrid" { zone_name = var.domain_name sendgrid_api_key_secret_name = var.sendgrid_api_key_secret_name } + +module "iam_users" { + source = "commitdev/zero/aws//modules//iam_users" + version = "0.0.1" + + project = var.project + environment = var.environment + + iam_users = var.iam_users + iam_roles = var.iam_roles +} diff --git a/templates/terraform/modules/environment/variables.tf b/templates/terraform/modules/environment/variables.tf index 573f2cb..f37a40c 100644 --- a/templates/terraform/modules/environment/variables.tf +++ b/templates/terraform/modules/environment/variables.tf @@ -143,3 +143,13 @@ variable "cf_signed_downloads" { description = "Enable Cloudfront signed URLs" default = false } + +variable "iam_roles" { + type = list(tuple([string, string])) + description = "IAM role list with policy" +} + +variable "iam_users" { + type = list(tuple([string, list(string)])) + description = "IAM user list with multiple roles assigned" +} From c55e477ba88078e5a1b0000395dda460b88d6c2e Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 17 Sep 2020 20:20:49 -0700 Subject: [PATCH 02/17] fix with review --- templates/terraform/modules/environment/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/terraform/modules/environment/main.tf b/templates/terraform/modules/environment/main.tf index 16c4709..fb871ab 100644 --- a/templates/terraform/modules/environment/main.tf +++ b/templates/terraform/modules/environment/main.tf @@ -48,7 +48,7 @@ module "eks" { worker_asg_max_size = var.eks_worker_asg_max_size worker_ami = var.eks_worker_ami # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html - iam_role_arns = module.iam_users.eks_iam_role_arns + iam_role_mapping = module.iam_users.eks_iam_role_mapping } From 362b6263679e91ce09a3ca65fb9d3c6940f34750 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Fri, 18 Sep 2020 00:10:46 -0700 Subject: [PATCH 03/17] enhancement with map structure --- templates/terraform/environments/prod/main.tf | 25 ++++++++++++---- .../environments/prod/user_access.tf | 29 +++++++++++++++++++ .../terraform/environments/stage/main.tf | 25 ++++++++++++---- .../environments/stage/user_access.tf | 29 +++++++++++++++++++ .../modules/environment/variables.tf | 10 +++++-- 5 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 templates/terraform/environments/prod/user_access.tf create mode 100644 templates/terraform/environments/stage/user_access.tf diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index b6b0851..192f138 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -65,12 +65,27 @@ module "prod" { # IAM roles and users iam_roles = [ - ["developer", data.aws_iam_policy_document.developer_access.json], - ["operator", data.aws_iam_policy_document.developer_access.json], + { + name = "developer" + policy = data.aws_iam_policy_document.developer_access.json + }, + { + name = "operator" + policy = data.aws_iam_policy_document.developer_access.json + } ] iam_users = [ - ["dev1", ["developer"]], - ["devops1", ["developer", "operator"]], - ["operator1", ["operator"]], + { + name = "dev1" + roles = ["developer"] + }, + { + name = "devops1" + roles = ["developer", "operator"] + }, + { + name = "operator1" + roles = ["operator"] + } ] } diff --git a/templates/terraform/environments/prod/user_access.tf b/templates/terraform/environments/prod/user_access.tf new file mode 100644 index 0000000..54e832f --- /dev/null +++ b/templates/terraform/environments/prod/user_access.tf @@ -0,0 +1,29 @@ +# define policy documents for developer policy +data "aws_iam_policy_document" "developer_access" { + # EKS + statement { + effect = "Allow" + actions = [ + "eks:List*", + "eks:Describe*" + ] + resources = ["arn:aws:eks:::cluster/piggycloud-me-stage-*"] + } + # ECR + statement { + effect = "Allow" + actions = [ + "ecr:DescribeImages", + "ecr:DescribeRepositories" + ] + resources = ["*"] + } + # S3 + statement { + effect = "Allow" + actions = [ + "s3:ListBucket" + ] + resources = ["arn:aws:s3:::piggycloud-staging.me"] + } +} diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index 41b6d28..9c2ba49 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -70,12 +70,27 @@ module "stage" { # IAM roles and users iam_roles = [ - ["developer", data.aws_iam_policy_document.developer_access.json], - ["operator", data.aws_iam_policy_document.developer_access.json], + { + name = "developer" + policy = data.aws_iam_policy_document.developer_access.json + }, + { + name = "operator" + policy = data.aws_iam_policy_document.developer_access.json + } ] iam_users = [ - ["dev1", ["developer"]], - ["devops1", ["developer", "operator"]], - ["operator1", ["operator"]], + { + name = "dev1" + roles = ["developer"] + }, + { + name = "devops1" + roles = ["developer", "operator"] + }, + { + name = "operator1" + roles = ["operator"] + } ] } diff --git a/templates/terraform/environments/stage/user_access.tf b/templates/terraform/environments/stage/user_access.tf new file mode 100644 index 0000000..54e832f --- /dev/null +++ b/templates/terraform/environments/stage/user_access.tf @@ -0,0 +1,29 @@ +# define policy documents for developer policy +data "aws_iam_policy_document" "developer_access" { + # EKS + statement { + effect = "Allow" + actions = [ + "eks:List*", + "eks:Describe*" + ] + resources = ["arn:aws:eks:::cluster/piggycloud-me-stage-*"] + } + # ECR + statement { + effect = "Allow" + actions = [ + "ecr:DescribeImages", + "ecr:DescribeRepositories" + ] + resources = ["*"] + } + # S3 + statement { + effect = "Allow" + actions = [ + "s3:ListBucket" + ] + resources = ["arn:aws:s3:::piggycloud-staging.me"] + } +} diff --git a/templates/terraform/modules/environment/variables.tf b/templates/terraform/modules/environment/variables.tf index f37a40c..834ec9e 100644 --- a/templates/terraform/modules/environment/variables.tf +++ b/templates/terraform/modules/environment/variables.tf @@ -145,11 +145,17 @@ variable "cf_signed_downloads" { } variable "iam_roles" { - type = list(tuple([string, string])) + type = list(object({ + name = string + policy = string + })) description = "IAM role list with policy" } variable "iam_users" { - type = list(tuple([string, list(string)])) + type = list(object({ + name = string + roles = list(string) + })) description = "IAM user list with multiple roles assigned" } From ffe2a670b96a065991821f49652db8cb7d360ef2 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Fri, 18 Sep 2020 15:08:21 -0700 Subject: [PATCH 04/17] enhancement with user group for AWS access --- templates/terraform/environments/prod/main.tf | 6 +++--- templates/terraform/environments/stage/main.tf | 6 +++--- templates/terraform/modules/environment/main.tf | 15 +++++++-------- .../terraform/modules/environment/variables.tf | 12 ++++++------ 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index 192f138..e0c7243 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -63,8 +63,8 @@ module "prod" { sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" - # IAM roles and users - iam_roles = [ + # Roles and Users configuration + roles = [ { name = "developer" policy = data.aws_iam_policy_document.developer_access.json @@ -74,7 +74,7 @@ module "prod" { policy = data.aws_iam_policy_document.developer_access.json } ] - iam_users = [ + users = [ { name = "dev1" roles = ["developer"] diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index 9c2ba49..395369e 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -68,8 +68,8 @@ module "stage" { sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" - # IAM roles and users - iam_roles = [ + # Roles and Users configuration + roles = [ { name = "developer" policy = data.aws_iam_policy_document.developer_access.json @@ -79,7 +79,7 @@ module "stage" { policy = data.aws_iam_policy_document.developer_access.json } ] - iam_users = [ + users = [ { name = "dev1" roles = ["developer"] diff --git a/templates/terraform/modules/environment/main.tf b/templates/terraform/modules/environment/main.tf index fb871ab..063bb57 100644 --- a/templates/terraform/modules/environment/main.tf +++ b/templates/terraform/modules/environment/main.tf @@ -48,8 +48,7 @@ module "eks" { worker_asg_max_size = var.eks_worker_asg_max_size worker_ami = var.eks_worker_ami # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html - iam_role_mapping = module.iam_users.eks_iam_role_mapping - + iam_role_mapping = module.user_access.eks_iam_role_mapping } @@ -136,13 +135,13 @@ module "sendgrid" { sendgrid_api_key_secret_name = var.sendgrid_api_key_secret_name } -module "iam_users" { - source = "commitdev/zero/aws//modules//iam_users" +module "user_access" { + source = "commitdev/zero/aws//modules//user_access" version = "0.0.1" - project = var.project - environment = var.environment + project = var.project + environment = var.environment - iam_users = var.iam_users - iam_roles = var.iam_roles + users = var.users + roles = var.roles } diff --git a/templates/terraform/modules/environment/variables.tf b/templates/terraform/modules/environment/variables.tf index 834ec9e..511b6aa 100644 --- a/templates/terraform/modules/environment/variables.tf +++ b/templates/terraform/modules/environment/variables.tf @@ -144,18 +144,18 @@ variable "cf_signed_downloads" { default = false } -variable "iam_roles" { - type = list(object({ +variable "roles" { + type = list(object({ name = string policy = string })) - description = "IAM role list with policy" + description = "Role list with policy" } -variable "iam_users" { - type = list(object({ +variable "users" { + type = list(object({ name = string roles = list(string) })) - description = "IAM user list with multiple roles assigned" + description = "User list with multiple roles granted" } From aa95690b203c669d11acb4284a579cddeee64076 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Fri, 18 Sep 2020 15:23:20 -0700 Subject: [PATCH 05/17] fix with template --- templates/terraform/environments/prod/user_access.tf | 4 ++-- templates/terraform/environments/stage/user_access.tf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/terraform/environments/prod/user_access.tf b/templates/terraform/environments/prod/user_access.tf index 54e832f..c96c871 100644 --- a/templates/terraform/environments/prod/user_access.tf +++ b/templates/terraform/environments/prod/user_access.tf @@ -7,7 +7,7 @@ data "aws_iam_policy_document" "developer_access" { "eks:List*", "eks:Describe*" ] - resources = ["arn:aws:eks:::cluster/piggycloud-me-stage-*"] + resources = ["arn:aws:eks:::cluster/<% .Name %>-prod*"] } # ECR statement { @@ -24,6 +24,6 @@ data "aws_iam_policy_document" "developer_access" { actions = [ "s3:ListBucket" ] - resources = ["arn:aws:s3:::piggycloud-staging.me"] + resources = ["arn:aws:s3:::<% .Name %>-prod*"] } } diff --git a/templates/terraform/environments/stage/user_access.tf b/templates/terraform/environments/stage/user_access.tf index 54e832f..6a71811 100644 --- a/templates/terraform/environments/stage/user_access.tf +++ b/templates/terraform/environments/stage/user_access.tf @@ -7,7 +7,7 @@ data "aws_iam_policy_document" "developer_access" { "eks:List*", "eks:Describe*" ] - resources = ["arn:aws:eks:::cluster/piggycloud-me-stage-*"] + resources = ["arn:aws:eks:::cluster/<% .Name %>-stag*"] } # ECR statement { @@ -24,6 +24,6 @@ data "aws_iam_policy_document" "developer_access" { actions = [ "s3:ListBucket" ] - resources = ["arn:aws:s3:::piggycloud-staging.me"] + resources = ["arn:aws:s3:::<% .Name %>-stag*"] } } From 19b6e6d785edda0bd2b6afb81deac7b61f0bc2e8 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Wed, 23 Sep 2020 16:48:13 -0700 Subject: [PATCH 06/17] enhancements with shared state and k8s rules etc. --- templates/Makefile | 3 +- templates/terraform/README.md | 8 ++ templates/terraform/environments/prod/main.tf | 26 ++--- .../environments/prod/user_access.tf | 96 +++++++++++++++++-- .../terraform/environments/shared/main.tf | 60 ++++++++++++ .../terraform/environments/stage/main.tf | 26 ++--- .../environments/stage/user_access.tf | 96 +++++++++++++++++-- .../terraform/modules/environment/main.tf | 27 +++++- .../modules/environment/variables.tf | 15 +-- 9 files changed, 292 insertions(+), 65 deletions(-) create mode 100644 templates/terraform/environments/shared/main.tf diff --git a/templates/Makefile b/templates/Makefile index bfd93da..78614df 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -1,6 +1,7 @@ SHELL = /usr/bin/env bash ENVIRONMENT ?= stage PROJECT = <% .Name %> +ROLE ?= admin export AWS_DEFAULT_REGION = <% index .Params `region` %> export AWS_PAGER = KUBE_CONTEXT := $(PROJECT)-$(ENVIRONMENT)-$(AWS_DEFAULT_REGION) @@ -40,7 +41,7 @@ apply-k8s-utils: terraform apply $(AUTO_APPROVE) update-k8s-conf: - aws eks --region $(AWS_DEFAULT_REGION) update-kubeconfig --role "arn:aws:iam::<% index .Params `accountId` %>:role/$(PROJECT)-kubernetes-admin-$(ENVIRONMENT)" --name $(KUBE_CONTEXT) --alias $(KUBE_CONTEXT) + aws eks --region $(AWS_DEFAULT_REGION) update-kubeconfig --role "arn:aws:iam::<% index .Params `accountId` %>:role/$(PROJECT)-kubernetes-$(ROLE)-$(ENVIRONMENT)" --name $(KUBE_CONTEXT) --alias $(KUBE_CONTEXT) post-apply-setup: cd scripts && ENVIRONMENT=$(ENVIRONMENT) PROJECT=$(PROJECT) sh post-apply.sh diff --git a/templates/terraform/README.md b/templates/terraform/README.md index 5c38195..b9d0d52 100644 --- a/templates/terraform/README.md +++ b/templates/terraform/README.md @@ -102,7 +102,15 @@ make update-k8s-conf ``` +## User Access +You may want to add people for AWS and Kubernetes access. + 1. Change user list with role and environment assignment in environments/shared/main.tf + 2. Change user access policy in environments//user_access.tf +After applied, people can switch to assigned k8s context, in the root of the project, run: +``` +ROLE=developer make update-k8s-conf +``` ## Upgrading an EKS Cluster diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index 580d415..0319c64 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -63,29 +63,17 @@ module "prod" { sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" - # Roles and Users configuration + # Roles configuration roles = [ { - name = "developer" - policy = data.aws_iam_policy_document.developer_access.json + name = "developer" + aws_policy = data.aws_iam_policy_document.developer_access.json + k8s_policies = local.k8s_developer_access }, { - name = "operator" - policy = data.aws_iam_policy_document.developer_access.json - } - ] - users = [ - { - name = "dev1" - roles = ["developer"] - }, - { - name = "devops1" - roles = ["developer", "operator"] - }, - { - name = "operator1" - roles = ["operator"] + name = "operator" + aws_policy = data.aws_iam_policy_document.operator_access.json + k8s_policies = local.k8s_operator_access } ] } diff --git a/templates/terraform/environments/prod/user_access.tf b/templates/terraform/environments/prod/user_access.tf index c96c871..6ae3a03 100644 --- a/templates/terraform/environments/prod/user_access.tf +++ b/templates/terraform/environments/prod/user_access.tf @@ -1,14 +1,27 @@ -# define policy documents for developer policy +# define AWS policy documents for developer data "aws_iam_policy_document" "developer_access" { - # EKS + # IAM statement { effect = "Allow" actions = [ - "eks:List*", - "eks:Describe*" + "iam:ListRoles", + "sts:AssumeRole" ] - resources = ["arn:aws:eks:::cluster/<% .Name %>-prod*"] + resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-developer-prod"] + } + + # EKS + statement { + effect = "Allow" + actions = ["eks:ListClusters"] + resources = ["*"] + } + statement { + effect = "Allow" + actions = ["eks:DescribeCluster"] + resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] } + # ECR statement { effect = "Allow" @@ -18,12 +31,81 @@ data "aws_iam_policy_document" "developer_access" { ] resources = ["*"] } + # S3 + statement { + effect = "Allow" + actions = ["s3:ListBucket"] + resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>"] + } + statement { + effect = "Allow" + actions = ["s3:GetObject"] + resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>/*"] + } +} + +# define AWS policy documents for operator +data "aws_iam_policy_document" "operator_access" { + # IAM statement { effect = "Allow" actions = [ - "s3:ListBucket" + "iam:ListRoles", + "sts:AssumeRole" ] - resources = ["arn:aws:s3:::<% .Name %>-prod*"] + resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-operator-prod"] } + + # EKS + statement { + effect = "Allow" + actions = ["eks:*"] + resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] + } + + # ECR + statement { + effect = "Allow" + actions = ["ecr:*"] + resources = ["*"] + } + + # S3 + statement { + effect = "Allow" + actions = ["s3:*"] + resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>"] + } + statement { + effect = "Allow" + actions = ["s3:*"] + resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>/*"] + } +} + + + +locals { + # define Kubernetes policy for developer + k8s_developer_access = [ + { + verbs = ["exec"] + api_groups = [""] + resources = ["pods", "pods/exec", "pods/log", "pods/portforward"] + }, { + verbs = ["get", "list", "watch"] + api_groups = [""] + resources = ["deployments", "configmaps", "pods", "services", "endpoints"] + } + ] + + # define Kubernetes policy for operator + k8s_operator_access = [ + { + verbs = ["exec", "create", "list", "get", "delete", "patch", "update"] + api_groups = [""] + resources = ["deployments", "configmaps", "pods", "secrets", "services", "endpoints"] + } + ] } diff --git a/templates/terraform/environments/shared/main.tf b/templates/terraform/environments/shared/main.tf new file mode 100644 index 0000000..5f315a4 --- /dev/null +++ b/templates/terraform/environments/shared/main.tf @@ -0,0 +1,60 @@ +terraform { + required_version = ">= 0.13" + backend "s3" { + bucket = "<% .Name %>-shared-terraform-state" + key = "infrastructure/terraform/environments/shared/main" + encrypt = true + region = "<% index .Params `region` %>" + dynamodb_table = "<% .Name %>-shared-terraform-state-locks" + } +} + +provider "aws" { + region = "<% index .Params `region` %>" + allowed_account_ids = ["<% index .Params `accountId` %>"] +} + +# Instantiate the environment +locals { + # Project configuration + project = "<% .Name %>" + + # Users configuration + users = [ + { + name = "dev1" + roles = [ + { name = "developer", environments = ["stage", "prod"] } + ] + }, { + name = "devops1" + roles = [ + { name = "developer", environments = ["stage", "prod"] }, + { name = "operator", environments = ["stage"] } + ] + }, { + name = "operator1" + roles = [ + { name = "operator", environments = ["stage", "prod"] } + ] + }, + ] +} + +## Create users +resource "aws_iam_user" "access_user" { + count = length(local.users) + name = "${local.project}-${local.users[count.index].name}" + + tags = { + for r in local.users[count.index].roles : "role:${r.name}" => join("/", r.environments) + } +} + +output "iam_users" { + value = aws_iam_user.access_user +} + +output "user_role_mapping" { + value = local.users +} diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index e361328..62ea9bc 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -68,29 +68,17 @@ module "stage" { sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" - # Roles and Users configuration + # Roles configuration roles = [ { - name = "developer" - policy = data.aws_iam_policy_document.developer_access.json + name = "developer" + aws_policy = data.aws_iam_policy_document.developer_access.json + k8s_policies = local.k8s_developer_access }, { - name = "operator" - policy = data.aws_iam_policy_document.developer_access.json - } - ] - users = [ - { - name = "dev1" - roles = ["developer"] - }, - { - name = "devops1" - roles = ["developer", "operator"] - }, - { - name = "operator1" - roles = ["operator"] + name = "operator" + aws_policy = data.aws_iam_policy_document.operator_access.json + k8s_policies = local.k8s_operator_access } ] } diff --git a/templates/terraform/environments/stage/user_access.tf b/templates/terraform/environments/stage/user_access.tf index 6a71811..076b97d 100644 --- a/templates/terraform/environments/stage/user_access.tf +++ b/templates/terraform/environments/stage/user_access.tf @@ -1,14 +1,27 @@ -# define policy documents for developer policy +# define AWS policy documents for developer data "aws_iam_policy_document" "developer_access" { - # EKS + # IAM statement { effect = "Allow" actions = [ - "eks:List*", - "eks:Describe*" + "iam:ListRoles", + "sts:AssumeRole" ] - resources = ["arn:aws:eks:::cluster/<% .Name %>-stag*"] + resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-developer-prod"] + } + + # EKS + statement { + effect = "Allow" + actions = ["eks:ListClusters"] + resources = ["*"] + } + statement { + effect = "Allow" + actions = ["eks:DescribeCluster"] + resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] } + # ECR statement { effect = "Allow" @@ -18,12 +31,81 @@ data "aws_iam_policy_document" "developer_access" { ] resources = ["*"] } + # S3 + statement { + effect = "Allow" + actions = ["s3:ListBucket"] + resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>"] + } + statement { + effect = "Allow" + actions = ["s3:GetObject"] + resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>/*"] + } +} + +# define AWS policy documents for operator +data "aws_iam_policy_document" "operator_access" { + # IAM statement { effect = "Allow" actions = [ - "s3:ListBucket" + "iam:ListRoles", + "sts:AssumeRole" ] - resources = ["arn:aws:s3:::<% .Name %>-stag*"] + resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-operator-prod"] } + + # EKS + statement { + effect = "Allow" + actions = ["eks:*"] + resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] + } + + # ECR + statement { + effect = "Allow" + actions = ["ecr:*"] + resources = ["*"] + } + + # S3 + statement { + effect = "Allow" + actions = ["s3:*"] + resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>"] + } + statement { + effect = "Allow" + actions = ["s3:*"] + resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>/*"] + } +} + + + +locals { + # define Kubernetes policy for developer + k8s_developer_access = [ + { + verbs = ["exec"] + api_groups = [""] + resources = ["pods", "pods/exec", "pods/log", "pods/portforward"] + }, { + verbs = ["get", "list", "watch"] + api_groups = [""] + resources = ["deployments", "configmaps", "pods", "services", "endpoints"] + } + ] + + # define Kubernetes policy for operator + k8s_operator_access = [ + { + verbs = ["exec", "create", "list", "get", "delete", "patch", "update"] + api_groups = [""] + resources = ["deployments", "configmaps", "pods", "secrets", "services", "endpoints"] + } + ] } diff --git a/templates/terraform/modules/environment/main.tf b/templates/terraform/modules/environment/main.tf index 41803bd..e385157 100644 --- a/templates/terraform/modules/environment/main.tf +++ b/templates/terraform/modules/environment/main.tf @@ -8,6 +8,31 @@ data "aws_iam_user" "ci_user" { user_name = "${var.project}-ci-user" # Should have been created in the bootstrap process } +# get users from remote state of "shared" +data "terraform_remote_state" "shared" { + backend = "s3" + config = { + bucket = "${var.project}-shared-terraform-state" + key = "infrastructure/terraform/environments/shared/main" + region = "us-west-2" + encrypt = true + dynamodb_table = "${var.project}-shared-terraform-state-locks" + } +} + +locals { + role_name_list = var.roles.*.name + users = [ + for u in data.terraform_remote_state.shared.outputs.user_role_mapping : { + name = u.name + roles = [ + for r in u.roles : + r.name if contains(local.role_name_list, r.name) && contains(r.environments, var.environment) + ] + } + ] +} + module "vpc" { source = "commitdev/zero/aws//modules/vpc" @@ -145,6 +170,6 @@ module "user_access" { project = var.project environment = var.environment - users = var.users roles = var.roles + users = local.users } diff --git a/templates/terraform/modules/environment/variables.tf b/templates/terraform/modules/environment/variables.tf index 3d68851..22fb3c7 100644 --- a/templates/terraform/modules/environment/variables.tf +++ b/templates/terraform/modules/environment/variables.tf @@ -149,16 +149,9 @@ variable "cf_signed_downloads" { variable "roles" { type = list(object({ - name = string - policy = string + name = string + aws_policy = string + k8s_policies = list(map(list(string))) })) - description = "Role list with policy" -} - -variable "users" { - type = list(object({ - name = string - roles = list(string) - })) - description = "User list with multiple roles granted" + description = "Role list with policies" } From 210b0ca7f48a4bcd97a04632f4db914192a5fc4f Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Wed, 23 Sep 2020 23:55:44 -0700 Subject: [PATCH 07/17] refined modules-environemnt-root parameters etc. after review --- templates/Makefile | 7 ++++++- templates/terraform/README.md | 15 ++++++++++++--- templates/terraform/environments/prod/main.tf | 14 ++++++++++++++ .../terraform/environments/prod/user_access.tf | 10 ---------- templates/terraform/environments/stage/main.tf | 14 ++++++++++++++ .../terraform/environments/stage/user_access.tf | 10 ---------- templates/terraform/modules/environment/main.tf | 14 +------------- .../terraform/modules/environment/variables.tf | 11 +++++++++++ 8 files changed, 58 insertions(+), 37 deletions(-) diff --git a/templates/Makefile b/templates/Makefile index 78614df..385be70 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -6,7 +6,7 @@ export AWS_DEFAULT_REGION = <% index .Params `region` %> export AWS_PAGER = KUBE_CONTEXT := $(PROJECT)-$(ENVIRONMENT)-$(AWS_DEFAULT_REGION) -apply: apply-remote-state apply-secrets apply-env update-k8s-conf pre-k8s apply-k8s-utils post-apply-setup +apply: apply-remote-state apply-secrets apply-shared-env apply-env update-k8s-conf pre-k8s apply-k8s-utils post-apply-setup apply-remote-state: aws s3 ls $(PROJECT)-$(ENVIRONMENT)-terraform-state > /dev/null 2>&1 || ( \ @@ -22,6 +22,11 @@ apply-secrets: terraform apply $(AUTO_APPROVE) && \ rm ./terraform.tfstate ) +apply-shared-env: + cd terraform/environments/shared; \ + terraform init && \ + terraform apply $(AUTO_APPROVE) + apply-env: cd terraform/environments/$(ENVIRONMENT); \ terraform init && \ diff --git a/templates/terraform/README.md b/templates/terraform/README.md index b9d0d52..803b2f1 100644 --- a/templates/terraform/README.md +++ b/templates/terraform/README.md @@ -105,12 +105,21 @@ ## User Access You may want to add people for AWS and Kubernetes access. - 1. Change user list with role and environment assignment in environments/shared/main.tf - 2. Change user access policy in environments//user_access.tf + 1. Change user list with role and environment assignment in main.tf under `environments/shared`, then run: +``` +make apply-shared-env +``` + + 2. Change user access policy in user_access.tf` under `environments/`, then run: +``` +ENVIRONENT= make apply-env +``` + After applied, people can switch to assigned k8s context, in the root of the project, run: ``` -ROLE=developer make update-k8s-conf +ROLE= make update-k8s-conf ``` +then, run `kubectl -n get pods` for confirmation. ## Upgrading an EKS Cluster diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index 0319c64..d478c45 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -14,6 +14,18 @@ provider "aws" { allowed_account_ids = ["<% index .Params `accountId` %>"] } +# remote state of "shared" +data "terraform_remote_state" "shared" { + backend = "s3" + config = { + bucket = "<% .Name %>-shared-terraform-state" + key = "infrastructure/terraform/environments/shared/main" + region = "<% index .Params `region` %>" + encrypt = true + dynamodb_table = "<% .Name %>-shared-terraform-state-locks" + } +} + # Instantiate the production environment module "prod" { source = "../../modules/environment" @@ -76,4 +88,6 @@ module "prod" { k8s_policies = local.k8s_operator_access } ] + + user_role_mapping = data.terraform_remote_state.shared.outputs.user_role_mapping } diff --git a/templates/terraform/environments/prod/user_access.tf b/templates/terraform/environments/prod/user_access.tf index 6ae3a03..288be7f 100644 --- a/templates/terraform/environments/prod/user_access.tf +++ b/templates/terraform/environments/prod/user_access.tf @@ -1,15 +1,5 @@ # define AWS policy documents for developer data "aws_iam_policy_document" "developer_access" { - # IAM - statement { - effect = "Allow" - actions = [ - "iam:ListRoles", - "sts:AssumeRole" - ] - resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-developer-prod"] - } - # EKS statement { effect = "Allow" diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index 62ea9bc..635f3fe 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -14,6 +14,18 @@ provider "aws" { allowed_account_ids = ["<% index .Params `accountId` %>"] } +# remote state of "shared" +data "terraform_remote_state" "shared" { + backend = "s3" + config = { + bucket = "<% .Name %>-shared-terraform-state" + key = "infrastructure/terraform/environments/shared/main" + region = "<% index .Params `region` %>" + encrypt = true + dynamodb_table = "<% .Name %>-shared-terraform-state-locks" + } +} + # Instantiate the staging environment module "stage" { source = "../../modules/environment" @@ -81,4 +93,6 @@ module "stage" { k8s_policies = local.k8s_operator_access } ] + + user_role_mapping = data.terraform_remote_state.shared.outputs.user_role_mapping } diff --git a/templates/terraform/environments/stage/user_access.tf b/templates/terraform/environments/stage/user_access.tf index 076b97d..5641a5e 100644 --- a/templates/terraform/environments/stage/user_access.tf +++ b/templates/terraform/environments/stage/user_access.tf @@ -1,15 +1,5 @@ # define AWS policy documents for developer data "aws_iam_policy_document" "developer_access" { - # IAM - statement { - effect = "Allow" - actions = [ - "iam:ListRoles", - "sts:AssumeRole" - ] - resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-developer-prod"] - } - # EKS statement { effect = "Allow" diff --git a/templates/terraform/modules/environment/main.tf b/templates/terraform/modules/environment/main.tf index e385157..6b220b0 100644 --- a/templates/terraform/modules/environment/main.tf +++ b/templates/terraform/modules/environment/main.tf @@ -8,22 +8,10 @@ data "aws_iam_user" "ci_user" { user_name = "${var.project}-ci-user" # Should have been created in the bootstrap process } -# get users from remote state of "shared" -data "terraform_remote_state" "shared" { - backend = "s3" - config = { - bucket = "${var.project}-shared-terraform-state" - key = "infrastructure/terraform/environments/shared/main" - region = "us-west-2" - encrypt = true - dynamodb_table = "${var.project}-shared-terraform-state-locks" - } -} - locals { role_name_list = var.roles.*.name users = [ - for u in data.terraform_remote_state.shared.outputs.user_role_mapping : { + for u in var.user_role_mapping : { name = u.name roles = [ for r in u.roles : diff --git a/templates/terraform/modules/environment/variables.tf b/templates/terraform/modules/environment/variables.tf index 22fb3c7..bf465a5 100644 --- a/templates/terraform/modules/environment/variables.tf +++ b/templates/terraform/modules/environment/variables.tf @@ -155,3 +155,14 @@ variable "roles" { })) description = "Role list with policies" } + +variable "user_role_mapping" { + type = list(object({ + name = string + roles = list(object({ + name = string + environments = list(string) + })) + })) + description = "User-Roles mapping with environment" +} From 5c75996b260c982d0ca42ff916cecd2b46702b22 Mon Sep 17 00:00:00 2001 From: Bill Monkman Date: Thu, 24 Sep 2020 10:19:36 -0700 Subject: [PATCH 08/17] Tweaked documentation a bit --- templates/terraform/README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/templates/terraform/README.md b/templates/terraform/README.md index 803b2f1..07351a8 100644 --- a/templates/terraform/README.md +++ b/templates/terraform/README.md @@ -102,24 +102,27 @@ make update-k8s-conf ``` +If a user has a role other than admin (dev, operations, etc.) they can specify it here as well: +``` + ROLE= make update-k8s-conf + ``` + ## User Access -You may want to add people for AWS and Kubernetes access. - 1. Change user list with role and environment assignment in main.tf under `environments/shared`, then run: +You may want to give memebers of your team access to the infrastructure. +Individual roles and permissions are defined in `environments//user_access.tf`, these will define the amount of access a user in that role has to both AWS and Kubernetes. + + 1. Add users in `environments/shared/main.tf` and specify the role they should have in each environment, then run: ``` make apply-shared-env ``` - 2. Change user access policy in user_access.tf` under `environments/`, then run: + 2. To do the assignment of users to roles in each environment, you must run this for each: ``` ENVIRONENT= make apply-env ``` +This should detect that there was a new user created, and put them into the necessary group. -After applied, people can switch to assigned k8s context, in the root of the project, run: -``` -ROLE= make update-k8s-conf -``` -then, run `kubectl -n get pods` for confirmation. ## Upgrading an EKS Cluster From c3cbbd9c7360ab8dbe0dfc2a354fac3f778bad54 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 24 Sep 2020 14:40:35 -0700 Subject: [PATCH 09/17] remove unnecessary templating and use variables --- templates/terraform/environments/prod/main.tf | 39 +++++++------ .../environments/prod/user_access.tf | 14 ++--- .../terraform/environments/shared/main.tf | 55 ++++++++++--------- .../terraform/environments/stage/main.tf | 41 ++++++++------ .../environments/stage/user_access.tf | 14 ++--- 5 files changed, 90 insertions(+), 73 deletions(-) diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index d478c45..0d464cc 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -1,28 +1,35 @@ +locals { + project = "<% .Name %>" + region = "<% index .Params `region` %>" + account_id = "<% index .Params `accountId` %>"] + domain_name = "<% index .Params `productionHostRoot` %>" +} + terraform { required_version = ">= 0.13" backend "s3" { - bucket = "<% .Name %>-prod-terraform-state" + bucket = "${local.project}-prod-terraform-state" key = "infrastructure/terraform/environments/prod/main" encrypt = true - region = "<% index .Params `region` %>" - dynamodb_table = "<% .Name %>-prod-terraform-state-locks" + region = local.region + dynamodb_table = "${local.project}-prod-terraform-state-locks" } } provider "aws" { - region = "<% index .Params `region` %>" - allowed_account_ids = ["<% index .Params `accountId` %>"] + region = local.region + allowed_account_ids = [local.account_id] } # remote state of "shared" data "terraform_remote_state" "shared" { backend = "s3" config = { - bucket = "<% .Name %>-shared-terraform-state" + bucket = "${local.project}-shared-terraform-state" key = "infrastructure/terraform/environments/shared/main" - region = "<% index .Params `region` %>" + region = local.region encrypt = true - dynamodb_table = "<% .Name %>-shared-terraform-state-locks" + dynamodb_table = "${local.project}-shared-terraform-state-locks" } } @@ -32,9 +39,9 @@ module "prod" { environment = "prod" # Project configuration - project = "<% .Name %>" - region = "<% index .Params `region` %>" - allowed_account_ids = ["<% index .Params `accountId` %>"] + project = local.project + region = local.region + allowed_account_ids = [local.account_id] random_seed = "<% index .Params `randomSeed` %>" # ECR configuration @@ -47,15 +54,15 @@ module "prod" { eks_worker_asg_max_size = 4 # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html - # https://<% index .Params `region` %>.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.17%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=<% index .Params `region` %> + # https://${local.region}.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.17%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=${local.region} eks_worker_ami = "<% index .Params `eksWorkerAMI` %>" # Hosting configuration. Each domain will have a bucket created for it, but may have mulitple aliases pointing to the same bucket. hosted_domains = [ - { domain : "<% index .Params `productionHostRoot` %>", aliases : [] }, - { domain : "<% index .Params `productionFrontendSubdomain` %><% index .Params `productionHostRoot` %>", aliases : [] }, + { domain : local.domain_name, aliases : [] }, + { domain : "<% index .Params `productionFrontendSubdomain` %>${local.domain_name}", aliases : [] }, ] - domain_name = "<% index .Params `productionHostRoot` %>" + domain_name = "${local.domain_name}" cf_signed_downloads = <% if eq (index .Params `fileUploads`) "yes" %>true<% else %>false<% end %> # DB configuration @@ -73,7 +80,7 @@ module "prod" { # See https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-limits.html sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> - sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" + sendgrid_api_key_secret_name = "${local.project}-sendgrid-<% index .Params `randomSeed` %>" # Roles configuration roles = [ diff --git a/templates/terraform/environments/prod/user_access.tf b/templates/terraform/environments/prod/user_access.tf index 288be7f..0d91aa8 100644 --- a/templates/terraform/environments/prod/user_access.tf +++ b/templates/terraform/environments/prod/user_access.tf @@ -9,7 +9,7 @@ data "aws_iam_policy_document" "developer_access" { statement { effect = "Allow" actions = ["eks:DescribeCluster"] - resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] + resources = ["arn:aws:eks:${local.region}:${local.account_id}:cluster/${local.project}-stage*"] } # ECR @@ -26,12 +26,12 @@ data "aws_iam_policy_document" "developer_access" { statement { effect = "Allow" actions = ["s3:ListBucket"] - resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>"] + resources = ["arn:aws:s3:::*${local.domain_name}"] } statement { effect = "Allow" actions = ["s3:GetObject"] - resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>/*"] + resources = ["arn:aws:s3:::*${local.domain_name}/*"] } } @@ -44,14 +44,14 @@ data "aws_iam_policy_document" "operator_access" { "iam:ListRoles", "sts:AssumeRole" ] - resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-operator-prod"] + resources = ["arn:aws:iam::${local.account_id}:role/${local.project}-kubernetes-operator-stage"] } # EKS statement { effect = "Allow" actions = ["eks:*"] - resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] + resources = ["arn:aws:eks:${local.region}:${local.account_id}:cluster/${local.project}-stage*"] } # ECR @@ -65,12 +65,12 @@ data "aws_iam_policy_document" "operator_access" { statement { effect = "Allow" actions = ["s3:*"] - resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>"] + resources = ["arn:aws:s3:::*${local.domain_name}"] } statement { effect = "Allow" actions = ["s3:*"] - resources = ["arn:aws:s3:::*<% index .Params `productionHostRoot` %>/*"] + resources = ["arn:aws:s3:::*${local.domain_name}/*"] } } diff --git a/templates/terraform/environments/shared/main.tf b/templates/terraform/environments/shared/main.tf index 5f315a4..b79df91 100644 --- a/templates/terraform/environments/shared/main.tf +++ b/templates/terraform/environments/shared/main.tf @@ -1,44 +1,47 @@ +locals { + project = "<% .Name %>" + region = "<% index .Params `region` %>" + account_id = "<% index .Params `accountId` %>"] +} + terraform { required_version = ">= 0.13" backend "s3" { - bucket = "<% .Name %>-shared-terraform-state" + bucket = "${local.project}-shared-terraform-state" key = "infrastructure/terraform/environments/shared/main" encrypt = true - region = "<% index .Params `region` %>" - dynamodb_table = "<% .Name %>-shared-terraform-state-locks" + region = "${local.region}" + dynamodb_table = "${local.project}-shared-terraform-state-locks" } } provider "aws" { - region = "<% index .Params `region` %>" - allowed_account_ids = ["<% index .Params `accountId` %>"] + region = local.region + allowed_account_ids = [local.account_id] } # Instantiate the environment locals { - # Project configuration - project = "<% .Name %>" - # Users configuration users = [ - { - name = "dev1" - roles = [ - { name = "developer", environments = ["stage", "prod"] } - ] - }, { - name = "devops1" - roles = [ - { name = "developer", environments = ["stage", "prod"] }, - { name = "operator", environments = ["stage"] } - ] - }, { - name = "operator1" - roles = [ - { name = "operator", environments = ["stage", "prod"] } - ] - }, - ] +# { +# name = "dev1" +# roles = [ +# { name = "developer", environments = ["stage", "prod"] } +# ] +# }, { +# name = "devops1" +# roles = [ +# { name = "developer", environments = ["stage", "prod"] }, +# { name = "operator", environments = ["stage"] } +# ] +# }, { +# name = "operator1" +# roles = [ +# { name = "operator", environments = ["stage", "prod"] } +# ] +# }, +# ] } ## Create users diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index 635f3fe..f5ae09c 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -1,28 +1,35 @@ +locals { + project = "<% .Name %>" + region = "<% index .Params `region` %>" + account_id = "<% index .Params `accountId` %>"] + domain_name = "<% index .Params `stagingHostRoot` %>" +} + terraform { required_version = ">= 0.13" backend "s3" { - bucket = "<% .Name %>-stage-terraform-state" + bucket = "${local.project}-stage-terraform-state" key = "infrastructure/terraform/environments/stage/main" encrypt = true - region = "<% index .Params `region` %>" - dynamodb_table = "<% .Name %>-stage-terraform-state-locks" + region = local.region + dynamodb_table = "${local.project}-stage-terraform-state-locks" } } provider "aws" { - region = "<% index .Params `region` %>" - allowed_account_ids = ["<% index .Params `accountId` %>"] + region = local.region + allowed_account_ids = [local.account_id] } # remote state of "shared" data "terraform_remote_state" "shared" { backend = "s3" config = { - bucket = "<% .Name %>-shared-terraform-state" + bucket = "${local.project}-shared-terraform-state" key = "infrastructure/terraform/environments/shared/main" - region = "<% index .Params `region` %>" + region = local.region encrypt = true - dynamodb_table = "<% .Name %>-shared-terraform-state-locks" + dynamodb_table = "${local.project}-shared-terraform-state-locks" } } @@ -32,13 +39,13 @@ module "stage" { environment = "stage" # Project configuration - project = "<% .Name %>" - region = "<% index .Params `region` %>" - allowed_account_ids = ["<% index .Params `accountId` %>"] + project = local.project + region = local.region + allowed_account_ids = [local.account_id] random_seed = "<% index .Params `randomSeed` %>" # ECR configuration - ecr_repositories = [ "<% .Name %>" ] + ecr_repositories = [ local.project ] # EKS configuration eks_cluster_version = "1.17" @@ -47,15 +54,15 @@ module "stage" { eks_worker_asg_max_size = 3 # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html - # https://<% index .Params `region` %>.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.17%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=<% index .Params `region` %> + # https://${local.region}.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.17%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=${local.region} eks_worker_ami = "<% index .Params `eksWorkerAMI` %>" # Hosting configuration. Each domain will have a bucket created for it, but may have mulitple aliases pointing to the same bucket. hosted_domains = [ - { domain : "<% index .Params `stagingHostRoot` %>", aliases : [] }, - { domain : "<% index .Params `stagingFrontendSubdomain` %><% index .Params `stagingHostRoot` %>", aliases : [] }, + { domain : local.domain_name, aliases : [] }, + { domain : "<% index .Params `stagingFrontendSubdomain` %>${local.domain_name}", aliases : [] }, ] - domain_name = "<% index .Params `stagingHostRoot` %>" + domain_name = local.domain_name cf_signed_downloads = <% if eq (index .Params `fileUploads`) "yes" %>true<% else %>false<% end %> # This will save some money as there a cost associated to each NAT gateway, but if the AZ with the gateway @@ -78,7 +85,7 @@ module "stage" { # See https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-limits.html sendgrid_enabled = <%if eq (index .Params `sendgridApiKey`) "" %>false<% else %>true<% end %> - sendgrid_api_key_secret_name = "<% .Name %>-sendgrid-<% index .Params `randomSeed` %>" + sendgrid_api_key_secret_name = "${local.project}-sendgrid-<% index .Params `randomSeed` %>" # Roles configuration roles = [ diff --git a/templates/terraform/environments/stage/user_access.tf b/templates/terraform/environments/stage/user_access.tf index 5641a5e..2f3e543 100644 --- a/templates/terraform/environments/stage/user_access.tf +++ b/templates/terraform/environments/stage/user_access.tf @@ -9,7 +9,7 @@ data "aws_iam_policy_document" "developer_access" { statement { effect = "Allow" actions = ["eks:DescribeCluster"] - resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] + resources = ["arn:aws:eks:${local.region}:${local.account_id}:cluster/${local.project}-prod*"] } # ECR @@ -26,12 +26,12 @@ data "aws_iam_policy_document" "developer_access" { statement { effect = "Allow" actions = ["s3:ListBucket"] - resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>"] + resources = ["arn:aws:s3:::*${local.domain_name}"] } statement { effect = "Allow" actions = ["s3:GetObject"] - resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>/*"] + resources = ["arn:aws:s3:::*${local.domain_name}/*"] } } @@ -44,14 +44,14 @@ data "aws_iam_policy_document" "operator_access" { "iam:ListRoles", "sts:AssumeRole" ] - resources = ["arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-operator-prod"] + resources = ["arn:aws:iam::${local.account_id}:role/${local.project}-kubernetes-operator-prod"] } # EKS statement { effect = "Allow" actions = ["eks:*"] - resources = ["arn:aws:eks:<% index .Params `region` %>:<% index .Params `accountId` %>:cluster/<% .Name %>-prod*"] + resources = ["arn:aws:eks:${local.region}:${local.account_id}:cluster/${local.project}-prod*"] } # ECR @@ -65,12 +65,12 @@ data "aws_iam_policy_document" "operator_access" { statement { effect = "Allow" actions = ["s3:*"] - resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>"] + resources = ["arn:aws:s3:::*${local.domain_name}"] } statement { effect = "Allow" actions = ["s3:*"] - resources = ["arn:aws:s3:::*<% index .Params `stagingHostRoot` %>/*"] + resources = ["arn:aws:s3:::*${local.domain_name}/*"] } } From e40810b609dc8c90cd257d0bc607fe5cee25e370 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 24 Sep 2020 14:47:28 -0700 Subject: [PATCH 10/17] replace space with tab --- templates/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/Makefile b/templates/Makefile index 385be70..2d70462 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -23,9 +23,9 @@ apply-secrets: rm ./terraform.tfstate ) apply-shared-env: - cd terraform/environments/shared; \ - terraform init && \ - terraform apply $(AUTO_APPROVE) + cd terraform/environments/shared; \ + terraform init && \ + terraform apply $(AUTO_APPROVE) apply-env: cd terraform/environments/$(ENVIRONMENT); \ From 64df991704050c39126e4d48d081835deb05d4f8 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 24 Sep 2020 14:48:25 -0700 Subject: [PATCH 11/17] typo fix --- templates/terraform/environments/shared/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/terraform/environments/shared/main.tf b/templates/terraform/environments/shared/main.tf index b79df91..56decc1 100644 --- a/templates/terraform/environments/shared/main.tf +++ b/templates/terraform/environments/shared/main.tf @@ -1,7 +1,7 @@ locals { project = "<% .Name %>" region = "<% index .Params `region` %>" - account_id = "<% index .Params `accountId` %>"] + account_id = "<% index .Params `accountId` %>" } terraform { From 660a6fe100f4d2cf6e645e53c00b2f9213237511 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 24 Sep 2020 14:57:33 -0700 Subject: [PATCH 12/17] recover templating for terraform block --- templates/terraform/environments/prod/main.tf | 20 +++++++++---------- .../terraform/environments/shared/main.tf | 18 ++++++++--------- .../terraform/environments/stage/main.tf | 20 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index 0d464cc..0956d4d 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -1,21 +1,21 @@ -locals { - project = "<% .Name %>" - region = "<% index .Params `region` %>" - account_id = "<% index .Params `accountId` %>"] - domain_name = "<% index .Params `productionHostRoot` %>" -} - terraform { required_version = ">= 0.13" backend "s3" { - bucket = "${local.project}-prod-terraform-state" + bucket = "<% .Name %>-prod-terraform-state" key = "infrastructure/terraform/environments/prod/main" encrypt = true - region = local.region - dynamodb_table = "${local.project}-prod-terraform-state-locks" + region = "<% index .Params `region` %>" + dynamodb_table = "<% .Name %>-prod-terraform-state-locks" } } +locals { + project = "<% .Name %>" + region = "<% index .Params `region` %>" + account_id = "<% index .Params `accountId` %>"] + domain_name = "<% index .Params `productionHostRoot` %>" +} + provider "aws" { region = local.region allowed_account_ids = [local.account_id] diff --git a/templates/terraform/environments/shared/main.tf b/templates/terraform/environments/shared/main.tf index 56decc1..472876c 100644 --- a/templates/terraform/environments/shared/main.tf +++ b/templates/terraform/environments/shared/main.tf @@ -1,20 +1,20 @@ -locals { - project = "<% .Name %>" - region = "<% index .Params `region` %>" - account_id = "<% index .Params `accountId` %>" -} - terraform { required_version = ">= 0.13" backend "s3" { - bucket = "${local.project}-shared-terraform-state" + bucket = "<% .Name %>-shared-terraform-state" key = "infrastructure/terraform/environments/shared/main" encrypt = true - region = "${local.region}" - dynamodb_table = "${local.project}-shared-terraform-state-locks" + region = "<% index .Params `region` %>" + dynamodb_table = "<% .Name %>-shared-terraform-state-locks" } } +locals { + project = "<% .Name %>" + region = "<% index .Params `region` %>" + account_id = "<% index .Params `accountId` %>" +} + provider "aws" { region = local.region allowed_account_ids = [local.account_id] diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index f5ae09c..d472fd6 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -1,21 +1,21 @@ -locals { - project = "<% .Name %>" - region = "<% index .Params `region` %>" - account_id = "<% index .Params `accountId` %>"] - domain_name = "<% index .Params `stagingHostRoot` %>" -} - terraform { required_version = ">= 0.13" backend "s3" { - bucket = "${local.project}-stage-terraform-state" + bucket = "<% .Name %>-stage-terraform-state" key = "infrastructure/terraform/environments/stage/main" encrypt = true - region = local.region - dynamodb_table = "${local.project}-stage-terraform-state-locks" + region = "<% index .Params `region` %>" + dynamodb_table = "<% .Name %>-stage-terraform-state-locks" } } +locals { + project = "<% .Name %>" + region = "<% index .Params `region` %>" + account_id = "<% index .Params `accountId` %>"] + domain_name = "<% index .Params `stagingHostRoot` %>" +} + provider "aws" { region = local.region allowed_account_ids = [local.account_id] From ce1da1b25a94e6ee19c78e63e32650f6ffc40e4e Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 24 Sep 2020 15:00:35 -0700 Subject: [PATCH 13/17] typo comment fix --- templates/terraform/environments/shared/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/terraform/environments/shared/main.tf b/templates/terraform/environments/shared/main.tf index 472876c..4b8b6d5 100644 --- a/templates/terraform/environments/shared/main.tf +++ b/templates/terraform/environments/shared/main.tf @@ -41,7 +41,7 @@ locals { # { name = "operator", environments = ["stage", "prod"] } # ] # }, -# ] + ] } ## Create users From e06db8cf4a6fd701e9932ec93f148c0ba60f7cce Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Thu, 24 Sep 2020 15:33:16 -0700 Subject: [PATCH 14/17] use remote modules and fixes --- templates/terraform/environments/prod/main.tf | 2 +- templates/terraform/environments/stage/main.tf | 2 +- templates/terraform/modules/environment/main.tf | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/terraform/environments/prod/main.tf b/templates/terraform/environments/prod/main.tf index 0956d4d..3b8ee43 100644 --- a/templates/terraform/environments/prod/main.tf +++ b/templates/terraform/environments/prod/main.tf @@ -12,7 +12,7 @@ terraform { locals { project = "<% .Name %>" region = "<% index .Params `region` %>" - account_id = "<% index .Params `accountId` %>"] + account_id = "<% index .Params `accountId` %>" domain_name = "<% index .Params `productionHostRoot` %>" } diff --git a/templates/terraform/environments/stage/main.tf b/templates/terraform/environments/stage/main.tf index d472fd6..b2756da 100644 --- a/templates/terraform/environments/stage/main.tf +++ b/templates/terraform/environments/stage/main.tf @@ -12,7 +12,7 @@ terraform { locals { project = "<% .Name %>" region = "<% index .Params `region` %>" - account_id = "<% index .Params `accountId` %>"] + account_id = "<% index .Params `accountId` %>" domain_name = "<% index .Params `stagingHostRoot` %>" } diff --git a/templates/terraform/modules/environment/main.tf b/templates/terraform/modules/environment/main.tf index 6b220b0..2f728e7 100644 --- a/templates/terraform/modules/environment/main.tf +++ b/templates/terraform/modules/environment/main.tf @@ -41,7 +41,7 @@ data "aws_caller_identity" "current" {} # Provision the EKS cluster module "eks" { source = "commitdev/zero/aws//modules/eks" - version = "0.0.2" + version = "0.1.2" providers = { aws = aws.for_eks } @@ -153,7 +153,7 @@ module "sendgrid" { module "user_access" { source = "commitdev/zero/aws//modules//user_access" - version = "0.0.1" + version = "0.1.2" project = var.project environment = var.environment From 0ca6c453e7dbb77c999546d49df83f55e3991661 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Fri, 25 Sep 2020 00:43:18 -0700 Subject: [PATCH 15/17] added tearndown steps for shared env --- templates/Makefile | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/templates/Makefile b/templates/Makefile index 2d70462..6dc4d4b 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -51,7 +51,7 @@ update-k8s-conf: post-apply-setup: cd scripts && ENVIRONMENT=$(ENVIRONMENT) PROJECT=$(PROJECT) sh post-apply.sh -teardown: teardown-k8s-utils teardown-env teardown-secrets teardown-remote-state +teardown: teardown-k8s-utils teardown-env teardown-shared-env teardown-secrets teardown-remote-state teardown-remote-state: @echo "Deleting remote state is not reversible, are you sure you want to delete the resources? [y/N]:" ; read ans ; [ $${ans:-N} == "y" ] || exit 1 @@ -60,6 +60,13 @@ teardown-remote-state: # TODO : This doesn't work because bucket versioning is enabled, we would need to loop through all versions of files and delete them manually aws s3 rb s3://$(PROJECT)-$(ENVIRONMENT)-terraform-state --force +teardown-shared-remote-state: + @echo "Deleting shared remote state is not reversible, are you sure you want to delete the resources? [y/N]:" ; read ans ; [ $${ans:-N} == "y" ] || exit 1 + aws dynamodb delete-table --region $(AWS_DEFAULT_REGION) --table-name $(PROJECT)-shared-terraform-state-locks + aws s3 rm s3://$(PROJECT)-shared-terraform-state --recursive + # TODO : This doesn't work because bucket versioning is enabled, we would need to loop through all versions of files and delete them manually + aws s3 rb s3://$(PROJECT)-shared-terraform-state --force + teardown-secrets: @echo "Deleting secrets is not reversible, are you sure you want to delete the secrets? [y/N]:" ; read ans ; [ $${ans:-N} == "y" ] || exit 1 aws secretsmanager list-secrets --region $(AWS_DEFAULT_REGION) --query "SecretList[?Tags[?Key=='project' && Value=='$(PROJECT)']].[Name] | [0][0]" | xargs aws secretsmanager delete-secret --region $(AWS_DEFAULT_REGION) --secret-id || echo "Secret already removed" @@ -75,8 +82,12 @@ teardown-env: cd terraform/environments/$(ENVIRONMENT) && \ terraform destroy +teardown-shared-env: + cd terraform/environments/shared && \ + terraform destroy + teardown-k8s-utils: cd kubernetes/terraform/environments/$(ENVIRONMENT) && \ terraform destroy -.PHONY: apply apply-remote-state apply-secrets apply-env apply-k8s-utils teardown-k8s-utils teardown-env teardown-secrets teardown-remote-state +.PHONY: apply apply-remote-state apply-secrets apply-env apply-k8s-utils teardown-k8s-utils teardown-env teardown-shared-env teardown-secrets teardown-remote-state teardown-shared-remote-state From 75e2f49d7920f9006fc009f4dad4db8527ef8dc5 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Fri, 25 Sep 2020 10:37:15 -0700 Subject: [PATCH 16/17] refer to newer versions of modules --- templates/terraform/modules/environment/main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/terraform/modules/environment/main.tf b/templates/terraform/modules/environment/main.tf index 2f728e7..3c6f268 100644 --- a/templates/terraform/modules/environment/main.tf +++ b/templates/terraform/modules/environment/main.tf @@ -103,7 +103,7 @@ module "s3_hosting" { module "db" { source = "commitdev/zero/aws//modules/database" - version = "0.0.1" + version = "0.1.1" project = var.project environment = var.environment @@ -126,7 +126,7 @@ module "ecr" { module "logging" { source = "commitdev/zero/aws//modules/logging" - version = "0.0.1" + version = "0.1.0" count = var.logging_type == "kibana" ? 1 : 0 From f731f2b783b78b1e705133b803cfff6816c1a812 Mon Sep 17 00:00:00 2001 From: Steven Shi Date: Fri, 25 Sep 2020 11:10:48 -0700 Subject: [PATCH 17/17] add shared-remote-state generation --- templates/Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/templates/Makefile b/templates/Makefile index 6dc4d4b..5ec2f47 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -6,7 +6,7 @@ export AWS_DEFAULT_REGION = <% index .Params `region` %> export AWS_PAGER = KUBE_CONTEXT := $(PROJECT)-$(ENVIRONMENT)-$(AWS_DEFAULT_REGION) -apply: apply-remote-state apply-secrets apply-shared-env apply-env update-k8s-conf pre-k8s apply-k8s-utils post-apply-setup +apply: apply-remote-state apply-shared-remote-state apply-secrets apply-shared-env apply-env update-k8s-conf pre-k8s apply-k8s-utils post-apply-setup apply-remote-state: aws s3 ls $(PROJECT)-$(ENVIRONMENT)-terraform-state > /dev/null 2>&1 || ( \ @@ -15,6 +15,13 @@ apply-remote-state: terraform apply -var "environment=$(ENVIRONMENT)" $(AUTO_APPROVE) && \ rm ./terraform.tfstate ) +apply-shared-remote-state: + aws s3 ls $(PROJECT)-shared-terraform-state > /dev/null 2>&1 || ( \ + cd terraform/bootstrap/remote-state && \ + terraform init && \ + terraform apply -var "environment=shared" $(AUTO_APPROVE) && \ + rm ./terraform.tfstate ) + apply-secrets: aws iam list-access-keys --user-name $(PROJECT)-ci-user > /dev/null 2>&1 || ( \ cd terraform/bootstrap/secrets && \ @@ -51,7 +58,7 @@ update-k8s-conf: post-apply-setup: cd scripts && ENVIRONMENT=$(ENVIRONMENT) PROJECT=$(PROJECT) sh post-apply.sh -teardown: teardown-k8s-utils teardown-env teardown-shared-env teardown-secrets teardown-remote-state +teardown: teardown-k8s-utils teardown-env teardown-shared-env teardown-secrets teardown-remote-state teardown-shared-remote-state teardown-remote-state: @echo "Deleting remote state is not reversible, are you sure you want to delete the resources? [y/N]:" ; read ans ; [ $${ans:-N} == "y" ] || exit 1