> ## Documentation Index
> Fetch the complete documentation index at: https://docs.envzero.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Install the agent with Terraform

> Create an EKS cluster, an agent pool, an agent secret, and the env zero self-hosted Kubernetes agent from one Terraform or OpenTofu configuration.

Use one Terraform or OpenTofu configuration to create an AWS EKS cluster and install the env zero self-hosted Kubernetes agent on it. The configuration combines the [k8s-modules](https://github.com/env0/k8s-modules) `aws` module with the [env zero provider](https://registry.terraform.io/providers/env0/env0/latest/docs) and the [Helm provider](https://registry.terraform.io/providers/hashicorp/helm/latest/docs).

The configuration creates:

* A VPC, an EKS cluster with a managed node group, and the cluster autoscaler
* An agent pool and an agent secret in env zero
* The `env0-agent` Helm release, configured with [env zero hosted encrypted state](/guides/admin-guide/self-hosted-kubernetes-agent/env-zero-hosted-encrypted-state), so the cluster needs no persistent volume

To install the agent on an EKS cluster you already have, see [Use an existing EKS cluster](#use-an-existing-eks-cluster).

## Prerequisites

<Warning>
  Creating agent pools and agent secrets requires an API key with the **Edit Organization Settings** permission. Self-hosted agents are available to Enterprise customers only.
</Warning>

* Terraform 1.3.2 or later, or OpenTofu
* AWS credentials that can create a VPC, an EKS cluster, IAM roles, and Auto Scaling group settings
* AWS CLI v2 and `bash` on the machine that runs Terraform or OpenTofu. The Helm provider authenticates with `aws eks get-token`, and the `aws` module runs `aws autoscaling` commands.
* The Helm CLI, to run the chart tests in [Verify the installation](#verify-the-installation)
* An env zero [API key](/guides/admin-guide/user-role-and-team-management/api-keys), exported as environment variables:

```shell theme={null}
export ENV0_API_KEY=<YOUR_API_KEY_ID>
export ENV0_API_SECRET=<YOUR_API_KEY_SECRET>
```

## Create the configuration

Save the configuration below as `main.tf` in an empty directory.

The example pins the module to `v1.2.0`. Check the [k8s-modules releases](https://github.com/env0/k8s-modules/releases) and use the latest tag.

```hcl main.tf theme={null}
terraform {
  required_providers {
    env0 = {
      source  = "env0/env0"
      version = "~> 1.33"
    }
    helm = {
      source  = "hashicorp/helm"
      version = ">= 2.13.0, < 3.0.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

variable "region" {
  default = "us-east-1"
}

variable "cluster_name" {
  default = "env0-agent"
}

module "cluster" {
  source = "github.com/env0/k8s-modules//aws?ref=v1.2.0"

  region       = var.region
  cluster_name = var.cluster_name

  create_efs_storage = false
}

provider "env0" {}

provider "helm" {
  kubernetes {
    host                   = module.cluster.cluster_endpoint
    cluster_ca_certificate = base64decode(module.cluster.cluster_certificate_authority_data)
    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      command     = "aws"
      args        = ["eks", "get-token", "--cluster-name", module.cluster.cluster_name, "--region", var.region]
    }
  }
}

resource "env0_agent_pool" "this" {
  name = var.cluster_name
}

resource "env0_agent_secret" "this" {
  agent_id = env0_agent_pool.this.id
}

# Encrypts the deployment state and working directory before the agent uploads them.
resource "random_password" "state_encryption_key" {
  length  = 32
  special = false
}

resource "helm_release" "env0_agent" {
  repository = "https://env0.github.io/self-hosted"
  chart      = "env0-agent"
  # Latest chart: https://github.com/env0/self-hosted/releases
  version = "v5.5.6"

  # Wait for the node group and the addons, so the agent pods can start
  depends_on = [module.cluster]

  name             = "env0-agent"
  namespace        = "env0-agent"
  create_namespace = true
  timeout          = 600

  set_sensitive {
    name  = "agentAccessToken"
    value = env0_agent_secret.this.secret
  }

  set_sensitive {
    name  = "env0StateEncryptionKey"
    value = base64encode(random_password.state_encryption_key.result)
  }
}
```

<Warning>
  Pass secret Helm values, such as `agentAccessToken`, `env0StateEncryptionKey`, or cloud credentials, with `set_sensitive`. Do not put them in `values`: the Helm provider stores `values` unredacted in the `helm_release` metadata, and `terraform plan` prints that metadata when the release changes.

  The state stores the agent secret and the state encryption key. Use an encrypted remote backend for this configuration. If the encryption key changes, existing environments that use local state lose their state. See [Key rotation](/guides/admin-guide/self-hosted-kubernetes-agent/env-zero-hosted-encrypted-state#key-rotation).
</Warning>

The `aws` module accepts more inputs, such as `kubernetes_version`, `instance_types`, and `max_capacity`. See the [aws module reference](https://github.com/env0/k8s-modules#aws-module-reference).

## Apply the configuration

1. Initialize the working directory:

   ```shell theme={null}
   terraform init
   ```

2. Apply the configuration:

   ```shell theme={null}
   terraform apply
   ```

For OpenTofu, run `tofu init` and `tofu apply`.

## Verify the installation

1. Configure `kubectl` for the new cluster. If you changed `cluster_name` or `region`, use your values:

   ```shell theme={null}
   aws eks update-kubeconfig --name env0-agent --region us-east-1
   ```

2. Run the chart tests:

   ```shell theme={null}
   helm test env0-agent --namespace env0-agent --logs --timeout 1m
   ```

3. In env zero, go to **Organization Settings** > **Agents**. The agent pool named after `cluster_name` (`env0-agent` by default) shows **Active**.

## Assign the agent to a project

Add an `env0_agent_project_assignment` resource to run a project's deployments on the new agent:

```hcl main.tf theme={null}
resource "env0_agent_project_assignment" "this" {
  agent_id   = env0_agent_pool.this.id
  project_id = "<YOUR_PROJECT_ID>"
}
```

Projects without an assignment use the organization default agent. See [Running multiple self-hosted agents](/guides/admin-guide/self-hosted-kubernetes-agent/multiple-self-hosted-agents).

## Use an existing EKS cluster

To install the agent on an EKS cluster that already exists, read the cluster with a data source instead of creating it:

1. Remove the `module "cluster"` block, and the `depends_on` line from `helm_release`.

2. Add the `aws` provider to `required_providers`:

   ```hcl main.tf theme={null}
   aws = {
     source  = "hashicorp/aws"
     version = ">= 5.95.0"
   }
   ```

3. Replace the `provider "helm"` block with:

   ```hcl main.tf theme={null}
   provider "aws" {
     region = var.region
   }

   data "aws_eks_cluster" "this" {
     name = var.cluster_name
   }

   provider "helm" {
     kubernetes {
       host                   = data.aws_eks_cluster.this.endpoint
       cluster_ca_certificate = base64decode(data.aws_eks_cluster.this.certificate_authority[0].data)
       exec {
         api_version = "client.authentication.k8s.io/v1beta1"
         command     = "aws"
         args        = ["eks", "get-token", "--cluster-name", data.aws_eks_cluster.this.name, "--region", var.region]
       }
     }
   }
   ```

4. Set the `cluster_name` variable default to the name of your cluster.

The cluster must meet the [Self-Hosted Kubernetes Agent requirements](/guides/admin-guide/self-hosted-kubernetes-agent/self-hosted-kubernetes-agent#requirements). With env zero hosted encrypted state, the cluster needs no `env0-state-sc` StorageClass.

## Keep the state in your AWS account

If the deployment state and working directory must stay in your AWS account, store them on EFS instead of env zero hosted encrypted state. Make this choice before the first deployment runs on the agent.

<Warning>
  On an agent that already ran deployments, removing `env0StateEncryptionKey` loses the local state of existing environments, and Terraform re-creates their resources. Move those environments to a remote backend first. See [Key rotation](/guides/admin-guide/self-hosted-kubernetes-agent/env-zero-hosted-encrypted-state#key-rotation).
</Warning>

1. In `module "cluster"`, remove `create_efs_storage = false`. The module then creates an EFS file system, the EFS CSI driver, and the `env0-state-sc` StorageClass.
2. Remove the `random_password` resource and the `env0StateEncryptionKey` block from `helm_release`. Without the key, the agent chart creates a persistent volume claim on `env0-state-sc`.

## Next steps

* [Custom/optional configuration](/guides/admin-guide/self-hosted-kubernetes-agent/custom-optional-configuration) - Add Helm values to `helm_release.env0_agent`.
* [Authenticating the agent on AWS EKS](/guides/admin-guide/self-hosted-kubernetes-agent/authenticating-the-agent-on-aws-eks) - Give deployments an IAM role. The `oidc_provider_arn` module output feeds the IAM role trust policy.
* [Hosting deployment logs](/guides/admin-guide/self-hosted-kubernetes-agent/hosting-the-deployment-logs) - Store deployment logs in your AWS account.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.