diff --git a/patch-aws-auth/data.eks.tf b/patch-aws-auth/data.eks.tf new file mode 100644 index 0000000..aa36539 --- /dev/null +++ b/patch-aws-auth/data.eks.tf @@ -0,0 +1,7 @@ +eata "aws_eks_cluster" "cluster" { + name = var.cluster_name +} + +data "aws_eks_cluster_auth" "cluster" { + name = var.cluster_name +} diff --git a/patch-aws-auth/data.tf b/patch-aws-auth/data.tf new file mode 120000 index 0000000..995624d --- /dev/null +++ b/patch-aws-auth/data.tf @@ -0,0 +1 @@ +../common/data.tf \ No newline at end of file diff --git a/patch-aws-auth/defaults.tf b/patch-aws-auth/defaults.tf new file mode 120000 index 0000000..a5556ac --- /dev/null +++ b/patch-aws-auth/defaults.tf @@ -0,0 +1 @@ +../common/defaults.tf \ No newline at end of file diff --git a/patch-aws-auth/examples/settings.aws-auth.tf b/patch-aws-auth/examples/settings.aws-auth.tf new file mode 100644 index 0000000..02cc2f2 --- /dev/null +++ b/patch-aws-auth/examples/settings.aws-auth.tf @@ -0,0 +1,12 @@ +locals { + aws_auth_users = [] + aws_auth_roles = [ + { + # rolearn: data.terraform_remote_state.applications_apps-adsd-eks_vpc_east_vpc2_apps_eks-adsd-cumulus-dev.outputs.role_cluster-admin-role_arn + rolearn : "" + aws_rolename : format("%v%v-cluster-admin", local._prefixes["eks-role"], var.cluster_name) + username : "admin" + groups = ["system:masters", "eks-console-dashboard-full-access-group"] + }, + ] +} diff --git a/patch-aws-auth/examples/variables.aws-auth.tf b/patch-aws-auth/examples/variables.aws-auth.tf new file mode 100644 index 0000000..d43c508 --- /dev/null +++ b/patch-aws-auth/examples/variables.aws-auth.tf @@ -0,0 +1,23 @@ +# maybe just ignore the ARN entirely and force a read + +variable "aws_auth_users" { + description = "A list of objects where each object has userarn, aws_username, (k8s) username, and (k8s) groups, where groups is a list of groups to associate with the user. Leaving userarn as an empty string will pull the user ARN from AWS." + type = list(object({ + userarn = string + aws_username = string + username = string + groups = list(string) + })) + default = [] +} + +variable "aws_auth_roles" { + description = "A list of objects where each object has rolearn, aws_rolename, (k8s) username, and (k8s) groups, where groups is a list of groups to associate with the role. Leaving rolearn as an empty string will pull the role ARN from AWS." + type = list(object({ + rolearn = string + aws_rolename = string + username = string + groups = list(string) + })) + default = [] +} diff --git a/patch-aws-auth/kubeconfig.tf b/patch-aws-auth/kubeconfig.tf new file mode 100644 index 0000000..f1adf79 --- /dev/null +++ b/patch-aws-auth/kubeconfig.tf @@ -0,0 +1,32 @@ +# establish kubeconfig file needed for kubectl patch command +# requires kubectl command in the path + +resource "null_resource" "kubeconfig" { +# triggers = { +# always_run = timestamp() +# } + provisioner "local-exec" { + command = "which kubectl > /dev/null 2>&1; if [ $? != 0 ]; then 'echo missing kubectl'; exit 1; else exit 0; fi" + } + provisioner "local-exec" { + command = "test -d '${path.root}/setup' || mkdir '${path.root}/setup'" + } + provisioner "local-exec" { + environment = { + AWS_PROFILE = var.profile + AWS_REGION = var.region + } + command = "aws eks update-kubeconfig --name ${var.cluster_name} --kubeconfig ${path.root}/setup/aws-auth.kube.config" + } + depends_on = [data.aws_eks_cluster.cluster] +} + +#--- +# call it like +#--- +## provisioner "local-exec" { +## environment = { +## KUBECONFIG = "${path.root}/setup/kube.config" +## } +## command = "kubectli set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true" +## } diff --git a/patch-aws-auth/locals.tf b/patch-aws-auth/locals.tf new file mode 100644 index 0000000..521b7f3 --- /dev/null +++ b/patch-aws-auth/locals.tf @@ -0,0 +1,6 @@ +locals { + region = data.aws_region.current.name + aws_eks_cluster_auth = data.aws_eks_cluster_auth.cluster + aws_eks_cluster = data.aws_eks_cluster.cluster +} + diff --git a/patch-aws-auth/main.tf b/patch-aws-auth/main.tf new file mode 100644 index 0000000..c6c2fd7 --- /dev/null +++ b/patch-aws-auth/main.tf @@ -0,0 +1,165 @@ +/* +* # About patch-aws-auth +* +* # Example variable usage +* +* ```hcl +* # settings.auto.tfvars +* aws_auth_users = [ +* { +* userarn = "" +* aws_username = "a-ashle001" +* username = "admin" +* groups = ["system:masters", "eks-console-dashboard-full-access-group"] +* }, +* ] +* aws_auth_roles = [ +* { +* rolearn : "" +* aws_rolename : "r-inf-cloud-admin" +* username : "admin" +* groups = ["eks-console-dashboard-full-access-group"] +* }, +* ] +* ``` +* +* ```hcl +* # patch-aws-auth.tf +* module "awsauth_base_users" { +* source = THIS +* +* cluster_name = "adsd-cumulus-dev" +* aws_auth_users = var.aws_auth_users +* aws_auth_roles = var.aws_auth_roles +* } +* ``` +*/ + + +# pull in current configmap aws-auth +data "kubernetes_config_map" "aws-auth" { + metadata { + name = "aws-auth" + namespace = "kube-system" + } +} + +# map users without ARNs to arns +data "aws_iam_user" "auth_users" { + for_each = toset([for u in local.joined_auth_users : u.aws_username]) + user_name = each.key +} + +# map roles without ARNs to arns +data "aws_iam_role" "auth_roles" { + for_each = toset([for r in local.joined_auth_roles : r.aws_rolename]) + name = each.key +} + +locals { + existing_roles_string = lookup(data.kubernetes_config_map.aws-auth.data, "mapRoles", "") + existing_users_string = lookup(data.kubernetes_config_map.aws-auth.data, "mapUsers", "") + + existing_roles = local.existing_roles_string != "" ? yamldecode(local.existing_roles_string) : [] + existing_users = local.existing_users_string != "" ? yamldecode(local.existing_users_string) : [] + + joined_auth_users = concat(local.aws_auth_users, var.aws_auth_users) + joined_auth_roles = concat(local.aws_auth_roles, var.aws_auth_roles) + + mapped_auth_users = [for u in local.joined_auth_users : { + userarn = data.aws_iam_user.auth_users[u.aws_username].arn + aws_username = u.aws_username + username = u.username + groups = u.groups + }] + mapped_auth_roles = [for u in local.joined_auth_roles : { + rolearn = data.aws_iam_role.auth_roles[u.aws_rolename].arn + aws_rolename = u.aws_rolename + username = u.username + groups = u.groups + }] + + merged_users = merge( + { for user in local.existing_users : user.userarn => user }, + # { for user in local.aws_auth_users : user.userarn => user }, + # { for user in var.aws_auth_users : user.userarn => user } + { for user in local.mapped_auth_users : user.userarn => user }, + ) + + merged_roles = merge( + { for role in local.existing_roles : role.rolearn => role }, + # { for role in local.aws_auth_roles : role.rolearn => role }, + # { for role in var.aws_auth_roles : role.rolearn => role } + { for role in local.mapped_auth_roles : role.rolearn => role }, + ) + + # patch = yamlencode({ + # "data" = { + # "mapUsers" = values(local.merged_users) + # "mapRoles" = values(local.merged_roles) + # } + # }) + patch = < 0~} + mapRoles: | +%{for k, v in local.merged_roles~} + - rolearn: ${v.rolearn} + username: ${v.username} + groups: +%{for g in v.groups~} + - ${g} +%{endfor~} +%{endfor~} +%{endif~} +%{if length(local.merged_users) > 0~} + mapUsers: | +%{for k, v in local.merged_users~} + - userarn: ${v.userarn} + username: ${v.username} + groups: +%{for g in v.groups~} + - ${g} +%{endfor~} +%{endfor~} +%{endif~} +EOM + + # patch_t = templatefile("${path.root}/config_map.aws-auth.yaml.tpl",{ + # users = values(local.merged_users) + # roles = values(local.merged_roles) + # }) +} + +resource "null_resource" "patch-aws-auth" { + triggers = { + users = join(",", sort(keys(local.merged_users))) + roles = join(",", sort(keys(local.merged_roles))) + } + depends_on = [null_resource.kubeconfig] + + provisioner "local-exec" { + command = "test -d ${path.root}/setup || mkdir ${path.root}/setup" + } + provisioner "local-exec" { + working_dir = "${path.root}/setup" + command = "echo '${local.patch}' > config_map.aws-auth.patch.yaml" + } + provisioner "local-exec" { + working_dir = "${path.root}/setup" + command = "kubectl --kubeconfig aws-auth.kube.config patch --type merge -n kube-system configmap/aws-auth --patch-file config_map.aws-auth.patch.yaml" + } +} + +# output "map" { +# value = data.kubernetes_config_map.aws-auth +# } +# output "map_output" { +# value = { +# "object" = data.kubernetes_config_map.aws-auth +# "existing_users" = local.existing_users +# "existing_roles" = local.existing_roles +# "patch" = local.patch +# "patch_text" = local.patch_t +# } +# } diff --git a/patch-aws-auth/prefixes.tf b/patch-aws-auth/prefixes.tf new file mode 120000 index 0000000..7e265d5 --- /dev/null +++ b/patch-aws-auth/prefixes.tf @@ -0,0 +1 @@ +../common/prefixes.tf \ No newline at end of file diff --git a/patch-aws-auth/providers.tf b/patch-aws-auth/providers.tf new file mode 100644 index 0000000..e881b38 --- /dev/null +++ b/patch-aws-auth/providers.tf @@ -0,0 +1,19 @@ +terraform { + required_version = ">= 0.12.31" +} + +provider "kubernetes" { + host = local.aws_eks_cluster.endpoint + + cluster_ca_certificate = base64decode(local.aws_eks_cluster.certificate_authority[0].data) + token = local.aws_eks_cluster_auth.token +} + +# provider "helm" { +# kubernetes { +# host = local.aws_eks_cluster.endpoint +# +# cluster_ca_certificate = base64decode(local.aws_eks_cluster.certificate_authority[0].data) +# token = local.aws_eks_cluster_auth.token +# } +# } diff --git a/patch-aws-auth/templates/config_map.aws-auth.yaml.tpl b/patch-aws-auth/templates/config_map.aws-auth.yaml.tpl new file mode 100644 index 0000000..7c58ada --- /dev/null +++ b/patch-aws-auth/templates/config_map.aws-auth.yaml.tpl @@ -0,0 +1,17 @@ +data: +%{ if length(roles) > 0 } + mapRoles: | + %{ for k, v in roles ~} + - rolearn: ${v.rolearn} + username: ${v.username} + groups: ${v.groups} + %{ endfor ~} +%{ endif } +%{ if length(users) > 0 } + mapUsers: | + %{ for k, v in users ~} + - userarn: ${v.userarn} + username: ${v.username} + groups: ${v.groups} + %{ endfor ~} +%{ endif } diff --git a/patch-aws-auth/variables.aws-auth.tf b/patch-aws-auth/variables.aws-auth.tf new file mode 100644 index 0000000..d43c508 --- /dev/null +++ b/patch-aws-auth/variables.aws-auth.tf @@ -0,0 +1,23 @@ +# maybe just ignore the ARN entirely and force a read + +variable "aws_auth_users" { + description = "A list of objects where each object has userarn, aws_username, (k8s) username, and (k8s) groups, where groups is a list of groups to associate with the user. Leaving userarn as an empty string will pull the user ARN from AWS." + type = list(object({ + userarn = string + aws_username = string + username = string + groups = list(string) + })) + default = [] +} + +variable "aws_auth_roles" { + description = "A list of objects where each object has rolearn, aws_rolename, (k8s) username, and (k8s) groups, where groups is a list of groups to associate with the role. Leaving rolearn as an empty string will pull the role ARN from AWS." + type = list(object({ + rolearn = string + aws_rolename = string + username = string + groups = list(string) + })) + default = [] +} diff --git a/patch-aws-auth/variables.eks.tf b/patch-aws-auth/variables.eks.tf new file mode 120000 index 0000000..e7dc7b7 --- /dev/null +++ b/patch-aws-auth/variables.eks.tf @@ -0,0 +1 @@ +../common/variables.eks.tf \ No newline at end of file