Stores one reusable XML policy snippet on an Azure API Management service, for other policies to include by name. Targets
hashicorp/azurerm ~> 4.0.
- π§© Creates one
azurerm_api_management_policy_fragmentβ a reusable snippet of policy statements. - π Emits the exact
<include-fragment .../>element to paste, because the reference is hand-written XML that Terraform cannot track. - π Explains the perpetual-diff trap: a wrong
formatloops rather than failing, because it decides howvalueis read back as well as sent. - π₯ Warns that an imported fragment always arrives as
xml, whatever it was authored as. - π States plainly that the snippet is stored and returned in clear β credentials belong in a named value.
- π·οΈ Carries no
tagsβ the resource has none. The universal tail istimeoutsonly.
π‘ Why it matters: a policy fragment's only interface is its name, used as a bare string inside someone else's XML. Terraform creates no dependency, enforces no ordering, and reports nothing when a rename orphans an include. This module makes that link as explicit as it can be made.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- β Star this repository to help others discover this Terraform module.
- π€ Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
flowchart TB
rg["terraform-azurerm-resource-group"]
kv["terraform-azurerm-key-vault"]
apim["terraform-azurerm-api-management"]
nv["terraform-azurerm-api-management-named-value"]
this["terraform-azurerm-api-management-policy-fragment"]
api["terraform-azurerm-api-management-api"]
svcpol["terraform-azurerm-api-management-policy"]
rg -->|"name"| apim
apim -->|"id"| this
apim -->|"name"| nv
kv -->|"secret behind a named value"| nv
nv -->|"referenced from the fragment as a double-brace token"| this
this -->|"name, pasted into an include-fragment element"| api
this -->|"name, pasted into an include-fragment element"| svcpol
classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
classDef keystone fill:#004578,stroke:#002b4d,color:#ffffff;
classDef sib fill:#eef3f8,stroke:#b9c7d6,color:#1b2a3a;
class this me;
class apim keystone;
class rg,kv,nv,api,svcpol sib;
The API Management family is large, so this diagram names only this module's direct neighbours rather than every api-management-* module in the library. Note that both downstream edges are dashed in practice rather than in Terraform: a policy includes this fragment by writing its name into XML, so nothing in the graph above is an actual resource reference until include_fragment_snippet is interpolated into the consuming document.
flowchart TB
subgraph inputs["Inputs"]
ident["name plus api_management_id, both force-new"]
content["value, the XML snippet"]
fmt["format, xml or rawxml"]
desc["description"]
end
this["azurerm_api_management_policy_fragment.this"]
subgraph outputs["Outputs"]
oid["id, name, include_fragment_snippet"]
oval["value, format, description"]
oshape["value_looks_like_a_full_policy_document, references_a_named_value"]
ofact["the_include_is_a_string_terraform_cannot_see, a_wrong_format_loops_instead_of_failing"]
end
ident --> this
content --> this
fmt -->|"decides how value is sent AND read back"| this
desc --> this
this --> oid
this --> oval
this --> oshape
this --> ofact
classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
classDef sib fill:#eef3f8,stroke:#b9c7d6,color:#1b2a3a;
class this me;
class ident,content,fmt,desc,oid,oval,oshape,ofact sib;
Resource inventory
| Resource | Count | Notes |
|---|---|---|
azurerm_api_management_policy_fragment.this |
1 | The keystone, and the only resource. |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/azurerm |
~> 4.0 |
| Provider block | None in this module β the caller configures provider "azurerm" { features {} }, auth and subscription |
Schema notes that bite
nameandapi_management_idare force-new, and both inherit that from provider helpers rather than from this resource's own field list. Everything else updates in place.formatdecides howvalueis read back as well as sent. A mismatch returns a differently-encoded string on every read, so Terraform proposes the same update forever β one that applies successfully and comes straight back.- An imported fragment always arrives as
xml. The provider's import callback sets it unconditionally, because the API only reports a format when one was requested and it is notxml. - A format change deliberately re-sends
value, because the same string means something different under the other format. - The diff suppression strips all whitespace and unescapes XML entities before comparing. Reformatting produces no plan β and neither does a genuine change in literal whitespace inside a text value, or a change purely in escaping.
- Delete has no not-found tolerance, unlike the sibling
azurerm_api_management_policyβ wrapped by theterraform-azurerm-api-management-policymodule β which does tolerate it. A fragment removed out of band makesterraform destroyfail rather than converge. valueis validated by nothing. The provider accepts any string, including one that is not XML at all.- No
tags, nolocation. The universal tail istimeoutsonly.
Microsoft.ApiManagement/service/policyFragments/*β write and delete, scoped to the parent API Management service. A custom role at the service is enough; API Management Service Contributor on the service, or Contributor on its resource group, both cover it more broadly than necessary.Microsoft.ApiManagement/service/policyFragments/readon the service, for the existence check before creating and for every refresh.
π This resource holds no credential β provided the fragment does not contain one. Reading it returns the fragment's full content, so plan access is content access: anyone who can refresh this resource can read every policy statement in it. That is a reason to reference a named value rather than paste a key into the XML, not a reason to restrict the plan.
- An existing API Management service.
- Any named value the snippet references must already exist on the same service. Nothing validates the reference β a
{{missing}}token applies cleanly and fails at request time. - A policy that includes the fragment, if it is to do anything at all.
- The caller configures
provider "azurerm" { features {} }, authentication and subscription.
terraform-azurerm-api-management-policy-fragment/
βββ providers.tf # required_version + pinned azurerm; no provider block
βββ variables.tf # deeply-typed inputs; the unambiguous content mistakes rejected at plan time
βββ main.tf # the single keystone resource
βββ outputs.tf # id first, then the include key, the content, and the silent behaviours
βββ README.md # this document
βββ SCOPE.md # the cross-module contract
βββ LICENSE # MIT
βββ .gitignore # the canonical library ignore set
provider "azurerm" {
features {}
}
module "cors_fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "cors-standard"
api_management_id = module.apim.id
value = file("${path.module}/fragments/cors-standard.xml")
description = "Standard CORS. Included by the orders and payments API policies."
}βΉοΈ The caller configures the provider, including the mandatory
features {}block. This module declares none.
β οΈ This creates the snippet and nothing more. Until a policy writes<include-fragment fragment-id="cors-standard" />, nothing runs it.
Consumes
| Input | Type | Source |
|---|---|---|
api_management_id |
string |
terraform-azurerm-api-management output id |
value |
string |
a local .xml file read with file() |
named-value tokens inside value |
string |
terraform-azurerm-api-management-named-value β referenced by name in the XML, not wired |
Emits
| Output | Consumed by |
|---|---|
name |
the XML of every consuming policy β this is the real interface |
include_fragment_snippet |
paste directly into a consuming policy |
id |
management locks, role assignments, review |
format / value |
troubleshooting a perpetual diff |
1 Β· Minimal CORS fragment
module "fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "cors-standard"
api_management_id = module.apim.id
value = file("${path.module}/fragments/cors-standard.xml")
}π‘ Read the snippet from a file rather than a heredoc. Terraform's own interpolation syntax collides with policy expressions, and
file()interpolates nothing at all.
2 Β· With a description that records its consumers
module "fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "cors-standard"
api_management_id = module.apim.id
value = file("${path.module}/fragments/cors-standard.xml")
description = "Standard CORS. Included by: orders API, payments API, service-wide inbound."
}π‘ A fragment does not know which policies include it, and no attribute anywhere lists them. The description is the only place that relationship is written down inside Azure.
3 Β· rawxml for expression-heavy snippets
module "fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "rate-limit-by-subscription"
api_management_id = module.apim.id
format = "rawxml"
value = file("${path.module}/fragments/rate-limit.xml")
}
β οΈ rawxmltakes the value literally, so a policy expression can be written as-is instead of entity-escaped. Both values are lower case and the check is case-sensitive β"RawXml"is rejected.
4 Β· Referencing a named value instead of a literal secret
module "backend_auth_fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "backend-auth"
api_management_id = module.apim.id
format = "rawxml"
# The XML references {{backend-api-key}} rather than carrying the key.
value = file("${path.module}/fragments/backend-auth.xml")
}π A fragment's content is stored on the service in clear and returned on every read, so anything pasted into it lands in Terraform state and in every plan that refreshes it. Reference a named value β ideally one backed by a Key Vault secret β and keep the secret out of the snippet entirely.
references_a_named_valuereports whether a fragment does this.
5 Β· Wiring the include into a policy
module "fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "cors-standard"
api_management_id = module.apim.id
value = file("${path.module}/fragments/cors-standard.xml")
}
# The include is a STRING, so build it from the module output rather than
# retyping the name. This also gives Terraform a real dependency edge.
resource "azurerm_api_management_policy" "service" {
api_management_id = module.apim.id
xml_content = <<-XML
<policies>
<inbound>
<base />
${module.fragment.include_fragment_snippet}
</inbound>
<backend><base /></backend>
<outbound><base /></outbound>
<on-error><base /></on-error>
</policies>
XML
}π‘ Interpolating
include_fragment_snippetis the one way to make the fragment a genuine Terraform dependency: the policy now cannot be created before the fragment, and a rename propagates instead of orphaning the include.
6 Β· for_each over a directory of fragments
locals {
fragments = {
cors = { file = "cors-standard.xml", format = "xml", note = "Standard CORS." }
rate_limit = { file = "rate-limit.xml", format = "rawxml", note = "Per-subscription rate limit." }
correlation = { file = "correlation-id.xml", format = "xml", note = "Propagates a correlation header." }
}
}
module "fragments" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
for_each = local.fragments
name = replace(each.key, "_", "-")
api_management_id = module.apim.id
value = file("${path.module}/fragments/${each.value.file}")
format = each.value.format
description = each.value.note
}π‘ The map key is stable, so adding a fourth fragment never touches the first three.
nameis force-new, which is exactly why keying on a stable identifier matters here.
7 Β· Emitting the include snippets for a policy library
output "fragment_includes" {
value = { for k, m in module.fragments : k => m.include_fragment_snippet }
}fragment_includes = {
"correlation" = "<include-fragment fragment-id=\"correlation-id\" />"
"cors" = "<include-fragment fragment-id=\"cors-standard\" />"
"rate_limit" = "<include-fragment fragment-id=\"rate-limit\" />"
}
π‘ Hand this to whoever writes the policies. Every one of those strings is otherwise typed by hand, and a typo fails at request time rather than at apply.
8 Β· Adopting an existing fragment
module "fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "cors-standard"
api_management_id = module.apim.id
value = file("${path.module}/fragments/cors-standard.xml")
# Import arrives as xml regardless of how the fragment was authored.
# If it was rawxml, set that AFTER importing and apply the correction.
format = "xml"
}terraform import 'module.fragment.azurerm_api_management_policy_fragment.this' \
"/subscriptions/$SUB/resourceGroups/rg-platform-apim/providers/Microsoft.ApiManagement/service/apim-platform-prod/policyFragments/cors-standard"
β οΈ an_imported_fragment_always_arrives_as_xmlstates this as a constant. Correcting the format afterwards is an in-place update, not a replacement β and the provider re-sends the value alongside it, because the same string means something different under the other format.
9 Β· Diagnosing a diff that will not settle
output "fragment_format_as_azure_reports_it" {
value = module.fragment.format
}
output "fragment_content_matches_configuration" {
value = module.fragment.value_matches_configuration
}
output "fragment_content_fingerprint" {
value = module.fragment.value_fingerprint
}
β οΈ value_matches_configurationreading false while the plan keeps proposing the same update is the signature of a format mismatch, not of a broken value. The provider requests the content in whateverformatis configured, so the wrong one returns a differently-encoded string every read. Switch the format rather than reformatting the XML.
10 Β· Custom timeouts
module "fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "cors-standard"
api_management_id = module.apim.id
value = file("${path.module}/fragments/cors-standard.xml")
timeouts = {
create = "45m"
read = "10m"
update = "45m"
delete = "45m"
}
}
β οΈ A key that is notcreate,read,updateordeleteis silently discarded by Terraform's type conversion β no error, and no timeout.
11 Β· Reviewing fragment hygiene across an estate
output "fragments_wrapped_as_full_documents" {
value = [for k, m in module.fragments : k if m.value_looks_like_a_full_policy_document]
}
output "fragments_with_an_xml_prolog" {
value = [for k, m in module.fragments : k if m.value_declares_an_xml_prolog]
}
output "fragments_not_using_a_named_value" {
value = [for k, m in module.fragments : k if !m.references_a_named_value]
}π‘ The first two are shape mistakes this module reports rather than refuses, because the provider accepts any string. The third is a prompt, not a verdict: plenty of fragments legitimately reference nothing β but a fragment that talks to a backend and references no named value is worth opening.
12 Β· ποΈ End-to-end composition
provider "azurerm" {
features {}
}
module "rg" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
name = "rg-platform-apim"
location = "eastus2"
}
module "apim" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management.git?ref=v1.0.0"
name = "apim-platform-prod"
resource_group_name = module.rg.name
location = module.rg.location
publisher_name = "Platform Engineering"
publisher_email = "platform-engineering@example.com"
sku_name = "Developer_1"
}
# The secret the fragment needs, held as a named value rather than in the XML.
module "backend_key" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-named-value.git?ref=v1.0.0"
name = "backend-api-key"
resource_group_name = module.rg.name
api_management_name = module.apim.name
display_name = "backend-api-key"
secret = true
value = var.backend_api_key
}
module "backend_auth_fragment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-api-management-policy-fragment.git?ref=v1.0.0"
name = "backend-auth"
api_management_id = module.apim.id
format = "rawxml"
description = "Adds the backend auth header from the backend-api-key named value."
# References {{backend-api-key}}; carries no secret itself.
value = file("${path.module}/fragments/backend-auth.xml")
depends_on = [module.backend_key]
}
# The include is a string, so interpolate it to create a real dependency.
resource "azurerm_api_management_policy" "service" {
api_management_id = module.apim.id
xml_content = <<-XML
<policies>
<inbound>
<base />
${module.backend_auth_fragment.include_fragment_snippet}
</inbound>
<backend><base /></backend>
<outbound><base /></outbound>
<on-error><base /></on-error>
</policies>
XML
}π The backend key never appears in the fragment. It lives in a named value marked secret, and the fragment references it by token β which keeps it out of the fragment's content, out of this module's state, and out of every plan that refreshes it.
β οΈ depends_onis used because the named-value reference is a string inside XML, not a Terraform reference: without it, nothing orders the two. The include, by contrast, gets its ordering for free from the interpolation.
| Group | Variables |
|---|---|
| Identity (both force-new) | name, api_management_id |
| Content | value, format, description |
| Universal tail | timeouts |
Full input schemas
name = string # force-new; 1-80 chars, alphanumeric plus - and _; also the include key
api_management_id = string # force-new; the SERVICE id, not its name
value = string # required; the XML snippet, validated by nothing at the provider
format = optional(string, "xml") # "xml" | "rawxml", lower case
description = optional(string)
timeouts = optional(object({ create, read, update, delete }))See variables.tf for the full descriptions and every validation.
| Output | Description | Notes |
|---|---|---|
id |
Resource ID of the policy fragment | Emitted first; little consumes it |
name |
Name of the fragment | The real interface β the include key |
include_fragment_snippet |
The exact <include-fragment .../> element |
Derived; paste it |
api_management_id |
Resource ID of the parent service | Rebuilt from the Resource ID on read |
api_management_name |
Name of the parent service | Derived from the ID |
resource_group_name |
Resource group holding the parent service | Derived from the ID |
value_fingerprint |
SHA-256 of the content as Azure returns it | The content itself is deliberately not emitted |
value_matches_configuration |
True when Azure's content matches the configuration, whitespace-normalised | Derived; false is the format-mismatch signature |
value_length_in_characters |
Length of the content as Azure returns it | Derived |
format |
xml or rawxml, as Azure reports it |
Defaults to xml when Azure says nothing |
description |
The fragment's description as Azure holds it | |
value_looks_like_a_full_policy_document |
True when the snippet has a <policies> root |
Derived; reported, not refused |
value_declares_an_xml_prolog |
True when the snippet starts with <?xml ... ?> |
Derived; reported, not refused |
uses_raw_xml_format |
True on the rawxml format |
Derived |
references_a_named_value |
True when the snippet uses a {{...}} token |
Derived |
fragment_alone_runs_nothing |
Constant true | |
the_include_is_a_string_terraform_cannot_see |
Constant true | |
a_wrong_format_loops_instead_of_failing |
Constant true | The perpetual-diff signature |
an_imported_fragment_always_arrives_as_xml |
Constant true | |
whitespace_and_element_order_never_produce_a_diff |
Constant true | Cuts both ways |
destroy_fails_if_the_fragment_is_already_gone |
Constant true | |
put_credentials_in_a_named_value_not_in_the_fragment |
Constant true | |
force_new_fields |
The fields whose change destroys and recreates the record | |
fields_azure_returns_on_read |
The only fields in which drift can be detected | |
this_resource_supports_no_azure_resource_tags |
Constant true |
The fragment's content is emitted, deliberately: it is configuration rather than a credential, Azure returns it on every read, and it is the only drift signal this resource has.
The name is the interface, and Terraform cannot see it being used. A policy includes a fragment by writing <include-fragment fragment-id="NAME" /> into its XML. That is a string, not a reference: no dependency is created, no ordering is enforced, and no plan reports that a rename has orphaned an include. Where the fragment and its consumers share a configuration, interpolating include_fragment_snippet is the one way to turn the string back into a dependency.
A wrong format loops instead of failing. The provider requests the fragment's value from Azure in whatever format is configured, so a mismatch returns a differently-encoded string on every read and Terraform proposes the same update forever β one that applies successfully and comes straight back. The published documentation describes the symptom without naming the cause. Switch the format; do not reformat the value.
Import is lossy in exactly one field. The API only reports a format when one was explicitly requested and it is not xml, so the provider's import callback sets xml unconditionally. A rawxml fragment therefore imports as xml and shows a diff until corrected β an in-place update, during which the provider deliberately re-sends the value.
The diff suppression is generous in both directions. It parses both sides as XML to absorb element ordering, and falls back to a string comparison with every space, tab, newline and indent stripped and XML entities unescaped. Reformatting a fragment file produces no plan, which is convenient. A genuine change in literal whitespace inside a text value, or in escaping alone, is equally invisible β which is not.
Destroy is stricter here than elsewhere in the family. The delete path has no not-found tolerance, so a fragment removed through the portal makes terraform destroy fail rather than converge. terraform state rm is the recovery. Azure may also refuse to delete a fragment a policy still includes; the provider surfaces whatever Azure says and adds no check of its own.
The content is not protected and should not need to be. It is stored on the service in clear and returned on every read, so it lands in state and in every refreshing plan. This module emits it because it is the only drift signal available. The correct response is to keep secrets out of the snippet by referencing a named value.
| Concern | Default in this module | Opt-out |
|---|---|---|
| Format | format = "xml", matching the provider and Azure |
set "rawxml" |
| Content validation | only unambiguous mistakes are refused β an empty value, a value with no XML at all, a value that is plainly a file path | none |
| Shape mistakes | a <policies> root and an XML prolog are reported through outputs, not refused, because the provider accepts any string and a validation failure blocks destroy |
none |
| Secrets | not withheld β the content is emitted, and the module says plainly that credentials belong in a named value instead | none |
| The include | emitted as a ready-to-paste element, so the one string that matters is not retyped | none |
| Tagging | no tags variable β the resource exposes none; use description |
tag the parent API Management service |
terraform init -backend=false
terraform validate
terraform fmt -checkPin the module by immutable tag β ?ref=v1.0.0 β never a branch. This module is plan-only from the library's point of view: a human applies from CI against a reviewed plan.
terraform validate covers the name pattern, the service-ID shape (including an ID that already carries a /policyFragments/ segment), the format enum with its lower-case requirement, and the three content checks β non-empty, contains XML at all, and is not plainly a file path.
A variables-only terraform plan fires the same validations against a .tfvars file and is also the cheapest way to confirm the derived flags. Drive both directions: a fully populated positive fixture is what catches a check that is wrong in the accepting direction, and Terraform skips a validation whose referenced variable has already failed, so a short error list is not proof a check is missing.
What only a real plan or apply reaches: whether the service exists, whether the name collides with an existing fragment, and whether Azure accepts the XML. What nothing reaches, at any stage: whether any policy includes the fragment, whether a referenced named value exists, and whether the format matches how the content is actually written β that last one surfaces as a diff that will not settle.
id = "/subscriptions/.../service/apim-platform-prod/policyFragments/backend-auth"
name = "backend-auth"
include_fragment_snippet = "<include-fragment fragment-id=\"backend-auth\" />"
api_management_id = "/subscriptions/.../service/apim-platform-prod"
api_management_name = "apim-platform-prod"
resource_group_name = "rg-platform-apim"
format = "rawxml"
description = "Adds the backend auth header from the backend-api-key named value."
value = "<set-header name=\"Authorization\" exists-action=\"override\"><value>{{backend-api-key}}</value></set-header>"
value_looks_like_a_full_policy_document = false
value_declares_an_xml_prolog = false
uses_raw_xml_format = true
references_a_named_value = true
force_new_fields = [
"name",
"api_management_id",
]
the_include_is_a_string_terraform_cannot_see = true
a_wrong_format_loops_instead_of_failing = true
| Symptom | Cause | Fix |
|---|---|---|
| An update applies successfully and returns on the next plan | format does not match how the content is written; the provider reads the value back in the configured format |
Switch the format. a_wrong_format_loops_instead_of_failing names the signature |
A diff appears immediately after terraform import |
Import always sets format = "xml", whatever the fragment was authored as |
Set the real format and apply. It is an in-place update, not a replacement |
| The fragment applies and nothing changes at runtime | No policy includes it. An unreferenced fragment is valid and inert | Add <include-fragment fragment-id="NAME" /> to a policy. Use include_fragment_snippet |
| A policy that includes the fragment fails at request time | The fragment name in the XML does not match, or a {{named-value}} in the snippet does not exist |
Compare against name; confirm the named value exists on the same service |
terraform destroy fails on a fragment that is already gone |
The delete path has no not-found tolerance, unlike most of this family | terraform state rm this resource, then continue |
| Azure refuses to delete the fragment | A policy still includes it. The provider adds no check and surfaces Azure's error | Remove the include first, then destroy |
| Reformatting the XML produces no plan | The diff suppression strips all whitespace and unescapes entities before comparing | Expected. Note it also hides a genuine whitespace or escaping change |
format = "RawXml" rejected |
Both values are lower case and the check is case-sensitive | Use "rawxml" |
value rejected as containing no XML |
A file path was passed instead of the file's contents | value = file("${path.module}/fragments/NAME.xml") |
| Renaming the fragment broke every policy using it | The include is a string Terraform cannot see, so the rename orphaned it | Interpolate include_fragment_snippet into the policy so the two move together |
azurerm_api_management_policy_fragmentβ provider resource reference- Reuse policy configurations in Azure API Management β what a fragment is and how a policy includes one
- API Management policy reference β the statements a fragment may contain
- Sibling modules:
terraform-azurerm-api-management,terraform-azurerm-api-management-policy,terraform-azurerm-api-management-named-value,terraform-azurerm-api-management-api - This module's
SCOPE.mdβ the cross-module contract, permissions and prerequisites
π "Infrastructure as Code should be standardized, consistent, and secure."