azureazure-bicepazure-application-gateway

Application gateway: how to define redirectConfigurations in bicep


I have an app gateway in azure which is written in bicep. It is successfully deployed via code. I need to add a redirectConfiguration which didn't exist in our module yet.

This is the agw code:

targetScope = 'resourceGroup'

@description('Deployment Location')
param parLocation string = resourceGroup().location

@description('The name of the AGW')
param parAppGwName string = 'alz-o-agw-01'

@description('Tags to assign to the AGW')
param parTags object = {}

param parApplicationGatewayConfiguration object = {
  sku: {
    name: 'WAF_v2'
    tier: 'WAF_v2'
    capacity: 1
  }
  identity: {
    type: 'SystemAssigned'
  }
  gatewayIPConfigurations: []
  frontendIPConfigurations: []
  frontendPorts: []
  sslCertificates: []
  trustedRootCertificates: []
  probes: []
  backendAddressPools: []
  backendHttpSettingsCollection: []
  httpListeners: []
  rewriteRuleSets: []
  requestRoutingRules: []
  urlPathMaps: []
  redirectConfigurations: []
}

param parWebApplicationFirewallConfiguration object = {
  enabled: false
  firewallMode: 'Detection'
  ruleSetType: 'OWASP'
  ruleSetVersion: '3.2'
  disabledRuleGroups: []
  requestBodyCheck: false
  exclusions: []
  maxRequestBodySizeInKb: 128
  fileUploadLimitInMb: 500
}

module resMappedPathRules 'getPathRules.bicep' = [
  for urlMapPath in parApplicationGatewayConfiguration.urlPathMaps: {
    name: urlMapPath.name
    params: {
      parPathRules: urlMapPath.pathRules
      parGatewayName: parAppGwName
    }
  }
]

