This check flags Azure Application Gateways that run without an explicit SSL policy, which means they may fall back to weak ciphers and outdated TLS versions. Lock it down by assigning a predefined or custom SSL policy that enforces TLS 1.2 or higher.
An Azure Application Gateway sits at the edge of your network and terminates TLS for the apps behind it. When you do not configure an SSL policy on that gateway, Azure applies its default policy, and that default has historically permitted protocol versions and cipher suites you almost certainly do not want exposed to the internet. This check catches gateways where no SSL policy has been set, so you can replace the silent default with something you actually control.
What this check detects
The appgateway_nosslpolicy check inspects each Application Gateway in your Azure subscription and looks at its sslPolicy configuration. If the property is absent, the gateway is relying on the Azure default SSL policy rather than an explicit one you have chosen.
There are three ways to configure TLS settings on an Application Gateway:
- Predefined policies maintained by Azure, such as
AppGwSslPolicy20220101, which bundle a minimum protocol version and a curated cipher list. - Customv2 policies that let you set a minimum protocol version and pick from the full cipher suite catalog.
- Custom policies where you specify the exact protocol floor and cipher suites yourself.
When none of these is present, the gateway uses whatever the default policy allows, and that default is not guaranteed to enforce modern TLS.
Note: The legacy default policy on older gateways still permits TLS 1.0 and TLS 1.1. Both have been deprecated by the IETF and rejected by PCI DSS for years. A gateway with no explicit policy can quietly accept handshakes from clients using these protocols.
Why it matters
TLS termination at the gateway is the boundary between the public internet and your internal services. If that boundary accepts weak protocols, every connection negotiated over them is at risk, regardless of how well the backend is hardened.
Real-world risk
- Downgrade attacks. If the gateway still offers TLS 1.0 or 1.1, an attacker positioned between client and gateway can attempt to force a downgrade to the weakest mutually supported protocol, then exploit known weaknesses like BEAST or POODLE-style padding oracle attacks.
- Weak cipher suites. Default policies may include ciphers using CBC mode or RSA key exchange without forward secrecy. A captured session can be decrypted later if the server private key is ever compromised.
- Compliance failure. PCI DSS requires TLS 1.2 or higher for cardholder data environments. HIPAA, SOC 2, and most security questionnaires expect the same. An unconfigured gateway is a finding waiting to surface in your next audit.
- Inconsistent posture. When policy is left to a default, that default can shift over time or differ between gateways provisioned at different dates. You lose the ability to say with confidence what TLS your edge actually enforces.
Warning: Tightening the SSL policy can break clients that only support older protocols. Before you raise the minimum to TLS 1.2, check your access logs and client base. Embedded devices, old payment terminals, and legacy integrations sometimes still negotiate TLS 1.0.
How to fix it
The fix is to assign an explicit SSL policy. For most workloads, a recent predefined policy is the fastest safe option. If you need fine-grained control over ciphers, use a custom policy.
Option 1: Apply a predefined policy with the Azure CLI
List the available predefined policies first so you pick the most current one:
az network application-gateway ssl-policy list-options
Then set a predefined policy on your gateway. AppGwSslPolicy20220101S enforces a TLS 1.2 minimum with a strong cipher set:
az network application-gateway ssl-policy set \
--resource-group my-rg \
--gateway-name my-appgw \
--policy-type Predefined \
--policy-name AppGwSslPolicy20220101S
Option 2: Apply a custom policy
If you need to pin specific ciphers, use a custom policy and set the minimum protocol version explicitly:
az network application-gateway ssl-policy set \
--resource-group my-rg \
--gateway-name my-appgw \
--policy-type Custom \
--min-protocol-version TLSv1_2 \
--cipher-suites \
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 \
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 \
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 \
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Warning: Applying an SSL policy updates the gateway configuration, which can take a few minutes to propagate. There is no traffic outage for the policy change itself, but ordering matters if you are scripting multiple gateway changes in sequence. Apply and verify one gateway at a time in production.
Option 3: Fix it in the Azure Portal
- Open the Application Gateway resource in the Azure Portal.
- Under Settings, select Configuration, then the SSL/TLS policy blade (sometimes labeled Listener TLS certificates depending on portal version).
- Set SSL policy type to Predefined or Custom.
- Choose a policy with a minimum of TLS 1.2, for example
AppGwSslPolicy20220101S. - Click Save and wait for the gateway to finish updating.
Verify the change
Confirm the policy is now set on the gateway:
az network application-gateway ssl-policy show \
--resource-group my-rg \
--gateway-name my-appgw
You should see minProtocolVersion set to TLSv1_2 and a defined policy name or cipher list. For an external test, scan the public endpoint:
nmap --script ssl-enum-ciphers -p 443 your-appgw-fqdn.example.com
Tip: Test the negotiated protocol directly with OpenSSL. A handshake against TLS 1.0 should now fail: openssl s_client -connect your-appgw-fqdn.example.com:443 -tls1 returning an error confirms the floor is holding.
Fixing it in infrastructure as code
Setting the policy in the console fixes one gateway today. Putting it in your IaC keeps it fixed and stops the next gateway from shipping without one.
Terraform
resource "azurerm_application_gateway" "main" {
name = "my-appgw"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
# ... sku, gateway_ip_configuration, listeners, etc.
ssl_policy {
policy_type = "Predefined"
policy_name = "AppGwSslPolicy20220101S"
}
}
For a custom policy in Terraform:
ssl_policy {
policy_type = "Custom"
min_protocol_version = "TLSv1_2"
cipher_suites = [
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
]
}
Bicep
resource appGw 'Microsoft.Network/applicationGateways@2023-09-01' = {
name: 'my-appgw'
location: location
properties: {
sslPolicy: {
policyType: 'Predefined'
policyName: 'AppGwSslPolicy20220101S'
}
// ... other gateway properties
}
}
How to prevent it from happening again
The most reliable way to keep gateways from drifting back to an undefined policy is to enforce the rule at provisioning time and again at runtime.
Azure Policy
Use a custom Azure Policy definition that audits or denies Application Gateways without an SSL policy of the required type. The key is targeting Microsoft.Network/applicationGateways/sslPolicy.minProtocolVersion:
{
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/applicationGateways"
},
{
"anyOf": [
{
"field": "Microsoft.Network/applicationGateways/sslPolicy.minProtocolVersion",
"exists": false
},
{
"field": "Microsoft.Network/applicationGateways/sslPolicy.minProtocolVersion",
"notIn": ["TLSv1_2", "TLSv1_3"]
}
]
}
]
},
"then": {
"effect": "deny"
}
}
}
Danger: Roll out a deny policy in audit mode first. A blanket deny can block legitimate gateway deployments and updates mid-pipeline, and if an existing gateway needs an unrelated change it can be refused until the policy is satisfied. Confirm compliance across your estate before flipping the effect to deny.
CI/CD gates
Catch the misconfiguration before it reaches Azure by scanning your IaC in the pipeline. Tools like Checkov and tfsec have rules for Application Gateway TLS settings. A simple Checkov gate:
checkov -d ./infra --framework terraform \
--check CKV_AZURE_218
Wire this into your pull request checks so a plan that omits ssl_policy fails review rather than merging.
Tip: Pair the pipeline scan with continuous monitoring in Lensix. The pipeline gate catches new code, while the runtime check catches gateways created out of band through the portal, ClickOps, or a forgotten script. You need both to close the loop.
Best practices
- Always set an explicit policy. Never rely on the Azure default. Defaults can change and they do not reflect a decision you made.
- Floor at TLS 1.2, aim for 1.3. Use the newest predefined policy that supports TLS 1.3 where your client base allows it. The 2022 predefined policies are a strong baseline.
- Prefer forward-secret ciphers. Stick to ECDHE key exchange and GCM modes. Avoid CBC ciphers and any suite without forward secrecy.
- Standardize across gateways. Define one approved SSL policy as a module or shared variable so every gateway in your estate enforces the same TLS posture.
- Review on a schedule. Cipher recommendations evolve. Revisit your custom policy or bump to the latest predefined policy at least once a year.
- Test before tightening. Pull access logs to understand client TLS support before raising the protocol floor, so you do not lock out legitimate traffic.
An Application Gateway without an SSL policy is a quiet gap. It does not throw errors and it does not break traffic, which is exactly why it lingers. Setting an explicit policy takes one command, encoding it in IaC takes a few lines, and enforcing it with policy-as-code makes sure it stays fixed. Do all three and your edge TLS stops being a guess.

