Skip to content

Instantly share code, notes, and snippets.

@naveedausaf
Last active July 4, 2025 16:53
Show Gist options
  • Select an option

  • Save naveedausaf/2ac60d1824590d28f625e18ad3a35abc to your computer and use it in GitHub Desktop.

Select an option

Save naveedausaf/2ac60d1824590d28f625e18ad3a35abc to your computer and use it in GitHub Desktop.
Terraform to create an Azure Container App protected by Cloudflare Free, that uses a user-assigned managed identity to pull image from Azure Container Registry and a secret from Azure Key Vault
data "azurerm_resource_group" "core" {
name = var.core_resource_group_name
}
data "azurerm_user_assigned_identity" "app" {
name = var.flowmazon_api_managed_identity
resource_group_name = data.azurerm_resource_group.core.name
}
data "azurerm_key_vault" "app" {
name = var.app_key_vault_name
resource_group_name = data.azurerm_resource_group.core.name
}
data "azurerm_key_vault_secret" "connstr_for_app" {
name = var.key_vault_secretname_connectionstring_for_api
key_vault_id = data.azurerm_key_vault.app.id
}
data "azurerm_container_registry" "app" {
name = var.acr_name
resource_group_name = data.azurerm_resource_group.core.name
}
locals {
full_image_name = "${data.azurerm_container_registry.app.login_server}/${var.image_repository_name}:${var.version_to_deploy}"
}
resource "azurerm_resource_group" "app" {
name = var.app_resource_group_name
location = var.app_resource_group_location
}
resource "azurerm_container_app_environment" "app" {
name = var.app_environment_name
location = azurerm_resource_group.app.location
resource_group_name = azurerm_resource_group.app.name
}
resource "azurerm_container_app" "app" {
name = var.app_name
container_app_environment_id = azurerm_container_app_environment.app.id
resource_group_name = azurerm_resource_group.app.name
revision_mode = var.app_revision_mode
ingress {
external_enabled = true
allow_insecure_connections = false
target_port = var.app_container_port
client_certificate_mode = "require"
dynamic "ip_security_restriction" {
for_each = local.cloudflare_ips
content {
# want to generate a unique name for the
# ip range. Better practice with dynamically
# generated blocks to do this than to
# assign index 1...n that we could have
# done with count instead of for-each
# as we have done in this dynamic block
name = "Allow-Cloudflare-${replace(replace(replace(ip_security_restriction.value, ".", "-"), ":", "--"), "/", "_")}"
description = "One of the CloudFlare's outbound IP ranges. Part of a fairly stable set."
ip_address_range = ip_security_restriction.value
action = "Allow"
}
}
traffic_weight {
latest_revision = true
percentage = 100
}
# Note this ingress would need to have a custom domain binding
# but we only add it (see use of azapi resources below to do
# that) once DNS records (CNAME and TXT) have been created
# at CloudFlare and have propagated.
# If we do it now we would get an error.
#
# I have verified that once the custom domain has been created,
# subsequent `terraform apply` operations DO NOT detect
# the addition of custom domain to this ingress as drift and
# try to deleted it. So we're ok.
}
registry {
server = data.azurerm_container_registry.app.login_server
identity = data.azurerm_user_assigned_identity.app.id
}
secret {
name = var.key_vault_secretname_connectionstring_for_api
identity = data.azurerm_user_assigned_identity.app.id
key_vault_secret_id = data.azurerm_key_vault_secret.connstr_for_app.id
}
identity {
# it is possible for a service to have both. I am giving it
# both.
# This is because while we need the UserAssigned identity to
# let the app access the key vault and ACR etc, the
# automatically created and assigned system-assigned identity
# of the app may be used in authN/authZ with ACA app environment
# which we have created in a way that its settings would be
# autogenerated and would not be changeable after creation.
type = "SystemAssigned, UserAssigned"
identity_ids = [data.azurerm_user_assigned_identity.app.id]
}
template {
container {
name = var.app_container_name
image = local.full_image_name
cpu = 0.5
memory = "1Gi"
env {
name = local.allowed_cors_origins_env_var_name
value = var.allowed_cors_origins_for_api
}
env {
name = local.connection_string_env_var_name
# We don't want to set it from the value of the secret
# read from key vault (the account executing this terraform
# config may not even have the permission to do that).value
#
# If we did that, the value would be visible in
# Environment Variables section of the app in plain text
#
# Therefore we simply provide the name of the secret
# we want the value to be retrieved from.
#
# For this to work, we need to reference the secret
# in a secret object in the app object (done above),
# which in turn requries a reference to a
# azurerm_key_vault_secret data source and for that a
# azurerm_key_vault data source.
secret_name = var.key_vault_secretname_connectionstring_for_api
}
liveness_probe {
transport = "HTTP"
port = var.app_container_port
path = "/health/live"
}
readiness_probe {
transport = "HTTP"
port = var.app_container_port
path = "/health/ready"
}
# because of the way liveness probe is implemented in
# the ASP.NET Core API, the liveness probe only
# responds with Healthy and 2xx if
# startup has completed. So we are using it as
# startup_probe also
startup_probe {
transport = "HTTP"
port = var.app_container_port
path = "/health/live"
}
}
}
}
# Create DNS settings in CloudFlare
# PREREQUISITE: CloudFlare's namesrevers should already been rwgistered
# with the Domain Name Registrar you bought the domain name from.
# This would have created a Zone in CLoudFlare for your apex domain
# e.g. `efast.uk`
# returns cloudflare's ip ranges so we can add them to ingress allow rules
# in ACA app
data "cloudflare_ip_ranges" "cloudflare" {}
locals {
# Create a set of Cloudflare IPv4 IPs which we
# will put in the allow list of the container app's ingress
cloudflare_ips = toset(data.cloudflare_ip_ranges.cloudflare.ipv4_cidrs)
subdomain = split(".", var.app_domain_name)[0] # e.g., 'api' for 'api.efast.uk'
apexdomain = trimprefix(var.app_domain_name, format("%s.", local.subdomain)) # e.g., 'efast.uk'
# this is what we would create a TXT record with for the app's domain name
custom_domain_verification_id = azurerm_container_app.app.custom_domain_verification_id
}
# To map the API's domain name to your ACA app, we need to create
# a CNAME record and a TXT record. This is normally done for the
# subdomain part of the app's domain name (for example create a
# CNAME record for `api` and TXT record for `asuid.api` where the
# domain name of the app is api.efast.uk).
#
# HOWEVER, with CLoudFlare's Terraform resources, you have to
# provide the full domain name (FQDN, e.g. api.efast.uk). If
# you only provider the prefix then it works on first apply but
# on every subsequent apply, perhaps because you made a change
# to some other resource, the cloudflare_dns_record resource
# would say there is a change and it has to change
# the current name in the actual CloudFlare DNS record, which
# would be `api.efast.uk` for CNAME record and `asuid.api.efast.uk
# for TXT record, back to `api` and `asuid.api` respectively.
resource "cloudflare_dns_record" "app_cname" {
zone_id = var.cloudflare_zone_id
name = var.app_domain_name
# this should be the target FQDN without the `https://` prefix,
# which is how ingress's fqdn property returns it
content = azurerm_container_app.app.ingress[0].fqdn
type = "CNAME"
ttl = 1 # 1 means TTL is automatically set by CloudFlare
# Setting proxied = true is what allows us to get CloudFlare
# protections such as DDoS protection, WAF and API Shield features.
#
# However we need it to be false initially to allow domain
# name verification to be successful for creating custom
# domain binding on the app's ingress and for managed
# certificate generation.
# Even so, I have a restful_operation resource that, in the
# the multi-step Cloudflare setup process (signposted below)
# turn it off before creating custom domain binding, generating
# managed cert and updating the custom domain binding with it.
# Once that is done, another restful_operation would turn
# it back on.
# SO I could have left proxied to true here.
#
# YET, the reason for leaving it to false at time of CNAME
# record creation is that if I leave it on, then even though
# it is later turned off during the process and turned back
# on again, and during this "blip", DNS record verififcation
# happens successfully, once I turn it bacl on, it took
# a long time, sometimes even 30 minutes, for the DNS cache
# in Cloudflare to be flushed and the domain name to be reachable.
# In the meantime, I would keep getting DNS_PROBE_FINISHED_NXDOMAIN
# error in the browser.
#
# Now this does not happen and as soon as terraform apply has
# finidhed, the domain is instantly reachable (although if
# container count is zero, it can take around 20 seconds for
# a new container to spinup so that request take a while
# to complete). I have verified this by destroying and applying
# again and again many times. After every apply, I was able
# to reach the domain name of the app immediately.
proxied = false
lifecycle {
ignore_changes = [proxied]
}
}
resource "cloudflare_dns_record" "app_txt" {
zone_id = var.cloudflare_zone_id
name = format("%s.%s", "asuid", var.app_domain_name)
# I think, but am not sure, that the content of TXT record
# must be contained within quotes. It works anyway.
content = format("\"%s\"", local.custom_domain_verification_id)
type = "TXT"
ttl = 1 # 1 means TTL is automatically set by CloudFlare
}
# ENABLE mTLS (mutual TLS)
#######################################################################
# tls_client_auth setting on the zone (beware it applies to whole zone)
# means CloudFlare would present the public key of its own certificate
# to the ACA app when relaying proxied traffic to it.
# The ACA app is set up to require a certificate from the caller.
# This is just an additional security measure on top of the
# TLS certificate that the ACA app itself has (for normal TLS)
# and the fact that we have restricted the ingress to accept
# traffic only from CloudFlare's IP addresses.
resource "cloudflare_zone_setting" "app_apex_domain" {
zone_id = var.cloudflare_zone_id
setting_id = "tls_client_auth"
value = "on"
}
# ENABLE RATE LIMITING ON THE ZONE
#######################################################################
# TODO: Put this in the environment setup documentation and that
# if such a rule doesn't exist, and you get an error, then
# you would need to create one in the UI with name "default" first.
# Also note there that:
#
# 1. this rule applies to whole zone as under the free plan,
# you can't set rate limiting rules by hostname.
# But beware that terraform destroy would be removing it for
# the whole zone as well.
#
# 2. The rule of "AUthenticate Origin Pulls" where CLoudFLare
# presents its own cert to the ACA app also applies to the whole
# zone. This we are leaving in. WE SHOULD REALLY BE
# REMOVING THIS AS WELL, TO BE CONSISTENT WITH (1) ABOVE
#
# 3. The zone for the apex domani must already exist. Zone ID
# for that is passed in as cvalue of the cloudflare_zone_id
# variable.
# In the free plan, this applies to all CNAME and A records in the
# zone that are proxied through cloudflare. If you upgrade, you can
# specify different rate limiting rules for hosts in your DNS records
#
# I generated this by creating the rule in the UI. They display
# API Call at the bottom of the page. I took it and tweaked
# it to be properties of this cloudflare_ruleset resource
#
# It still didn't work because a previously deleted rate limiting
# rule, named "default" still existed. All that creating or
# deleting a rate limiting rule for the zone in Cloudflare Dashboard
# was doing was turning it on or off.
#
# This may be because there's
# a limit of 1 rate limiting rule in the free account, but I am
# not sure sure if there wouldn't already be a rate limiting.
#
# So I executed the following cURL, which returned all ruleset.
# From this I picked out the "name" of the ruleset for
# phase = "http_ratelimit" and specified its name - "default" - in
# the name field of the ruleset.
#
# curl -X GET "https://api.cloudflare.com/client/v4/zones/{zone_id}/rulesets" \
# -H "Authorization: Bearer {api_token}" \
# -H "Content-Type: application/json"
#
# Contrary to what the documentation for this Terraform resoruce
# suggested (at version 5.6.0), I would not set "id" of that
# ruleset to the "id" that I saw in the returned results. When I
# tried to do that, and got the error that the provider doesn't allow
# "id" to be set and it is a read-only field.
resource "cloudflare_ruleset" "zone_rate_limit" {
zone_id = var.cloudflare_zone_id
name = "default"
# the only kind allowed in free plan
kind = "zone"
phase = "http_ratelimit"
rules = [{
# We need to set this.
# But I have verified that it still deltes the rate limiting
# rule in the Cloudflare Dashboard on terraform destroy
# (although as described in the comment above, deletion in UI
# just means disablement of the rule named "default")
enabled = true
# Dashboard UI generated this expressions. It means
# reqeusts to any path in the zone are counted
# towards the rate limiting threshold
expression = "(http.request.uri.path wildcard \"/*\")"
action = "block"
ratelimit = {
characteristics = [
# This just has to be there, don't know why, otherwise
# we get an error on terraform apply
"cf.colo.id",
# I believe that this means that number of requests
# will be evaluated for the same source IP, i.e.
# if the same IP sends a number of requests specified
# by `requests_per_period` argument below, then that
# ip would be blocked.
# In free account, this is one of the few options available.
"ip.src"
]
# Defines if ratelimit counting is only done when an origin
# is reached. I am taking setting it to false to mean that
# request reaching cloudflare but that were not proxied
# would still count towards the rate limit.
requests_to_origin = false
# This is the number of requests in perion specified by `period`
# property that, if received, would trigger block on subsequent
# requests for a period specified by `mitigation_timeout` property.
requests_per_period = var.rate_limit_requests_per_period
# Over how many seconds should the specified number of requests
# (specified in requests_per_period above) should be received
# or exceeded for the block to be put in place.
# 10 is the only allowable value in free plan
period = 10
# How many seconds the block will stay in place for
# 10 is the only allowable value in free plan
mitigation_timeout = 10
}
}]
}
# PROCESS IMPLEMENTED BELOW:
######################################################
# If source (domain name) or target (ACA app's FQDN)
# of the CNAME change - or if this is the the first
# terraform apply of the config - we need to go through
# the following steps:
#
# 1. Wait for the chagnes to propagate.
# 2. Set 'proxied=off' on the CNAME record. Otherwise
# managed cert creation and almost certinly the
# custom domain binding creation and update would fail.
# 3. recreate the manager cert
# 4. in order to recerate the cert, we need to destroy
# any existing custom domain binding, then recreate is
# as Disabled
# 5. Once managed cert has been created, we need to
# update the custom domain binding with the cert.
# 6. Set proxied back on the CNAME record.
#
# PROCESS IMPLEMENATTION NOTE:
# In implementing the provess, I have borne in mind the
# details given in document
# [Terraform Core Resource Destruction Notes](https://github.com/hashicorp/terraform/blob/main/docs/destroying.md)
# This data resource to collect the key attributes of CNAME
# record that, if they change, should trigger a replace
# of the key recources that follow whose purpose is to
# suppor the above process
resource "terraform_data" "cname_and_txt_info_for_triggering_replacement" {
input = {
targetfqdn = cloudflare_dns_record.app_cname.content
sourcefqdn = var.app_domain_name
custom_domain_verification_id = local.custom_domain_verification_id
}
}
# First we wait for the DNS records to have propagated
resource "time_sleep" "cname_and_txt_propagated" {
create_duration = "60s" # 60 seconds always seems to work with CloudFlare
depends_on = [cloudflare_dns_record.app_cname, cloudflare_dns_record.app_txt]
# this argument, which predates replace_triggered_by lifecycle property,
# requries a map of values rather than resources. So we set it to
# .output of the terraform_data resoruce rather than the resource itself.
triggers = terraform_data.cname_and_txt_info_for_triggering_replacement.output
}
# Set proxied=false on the CNAME record.
#
# NOTE:
# It would probably have been enough to set proxied=false in CNAME
# record resource with lifecycle.ignore_changes = ["proxied"].
# The problem with that is that if for any reason the cert or
# custom domain binding had to change or be recreated (e.g,
# we change the domain name that is the source of CNAME record,
# or the ACAP app is replaced as that would generate a new FQDN
# to set as content - i.e. target - of the CNAME record), then
# managed cert would need to be regenerated and custom domain
# binding updated which would fail because the last time round
# we had set `proxied==true` right at the end of the process
# when managed cert had been created and custom domain binding
# updated.
#
# We would still set proxied=true in that way (at the end
# of the process), but we now we explicitly turn it to off
# just after updates to key data in cname or txt records
# (domain name, target of CNAME record, or TXT value) have
# occurred and propagated, and just before any updates to
# mnaged cert and custom domain binding may need to happen
# as a result.
resource "restful_operation" "turn_off_proxied_in_cname_record" {
depends_on = [time_sleep.cname_and_txt_propagated]
lifecycle {
replace_triggered_by = [terraform_data.cname_and_txt_info_for_triggering_replacement]
}
provider = restful.cloudflare
path = "/zones/${cloudflare_dns_record.app_cname.zone_id}/dns_records/${cloudflare_dns_record.app_cname.id}"
method = "PATCH"
body = { "proxied" : false }
# I think we can also poll for retry. See resource
# documentation in terraform registry (under
# magodo/restful provider)
}
# Initialize a custom domain binding in the ingress of the app
# We can do it now, but couldn't when we created the app, because
# now the DNS records with CloudFlare would have propagated. Those
# would be validated by Azure when it creates the binding.
resource "azapi_resource_action" "custom_domain_binding_initialize" {
depends_on = [time_sleep.cname_and_txt_propagated, restful_operation.turn_off_proxied_in_cname_record]
lifecycle {
# This resource does nothing on update or destroy.
# But we do want it to be re-created if key bits in CNAME
# or TXT change. Hence setting replace_triggered_by below.
#
# During replacement, the actual destruction of the custom
# binding that this resource creates/initialisez is achieved by
# resource azapi_resource_action.custom_domain_binding_destroy
# which has been configured to ONLY operate during destroys and
# deletes the very custom binding that resource below initializes.
replace_triggered_by = [terraform_data.cname_and_txt_info_for_triggering_replacement]
}
type = "Microsoft.App/containerApps@2023-05-01"
resource_id = azurerm_container_app.app.id
# It seems `action` just gets appended to the URL obtained by
# appending `resource_id` to the base url for the `type`
# this gives us URLs for actions on some resources like
# `/start` on a Web App in an App Service Plan.
# WE DON't WANT TO USE IT HERE
#
#action = ""
# default for this resource is POST, unlike for the
# azapi_update_resource for which it seems to be PATCH
# from my Fiddler investigations
method = "PATCH"
body = {
properties = {
configuration = {
ingress = {
customDomains = [
{
bindingType = "Disabled",
name = var.app_domain_name,
}
]
}
}
}
}
}
# azurerm can't create a managed TLS certificate -
# see https://github.com/hashicorp/terraform-provider-azurerm/issues/21866
#
# So instead of using azurerm provider the following resources are
# created using AzAPI provider to make directy API calls to Azure.
resource "azapi_resource" "managed_certificate" {
depends_on = [azapi_resource_action.custom_domain_binding_initialize, time_sleep.cname_and_txt_propagated, cloudflare_dns_record.app_cname, cloudflare_dns_record.app_txt, restful_operation.turn_off_proxied_in_cname_record]
# Unlike azapi_update_resource and azapi_resource_action, that we have used in this
# process to patch the custom domain binding on the ingress of the ACA app
# this resource is fully managed through create update and destroy. However,
# an actual managed cert needs repalcement when the domain name or the FQDN
# of the backing ACA app changes. Hence lifecycle.repalce_triggered_by below
# is the same as that for the other azapi resources and restful_operation
# resources used in this process.
lifecycle {
replace_triggered_by = [terraform_data.cname_and_txt_info_for_triggering_replacement]
}
type = "Microsoft.App/managedEnvironments/managedCertificates@2023-05-01"
name = "${lower(var.app_name)}-cert"
parent_id = azurerm_container_app_environment.app.id
location = azurerm_resource_group.app.location
body = {
properties = {
subjectName = var.app_domain_name
domainControlValidation = "CNAME"
}
}
response_export_values = ["*"]
}
# update the already-create custom domain binding in the app's ingress
# with the certificate and make the binding enabled.
resource "azapi_update_resource" "custom_domain_binding_update" {
depends_on = [restful_operation.turn_off_proxied_in_cname_record, azapi_resource_action.custom_domain_binding_initialize, azapi_resource.managed_certificate]
type = "Microsoft.App/containerApps@2023-05-01"
resource_id = azurerm_container_app.app.id
lifecycle {
# this resource only operates during create and update, not destroy
# we do want the underlying domain binding to be destroyed when
# this resource needs to be recreated. That is acheived by the
# azapi_resource_action.custom_domain_binding_destroy resource
# which would run only on destroy and deleted the custom domain
# binding being updated by this resource.
replace_triggered_by = [terraform_data.cname_and_txt_info_for_triggering_replacement]
}
body = {
properties = {
configuration = {
ingress = {
customDomains = [
{
bindingType = "SniEnabled",
name = var.app_domain_name,
certificateId = azapi_resource.managed_certificate.output.id
}
]
}
}
}
}
}
# The azapiupdate_resource that creates custom binding above works
# during create and update phases of the lifecycle.
# However, it does nothing during destroy. This leads to a problem
# when destroying the managed cert with the error that the cert
# is in use in a custom binding (which has not beeen destroyed).
#
# This resource does nothing during update and, depending on setting
# of 'when' argument, works eother during create, or during destroy.
# We have set it to run during destroy so it destroys the
# custom domain binding, so that the managed cert can
# then be destroyed or the cusomt domain binding re-created by
# the recreation of custim_domain_binding_initialize and
# custom_domain_binding_update resources above in the right order.
resource "azapi_resource_action" "custom_domain_binding_destroy" {
type = "Microsoft.App/containerApps@2023-05-01"
lifecycle {
replace_triggered_by = [terraform_data.cname_and_txt_info_for_triggering_replacement]
}
# following line also makes this resource a dependent
# on the custom_domain_binding_create resource.
resource_id = azapi_update_resource.custom_domain_binding_update.resource_id
method = "PATCH"
response_export_values = ["*"]
body = {
properties = {
configuration = {
ingress = {
customDomains = [] # ensures the custom domain would be deleted
}
}
}
}
when = "destroy"
}
resource "restful_operation" "turn_on_proxied_in_cname_record" {
depends_on = [azapi_update_resource.custom_domain_binding_update, azapi_resource.managed_certificate,
# We know that resource named below would
# definitely be replaced for the same reasons as this one is.
# However, we want to be certain that the the present resource
# would run to turn proxied back on definitely after it has
# been turned off. Hence the following dependency.
restful_operation.turn_off_proxied_in_cname_record]
lifecycle {
replace_triggered_by = [terraform_data.cname_and_txt_info_for_triggering_replacement]
}
provider = restful.cloudflare
path = "/zones/${cloudflare_dns_record.app_cname.zone_id}/dns_records/${cloudflare_dns_record.app_cname.id}"
method = "PATCH"
body = { "proxied" : true }
# I think we can also poll for retry. See resource
# documentation in terraform registry (under
# magodo/restful provider)
}
terraform {
required_providers {
azurerm = {
# Following environment variables must be provided for this provider:
# ARM_SUBSCRIPTION_ID, ARM_CLIENT_ID, ARM_TENANT_ID, ARM_CLIENT_SECRET
#
# All of these - except ARM_SUBSCRIPTION_ID can be obtained when
# you create a service principle in Entra in Azure portal.
#
# For ARM_SUBSCRIPTION_ID, provide the ID of a subscription
# in your Azure account.
source = "hashicorp/azurerm"
version = "4.34.0" # Pinned to an exact version for repeatabilityneeded
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "5.6.0" # pinned to exact version for repeatability
}
azapi = {
source = "azure/azapi"
version = "2.4.0" # pinned to exact version for repeatability
}
time = {
source = "hashicorp/time"
version = "0.13.1" # pinned version for repeatability
}
# restapi = {
# source = "Mastercard/restapi"
# version = "2.0.1" # version pinned for repeatability
# }
restful = {
source = "magodo/restful"
version = "0.22.0" # version pinned for repeatability
}
}
}
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
}
}
provider "azapi" {
# this needs to the same four environment
# variables to be provided that we are setting
# for the azurerm provider above
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
provider "time" {
}
provider "restful" {
alias = "cloudflare"
# Configuration options
base_url = "https://api.cloudflare.com/client/v4"
header = {
Content-Type = "application/json"
Authorization = "Bearer ${var.cloudflare_api_token}"
}
}
variable "app_resource_group_name" {
description = "The name of the Azure resource group in which the app would be created."
type = string
}
variable "app_resource_group_location" {
description = "Location of the Azure resource group n which the app would be created."
type = string
}
variable "allowed_cors_origins_for_api" {
description = "the string that would be set as value of config key ALLOWED_CORS_ORIGINS for the API"
type = string
}
variable "core_resource_group_name" {
description = "The name of the Azure resource group that contains supporting resources such as key vault, ACR etc. this would already have been created by a different module/workspace."
type = string
}
variable "app_name" {
description = "Name of the ACA app that would be created."
type = string
}
variable "app_environment_name" {
description = "Name of the ACA environment that would be created and would contain the ACA app"
type = string
}
variable "flowmazon_api_managed_identity" {
description = "Name of the user-assigned managed identity that would be assigned to the ACA app"
type = string
}
variable "app_key_vault_name" {
description = "The name of the key vault from which secrets required by the app will be read."
type = string
}
variable "key_vault_secretname_connectionstring_for_api" {
description = "name of the secret whose value is the connection string to be used by the API to connect to the database"
type = string
}
variable "app_domain_name" {
description = "The custom domain name for the app, e.g. api.efast.uk"
type = string
}
variable "app_container_name" {
description = "Name of the container that would be created in the ACA app"
type = string
}
variable "app_container_port" {
description = "port at which the app container listens"
type = number
# default = 80
}
variable "acr_name" {
description = "The name of the Azure Ccontainer Registry instance which contains the image to be deployed to the ACA app."
type = string
}
variable "image_repository_name" {
description = "Name of the Docker image to deploy (excluding the '<registry name>.azurecr.io/' prefix and the ':<tag>' suffix)"
type = string
}
variable "version_to_deploy" {
description = "SemVer version to deploy, starting with 'v'. this would be the tag of the image for flowmazonapi in the container registry e.g. `v1.0.1`"
type = string
}
variable "app_revision_mode" {
description = "revision mode of the container app. Multiple or Single"
type = string
# # better practice for revision mode to be "Multiple"
# # but "Single" is a simpler default
# default = "Single"
}
locals {
allowed_cors_origins_env_var_name = "ALLOWED_CORS_ORIGINS"
connection_string_env_var_name = "ConnectionStrings__FlowmazonDB"
}
# Cloudflare variables
# To generate this token, create an Account API token by going
# to your user profile in your CloudFalre account, then clicking
# **API Token** in the nav on the left hand side.
#
# Note an Account API token is preferred to a User API token.
#
# Give the token the following permissions:
# 1. 'Zone | DNS | Edit` (to create and modify TXT and CNAME records)
# 2. 'Zone | Zone Settings | Edit' (to set 'tls_client_auth' setting
# 3. 'Zone | Zone WAF | Edit` (to set rate limiting rule)
# to 'on' which ensures that CloudFlare would present a certificate
# to target of CNAME record when communicating with it; this
# achieves mTLS.
#
# Make sure that the zone of interest on which these permissions apply
# is also selected under 'Zone Resources' - or 'All zones from the acount'
# is selected - when you assign permissions to the token.
#
variable "cloudflare_api_token" {
description = "CloudFlare's API Token."
type = string
sensitive = true
}
# TODO: document this in my environments documentation
#
# I assume you already have a DNS zone with CloudFlare for
# the apex domain used in the domain name of the app. For example
# if the domain name if `api.efast.uk`, the apex domain is
# efast.uk.
#
# Create this zone by transferring setting Cloudflare's nameservers
# as nameservers of the apex domain in control panel of the registrar
# from whom you purchased the apex domain.
# Cloudflare would also guide you through the process.
# Once you have created an zone, i.e. Cloudflare manage
# DNS queries for the apex domain, then go to CloudFlare Dashboard.
# There click the apex domain name, and you would be taken to
# the detail page of the associated zone. ZoneID would be
# displayed on this page (you may have to scroll down the page).
variable "cloudflare_zone_id" {
description = "Zone ID of the apex domain for domain name specified in api_domain_name variable."
type = string
sensitive = true
}
variable "rate_limit_requests_per_period" {
description = "number of request that, if received in the configured `period` (fixed to 10 seconds in free plan), would lead to the sending IP being blocked for the configured `mitigation_timeout` (also fixed to 10 seconds in free plan)"
type = number
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment