diff --git a/examples/workloads.tf b/examples/workloads.tf index 7132dff..d78378f 100644 --- a/examples/workloads.tf +++ b/examples/workloads.tf @@ -4,13 +4,14 @@ module "eks_observability_accelerator" { #source = "aws-ia/aws-observability-accelerator/terraform/eks" source = "../" - aws_region = var.aws_region - + aws_region = var.aws_region eks_cluster_id = var.eks_cluster_id - - # # -- or use an existing cluster - # eks_cluster_id = var.eks_cluster_id + # deploys AWS Distro for OpenTelemetry operator into the cluster + enable_amazon_eks_adot = true + # amazon_eks_adot_config = object( + # enable_cert_manager = false + # ) # # enable managed add-on for ADOT. Do we enforce this or let users # # have their own configs for OTEL operator @@ -62,6 +63,10 @@ module "eks_observability_accelerator" { # # -- or using existing amg workspace. so we can use API for keys # managed_grafana_workspace_id = var.managed_grafana_workspace_id + tags = { + Env = "Test" + } + } # module "amp" { diff --git a/locals.tf b/locals.tf new file mode 100644 index 0000000..01a9ef9 --- /dev/null +++ b/locals.tf @@ -0,0 +1,32 @@ +data "aws_partition" "current" {} + +data "aws_caller_identity" "current" {} + +data "aws_region" "current" {} + +data "aws_eks_cluster" "eks_cluster" { + name = var.eks_cluster_id +} + +locals { + # eks_oidc_issuer_url = var.eks_oidc_provider != null ? var.eks_oidc_provider : replace(data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer, "https://", "") + # eks_cluster_endpoint = var.eks_cluster_endpoint != null ? var.eks_cluster_endpoint : data.aws_eks_cluster.eks_cluster.endpoint + # eks_cluster_version = var.eks_cluster_version != null ? var.eks_cluster_version : data.aws_eks_cluster.eks_cluster.version + eks_oidc_issuer_url = replace(data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer, "https://", "") + eks_cluster_endpoint = data.aws_eks_cluster.eks_cluster.endpoint + eks_cluster_version = data.aws_eks_cluster.eks_cluster.version + + context = { + aws_caller_identity_account_id = data.aws_caller_identity.current.account_id + aws_caller_identity_arn = data.aws_caller_identity.current.arn + aws_eks_cluster_endpoint = local.eks_cluster_endpoint + aws_partition_id = data.aws_partition.current.partition + aws_region_name = data.aws_region.current.name + eks_cluster_id = var.eks_cluster_id + eks_oidc_issuer_url = local.eks_oidc_issuer_url + eks_oidc_provider_arn = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${local.eks_oidc_issuer_url}" + tags = var.tags + irsa_iam_role_path = var.irsa_iam_role_path + irsa_iam_permissions_boundary = var.irsa_iam_permissions_boundary + } +} diff --git a/main.tf b/main.tf index b1ce5b0..c4292bf 100644 --- a/main.tf +++ b/main.tf @@ -4,7 +4,14 @@ # EKS and Managed node groups # +module "adot_operator" { + source = "./modules/core/opentelemetry-operator" + + count = var.enable_amazon_eks_adot ? 1 : 0 + + addon_context = local.context +} module "java" { - source = "./modules/java" + source = "./modules/workloads/java" } diff --git a/modules/core/cert-manager/README.md b/modules/core/cert-manager/README.md new file mode 100644 index 0000000..9db6d79 --- /dev/null +++ b/modules/core/cert-manager/README.md @@ -0,0 +1,69 @@ +# Cert Manager Deployment Guide + +## Introduction + +Cert Manager adds certificates and certificate issuers as resource types in Kubernetes clusters, and simplifies the process of obtaining, renewing and using those certificates. + +## Helm Chart + +### Instructions to use the Helm Chart + +See the [cert-manager documentation](https://cert-manager.io/docs/installation/helm/). + +# Docker Image for Cert Manager + +cert-manager docker image is available at this repo: + + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [aws](#requirement\_aws) | >= 4.10 | +| [helm](#requirement\_helm) | >= 2.4.1 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.10 | +| [helm](#provider\_helm) | >= 2.4.1 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [helm\_addon](#module\_helm\_addon) | ../helm-addon | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.cert_manager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [helm_release.cert_manager_ca](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [helm_release.cert_manager_letsencrypt](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [aws_iam_policy_document.cert_manager_iam_policy_document](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_route53_zone.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [addon\_context](#input\_addon\_context) | Input configuration for the addon |
object({
aws_caller_identity_account_id = string
aws_caller_identity_arn = string
aws_eks_cluster_endpoint = string
aws_partition_id = string
aws_region_name = string
eks_cluster_id = string
eks_oidc_issuer_url = string
eks_oidc_provider_arn = string
tags = map(string)
irsa_iam_role_path = string
})
| n/a | yes | +| [domain\_names](#input\_domain\_names) | Domain names of the Route53 hosted zone to use with cert-manager. | `list(string)` | `[]` | no | +| [helm\_config](#input\_helm\_config) | cert-manager Helm chart configuration | `any` | `{}` | no | +| [install\_letsencrypt\_issuers](#input\_install\_letsencrypt\_issuers) | Install Let's Encrypt Cluster Issuers. | `bool` | `true` | no | +| [irsa\_policies](#input\_irsa\_policies) | Additional IAM policies used for the add-on service account. | `list(string)` | `[]` | no | +| [letsencrypt\_email](#input\_letsencrypt\_email) | Email address for expiration emails from Let's Encrypt. | `string` | `""` | no | +| [manage\_via\_gitops](#input\_manage\_via\_gitops) | Determines if the add-on should be managed via GitOps. | `bool` | `false` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [argocd\_gitops\_config](#output\_argocd\_gitops\_config) | Configuration used for managing the add-on with ArgoCD | +| [eks\_cluster\_id](#output\_eks\_cluster\_id) | Current AWS EKS Cluster ID | + diff --git a/modules/core/cert-manager/cert-manager-ca/Chart.yaml b/modules/core/cert-manager/cert-manager-ca/Chart.yaml new file mode 100644 index 0000000..364b25e --- /dev/null +++ b/modules/core/cert-manager/cert-manager-ca/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: cert-manager-ca +description: A Helm chart to install a Cert Manager CA +type: application +version: 0.2.0 +appVersion: v0.1.0 diff --git a/modules/core/cert-manager/cert-manager-ca/templates/certificate.yaml b/modules/core/cert-manager/cert-manager-ca/templates/certificate.yaml new file mode 100644 index 0000000..25c1240 --- /dev/null +++ b/modules/core/cert-manager/cert-manager-ca/templates/certificate.yaml @@ -0,0 +1,21 @@ +{{- range .Values.clusterIssuers }} +{{- if eq .type "CA" }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ .name }} + namespace: {{ $.Release.Namespace }} +spec: + isCA: true + commonName: {{ .name }} + secretName: {{ .secretName }} + {{- with .privateKey }} + privateKey: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .issuer }} + issuerRef: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/modules/core/cert-manager/cert-manager-ca/templates/clusterissuers.yaml b/modules/core/cert-manager/cert-manager-ca/templates/clusterissuers.yaml new file mode 100644 index 0000000..86040a2 --- /dev/null +++ b/modules/core/cert-manager/cert-manager-ca/templates/clusterissuers.yaml @@ -0,0 +1,14 @@ +{{- range .Values.clusterIssuers }} +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: {{ .name }} +spec: + {{- if eq .type "selfSigned" }} + selfSigned: {} + {{- else if eq .type "CA" }} + ca: + secretName: {{ .secretName }} + {{- end }} +{{- end }} diff --git a/modules/core/cert-manager/cert-manager-ca/values.yaml b/modules/core/cert-manager/cert-manager-ca/values.yaml new file mode 100644 index 0000000..02e952b --- /dev/null +++ b/modules/core/cert-manager/cert-manager-ca/values.yaml @@ -0,0 +1,13 @@ +clusterIssuers: + - name: cert-manager-selfsigned + type: selfSigned + - name: cert-manager-ca + type: CA + secretName: cert-manager-ca-root + privateKey: + algorithm: ECDSA + size: 256 + issuer: + name: cert-manager-selfsigned + kind: ClusterIssuer + group: cert-manager.io diff --git a/modules/core/cert-manager/cert-manager-letsencrypt/Chart.yaml b/modules/core/cert-manager/cert-manager-letsencrypt/Chart.yaml new file mode 100644 index 0000000..1ba9f1b --- /dev/null +++ b/modules/core/cert-manager/cert-manager-letsencrypt/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: cert-manager-letsencrypt +description: Cert Manager Cluster Issuers for Let's Encrypt certificates with DNS01 protocol +type: application +version: 0.1.0 +appVersion: v0.1.0 diff --git a/modules/core/cert-manager/cert-manager-letsencrypt/templates/clusterissuer-production.yaml b/modules/core/cert-manager/cert-manager-letsencrypt/templates/clusterissuer-production.yaml new file mode 100644 index 0000000..b0c93cb --- /dev/null +++ b/modules/core/cert-manager/cert-manager-letsencrypt/templates/clusterissuer-production.yaml @@ -0,0 +1,27 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: {{ .Release.Name }}-production-route53 + labels: + ca: letsencrypt + environment: production + solver: dns01 + provider: route53 +spec: + acme: + {{- if .Values.email }} + email: {{ .Values.email }} + {{- end }} + server: https://acme-v02.api.letsencrypt.org/directory + preferredChain: ISRG Root X1 + privateKeySecretRef: + name: {{ .Release.Name }}-production-route53 + solvers: + - dns01: + route53: + region: {{ .Values.region | default "global" }} + {{- if .Values.dnsZones }} + selector: + dnsZones: + {{- .Values.dnsZones | toYaml | nindent 12 }} + {{- end }} diff --git a/modules/core/cert-manager/cert-manager-letsencrypt/templates/clusterissuer-staging.yaml b/modules/core/cert-manager/cert-manager-letsencrypt/templates/clusterissuer-staging.yaml new file mode 100644 index 0000000..765ae0d --- /dev/null +++ b/modules/core/cert-manager/cert-manager-letsencrypt/templates/clusterissuer-staging.yaml @@ -0,0 +1,27 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: {{ .Release.Name }}-staging-route53 + labels: + ca: letsencrypt + environment: staging + solver: dns01 + provider: route53 +spec: + acme: + {{- if .Values.email }} + email: {{ .Values.email }} + {{- end }} + server: https://acme-staging-v02.api.letsencrypt.org/directory + preferredChain: ISRG Root X1 + privateKeySecretRef: + name: {{ .Release.Name }}-staging-route53 + solvers: + - dns01: + route53: + region: {{ .Values.region | default "global" }} + {{- if .Values.dnsZones }} + selector: + dnsZones: + {{- .Values.dnsZones | toYaml | nindent 12 }} + {{- end }} diff --git a/modules/core/cert-manager/cert-manager-letsencrypt/values.yaml b/modules/core/cert-manager/cert-manager-letsencrypt/values.yaml new file mode 100644 index 0000000..49c7071 --- /dev/null +++ b/modules/core/cert-manager/cert-manager-letsencrypt/values.yaml @@ -0,0 +1,6 @@ +# email: user@example.com + +# region: global + +# dnsZones: +# - domain.name diff --git a/modules/core/cert-manager/data.tf b/modules/core/cert-manager/data.tf new file mode 100644 index 0000000..1f9304a --- /dev/null +++ b/modules/core/cert-manager/data.tf @@ -0,0 +1,32 @@ +data "aws_route53_zone" "selected" { + for_each = toset(var.domain_names) + + name = each.key +} + +data "aws_iam_policy_document" "cert_manager_iam_policy_document" { + statement { + effect = "Allow" + resources = ["arn:${var.addon_context.aws_partition_id}:route53:::change/*"] + actions = ["route53:GetChange"] + } + + dynamic "statement" { + for_each = { for k, v in toset(var.domain_names) : k => data.aws_route53_zone.selected[k].arn } + + content { + effect = "Allow" + resources = [statement.value] + actions = [ + "route53:ChangeresourceRecordSets", + "route53:ListresourceRecordSets" + ] + } + } + + statement { + effect = "Allow" + resources = ["*"] + actions = ["route53:ListHostedZonesByName"] + } +} diff --git a/modules/core/cert-manager/locals.tf b/modules/core/cert-manager/locals.tf new file mode 100644 index 0000000..9afc1c0 --- /dev/null +++ b/modules/core/cert-manager/locals.tf @@ -0,0 +1,45 @@ +locals { + name = "cert-manager" + service_account_name = "cert-manager" # AWS PrivateCA is expecting the service account name as `cert-manager` + + default_helm_config = { + name = local.name + chart = local.name + repository = "https://charts.jetstack.io" + version = "v1.8.0" + namespace = local.name + description = "Cert Manager Add-on" + values = local.default_helm_values + } + + default_helm_values = [templatefile("${path.module}/values.yaml", {})] + + helm_config = merge( + local.default_helm_config, + var.helm_config + ) + + set_values = [ + { + name = "serviceAccount.name" + value = local.service_account_name + }, + { + name = "serviceAccount.create" + value = false + } + ] + + irsa_config = { + kubernetes_namespace = local.helm_config["namespace"] + kubernetes_service_account = local.service_account_name + create_kubernetes_namespace = try(local.helm_config["create_namespace"], true) + create_kubernetes_service_account = true + irsa_iam_policies = concat([aws_iam_policy.cert_manager.arn], var.irsa_policies) + } + + argocd_gitops_config = { + enable = true + serviceAccountName = local.service_account_name + } +} diff --git a/modules/core/cert-manager/main.tf b/modules/core/cert-manager/main.tf new file mode 100644 index 0000000..7d137ac --- /dev/null +++ b/modules/core/cert-manager/main.tf @@ -0,0 +1,47 @@ +module "helm_addon" { + source = "../helm-addon" + manage_via_gitops = var.manage_via_gitops + set_values = local.set_values + helm_config = local.helm_config + irsa_config = local.irsa_config + addon_context = var.addon_context +} + +resource "helm_release" "cert_manager_ca" { + count = var.manage_via_gitops ? 0 : 1 + name = "cert-manager-ca" + chart = "${path.module}/cert-manager-ca" + version = "0.2.0" + namespace = local.helm_config["namespace"] + + depends_on = [module.helm_addon] +} + +resource "helm_release" "cert_manager_letsencrypt" { + count = var.manage_via_gitops || !var.install_letsencrypt_issuers ? 0 : 1 + name = "cert-manager-letsencrypt" + chart = "${path.module}/cert-manager-letsencrypt" + version = "0.1.0" + namespace = local.helm_config["namespace"] + + set { + name = "email" + value = var.letsencrypt_email + type = "string" + } + + set { + name = "dnsZones" + value = "{${join(",", toset(var.domain_names))}}" + type = "string" + } + + depends_on = [module.helm_addon] +} + +resource "aws_iam_policy" "cert_manager" { + description = "cert-manager IAM policy." + name = "${var.addon_context.eks_cluster_id}-${local.helm_config["name"]}-irsa" + path = var.addon_context.irsa_iam_role_path + policy = data.aws_iam_policy_document.cert_manager_iam_policy_document.json +} diff --git a/modules/core/cert-manager/outputs.tf b/modules/core/cert-manager/outputs.tf new file mode 100644 index 0000000..42f0003 --- /dev/null +++ b/modules/core/cert-manager/outputs.tf @@ -0,0 +1,9 @@ +output "argocd_gitops_config" { + description = "Configuration used for managing the add-on with ArgoCD" + value = var.manage_via_gitops ? local.argocd_gitops_config : null +} + +output "eks_cluster_id" { + description = "Current AWS EKS Cluster ID" + value = var.addon_context.eks_cluster_id +} diff --git a/modules/core/cert-manager/values.yaml b/modules/core/cert-manager/values.yaml new file mode 100644 index 0000000..345aa9b --- /dev/null +++ b/modules/core/cert-manager/values.yaml @@ -0,0 +1,8 @@ +extraArgs: + - --enable-certificate-owner-ref=true + +installCRDs: true + +securityContext: + enabled: true + fsGroup: 1001 diff --git a/modules/core/cert-manager/variables.tf b/modules/core/cert-manager/variables.tf new file mode 100644 index 0000000..467f124 --- /dev/null +++ b/modules/core/cert-manager/variables.tf @@ -0,0 +1,51 @@ +variable "helm_config" { + description = "cert-manager Helm chart configuration" + type = any + default = {} +} + +variable "manage_via_gitops" { + description = "Determines if the add-on should be managed via GitOps." + type = bool + default = false +} + +variable "irsa_policies" { + description = "Additional IAM policies used for the add-on service account." + type = list(string) + default = [] +} + +variable "domain_names" { + description = "Domain names of the Route53 hosted zone to use with cert-manager." + type = list(string) + default = [] +} + +variable "install_letsencrypt_issuers" { + description = "Install Let's Encrypt Cluster Issuers." + type = bool + default = true +} + +variable "letsencrypt_email" { + description = "Email address for expiration emails from Let's Encrypt." + type = string + default = "" +} + +variable "addon_context" { + description = "Input configuration for the addon" + type = object({ + aws_caller_identity_account_id = string + aws_caller_identity_arn = string + aws_eks_cluster_endpoint = string + aws_partition_id = string + aws_region_name = string + eks_cluster_id = string + eks_oidc_issuer_url = string + eks_oidc_provider_arn = string + tags = map(string) + irsa_iam_role_path = string + }) +} diff --git a/modules/core/cert-manager/versions.tf b/modules/core/cert-manager/versions.tf new file mode 100644 index 0000000..aca51a0 --- /dev/null +++ b/modules/core/cert-manager/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.10" + } + helm = { + source = "hashicorp/helm" + version = ">= 2.4.1" + } + } +} diff --git a/modules/core/opentelemetry-operator/README.md b/modules/core/opentelemetry-operator/README.md new file mode 100644 index 0000000..884ffb4 --- /dev/null +++ b/modules/core/opentelemetry-operator/README.md @@ -0,0 +1,72 @@ +# AWS OpenTelemetry Operator + +[AWS Distro for OpenTelemetry (ADOT)](https://aws-otel.github.io/) is a secure, +production-ready, AWS-supported distribution of the OpenTelemetry project. +Part of the Cloud Native Computing Foundation, OpenTelemetry provides open +source APIs, libraries, and agents to collect distributed traces and metrics +for application monitoring. + +This modules deploys either the +[AWS Managed ADOT OpenTelemetry Operator for EKS](https://aws.amazon.com/about-aws/whats-new/2022/04/eks-opentelemetry-operator-now-available/) +or [the OpenTelemetry Operator](https://github.com/open-telemetry/opentelemetry-helm-charts) +through helm. +The OpenTelemetry Operator is an implementation of a Kubernetes Operator. +A Kubernetes Operator is a method of packaging, deploying and managing a +Kubernetes-native application, which is both deployed on Kubernetes and +managed using the Kubernetes APIs and kubectl tooling. The Kubernetes Operator +is a custom controller, which introduces new object types through Custom Resource +Definition (CRD), an extension mechanism in Kubernetes. +In this case, the CRD that is managed by the OpenTelemetry Operator is the Collector. + +> :warning: We do install [cert-manager](https://cert-manager.io/) as a [hard requirement](https://docs.aws.amazon.com/eks/latest/userguide/opentelemetry.html) for +the ADOT Operator. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [aws](#requirement\_aws) | >= 3.72 | +| [kubernetes](#requirement\_kubernetes) | >= 2.10 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 3.72 | +| [kubernetes](#provider\_kubernetes) | >= 2.10 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [cert\_manager](#module\_cert\_manager) | ../cert-manager | n/a | +| [helm\_addon](#module\_helm\_addon) | ../helm-addon | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_eks_addon.adot](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon) | resource | +| [kubernetes_cluster_role_binding_v1.adot](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster_role_binding_v1) | resource | +| [kubernetes_cluster_role_v1.adot](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cluster_role_v1) | resource | +| [kubernetes_namespace_v1.adot](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace_v1) | resource | +| [kubernetes_role_binding_v1.adot](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_binding_v1) | resource | +| [kubernetes_role_v1.adot](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_v1) | resource | +| [aws_eks_addon_version.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_addon_version) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [addon\_config](#input\_addon\_config) | Amazon EKS Managed CoreDNS Add-on config | `any` | `{}` | no | +| [addon\_context](#input\_addon\_context) | Input configuration for the addon |
object({
aws_caller_identity_account_id = string
aws_caller_identity_arn = string
aws_eks_cluster_endpoint = string
aws_partition_id = string
aws_region_name = string
eks_cluster_id = string
eks_oidc_issuer_url = string
eks_oidc_provider_arn = string
irsa_iam_role_path = string
tags = map(string)
})
| n/a | yes | +| [enable\_amazon\_eks\_adot](#input\_enable\_amazon\_eks\_adot) | Enable Amazon EKS ADOT add-on | `bool` | `true` | no | +| [enable\_opentelemetry\_operator](#input\_enable\_opentelemetry\_operator) | Enable opentelemetry operator addon | `bool` | `false` | no | +| [helm\_config](#input\_helm\_config) | Helm provider config for ADOT Operator AddOn | `any` | `{}` | no | + +## Outputs + +No outputs. + diff --git a/modules/core/opentelemetry-operator/locals.tf b/modules/core/opentelemetry-operator/locals.tf new file mode 100644 index 0000000..d84b7d0 --- /dev/null +++ b/modules/core/opentelemetry-operator/locals.tf @@ -0,0 +1,25 @@ +locals { + name = "adot" + eks_addon_role_name = "eks:addon-manager" + eks_addon_clusterrole_name = "eks:addon-manager-otel" + addon_namespace = "opentelemetry-operator-system" + + create_namespace = var.enable_opentelemetry_operator ? true : try(var.helm_config.create_namespace, true) + namespace = local.create_namespace ? kubernetes_namespace_v1.adot[0].metadata[0].name : try(var.helm_config.namespace, local.addon_namespace) + + default_helm_config = { + name = "opentelemetry" + repository = "https://open-telemetry.github.io/opentelemetry-helm-charts" + chart = "opentelemetry-operator" + version = "0.8.2" + namespace = local.namespace + timeout = "1200" + description = "ADOT Operator helm chart" + values = [] + } + + helm_config = merge( + local.default_helm_config, + var.helm_config + ) +} diff --git a/modules/core/opentelemetry-operator/main.tf b/modules/core/opentelemetry-operator/main.tf new file mode 100644 index 0000000..314e0d0 --- /dev/null +++ b/modules/core/opentelemetry-operator/main.tf @@ -0,0 +1,297 @@ +module "cert_manager" { + source = "github.com/aws-ia/terraform-aws-eks-blueprints/modules/kubernetes-addons/cert-manager" + + helm_config = { version = "v1.8.2" } + addon_context = var.addon_context +} + +resource "kubernetes_namespace_v1" "adot" { + count = local.create_namespace ? 1 : 0 + + metadata { + # If using EKS addon, namespace must be "opentelemetry-operator-system" + name = var.enable_amazon_eks_adot ? local.addon_namespace : try(var.helm_config.namespace, local.addon_namespace) + + labels = { + # Prerequisite for EKS addon + "control-plane" = "controller-manager" + } + } +} + +data "aws_eks_addon_version" "this" { + count = var.enable_amazon_eks_adot ? 1 : 0 + + addon_name = local.name + # Need to allow both config routes - for managed and self-managed configs + kubernetes_version = try(var.addon_config.kubernetes_version, var.helm_config.kubernetes_version) + most_recent = try(var.addon_config.most_recent, var.helm_config.most_recent, true) +} + +resource "aws_eks_addon" "adot" { + count = var.enable_amazon_eks_adot ? 1 : 0 + + cluster_name = var.addon_context.eks_cluster_id + addon_name = local.name + addon_version = try(var.addon_config.addon_version, data.aws_eks_addon_version.this[0].version) + resolve_conflicts = try(var.addon_config.resolve_conflicts, "OVERWRITE") + service_account_role_arn = try(var.addon_config.service_account_role_arn, null) + preserve = try(var.addon_config.preserve, true) + + tags = merge( + var.addon_context.tags, + try(var.addon_config.tags, {}), + # implicit dependency with roles + { + RoleVersion = try(kubernetes_role_v1.adot[0].metadata[0].resource_version, ""), + ClusterRoleVersion = try(kubernetes_cluster_role_v1.adot[0].metadata[0].resource_version, "") + } + ) + + depends_on = [module.cert_manager] +} + +resource "kubernetes_role_v1" "adot" { + count = var.enable_amazon_eks_adot ? 1 : 0 + + metadata { + name = local.eks_addon_role_name + namespace = kubernetes_namespace_v1.adot[0].metadata[0].name + } + + rule { + api_groups = [""] + resources = ["serviceaccounts"] + resource_names = ["opentelemetry-operator-controller-manager"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["rbac.authorization.k8s.io"] + resources = ["roles"] + resource_names = ["opentelemetry-operator-leader-election-role"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["rbac.authorization.k8s.io"] + resources = ["rolebindings"] + resource_names = ["opentelemetry-operator-leader-election-rolebinding"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = [""] + resources = ["services"] + resource_names = ["opentelemetry-operator-controller-manager-metrics-service", "opentelemetry-operator-webhook-service"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["apps"] + resources = ["deployments"] + resource_names = ["opentelemetry-operator-controller-manager"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["cert-manager.io"] + resources = ["certificates", "issuers"] + resource_names = ["opentelemetry-operator-serving-cert", "opentelemetry-operator-selfsigned-issuer"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = [""] + resources = ["configmaps"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = [""] + resources = ["configmaps/status"] + verbs = ["get", "update", "patch"] + } + rule { + api_groups = [""] + resources = ["events"] + verbs = ["create", "patch"] + } + rule { + api_groups = [""] + resources = ["pods"] + verbs = ["list"] + } +} + +resource "kubernetes_role_binding_v1" "adot" { + count = var.enable_amazon_eks_adot ? 1 : 0 + + metadata { + name = local.eks_addon_role_name + namespace = kubernetes_namespace_v1.adot[0].metadata[0].name + } + + subject { + kind = "User" + name = local.eks_addon_role_name + api_group = "rbac.authorization.k8s.io" + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "Role" + name = local.eks_addon_role_name + } +} + +resource "kubernetes_cluster_role_v1" "adot" { + count = var.enable_amazon_eks_adot ? 1 : 0 + + metadata { + name = local.eks_addon_clusterrole_name + } + + rule { + api_groups = ["apiextensions.k8s.io"] + resources = ["customresourcedefinitions"] + resource_names = ["opentelemetrycollectors.opentelemetry.io", "instrumentations.opentelemetry.io"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = [""] + resources = ["namespaces"] + resource_names = [kubernetes_namespace_v1.adot[0].metadata[0].name] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["rbac.authorization.k8s.io"] + resources = ["clusterroles"] + resource_names = ["opentelemetry-operator-manager-role", "opentelemetry-operator-metrics-reader", "opentelemetry-operator-proxy-role"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["rbac.authorization.k8s.io"] + resources = ["clusterrolebindings"] + resource_names = ["opentelemetry-operator-manager-rolebinding", "opentelemetry-operator-proxy-rolebinding"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["admissionregistration.k8s.io"] + resources = ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"] + resource_names = ["opentelemetry-operator-mutating-webhook-configuration", "opentelemetry-operator-validating-webhook-configuration"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + non_resource_urls = ["/metrics"] + verbs = ["get"] + } + rule { + api_groups = [""] + resources = ["configmaps"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = [""] + resources = ["events"] + verbs = ["create", "patch"] + } + rule { + api_groups = [""] + resources = ["namespaces"] + verbs = ["list", "watch"] + } + rule { + api_groups = [""] + resources = ["serviceaccounts"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = [""] + resources = ["services"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["apps"] + resources = ["daemonsets"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["apps"] + resources = ["deployments"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["apps"] + resources = ["replicasets"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["apps"] + resources = ["statefulsets"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["autoscaling"] + resources = ["horizontalpodautoscalers"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["coordination.k8s.io"] + resources = ["leases"] + verbs = ["create", "get", "list", "update"] + } + rule { + api_groups = ["opentelemetry.io"] + resources = ["opentelemetrycollectors"] + verbs = ["create", "delete", "get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["opentelemetry.io"] + resources = ["opentelemetrycollectors/finalizers"] + verbs = ["get", "patch", "update"] + } + rule { + api_groups = ["opentelemetry.io"] + resources = ["opentelemetrycollectors/status"] + verbs = ["get", "patch", "update"] + } + rule { + api_groups = ["opentelemetry.io"] + resources = ["instrumentations"] + verbs = ["get", "list", "patch", "update", "watch"] + } + rule { + api_groups = ["authentication.k8s.io"] + resources = ["tokenreviews"] + verbs = ["create"] + } + rule { + api_groups = ["authorization.k8s.io"] + resources = ["subjectaccessreviews"] + verbs = ["create"] + } +} + +resource "kubernetes_cluster_role_binding_v1" "adot" { + count = var.enable_amazon_eks_adot ? 1 : 0 + + metadata { + name = local.eks_addon_clusterrole_name + } + subject { + kind = "User" + name = local.eks_addon_role_name + api_group = "rbac.authorization.k8s.io" + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = local.eks_addon_clusterrole_name + } + +} + + +module "helm_addon" { + source = "github.com/aws-ia/terraform-aws-eks-blueprints/modules/kubernetes-addons/helm-addon" + count = var.enable_opentelemetry_operator ? 1 : 0 + + helm_config = local.helm_config + irsa_config = null + addon_context = var.addon_context + + depends_on = [module.cert_manager] +} diff --git a/modules/core/opentelemetry-operator/outputs.tf b/modules/core/opentelemetry-operator/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/core/opentelemetry-operator/variables.tf b/modules/core/opentelemetry-operator/variables.tf new file mode 100644 index 0000000..c986188 --- /dev/null +++ b/modules/core/opentelemetry-operator/variables.tf @@ -0,0 +1,40 @@ +variable "helm_config" { + description = "Helm provider config for ADOT Operator AddOn" + type = any + default = {} +} + +variable "addon_context" { + description = "Input configuration for the addon" + type = object({ + aws_caller_identity_account_id = string + aws_caller_identity_arn = string + aws_eks_cluster_endpoint = string + aws_partition_id = string + aws_region_name = string + eks_cluster_id = string + eks_oidc_issuer_url = string + kubernetes_version = string + eks_oidc_provider_arn = string + irsa_iam_role_path = string + tags = map(string) + }) +} + +variable "addon_config" { + description = "Amazon EKS Managed CoreDNS Add-on config" + type = any + default = {} +} + +variable "enable_amazon_eks_adot" { + description = "Enable Amazon EKS ADOT add-on" + type = bool + default = true +} + +variable "enable_opentelemetry_operator" { + description = "Enable opentelemetry operator addon" + type = bool + default = false +} diff --git a/modules/core/opentelemetry-operator/versions.tf b/modules/core/opentelemetry-operator/versions.tf new file mode 100644 index 0000000..d2ddf87 --- /dev/null +++ b/modules/core/opentelemetry-operator/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.72" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.10" + } + } +} diff --git a/modules/haproxy/main.tf b/modules/workloads/haproxy/main.tf similarity index 100% rename from modules/haproxy/main.tf rename to modules/workloads/haproxy/main.tf diff --git a/modules/java/main.tf b/modules/workloads/java/main.tf similarity index 100% rename from modules/java/main.tf rename to modules/workloads/java/main.tf diff --git a/variables.tf b/variables.tf index 712e724..7c18c30 100644 --- a/variables.tf +++ b/variables.tf @@ -14,3 +14,27 @@ variable "aws_region" { description = "AWS Region" type = string } + +variable "enable_amazon_eks_adot" { + type = bool + default = true +} + + +variable "irsa_iam_role_path" { + description = "IAM role path for IRSA roles" + type = string + default = "/" +} + +variable "irsa_iam_permissions_boundary" { + description = "IAM permissions boundary for IRSA roles" + type = string + default = "" +} + +variable "tags" { + description = "Additional tags (e.g. `map('BusinessUnit`,`XYZ`)" + type = map(string) + default = {} +}