Search code examples
xmlazureterraformterraform-provider-azure

The 'condition' attribute is invalid - The value '@contains(string, 'substring')' is not within allowed values range


I am writing a terraform code where I have to add a XML as well. I also want to add condition to xml for filtering with basepath. My substring will be a basepath. I am adding it like a below code

resource "azurerm_api_management_api_policy" "auth-policy" {   
     for_each            = azurerm_api_management_api.api   
     xml_content = <<XML
    <policies>
      <inbound>
        <cors allow-credentials="true">
          <allowed-methods>
            <method>*</method>
          </allowed-methods>
        </cors>
        <base />
        <set-header name="ApiKey" exists-action="skip">
            <value>Default</value>
        </set-header>
        <choose>
            <when condition='@(context.Request.Headers.ContainsKey("X-Forwarded-Host"))'>
                  <set-header name="X-Forwarded-Host" exists-action="override">
                      <value>@(context.Request.OriginalUrl.ToUri().Host)</value>
                  </set-header>
            </when>
        </choose>
        <choose>
            <when condition="@contains(${each.value.name}, 'substring')">
                <rate-limit-by-key calls="100" renewal-period="60" counter-key="@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Subject)" />
            </when>
            <otherwise>
                <rate-limit-by-key calls="100000" renewal-period="60" counter-key="@(context.Subscription.Id)" />
            </otherwise>
        </choose>
        <validate-jwt header-name="Authorization" require-scheme="Bearer"
          failed-validation-httpcode="401"
          failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
          <openid-config url="https://*******************" />
          <audiences>
            ...........
          </audiences>
          <issuers>
            ...............
          </issuers>
        </validate-jwt>
      </inbound>
      <backend>
        <base />
      </backend>
      <outbound>
        <base />
      </outbound>
      <on-error>
        <base />
      </on-error>
    </policies>   
XML
 }

but it throws error

Code="ValidationError" Message="One or more fields contain incorrect values:" Details=[{"code":"ValidationError","message":"Error in element 'choose' on line 28, column 10: The 'condition' attribute is invalid - The value '@contains(string, 'sunstring')' is not within allowed values range."

Is my condition correct, or do we get basepath with a request where I can use it directly instead of each.value.name(I get the names of the APIs) ? I am new to xml and APIM, Thanks in advance!


Solution

  • What I would suggest in a case like this is to use the built-in templatefile function. This way, you do not have to specify the XML file inline, rather as a file with some placeholder variable names. For example, you could create a file in the same directory with the name api_policy.xml.tftpl. The tftpl suffix is just a sign it is a terraform template file. The content of the file would then be:

    <policies>
          <inbound>
            <cors allow-credentials="true">
              <allowed-methods>
                <method>*</method>
              </allowed-methods>
            </cors>
            <base />
            <set-header name="ApiKey" exists-action="skip">
                <value>Default</value>
            </set-header>
            <choose>
                <when condition='@(context.Request.Headers.ContainsKey("X-Forwarded-Host"))'>
                      <set-header name="X-Forwarded-Host" exists-action="override">
                          <value>@(context.Request.OriginalUrl.ToUri().Host)</value>
                      </set-header>
                </when>
            </choose>
            <choose>
                <when condition="@contains(${api_name}, 'substring')">
                    <rate-limit-by-key calls="100" renewal-period="60" counter-key="@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Subject)" />
                </when>
                <otherwise>
                    <rate-limit-by-key calls="100000" renewal-period="60" counter-key="@(context.Subscription.Id)" />
                </otherwise>
            </choose>
            <validate-jwt header-name="Authorization" require-scheme="Bearer"
              failed-validation-httpcode="401"
              failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
              <openid-config url="https://*******************" />
              <audiences>
                ...........
              </audiences>
              <issuers>
                ...............
              </issuers>
            </validate-jwt>
          </inbound>
          <backend>
            <base />
          </backend>
          <outbound>
            <base />
          </outbound>
          <on-error>
            <base />
          </on-error>
        </policies> 
    

    Note that the condition line now has this:

    when condition="@contains(${api_name}, 'substring')"
    

    The ${api_name} syntax tells terraform to look for values assigned to the variable with the same name which is provided when calling the templatefile function:

    resource "azurerm_api_management_api_policy" "auth-policy" {   
         for_each    = azurerm_api_management_api.api   
         xml_content = templatefile("${path.root}/api_policy.xml.tftpl", {
           api_name = each.value.name
         })
    }