Skip to main content

Azure Automated Deployment

Fully automated production deployment on Microsoft Azure using Terraform. Already comfortable writing your own Terraform? See Using this as a Terraform Registry module below instead.


Overview​

The Azure deployment automatically provisions:

  • AKS Cluster — Kubernetes cluster for container orchestration
  • PostgreSQL Flexible Server — Managed PostgreSQL database
  • nginx Ingress + Azure Load Balancer — External access with SSL termination
  • Azure DNS Records — Automatic domain configuration
  • SSL Certificates — Let's Encrypt certificates via cert-manager (DNS-01 challenge against Azure DNS)

Deployment Time: 30-45 minutes


Prerequisites​

Domain & DNS required

Ekai on Azure needs a domain or subdomain you control (for example ekai.yourcompany.com) to host the client-facing web app.

You must own the domain and be able to configure its nameservers at your registrar or parent DNS zone. You might need your domain admin to add the nameservers after deployment completes.

During install you enter this as the DNS zone; Terraform creates an Azure DNS zone and prints Azure's nameservers. After success, delegate the subdomain. Like GCP (and unlike AWS), the wildcard TLS certificate is issued by cert-manager via a Let's Encrypt DNS-01 challenge, which doesn't block the deploy itself — until delegation propagates (minutes to hours), the URL may not resolve and HTTPS may fail or show a certificate warning.

Before starting, ensure you have:

  • A domain (or subdomain) you own (you may need your domain admin to add nameservers after deployment)
  • Azure subscription with billing enabled
  • Azure CLI installed and authenticated (az login)
  • terraform CLI installed (v1.7+)
  • kubectl CLI installed
  • jq and dig utilities installed

See Prerequisites for detailed installation instructions.


Required Azure Permissions​

Unlike the long-lived identity Terraform ultimately runs as, the account you authenticate with to start the install only needs enough access to register a handful of resource providers and create a resource group, a Terraform state storage account, and a scoped deployer Service Principal — it never touches AKS/PostgreSQL/the VNet/etc. directly. Azure's RBAC catalog has no built-in role narrower than Owner that can both create arbitrary resources and create role assignments (granting the deployer SP its own, narrower permissions), so in practice grant the authenticating account:

Permissions neededPurpose
Owner, subscription scope (or Contributor + User Access Administrator together)Register required Azure resource providers; create the resource group and Terraform state storage account; create the scoped deployer Service Principal and grant it Owner on that one resource group
Key Vault purge role, subscription scope (optional)Only granted if you opt into purge_vault_on_destroy = true for a fast destroy/reapply test loop — never needed for a normal deploy

The deployer Service Principal Terraform actually runs as is created and scoped for you — Owner on just the one resource group this deploy creates, nothing subscription-wide — you never author or review its permissions by hand. See terraform-azurerm-ekai's PERMISSIONS.md for the full reasoning.


Installation Steps​

Step 1: Start from the licensing portal​

Go to licensing.ekai.ai and follow the install instructions shown there.

Step 2: Authenticate with ekai​

  • Browser opens for OAuth login
  • Log in with your ekai credentials
  • Authorize the deployment
  • Return to terminal

Step 3: Select Cloud Deployment​

Choose deployment type:
1) Local (Docker)
2) Cloud

Enter your choice [1-2]: 2

Step 4: Select Azure Provider​

Which cloud provider?
1) AWS
2) Azure
3) GCP
4) Other (manual)

Enter choice [1-4]: 2

Step 5: Dependency & Credential Check​

The install verifies the Azure CLI is authenticated and checks for the common cloud tools (terraform, kubectl, jq, dig), offering to auto-install whatever's missing:

Checking Azure credentials…
✓ Azure CLI found: 2.65.0
✓ Azure credentials valid
Subscription Id
-------------- ------------------------------------
My Subscription 00000000-0000-0000-0000-000000000000

Checking dependencies...
✓ terraform found
✓ kubectl found
✗ jq not found

Install missing tools? [Y/n]: Y

Step 6: Configure Deployment​

Provide the following information when prompted:

Azure Subscription ID​

Everything this creates (resource group, AKS, PostgreSQL, ...) lives inside this one subscription.

