This check flags Azure Container Apps that accept traffic from the public internet via external ingress. If the app does not need to be reachable from outside your network, switch ingress to internal so it only accepts traffic from within its Container Apps environment and your VNet.
Azure Container Apps make it easy to run containerized workloads without managing the underlying Kubernetes plumbing. That convenience cuts both ways. With a single property set to external, your app gets a public FQDN and starts answering requests from anyone on the internet. Sometimes that is exactly what you want. Often it is not, and the exposure goes unnoticed until a scanner finds it.
The Container App Is Externally Accessible check looks at the ingress configuration of each Container App and raises a finding when ingress.external is set to true.
What this check detects
Every Container App that exposes a port has an ingress configuration. The key field is external:
- External ingress (
external: true) assigns a public FQDN and routes traffic from the internet to your app. - Internal ingress (
external: false) assigns an FQDN that resolves only inside the Container Apps environment and the VNet it is integrated with.
This check fires when a Container App has ingress enabled and external is set to true. It does not assert that public access is wrong, only that it exists and should be reviewed against your intent.
Note: Internal ingress requires the Container Apps environment to be deployed with VNet integration using a custom virtual network. An environment created without a custom VNet can only host externally accessible apps, so you may need to recreate the environment to get internal-only ingress.
Why it matters
A public endpoint is an open invitation for traffic you did not ask for. The risk depends entirely on what sits behind the ingress, and the problem is that internal services often end up exposed by accident.
Internal APIs leaking to the internet
A common pattern is a frontend app that should be public and a backend API that should only be called by that frontend. If both are deployed with external: true, the backend is now directly reachable. Anyone who discovers the FQDN can call its endpoints, skipping whatever authentication or rate limiting the frontend was supposed to enforce.
Attack surface for unauthenticated workloads
Container Apps do not authenticate requests unless you configure it. A service that assumes it lives in a trusted network, for example one that exposes admin routes or health endpoints with debug data, becomes a target the moment it is published. Automated scanners catalog new Azure FQDNs within hours.
Data exposure and lateral movement
If a publicly accessible app holds connection strings, managed identity tokens, or can reach databases and storage accounts, a compromise gives an attacker a foothold inside your environment. From there they probe for the next hop. Reducing exposure on the front door limits how far an intrusion can travel.
The cheapest vulnerability to fix is the one that was never exposed. If a service has no reason to be public, internal ingress removes an entire class of attacks before they start.
How to fix it
The fix is to set ingress to internal for any app that does not need public reachability. Confirm the app is genuinely internal first, since flipping this on a public-facing service will break it.
Check the current ingress setting
az containerapp ingress show \
--name my-container-app \
--resource-group my-rg \
--query "{external: external, fqdn: fqdn, targetPort: targetPort}" \
-o json
If external comes back true and the app is meant to be internal, switch it.
Warning: Changing ingress from external to internal changes the app's FQDN and stops it from resolving on the public internet. Anything calling the old public address, including DNS records, API gateways, or partner integrations, will break. Update those callers before or during the change.
Set ingress to internal with the CLI
az containerapp ingress enable \
--name my-container-app \
--resource-group my-rg \
--type internal \
--target-port 8080 \
--transport auto
If the app should not accept any ingress at all, for example a background worker, disable ingress entirely:
az containerapp ingress disable \
--name my-container-app \
--resource-group my-rg
Fix it in the Azure Portal
- Open the Container App in the Azure Portal.
- Under Settings, select Ingress.
- With ingress enabled, change Ingress traffic from Accepting traffic from anywhere to Limited to Container Apps Environment.
- Confirm the target port and transport, then save.
Fix it in Bicep
resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
name: 'my-container-app'
location: location
properties: {
managedEnvironmentId: environmentId
configuration: {
ingress: {
external: false // internal only
targetPort: 8080
transport: 'auto'
allowInsecure: false
}
}
template: {
containers: [
{
name: 'app'
image: 'myregistry.azurecr.io/app:latest'
}
]
}
}
}
Fix it in Terraform
resource "azurerm_container_app" "app" {
name = "my-container-app"
container_app_environment_id = azurerm_container_app_environment.env.id
resource_group_name = azurerm_resource_group.rg.name
revision_mode = "Single"
ingress {
external_enabled = false # internal only
target_port = 8080
transport = "auto"
traffic_weight {
latest_revision = true
percentage = 100
}
}
template {
container {
name = "app"
image = "myregistry.azurecr.io/app:latest"
cpu = 0.25
memory = "0.5Gi"
}
}
}
Danger: Disabling ingress on a live app makes it unreachable immediately, including by any internal services that call it. Verify that no production traffic depends on the endpoint before running ingress disable, and prefer rolling the change through a non-production environment first.
What to do when an app must stay public
Some apps legitimately need external ingress. A public web frontend is the obvious case. When that is true, do not just accept the finding, harden the exposure:
- Put a WAF in front of it. Use Azure Front Door or Application Gateway with the Web Application Firewall, and restrict the Container App so it only accepts traffic from the WAF.
- Enforce HTTPS. Set
allowInsecure: falseso HTTP requests are redirected to HTTPS. - Add authentication. Use built-in authentication (Easy Auth) with Microsoft Entra ID or another identity provider for apps that should not be anonymous.
- Scope IP restrictions. Container Apps support IP ingress rules. If the audience is known, allowlist their ranges.
az containerapp ingress access-restriction set \
--name my-container-app \
--resource-group my-rg \
--rule-name "office-only" \
--ip-address 203.0.113.0/24 \
--action Allow
How to prevent it from happening again
Manual review does not scale. Catch external ingress before it reaches production by gating it in your pipeline and in Azure itself.
Azure Policy
Use a policy with the deny effect to block Container App deployments that set external ingress. The policy below targets the ingress property directly:
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.App/containerApps"
},
{
"field": "Microsoft.App/containerApps/configuration.ingress.external",
"equals": true
}
]
},
"then": {
"effect": "deny"
}
}
Assign this at the subscription or management group scope. If a handful of apps need to be public, use an exemption for those specific resources rather than weakening the policy for everyone.
Tip: Start the policy in audit mode to find existing external apps without breaking deployments, then flip it to deny once you have triaged the legitimate cases and added exemptions.
Policy-as-code in CI
For Terraform and Bicep, scan plans before they apply. A Checkov or Conftest rule that fails when external_enabled = true stops the misconfiguration at the pull request stage, where it is cheapest to fix.
# Run Checkov against a Terraform plan in CI
checkov -f tfplan.json --framework terraform_plan
Continuous scanning
Pipeline gates only cover what flows through the pipeline. Console changes, emergency fixes, and drift slip past them. Lensix continuously scans your live Azure subscriptions for externally accessible Container Apps and surfaces them with the affected resource and remediation guidance, so an app that gets flipped to public after deployment does not stay hidden.
Best practices
- Default to internal. Treat public ingress as an explicit decision, not a default. Most backend services and workers never need it.
- Build VNet-integrated environments. Create Container Apps environments with a custom VNet from the start so internal ingress is available without recreating the environment later.
- Front public apps with a WAF. Never expose a Container App directly when you can place Front Door or Application Gateway in front of it and lock the app down to that source.
- Authenticate by default. Anonymous public endpoints should be rare and deliberate. Use Easy Auth or app-level authentication for everything else.
- Review ingress in code review. Make
externalandexternal_enabledvalues something reviewers actively check, not boilerplate they skim past. - Tag intentional exceptions. When an app is meant to be public, document it with a tag or policy exemption so audits do not keep re-flagging it and real findings do not get lost in the noise.
External ingress is a one-property switch, which is exactly why it is easy to get wrong. Decide deliberately which apps face the internet, enforce that decision with policy, and scan continuously so the ones that should be private stay private.

