Skip to content

About

🧩 Terraform module for APIM gateway configuration: APIs, products, backends, named values, subscriptions, loggers in one cross-referencing call

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Terraform AzureRM API Management Config

An API Management gateway's working configuration in one call: APIs (with their policies, imported definitions, product links, and diagnostics), products (with their policies), backends, named values, loggers, and subscriptions, all keyed by readable names that cross-reference each other. The second layer of the estate's MCP gateway architecture, and the consolidation of six former single-resource wrapper modules into one surface that composes.

CI Release Terraform Registry License


How the pieces reference each other

The instance arrives as its resource id (api_management_id; name and resource group parse from it). Everything else is a map keyed by a readable name, and the maps point at each other by key, never by constructed id: an API's products list joins products, its diagnostic.logger_key names a logger, a subscription's product_key or api_key scopes it, and a backend's credentials can reference a named value as {{its-key}} so secrets stay server-side. The keys are plan-known, so the cross-links never trip the for_each unknown-key trap.

Defaults that lean secure

  • APIs default to https only and subscription_required = true: an anonymous API is a choice, never a default.
  • Subscriptions default to state = active (a Terraform-managed subscription should work on apply, not sit in an approval queue) and allow_tracing = false (traces expose backend internals; enable deliberately, briefly).
  • Backend TLS validation stays on; a Key Vault-sourced named value is secret whether you said so or not.

Notes worth knowing

  • Policies attach at API and product scope via policy_xml_content (inline, templatefile friendly) or policy_xml_link. Operation-level policies and operations themselves are out of scope on purpose: import an OpenAPI definition and the operations come with it.
  • The subscription_primary_keys output is sensitive; it is what a client presents in Ocp-Apim-Subscription-Key.
  • Loggers take Application Insights (connection string, or identity_client_id for keyless) or Event Hub; an API opts into telemetry with its diagnostic object.

Security scan exceptions

CI runs a trivy config scan that gates on HIGH and CRITICAL findings. Waivers live in .trivyignore.yaml, and every waiver is recorded here. There are currently none.

ID Where Justification

Requirements

Name Version
terraform >= 1.9.0, < 2.0.0
azurerm >= 4.0.0, < 5.0.0

Providers

Name Version
azurerm 4.81.0

Modules

No modules.

Resources

Name Type
azurerm_api_management_api.this resource
azurerm_api_management_api_diagnostic.this resource
azurerm_api_management_api_policy.this resource
azurerm_api_management_backend.this resource
azurerm_api_management_logger.this resource
azurerm_api_management_named_value.this resource
azurerm_api_management_product.this resource
azurerm_api_management_product_api.this resource
azurerm_api_management_product_policy.this resource
azurerm_api_management_subscription.this resource

Inputs