Azure subscription ID: 00000000-0000-0000-0000-000000000000

Azure Region​

Azure region [northeurope]: northeurope

Environment Name​

The environment name becomes part of every Azure resource this creates. It's more tightly constrained than AWS/GCP's: 11 characters or fewer, lowercase letters and numbers only — the tightest constraint is the Terraform state storage account name (ekaiterraform<env>), since Azure storage account names allow only lowercase letters/digits and cap at 24 characters total.

Environment name: ekaiprod

DNS Zone​

You must own this domain (or subdomain). Terraform creates the Azure DNS zone for it as part of the deploy — you'll delegate it to Azure's nameservers at your registrar once the install finishes.

DNS zone (e.g. example.com): ekai.yourcompany.com
Delegation happens after deploy

Like GCP, the installer doesn't pause mid-install waiting for DNS — it prints the nameservers once the deploy finishes. See How to delegate a subdomain below.

ACME Email​

Used by cert-manager to register a Let's Encrypt ACME account and issue the wildcard TLS certificate — registration fails without a real address.

ACME email: you@yourcompany.com

Step 7: Transactional Email (Optional)​

By default, ekai's own licensing portal sends app emails (invites, notifications) for you — no setup needed. You can optionally provide your own SendGrid or AWS SES credentials instead:

Transactional email (invites, notifications sent by the app)
Provide your own SendGrid or AWS SES credentials for this? [y/N]: N

Step 8: Terraform Execution​

Once configuration is complete, the install automatically:

  1. Downloads the terraform-azurerm-ekai repository
  2. Registers required Azure resource providers
  3. Creates the resource group and the scoped deployer Service Principal (Owner, scoped to that one resource group)
  4. Provisions infrastructure:
    • AKS cluster
    • PostgreSQL Flexible Server
    • Azure DNS zone
    • nginx Ingress behind an Azure Load Balancer
  5. Deploys ekai via Helm charts, including ArgoCD and cert-manager

Step 9: Save Credentials​

Generated keys and secrets are saved under .self-deploy/. Copy them into your password manager, then delete the local files.

Security

These files provide administrative access to your deployment. Store them securely and remove them from disk after saving.


Expected Timeline​

PhaseDuration
Dependency & credential check2-5 minutes
OAuth authentication1-2 minutes
Infrastructure provisioning15-20 minutes
Application deployment3-5 minutes
Total30-45 minutes (DNS delegation/propagation happens afterward, in your own time)
Progress Monitoring

The install displays real-time progress. Infrastructure provisioning is the longest automatic phase as Azure creates compute resources, databases, and networking components.


Deployment Success​

Upon successful completion, you will see output similar to:

✓ Deploy complete for env=ekaiprod.
Generated credentials are saved at: ./terraform-azurerm-ekai/.self-deploy/ekaiprod-generated-secrets.txt — copy into your password manager, then delete the file.

✓ Ekai is running!

Ekai: https://portal.yourcompany.com

Before ekai.yourcompany.com works: delegate it to these nameservers at your domain registrar
(or parent DNS zone) — add an NS record for ekai.yourcompany.com pointing at each:
ns1-01.azure-dns.com.
ns2-01.azure-dns.net.
ns3-01.azure-dns.org.
ns4-01.azure-dns.info.
DNS propagation can take anywhere from a few minutes to a few hours. Until it's
done, the wildcard TLS cert can't be issued either — the URL above will fail or
show a certificate warning in the meantime.

ArgoCD: https://argocd.ekai.yourcompany.com
(user: admin, password:
cd terraform-azurerm-ekai/examples/self-deploy/root
terraform output -raw argocd_admin_password_plaintext)
Service Startup Time

After successful deployment, it can take up to 15 minutes for all services to come online. Please wait before accessing or using the web app.

How to delegate a subdomain​

Steps at your registrar (or parent DNS)
  1. Log in to the DNS management panel for your root domain (where primary DNS is hosted).
  2. Create new records of type NS.
  3. In the name field, enter only the subdomain label (for example ekai if the zone is ekai.yourcompany.com).
  4. In the value/destination fields, add the Azure nameservers from the installer's output (usually four NS records).
  5. Save the records and wait for DNS propagation (often minutes to a few hours).

