> For the complete documentation index, see [llms.txt](https://docs.nected.ai/nected-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nected.ai/nected-docs/self-hosting/new-installation/terraform/azure.md).

# Azure

This guide explains how to deploy the full **Nected Platform** on **Microsoft Azure** using Terraform. The Terraform configuration provisions networking, an AKS cluster, databases, caching, logging infrastructure, DNS, SSL certificates, and all runtime services required for a production-grade Nected setup. Every step below prepares your system for a deterministic and reproducible deployment.

#### Prerequisites

Install the following tools on your machine and verify that each one runs correctly.

| Requirement | Version | Purpose                                                            |
| ----------- | ------- | ------------------------------------------------------------------ |
| Terraform   | ≥ 1.6   | Infrastructure as Code (IaC) tool for provisioning Azure resources |
| Azure CLI   | ≥ 2.60  | Command-line tool for interacting with Azure services              |
| kubectl     | Latest  | Kubernetes command-line tool for cluster management                |
| Helm        | Latest  | Kubernetes package manager for deploying applications              |
| Git         | Latest  | Version control system for cloning the repository                  |

Terraform manages Azure resources, the Azure CLI handles authentication and cluster access, kubectl interacts with AKS, and Helm deploys Nected’s workloads.

**Azure Resources & Nected license key**

To successfully deploy the infrastructure, ensure you have:

* **An active Azure Subscription** and its **Subscription ID**
* **One Azure DNS Hosted Zone**, which will be used for:
  * Creating CNAME records for all Nected services
  * Adding DNS entries required for SSL certificate validation
  * **Important:** To issue an SSL certificate using Cert-Manager, the domain must have a public DNS zone for verification.
* **One Azure Resource Group** where the entire infrastructure will be created.
  * The **Hosted Zone** can be in the *same* Resource Group or a *different* one.
* **Nected License Key** required for installing the full Nected service
  * Remove it from `terraform.tfvars` if you want to use the free & limited version

#### Authentication

Authenticate your environment with Azure.

Login:

```bash
az login
```

Select the correct subscription:

```bash
az account set --subscription "<YOUR_SUBSCRIPTION_ID>"
```

Confirm your context:

```bash
az account show
```

Terraform uses these credentials to communicate with Azure Resource Manager APIs.\
If authentication fails, no resource can be created. Always verify this step before proceeding.

#### Repository Setup and Installation Steps

Clone the Terraform deployment repository from GitHub. These files contain all resource definitions, Terraform modules, and environment-specific variables.

Clone the repository:

```bash
git clone https://github.com/nected/nected-terraform.git
```

Move into the project directory:

```bash
cd nected-terraform
```

Spend a moment reviewing the folder layout. Understanding which module controls which resource helps you troubleshoot or extend the deployment later.

#### Configuration

You must create a `terraform.tfvars` file to supply the environment-specific values. Terraform loads this file automatically and merges it into your configuration.

**Example `terraform.tfvars`**

```hcl
# Prerequisites
subscription_id     = "<YOUR_SUBSCRIPTION_ID>"
resource_group_name = "<YOUR_RESOURCE_GROUP>"
base_domain         = "<YOUR_BASE_DOMAIN>"

# Set false if base_domain hosted zone is not available
# If az_hosted_zone = false
#  - Manual DNS mapping required post deployement
#  - Required key_vault_name for SSL
az_hosted_zone      = true

# set hosted_zone_rg if base_domain hosted zone is in different resource group
# default: "null" and expect hosted zone in resource_group_name
# hosted_zone_rg    = "<HOSTED_ZONE_RESOURCE_GROUP>"

# SSL certificates, provide vault name & certificate name
# default: "null" and generate using Let's Encrypt
# key_vault_name = "<KEY_VAULT_NAME>"
# key_vault_certificate_name = "<KEY_VAULT_CERTIFICATE_NAME>"

# Nected License (uncomment to use paid version)
# default: free key with limited usage
# nected_pre_shared_key = "<NECTED_LICENSE_KEY>"

# Project Information
project             = "nected"
environment         = "dev"

# Network Configuration
vnet_address_space = "10.50.0.0/16"

# AKS Configuration
kubernetes_version = "1.33"
aks_node_count     = 2
aks_min_node_count = 2
aks_max_node_count = 4
aks_vm_size        = "Standard_D4s_v6"

# Application Gateway private ip
# default public ip
agic_internal = false

# PostgreSQL
pg_version      = 17
pg_admin_user   = "psqladmin"
pg_admin_passwd = "<password>"
pg_sku_name     = "GP_Standard_D4ds_v5"
pg_disk_size    = 262144       # 256 GB
pg_backup_retention = 7

# Redis (use Azure Redis Cache)
# default redis via datastore helm chart
use_managed_redis = false

# Elasticsearch
elasticsearch_version        = "8.12.0"
elasticsearch_vm_size        = "Standard_D2ds_v4"
elasticsearch_admin_username = "elastic"
elasticsearch_admin_password = "<password>"

# Application variables
# Chart versions
nected_chart_version = "0.4.35"

# Domain Configuration
ui_domain_prefix      = "ui"
backend_domain_prefix = "backend"
router_domain_prefix  = "router"

# Serivices env configs
nected_env_overrides = {
  "nalanda" = {
    # notifications setiings
    SEND_EMAIL         = "false"
    EMAIL_PROVIDER     = "smtp"
    SENDER_EMAIL       = ""
    SENDER_NAME        = ""
    EMAIL_INSECURE_TLS = ""
    EMAIL_HOST         = ""
    EMAIL_PORT         = ""
    EMAIL_USERNAME     = ""
    EMAIL_PASSWORD     = ""

    # to restrict signup/invite user email domain
    SIGNUP_DOMAINS = ""
  }
}

# Console Access
# username is always an email and password should be alphanumeric at least 8 characters
console_signup_domains = ""
console_user_email    = "<<user email>>"
console_user_password = "<<password>>"
```

> ⚠️ Do not commit terraform.tfvars to version control. Add it to .gitignore.

#### Remote Terraform State (Optional)

If you want to use **remote Terraform state** in **Azure Blob Storage**, create the following:

1. **Azure Storage Account**
2. **Blob Container** inside the storage account
3. Update your `backend.tf` file with the correct values.

Example `backend.tf` configuration:

```hcl
terraform {
  backend "azurerm" {
    resource_group_name  = "<RESOURCE_GROUP>"
    storage_account_name = "<STORAGE_ACCOUNT_NAME>"
    container_name       = "<CONTAINER_NAME>"
    key                  = "<TFSTATE_FILE_NAME>.tfstate"
  }
}
```

Ensure these resources are created **before** running `terraform init`.

Each variable directly influences the resources deployed on Azure.\
For example:

* `vnet_address_space` defines the private network boundary for all Nected services.
* `aks_vm_size` decides the compute capacity of each AKS worker node.
* `base_domain` combined with domain prefixes produces all public URLs for UI, API, and router.
* `nected_pre_shared_key` activates your Nected license inside the cluster.

Always verify that your values comply with Azure’s constraints—especially VM SKUs, PostgreSQL SKUs, and address spaces.

#### Deployment Steps

Execute the commands below from inside the repository directory. Terraform will detect modules, providers, and variable files automatically.

**Step 1: Initialize the Terraform Workspace**

```bash
terraform init
```

Terraform downloads provider plugins, prepares the backend, and scans the modules.\
This process ensures all components needed for deployment are present.

**Step 2: Validate the Configuration**

```bash
terraform validate
```

This step checks your file structure and ensures that Terraform syntax, variable types, and references are correct.\
It prevents many common errors before provisioning.

**Step 3: Preview the Changes**

```bash
terraform plan -out=tfplan
```

The plan lists all Azure resources Terraform will create, modify, or remove.\
Read the output carefully because Azure resources incur cost.\
This step provides visibility before making irreversible changes.

**Step 4: Apply the Deployment**

```bash
terraform apply tfplan
```

Terraform provisions:

* The Azure Resource Group
* The Virtual Network and all subnets
* The AKS cluster using your specified VM size and scaling configuration
* A PostgreSQL Flexible Server
* A Redis Cache instance
* An Elasticsearch VM-based cluster
* DNS entries, routing rules, and SSL certificates
* Helm deployments that install Nected services inside AKS

Azure typically takes several minutes to create managed services.\
The AKS cluster and PostgreSQL server are usually the slowest resources.

{% hint style="warning" %}
The Deployment Process might take \~25 minutes to complete
{% endhint %}

#### Post-Deployment Tasks

Once Terraform finishes, retrieve the outputs:

```bash
terraform output
```

This returns critical information for operations:

* The AKS cluster name
* Resource group names
* Fully qualified domain names for all Nected services
* PostgreSQL and Redis endpoints
* Elasticsearch connection details
* Admin console credentials
* kubeconfig contents (if enabled)

Use these values to connect to your environment.

**Access AKS**

Fetch Kubernetes credentials:

```bash
az aks get-credentials \
  --resource-group <resource_group> \
  --name <aks_name>
```

Check cluster access:

```bash
kubectl get nodes
```

You should see your AKS worker node pool listed.\
If access fails, confirm that your Azure user has the correct RBAC role.

**Alternative kubeconfig extraction**

If you want to use the kubeconfig file generated by Terraform:

```bash
terraform output -raw kube_config > /tmp/kubeconfig
export KUBECONFIG=/tmp/kubeconfig
```

This method is useful in CI systems or non-interactive environments.

**Retrieving encryption key**

Ensure the following secret is retrieved and backed up:

```bash
kubectl get secret encryption-at-rest-secret -o yaml > encryption-at-rest-secret
```

#### Upgrade Nected apps

**Upgrade using Terraform**

Update `terraform.tfvars`:

```hcl
nected_chart_version = "<version-number>"
```

Apply the upgrade:

```bash
terraform apply
```

**Upgrade using Helm**

```bash
helm get values nected > nected-values.yaml
helm upgrade -i nected nected/nected -f nected-values.yaml --version <version-number>
```

#### Destroying the Environment

Remove all resources created by Terraform:

```bash
terraform destroy
```

Terraform deletes the VNet, AKS, databases, Redis, Elasticsearch, and DNS records.\
Azure may take a few minutes to release large managed services.\
Run this only when the environment is no longer required.

#### Accessing the Nected Application

Your deployed instance becomes available at:

* UI: `https://<ui_domain_prefix>.<base_domain>`
* Backend: `https://<backend_domain_prefix>.<base_domain>`
* Router: `https://<router_domain_prefix>.<base_domain>`

Login using the credentials defined in `terraform.tfvars`:

* Email: `console_user_email`
* Password: `console_user_password`

Once logged in, verify UI connectivity, API responsiveness, webhook endpoints, and rule execution.

#### Terraform Azure: Public Domain vs Private Domain Configuration

This section explains how domain configuration behaves in Azure-based Terraform deployments and what changes when you deploy Nected using a public domain versus a private domain.

**Public Domain Deployment (Azure DNS / Public Access)**

When you deploy Nected on a **public domain**, Terraform provisions the Application Gateway and related resources for internet-facing access.

Key characteristics:

* Application endpoints are exposed using the **public IP of the Azure Application Gateway**
* DNS records are created in **Azure DNS (Public Zone)**
* If `az_hosted_zone = false`, manual DNS mapping is required after deployment

Required `terraform.tfvars` configuration:

* Set `agic_internal = false`

This is the default configuration. Use this setup when Nected must be accessible over the public internet using a public IP address.

**Private Domain Deployment (VNet / Internal Access Only)**

When you deploy Nected on a **private domain**, all application traffic remains within your Azure virtual network.

Key characteristics:

* Application endpoints use the **private IP of the Azure Application Gateway**
* DNS records are created in **Azure DNS Private Zones** mapped to private IP addresses
* Access is limited to internal networks, VPN, or private connectivity

Required `terraform.tfvars` configuration:

* Set `agic_internal = true`

Use this configuration for regulated environments or deployments that require network isolation and restricted access.

***

**Summary**

| Deployment Type | Public Domain | Private Domain |
| --------------- | ------------- | -------------- |
| Internet Access | Yes           | No             |
| Domain Mapping  | Public IP     | Private IP     |

Select the deployment model based on your access and security requirements before running `terraform apply`.

#### Terraform Azure: Managed Redis vs Redis in AKS

This section explains how Redis is provisioned in Azure-based Terraform deployments and when to use managed Azure Redis Cache versus Redis deployed inside AKS.

**Default: Redis Deployed in AKS (Recommended)**

By default, Terraform deploys Redis using the **Nected datastore Helm chart** inside the AKS cluster.

Key characteristics:

* Redis runs as a Kubernetes workload inside AKS
* Used only for **caching**, not for persistent storage
* Faster overall deployment time
* No additional network latency between AKS and Redis

This setup is recommended for most deployments.

**Optional: Azure Managed Redis Cache**

If you choose to use **Azure Redis Cache**, Terraform provisions a managed Redis instance instead of deploying Redis in AKS.

Required `terraform.tfvars` configuration:

* Set `use_managed_redis = true`

Important considerations:

* Adds approximately **20 minutes** to the deployment time
* Introduces additional network latency compared to in-cluster Redis
* Suitable when organizational policies mandate the use of managed cloud services

Use this option only when managed Redis is explicitly required by your infrastructure or compliance standards.

#### JMeter Load testing

Update the following placeholders in the configuration:

* \[RULE\_ID]
* \[RULE\_DOMAIN]
* \[RULE\_DOMAIN\_IP] (optional)
* \[NECTED\_API\_KEY]
* \[RULE\_PAYLOAD] {"environment": "production", "params": {"a": 1}}

**Run JMeter**

```bash
kubectl create ns jmeter
kubectl apply -f jmeter-test/jmeter-k8s.yaml
kubectl -n jmeter get pods
kubectl -n jmeter logs -f jmeter-master
```

**Retrieve JMeter Report**

To copy the generated JMeter report after the test completes:

```bash
kubectl -n jmeter cp jmeter-master:/test/output .
```

> Default configuration supports approximately 25 RPS.

#### Support

For deployment questions or feedback:

* Read our documentation
* Join the conversation on [LinkedIn](https://www.linkedin.com/company/nected-ai/)
* Email: [**support@nected.ai**](mailto:support@nected.ai)