resource resAppGW 'Microsoft.Network/applicationGateways@2024-03-01' = {
  name: parAppGwName
  location: parLocation
  zones: [
    '1'
    '2'
    '3'
  ]
  identity: parApplicationGatewayConfiguration.identity
  tags: parTags
  properties: {
    sku: parApplicationGatewayConfiguration.sku
    sslPolicy: {
      policyType: 'Predefined'
      policyName: 'AppGwSslPolicy20170401S'
    }
    enableHttp2: true
    webApplicationFirewallConfiguration: {
      enabled: parWebApplicationFirewallConfiguration.enabled
      firewallMode: parWebApplicationFirewallConfiguration.firewallMode
      ruleSetType: parWebApplicationFirewallConfiguration.ruleSetType
      ruleSetVersion: parWebApplicationFirewallConfiguration.ruleSetVersion
      disabledRuleGroups: parWebApplicationFirewallConfiguration.disabledRuleGroups
      exclusions: parWebApplicationFirewallConfiguration.exclusions
      requestBodyCheck: parWebApplicationFirewallConfiguration.requestBodyCheck
      maxRequestBodySizeInKb: parWebApplicationFirewallConfiguration.maxRequestBodySizeInKb
      fileUploadLimitInMb: parWebApplicationFirewallConfiguration.fileUploadLimitInMb
    }
    sslCertificates: [
      for sslcert in parApplicationGatewayConfiguration.sslCertificates: {
        name: sslcert.name
        properties: {
          keyVaultSecretId: 'https://${sslcert.keyvaultName}${environment().suffixes.keyvaultDns}/secrets/${sslcert.secretName}'
        }
      }
    ]
    trustedRootCertificates: parApplicationGatewayConfiguration.trustedRootCertificates
    gatewayIPConfigurations: parApplicationGatewayConfiguration.gatewayIPConfigurations
    frontendIPConfigurations: parApplicationGatewayConfiguration.frontendIPConfigurations
    frontendPorts: parApplicationGatewayConfiguration.frontendPorts
    httpListeners: [
      for httplistener in parApplicationGatewayConfiguration.httpListeners: {
        name: httplistener.name
        properties: {
          protocol: 'Https'
          hostName: httplistener.hostName
          frontendIPConfiguration: {
            id: resourceId(
              'Microsoft.Network/applicationGateways/frontendIPConfigurations',
              parAppGwName,
              httplistener.frontendIpName
            )
          }
          frontendPort: {
            id: resourceId(
              'Microsoft.Network/applicationGateways/frontendPorts',
              parAppGwName,
              httplistener.frontendPortName
            )
          }
          sslCertificate: httplistener.sslCertificateName != null
            ? {
                id: resourceId(
                  'Microsoft.Network/applicationGateways/sslCertificates',
                  parAppGwName,
                  httplistener.sslCertificateName
                )
              }
            : null
          firewallPolicy: httplistener.wafPolicyName != null
            ? {
                id: resourceId(
                  'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies',
                  httplistener.wafPolicyName
                )
              }
            : null
        }
      }
    ]
    backendAddressPools: [
      for (backendAddressPool, index) in parApplicationGatewayConfiguration.backendAddressPools: {
        name: backendAddressPool.name
        properties: {
          backendAddresses: [
            {
              fqdn: backendAddressPool.fqdn
            }
          ]
        }
      }
    ]
    backendHttpSettingsCollection: [
      for (bhsc, index) in parApplicationGatewayConfiguration.backendHttpSettingsCollection: {
        name: bhsc.name
        properties: {
          port: bhsc.port
          protocol: 'Https'
          cookieBasedAffinity: 'Disabled'
          pickHostNameFromBackendAddress: bhsc.pickHostNameFromBackendAddress
          hostName: !bhsc.pickHostNameFromBackendAddress ? bhsc.hostName : null
          requestTimeout: bhsc.?requestTimeout ?? 20
          probe: {
            id: resourceId('Microsoft.Network/applicationGateways/probes', parAppGwName, bhsc.probeName)
          }
        }
      }
    ]
    probes: [
      for (probe, index) in parApplicationGatewayConfiguration.probes: {
        name: probe.name
        properties: {
          protocol: 'Https'
          pickHostNameFromBackendHttpSettings: probe.pickHostNameFromBackendHttpSettings
          path: probe.path
          interval: 30
          timeout: 30
          unhealthyThreshold: 3
          minServers: 0
          match: {
            body: ''
            statusCodes: [
              '200-299'
            ]
          }
        }
      }
    ]
    rewriteRuleSets: parApplicationGatewayConfiguration.rewriteRuleSets
    requestRoutingRules: [
      for requestRouting in parApplicationGatewayConfiguration.requestRoutingRules: {
        name: requestRouting.name
        properties: {
          ruleType: requestRouting.ruleType
          httpListener: {
            id: resourceId(
              'Microsoft.Network/applicationGateways/httpListeners',
              parAppGwName,
              requestRouting.httpListenerName
            )
          }
          urlPathMap: requestRouting.urlPathMapName != null
            ? {
                id: resourceId(
                  'Microsoft.Network/applicationGateways/urlPathMaps',
                  parAppGwName,
                  requestRouting.urlPathMapName
                )
              }
            : null
          backendAddressPool: requestRouting.backendAddressPoolName != null
            ? {
                id: resourceId(
                  'Microsoft.Network/applicationGateways/backendAddressPools',
                  parAppGwName,
                  requestRouting.backendAddressPoolName
                )
              }
            : null
          rewriteRuleSet: requestRouting.rewriteRuleSetName != null
            ? {
                id: resourceId(
                  'Microsoft.Network/applicationGateways/rewriteRuleSets',
                  parAppGwName,
                  requestRouting.rewriteRuleSetName
                )
              }
            : null
          backendHttpSettings: requestRouting.backendHttpSettingsName != null
            ? {
                id: resourceId(
                  'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',
                  parAppGwName,
                  requestRouting.backendHttpSettingsName
                )
              }
            : null
          priority: requestRouting.priority
        }
      }
    ]
    urlPathMaps: [
      for (urlPathMap, index) in parApplicationGatewayConfiguration.urlPathMaps: {
        name: urlPathMap.name
        properties: {
          defaultBackendAddressPool: {
            id: resourceId(
              'Microsoft.Network/applicationGateways/backendAddressPools',
              parAppGwName,
              urlPathMap.defaultBackendAddressPoolName
            )
          }
          defaultBackendHttpSettings: {
            id: resourceId(
              'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',
              parAppGwName,
              urlPathMap.defaultBackendHttpSettingsName
            )
          }
          pathRules: resMappedPathRules[index].outputs.mappedPathRules
        }
      }
    ]
    redirectConfigurations: [
      for redirectConfig in parApplicationGatewayConfiguration.redirectConfigurations: {
        name: redirectConfig.name
        id: resourceId(
          'Microsoft.Network/applicationGateways/redirectConfigurations',
          parAppGwName,
          redirectConfig.name
        )
        properties: {
          redirectType: redirectConfig.redirectType
          targetUrl: redirectConfig.targetUrl
          includePath: redirectConfig.includePath
          includeQueryString: redirectConfig.includeQueryString
          requestRoutingRules: [
            {
              id: resourceId(
                'Microsoft.Network/applicationGateways/requestRoutingRules',
                parAppGwName,
                redirectConfig.requestRoutingRuleName
              )
            }
          ]
        }
      }
    ]
  }
}

output appgwResourceId string = resAppGW.id

And this is the relevant input for the redirectconfig:

  redirectConfigurations: [
    {
      name: 'test-t-redirect-config',
      redirectType: 'Permanent',
      targetUrl: 'https://testurl/arm/webclient/index.html',
      requestRoutingRuleName: 'test-t-https-rule',
      includePath: true,
      includeQueryString: true
    }
  ]

I get the following error:

Unable to process template language expressions for resource '/subscriptions/0123456789/resourceGroups/alz-network-o-rg/providers/Microsoft.Network/applicationGateways/alz-o-agw-01' at line '1' and column '5519'. 'Unable to evaluate template language function 'resourceId': all function arguments must be string literals. Please see https://aka.ms/arm-resource-functions/#resourceid for usage details.'

I've tried to use the following ways already:

id: resourceId(
                'Microsoft.Network/applicationGateways/requestRoutingRules',
                '${parAppGwName}/${redirectConfig.requestRoutingRuleName}'
               )

And another attempt was with concat.

It is probably something very silly that I am missing, any ideas?


Solution

  • Because of the fact that a redirect only needs a listener and a rule, a lot of objects where checking whether the params were being sent. They weren't because they are not necessary for a redirect. To catch this, I made the following statements on all optional components of the agw:

    probe: contains(bhsc, 'probeName') && bhsc.probeName != null
      ? {
           id: resourceId('Microsoft.Network/applicationGateways/probes', 
               parAppGwName, bhsc.probeName)
        }
      : null
    

    the contains checks whether the value is being sent. This, combined with the null check, makes that no resourceId will be retrieved if the object which is looped over does not have the relevant keys.