Name Description Type Default Required
api_management_id Resource id of the API Management instance to configure; the instance name and resource group parse from it, per the estate's pass-ids principle. string n/a yes
apis APIs on the instance, keyed by API name. display_name defaults to the key, protocols to https,
and subscription_required to true (anonymous APIs are a choice, never a default). Definitions
arrive through import (an OpenAPI/Swagger/WSDL document, inline or by link); policy_xml_content
or policy_xml_link attaches the API-scope policy; products lists product keys from var.products
to link; diagnostic wires the API to a logger from var.loggers by key.
map(object({
revision = optional(string, "1")
display_name = optional(string)
path = optional(string)
protocols = optional(list(string), ["https"])
api_type = optional(string, "http")
service_url = optional(string)
description = optional(string)
subscription_required = optional(bool, true)
terms_of_service_url = optional(string)
version = optional(string)
version_set_id = optional(string)
version_description = optional(string)
revision_description = optional(string)
source_api_id = optional(string)

subscription_key_parameter_names = optional(object({
header = string
query = string
}))

contact = optional(object({
email = optional(string)
name = optional(string)
url = optional(string)
}))

license = optional(object({
name = optional(string)
url = optional(string)
}))

oauth2_authorization = optional(object({
authorization_server_name = string
scope = optional(string)
}))

openid_authentication = optional(object({
openid_provider_name = string
bearer_token_sending_methods = optional(list(string))
}))

import = optional(object({
content_format = string
content_value = string
wsdl_selector = optional(object({
service_name = string
endpoint_name = string
}))
}))

policy_xml_content = optional(string)
policy_xml_link = optional(string)

products = optional(list(string), [])

diagnostic = optional(object({
logger_key = string
identifier = optional(string, "applicationinsights")
sampling_percentage = optional(number, 100)
verbosity = optional(string, "information")
log_client_ip = optional(bool, true)
always_log_errors = optional(bool, true)
http_correlation_protocol = optional(string, "W3C")
operation_name_format = optional(string, "Name")
}))
}))
{} no
backends Backends, keyed by name: the targets policies route to with set-backend-service. Credentials can inject headers or query parameters (a named value reference like {{my-secret}} keeps the secret server-side), and tls validation stays on unless self-signed backends force it off.
map(object({
url = string
protocol = optional(string, "http")
description = optional(string)
resource_id = optional(string)
title = optional(string)

credentials = optional(object({
certificate = optional(list(string))
header = optional(map(string))
query = optional(map(string))
authorization = optional(object({
scheme = optional(string)
parameter = optional(string)
}))
}))

proxy = optional(object({
url = string
username = optional(string)
password = optional(string)
}))

tls = optional(object({
validate_certificate_chain = optional(bool, true)
validate_certificate_name = optional(bool, true)
}), {})

circuit_breaker_rule = optional(object({
name = string
trip_duration = string
accept_retry_after_enabled = optional(bool, false)
failure_condition = object({
interval_duration = string
count = optional(number)
percentage = optional(number)
error_reasons = optional(list(string))
status_code_ranges = optional(list(object({
min = number
max = number
})), [])
})
}))
}))
{} no
loggers Loggers, keyed by name: Application Insights (connection string, or identity_client_id for keyless) or Event Hub sinks that APIs reference by key in their diagnostic.
map(object({
description = optional(string)
buffered = optional(bool, true)
resource_id = optional(string)

application_insights = optional(object({
connection_string = optional(string)
instrumentation_key = optional(string)
identity_client_id = optional(string)
}))

eventhub = optional(object({
name = string
connection_string = optional(string)
endpoint_uri = optional(string)
user_assigned_identity_client_id = optional(string)
}))
}))
{} no
named_values Named values, keyed by name: the {{placeholders}} policies reference. Exactly one of value or value_from_key_vault per entry; a Key Vault value is implicitly secret.
map(object({
display_name = optional(string)
value = optional(string)
secret = optional(bool, false)
tags = optional(list(string))

value_from_key_vault = optional(object({
secret_id = string
identity_client_id = optional(string)
}))
}))
{} no
products Products, keyed by product id. display_name defaults to the key, published to true, and subscription_required to true. APIs join a product from the API side (apis[*].products).
map(object({
display_name = optional(string)
description = optional(string)
terms = optional(string)
published = optional(bool, true)
approval_required = optional(bool, false)
subscription_required = optional(bool, true)
subscriptions_limit = optional(number)

policy_xml_content = optional(string)
policy_xml_link = optional(string)
}))
{} no
subscriptions Subscriptions, keyed by a stable logical name. Scope by product_key or api_key (keys into var.products / var.apis), or neither for an all-APIs subscription. state defaults to active (a Terraform-managed subscription should work on apply, not sit in an approval queue) and allow_tracing to false (traces expose backend internals; turn it on deliberately, briefly).
map(object({
display_name = optional(string)
product_key = optional(string)
api_key = optional(string)
state = optional(string, "active")
allow_tracing = optional(bool, false)
subscription_id = optional(string)
user_id = optional(string)
primary_key = optional(string)
secondary_key = optional(string)
}))
{} no

Outputs

Name Description
api_ids Map of API key to resource id.
api_ids_zipmap Map of API key to {name, id} for easy composition.
backend_ids Map of backend key to resource id.
logger_ids Map of logger key to resource id.
named_value_ids Map of named value key to resource id.
product_ids Map of product key to resource id.
subscription_ids Map of subscription key to resource id.
subscription_primary_keys Map of subscription key to its primary key; what a client presents in Ocp-Apim-Subscription-Key.

About

🧩 Terraform module for APIM gateway configuration: APIs, products, backends, named values, subscriptions, loggers in one cross-referencing call

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages