Proxy on-prem Terraform deployment (Azure)
This guide explains how to deploy Espresso AI's Proxy Service on your Azure infrastructure with Terraform.
You can deploy either:
In a dedicated VNet that Terraform creates.
In an existing VNet that you provide.
Prerequisites
Access to an Azure subscription with permissions to create resource groups, VNets, AKS clusters, public IPs, role assignments, user-assigned managed identities, and (optionally) Key Vault, Azure DNS records, and Azure Front Door.
In the Espresso AI dashboard, go to
Proxy Onboardingand:Enter your Azure Subscription ID so we can grant ACR access for the Proxy image. We will generate a username and password for you to be able to pull the image from our ACR.
Copy your customer name.
Generate an API key for Espresso API authentication.
What this module creates
Resource group (optional) or uses your existing resource group.
VNet with a node subnet (optional) or uses your existing VNet/subnet.
AKS cluster with a system-assigned identity, Workload Identity + OIDC issuer enabled, and an autoscaling node pool.
Static public IP for ingress (placed in the AKS-managed node resource group).
ingress-nginxcontroller wired to the static public IP, with Azure LB annotations tuned for AKS Standard LB.Proxy deployment, service, and HPA in Kubernetes.
TLS for the Ingress, via either:
cert-manager + Let's Encrypt (HTTP-01 by default, DNS-01 via Azure DNS for wildcard hosts), or
a bring-your-own
kubernetes.io/tlssecret you pre-create in the proxy namespace.
Optional Azure DNS A record pointing at the ingress public IP.
Optional managed API key flow via Azure Key Vault + External Secrets Operator (federated workload identity).
Optional Azure Front Door fronting the AKS LB (recommended when clients enforce strict OCSP behavior, e.g. Snowflake's connector).
Example usage
Dedicated VNet + cert-manager (Let's Encrypt) + Azure DNS record
Existing VNet + bring-your-own TLS secret + managed API key in Key Vault
Wildcard ingress + Azure Front Door + DNS-01 wildcard cert
Use this shape when fronting Snowflake's connector (or any client with strict OCSP behavior). AFD terminates TLS for clients with a DigiCert-issued managed cert, and the LE cert on the AKS LB only secures the AFD-to-origin hop.
Argument reference
Top-level arguments
location: Required. Azure region for deployment.customer: Required. Customer identifier used in naming andAPI_URLsuffixing.resource_group_config: Optional. Setcreate = falseand supplynameto deploy into an existing resource group. Default: createsespresso-ai-proxy-rg.create_dedicated_vnet: Optional. Creates a dedicated VNet (true) or uses an existing VNet (false). Default:true.vnet_config: Optional/conditional. Used whencreate_dedicated_vnet = true.existing_vnet_config: Optional/conditional. Required whencreate_dedicated_vnet = false.aks_config: Optional. AKS cluster and node pool settings.proxy_config: Required. Proxy runtime configuration.proxy_api_key_value: Optional/conditional, sensitive. Required whenproxy_config.api_key_secret_mode = MANAGED_AZURE_KEY_VAULT.ingress_config: Optional. NGINX ingress configuration, TLS provisioning, and optional Azure Front Door fronting.dns_config: Optional. Azure DNS A-record configuration.letsencrypt_dns01_azure_dns: Optional. Switches cert-manager to the DNS-01 solver via Azure DNS. Required wheningress_config.ingress_hostis a wildcard.autoscaling_config: Optional. Proxy HPA configuration.tags: Optional. Additional Azure tags. Default:{}.
resource_group_config
resource_group_configcreate: Optional. Default:true.name: Optional. Default:espresso-ai-proxy-rg. Must be non-empty. Whencreate = false, an existing resource group with this name must exist.
vnet_config
vnet_configvnet_name: Optional. Default:espresso-ai-proxy-vnet.address_space: Optional. Default:["10.240.0.0/16"]. Must contain at least one valid CIDR whencreate_dedicated_vnet = true.node_subnet_cidr: Optional. Default:10.240.0.0/22. Must be a valid CIDR withinaddress_space.
existing_vnet_config
existing_vnet_configvnet_id: Required in existing-VNet mode.node_subnet_id: Required in existing-VNet mode.
aks_config
aks_configcluster_name: Optional. Default:espresso-ai-proxy. Used as the resource name prefix throughout the module.kubernetes_version: Optional. Default:1.35.api_server_authorized_ranges: Optional. Required whenenable_private_cluster = false. CIDRs allowed to reach the AKS API server.enable_private_cluster: Optional. Default:false. Whentrue, the API server has only a private endpoint.pod_cidr: Optional. Default:10.244.0.0/16. CNI Overlay pod range; not part of the VNet.service_cidr: Optional. Default:10.245.0.0/16. ClusterIP service range.dns_service_ip: Optional. Default:10.245.0.10. Must lie withinservice_cidr.vm_size: Optional. Default:Standard_D8s_v5.node_pool_min_count: Optional. Default:2.node_pool_max_count: Optional. Default:10. Must be ≥node_pool_min_count.enable_log_analytics: Optional. Default:false. Whentrue, attaches a Log Analytics workspace and enables Container Insights.log_analytics_retention_days: Optional. Default:90.
proxy_config
proxy_configimage: Required. Proxy container image URI in Espresso AI's ACR (e.g.espressoai.azurecr.io/proxy:<tag>). The exact value is shown in the Espresso AI dashboard once your tenant ID has been registered.replicas: Optional. Default:2.proxy_host: Required. Non-empty value injected asPROXY_HOST.otel_exporter_otlp_endpoint: Optional. Injected asOTEL_EXPORTER_OTLP_ENDPOINT. Default:https://metrics.espressocomputing.com:443.api_key_secret_name: Optional. Kubernetes secret name in the proxy namespace from whichESPRESSO_AI_API_KEYis mounted. Default:espresso-ai.API key secret key name is fixed to
ESPRESSO_AI_API_KEYand is not configurable.api_key_secret_mode: Optional.BYO_K8S_SECRETorMANAGED_AZURE_KEY_VAULT. Default:BYO_K8S_SECRET.api_key_azure_key_vault_secret: Optional. Key Vault secret name used in managed mode. Default:espresso-ai-proxy-api-key. Required (non-empty) whenapi_key_secret_mode = MANAGED_AZURE_KEY_VAULT.api_url: Optional. Base URL. Default:https://api.espressocomputing.com:25831.env_vars: Optional. Map of environment variable key/value pairs. Currently supported keys:keytypedefinitionEXCLUDE_QUERY_TEXTboolDefault:
false. Whether to exclude query text on requests to Espresso AI's API. Note: Enabling this will limit supported functionality.
ingress_config
ingress_configenable_ingress: Optional. Enables the nginx Ingress. Default:true.ingress_host: Required whenenable_ingress = true. Hostname (or wildcard, e.g.*.example.com) the Ingress serves.letsencrypt_email: Optional. When set, installs cert-manager and a Let's EncryptClusterIssuer, and cert-manager auto-issues/renews akubernetes.io/tlssecret for the Ingress. Mutually exclusive withtls_secret_name. Exactly one must be provided whenenable_ingress = true.use_letsencrypt_staging: Optional. Default:false. Switches the issuer to Let's Encrypt staging (useful while iterating to avoid hitting prod rate limits).tls_secret_name: Optional. Bring-your-ownkubernetes.io/tlssecret name in the proxy namespace (e.g. synced from a Key Vault cert via the Secrets Store CSI driver). Mutually exclusive withletsencrypt_email.front_door: Optional. Azure Front Door fronting configuration:enabled: Optional. Default:false.sku_name: Optional.Standard_AzureFrontDoororPremium_AzureFrontDoor. Default:Standard_AzureFrontDoor. Premium adds WAF and Private Link to origin.
dns_config
dns_configcreate_record: Optional. Creates an Azure DNS A record pointing at the ingress public IP. Default:false.zone_name: Required whencreate_record = true. Apex of the existing Azure DNS zone (e.g.customer.example.com).zone_resource_group_name: Required whencreate_record = true. Resource group of the DNS zone.record_name: Optional. Falls back toingress_config.ingress_hostwhen omitted.ttl: Optional. Default:300.
letsencrypt_dns01_azure_dns
letsencrypt_dns01_azure_dnsWhen set, cert-manager uses DNS-01 (which supports wildcard certs) instead of HTTP-01. Required when ingress_config.ingress_host is a wildcard, since Let's Encrypt only issues wildcards via DNS-01. The module provisions a user-assigned managed identity, federates it to cert-manager's controller service account, and grants it DNS Zone Contributor on the named zone — no static credentials are needed.
zone_name: Required.zone_resource_group_name: Required.
autoscaling_config
autoscaling_configmin_replicas: Optional. Default:2.max_replicas: Optional. Default:10. Must be ≥min_replicas.target_cpu_utilization: Optional. Default:70. Must be between 1 and 100.
Secret modes
BYO_K8S_SECRET(default): Proxy reads from an existing Kubernetes secret (api_key_secret_name) in the proxy namespace using fixed keyESPRESSO_AI_API_KEY.MANAGED_AZURE_KEY_VAULT: Module provisions an Azure Key Vault, writes the API key as a secret, federates a user-assigned managed identity to the External Secrets Operator service account, installs ESO, and creates anExternalSecretthat syncs the Key Vault secret into akubernetes.io/tls-style Kubernetes secret in the proxy namespace.
TLS modes
Let's Encrypt (default path). Set
ingress_config.letsencrypt_email. cert-manager runs an HTTP-01 challenge through the nginx Ingress and writes aproxy-tlssecret in the proxy namespace. Renewals are automatic.Let's Encrypt with DNS-01. Add
letsencrypt_dns01_azure_dnsto switch the solver to Azure DNS. Required for wildcard hosts. The module wires up a federated managed identity scoped toDNS Zone Contributoron the target zone — no service-principal credentials needed.Bring-your-own TLS secret. Set
ingress_config.tls_secret_name(and pre-create that secret in the proxy namespace) — useful when an existing process syncs a Key Vault cert via the Secrets Store CSI driver, or when cert-manager is managed outside this module.Front Door fronting. When
ingress_config.front_door.enabled = true, AFD terminates TLS for end clients with a DigiCert-issued managed cert (which has working OCSP). The LE/BYO cert on the AKS LB then only secures the AFD-to-origin hop. Use this when the client enforces strict OCSP (e.g. Snowflake's connector).
Outputs
The module exports:
resource_group_namevnet_idnode_subnet_idaks_cluster_nameaks_node_resource_groupaks_oidc_issuer_urlaks_kubelet_identity_object_id— exposed for advanced cases (e.g. grantingAcrPullon a private registry of your own). Not needed for the standard flow, which uses Espresso AI's ACR.proxy_namespaceproxy_service_nameproxy_ingress_public_ipproxy_hpa_nameproxy_dns_fqdnproxy_api_key_key_vault_name— only set whenMANAGED_AZURE_KEY_VAULTis enabled.front_door_endpoint_hostname— point a CNAME from your custom domain at this. Only set when AFD fronting is enabled.front_door_custom_domain_validation_token— publish at_dnsauth.<ingress_host>so AFD will issue the managed cert. Only set when AFD fronting is enabled.front_door_route_id,front_door_custom_domain_id,front_door_custom_domain_association_id— AFD resource IDs, only set when AFD fronting is enabled.
Customer-side DNS work after AFD provisioning
When ingress_config.front_door.enabled = true, after terraform apply finishes, publish:
CNAME <ingress_host> → <front_door_endpoint_hostname>TXT _dnsauth.<ingress_host> → <front_door_custom_domain_validation_token>
Both values are exposed as outputs above.
How to deploy
Deployment typically takes around 20-30 minutes (longer when Azure Front Door is enabled and waiting on managed-cert validation).
ACR pull is handled on the Espresso AI side — once your tenant ID is registered in the dashboard, our onboarding workflow grants your AKS cluster pull access on the proxy repository in Espresso AI's ACR. No az role assignment is needed in your subscription.
Best practices
Manage sensitive variables (e.g.
proxy_api_key_value) via environment variables or.tfvarsfiles excluded from source control.Keep
aks_config.api_server_authorized_rangestight when running a public API server, or setenable_private_cluster = trueand reach the cluster via a peered network/jumpbox.For wildcard ingress hosts, always pair
letsencrypt_emailwithletsencrypt_dns01_azure_dns— HTTP-01 cannot validate a wildcard.When fronting clients with strict OCSP behavior (Snowflake's connector, in particular), enable
ingress_config.front_doorso end clients see AFD's DigiCert cert instead of the LE cert on the AKS LB.
Last updated