Describe the bug
az containerapp up checks twice whether the target app exists, and both checks swallow every exception:
ContainerApp.create() in _up_utils.py calls get_container_app_if_exists(), which wraps ContainerAppClient.show in a bare except: pass (_utils.py L617-L623). Its result only chooses the Updating … / Creating … log line (_up_utils.py L384-L391).
containerapp_up_logic() then calls ContainerAppClient.show again, also inside a bare except: pass, and if that returns nothing it calls create_containerapp(...) (custom.py L3747-L3757).
So when the second GET fails for any reason other than the app not existing — a throttled or transient error, a network or token hiccup — up treats an existing app as missing and sends a full create PUT built only from its own arguments. That PUT replaces the app's configuration: everything not passed to up is reset.
We hit this in a CI deploy of an existing app. The log shows both branches in one run:
WARNING: Updating Containerapp <app> in resource group <rg>
...
Container app created. To access it over HTTPS, enable ingress: az containerapp ingress enable -n <app> -g <rg> --type external --target-port <port> --transport auto
Your container app <app> has been created and deployed! Congrats!
Container app created. To access it over HTTPS, enable ingress is printed only by ContainerAppCreateDecorator.post_process (containerapp_decorator.py L519), so the first check found the app and the second did not. Azure Resource Graph change history for that write shows:
identity.type → None — the system-assigned identity was deleted, so its principal (and every role assignment and database user granted to it) is gone for good
- ingress removed
- every secret except the registry credential
up rebuilds removed, including the Easy Auth client secret; the authConfigs child resource survived, so Easy Auth was left enabled pointing at a secret that no longer existed and the app could not start
- scale settings reset to defaults
The command exited 0. The same command with the same CLI version against another existing app logged Updating Containerapp … followed by Browse to your container app at …, took the update path and left its configuration intact; the failing run took several times longer, consistent with a create.
The exception from the second GET is swallowed, so we cannot tell what it was.
Related command
az containerapp up
Errors
None — the command succeeds. That is the problem: a failed read becomes a destructive create with exit code 0.
Issue script & Debug output
Not reproducible on demand, since it needs the second show to fail. Command as run in CI:
az config set extension.use_dynamic_install=yes_without_prompt
az containerapp up \
--name <app> \
--resource-group <rg> \
--image <registry>.azurecr.io/<image>:<tag> \
--environment /subscriptions/<sub>/resourceGroups/<env-rg>/providers/Microsoft.App/managedEnvironments/<env> \
--location <location> \
--env-vars KEY1="value" KEY2="value"
It can be reproduced deterministically by making the second ContainerAppClient.show call in containerapp_up_logic raise (for example by patching it in a test) against an existing app: up then calls create_containerapp.
Expected behavior
up must not create when it cannot tell whether the app exists. Only a definite not-found (ResourceNotFound / HTTP 404) should lead to create; any other error from the existence check should be raised.
- The existence check should happen once, and the same answer should decide both the log line and the create/update branch.
- Ideally,
up on an existing app should never send a full PUT built from its own arguments, since that silently discards identity, ingress, secrets, scale and revision settings that up has no flags for.
Environment Summary
azure-cli 2.90.0 (GitHub-hosted ubuntu-24.04 runner)
containerapp extension: not installed
Python (Linux)
Additional context
Describe the bug
az containerapp upchecks twice whether the target app exists, and both checks swallow every exception:ContainerApp.create()in_up_utils.pycallsget_container_app_if_exists(), which wrapsContainerAppClient.showin a bareexcept: pass(_utils.pyL617-L623). Its result only chooses theUpdating …/Creating …log line (_up_utils.pyL384-L391).containerapp_up_logic()then callsContainerAppClient.showagain, also inside a bareexcept: pass, and if that returns nothing it callscreate_containerapp(...)(custom.pyL3747-L3757).So when the second GET fails for any reason other than the app not existing — a throttled or transient error, a network or token hiccup —
uptreats an existing app as missing and sends a full create PUT built only from its own arguments. That PUT replaces the app's configuration: everything not passed toupis reset.We hit this in a CI deploy of an existing app. The log shows both branches in one run:
Container app created. To access it over HTTPS, enable ingressis printed only byContainerAppCreateDecorator.post_process(containerapp_decorator.pyL519), so the first check found the app and the second did not. Azure Resource Graph change history for that write shows:identity.type→None— the system-assigned identity was deleted, so its principal (and every role assignment and database user granted to it) is gone for gooduprebuilds removed, including the Easy Auth client secret; theauthConfigschild resource survived, so Easy Auth was left enabled pointing at a secret that no longer existed and the app could not startThe command exited 0. The same command with the same CLI version against another existing app logged
Updating Containerapp …followed byBrowse to your container app at …, took the update path and left its configuration intact; the failing run took several times longer, consistent with a create.The exception from the second GET is swallowed, so we cannot tell what it was.
Related command
az containerapp upErrors
None — the command succeeds. That is the problem: a failed read becomes a destructive create with exit code 0.
Issue script & Debug output
Not reproducible on demand, since it needs the second
showto fail. Command as run in CI:It can be reproduced deterministically by making the second
ContainerAppClient.showcall incontainerapp_up_logicraise (for example by patching it in a test) against an existing app:upthen callscreate_containerapp.Expected behavior
upmust not create when it cannot tell whether the app exists. Only a definite not-found (ResourceNotFound/ HTTP 404) should lead to create; any other error from the existence check should be raised.upon an existing app should never send a full PUT built from its own arguments, since that silently discards identity, ingress, secrets, scale and revision settings thatuphas no flags for.Environment Summary
Additional context
exceptblocks were left in place by [Containerapp] Fix #26688:az containerapp up: Fix logic about updating an existing containerapp #29336, which addressed an earlier existence-detection problem inup("az containerapp up" will sometimes use a container app environment that the container app IS NOT located in #26688).containerapp upis documented as "create or update"; nothing in the docs warns that a transient read failure turns an update into a re-create of an existing app.az containerapp updatefor existing apps and create only after a confirmed not-found, failing the run on any other lookup error.