Resume Interrupted Deployments​

If deployment fails or is interrupted (network issues, timeout, etc.), re-run the install:

Step 1: Start the install again​

Return to licensing.ekai.ai and follow the install instructions again.

Step 2: Choose to Resume​

Found previous run artifacts for Azure env 'ekaiprod'.
Retry that deploy instead of starting a new one? [y/N]: y

Step 3: Automatic Resume​

  • Skips configuration prompts already answered
  • Reads settings from the previous run's tfvars
  • The resource group and any already-created resources are reused, not recreated
  • Continues from last Terraform state
Idempotent Operations

Terraform operations are idempotent — re-running is safe and will only create missing resources, not duplicate existing ones.


Accessing the Kubernetes Cluster​

Connect to your AKS cluster:

az aks get-credentials --resource-group ekai-saas-ekaiprod --name ekai-saas-aks-ekaiprod

Verify connection:

kubectl get pods -n ekai-saas
kubectl get svc -n ekai-saas

SSL Certificate Provisioning​

TLS certificates are provisioned via cert-manager using a Let's Encrypt DNS-01 challenge against the Azure DNS zone created during the deploy — this completes after DNS delegation propagates, not during the apply itself.

Check certificate status:

kubectl get certificate -n cert-manager
kubectl describe certificate <tls_name output> -n cert-manager

Troubleshooting​

Tool Installation Failures​

If auto-installation fails, install the required tools using the official documentation in Prerequisites, then start the install again from licensing.ekai.ai.

Terraform Errors​

Check Terraform state:

cd .self-deploy/terraform-azurerm-ekai/
terraform state list

Azure Permission Errors​

Confirm your current identity, and re-check it holds Owner (or Contributor + User Access Administrator) at subscription scope:

az account show --query user
az role assignment list --assignee <your-object-id> --query "[].roleDefinitionName"

There's no automated pre-flight permission check for Azure yet (unlike AWS/GCP) — self-deploy.sh fails clearly on its own if the authenticated identity lacks subscription-level rights, naming the resource group and principal it couldn't grant a role to.

Stale Azure CLI Identity​

If a re-run fails partway with "Insufficient privileges," your local az CLI is almost always still authenticated as a previous run's scoped deployer Service Principal (it only has Owner on that one run's resource group). Check with:

az account show --query user.type   # "servicePrincipal" instead of "user" means this
az login # re-authenticate with your own admin account

Availability Zone / Quota Errors​

AvailabilityZoneNotSupported or InsufficientVCPUQuota mean the target region/subscription combination doesn't support the requested availability zones, or the subscription's "Total Regional vCPUs" quota is too low (common on smaller or trial subscriptions). See terraform-azurerm-ekai's README Troubleshooting section for the exact tfvars knobs to adjust.

SSL Certificate Issues​

Since issuance happens asynchronously after the apply, a stuck certificate almost always means DNS delegation isn't propagated yet:

dig NS ekai.yourcompany.com
kubectl logs -n cert-manager deployment/cert-manager

For more detailed troubleshooting, see Troubleshooting Guide.


Using this as a Terraform Registry module​

The installer above runs terraform-azurerm-ekai's own self-deploy.sh script for you. If you'd rather write your own Terraform root module instead of using the guided installer, that repo is published directly on the Terraform Registry: registry.terraform.io/modules/ekai-ai/ekai/azurerm.

module "infra" {
source = "registry.terraform.io/ekai-ai/ekai/azurerm"
version = "~> 0.1"

subscription_id = "00000000-0000-0000-0000-000000000000"
region = "northeurope"
env_name = "acme"
# ...every variable in variables.tf
}

module "cicd" {
source = "registry.terraform.io/ekai-ai/ekai/azurerm//cicd"
version = "~> 0.1"
# ...every variable in cicd/variables.tf, plus the values module.infra
# exports that cicd needs
}

This assumes you're comfortable writing your own provider configuration and wiring the two modules together yourself — see the repo's ARCHITECTURE.md for why it's 2 separate applies and the full module/variable reference.


Next Steps​