Integrating Existing Helm Charts with Terraform for Kubernetes Management

Rafael Medeiros
4 min readSep 11, 2024

--

In this blog post, we’re going to explore how to add existing Helm charts to your Terraform setup. Whether you’re looking to streamline your deployments or just curious about how to integrate these tools effectively. Let’s get started!

The Terraform Code

We need to first have the helm provider, Please adjust it so that you can connect to your own K8s cluster:

terraform.tf

provider "helm" {
kubernetes {
host = eks_cluster_endpoint
cluster_ca_certificate = base64decode(eks_cluster_certificate_authority_data)
config_path = ""
config_context = ""

exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "aws"
args = ["eks", "get-token", "--cluster-name", eks_cluster_name]
}
}
}

In this post, we will be using metrics server as an example. You can set up its configuration like the following:

main.tf

resource "helm_release" "metrics_server" {
name = "metrics-server"
namespace = "kube-system"
repository = "https://kubernetes-sigs.github.io/metrics-server"
chart = "metrics-server"
version = "3.12.1"
}

The chart repository can be found here:

https://kubernetes-sigs.github.io/metrics-server/

Assuming that you have deployed the resource before, as soon as you run terraform plan, you immediately get the following issue:

This will not only happen with the deployment but with all the resources deployed by the helm chart.

If you are migrating deployment away from ArgoCD back to helm, this will also throw the same error!

So, How do I Fix That?

Terraform is complaining that 2 annotations are missing in that specific resource, but it will complain about all resources if you fix them one by one. There is no straightforward way to find all the resources deployed by a helm chart using Kubectl only, but here is what has worked for me:

kubectl get sa,role,rolebinding,clusterrole,clusterrolebindings,deployments,daemonsets -n kube-system | grep -i metrics-server
  • The first part of the pipe lists resources such as service accounts, roles, role bindings, and deployments in the kube-system namespace (where I deployed this metric-server chart before)
  • The second part filters the results to show only those related to metrics-server, it will be something like this:

From this output, we know that we need to update:

  • Service Account: 1
  • RoleBinding: 1
  • ClusterRole: 2
  • ClusterRoleBinding: 2
  • Deployment: 1

You need to patch all of these, otherwise they will keep throwing that error:

For each service, edit them and add the annotations required in the error message:

kubectl edit sa metrics-server -n kube-system
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
meta.helm.sh/release-name: metrics-server #<---ADD THIS
meta.helm.sh/release-namespace: kube-system #<---ADD THIS

After patching all the resources and run terraform apply again, there is one more that you can’t easily find with GUI tools like Lens or k8s dashboard, which is the API service:

This happens because your v1beta.metrics.k8s.io API created by metrics-server chart has none of these requested annotations as well, let’s first find them:

kubectl api-resources

If you see the output, we can actually confirm that the resource is there:

So, let’s edit the resource and add the annotations:

kubectl edit apiservice v1beta1.metrics.k8s.io

Again, the same two lines:

    meta.helm.sh/release-name: metrics-server
meta.helm.sh/release-namespace: kube-system

After that, you can finally run terraform apply and enjoy a happy ending:

Conclusion

With the steps we’ve covered, you should now have a good understanding on how to integrate Helm charts into your Terraform projects.

Happy Terraforming and keep studying!
Follow me on Linkedin

Update:

I came up with the following script that helped me to fill in the annotations quickly:

#!/bin/bash


NAMESPACE=kube-system


# Define the annotations
ANNOTATIONS=(
"meta.helm.sh/release-name=metrics-server"
"meta.helm.sh/release-namespace=kube-system"
)

# Define the resources to annotate
RESOURCES=(
"serviceaccount/metrics-server"
"rolebinding/metrics-server-auth-reader"
"clusterrole/system:metrics-server"
"clusterrole/system:metrics-server-aggregated-reader"
"clusterrolebinding/metrics-server:system:auth-delegator"
"clusterrolebinding/system:metrics-server"
"deployment/metrics-server"
)

# Annotate each resource
for RESOURCE in "${RESOURCES[@]}"; do
kubectl annotate $RESOURCE ${ANNOTATIONS[@]} -n $NAMESPACE
done

So, basically you get the output from:

kubectl get csidriver,sa,role,rolebinding,clusterrole,clusterrolebindings,deployments,daemonsets -n kube-system | grep -i metrics-server

and add to the resources block:

Finally, you get your resources annotated:

Much better, eh?

--

--

Rafael Medeiros
Rafael Medeiros

Written by Rafael Medeiros

DevOps Engineer | CNCF Kubestronaut | 3x Azure | Terraform Fanatic | Another IT Professional willing to help the community

No responses yet