Security Issue: "az ad sp create-for-rbac" destroys existing integrations and assigns permissions to the wrong identity!
- Dominant language
- Python
- Stars
- 4.6k
- Forks
- 3.5k
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 60
Description
### Describe the bug
This command (and likely the other variations) performs object lookups in graph by display name. This is fundamentally inappropriate especially for a security-focused command that is meant to create a new identity and permission. Instead, this command attempts to find any existing one, and remove its existing credentials, set new ones, and assign RBAC to whatever it found.
Display name is not unique for applications or service principals, can be edited at any time, and can be different between them. Yet these commands treat display name like a durable identifier and attempt make arbitrary credential and rbac modifications to resolved identities.
### Related command
az ad sp create-for-rbac
### Errors
n/a
### Issue script & Debug output
Take a look at this sequence of calls (I have edited the values of IDs etc for the purposes of this comment):
```shell
az ad sp create-for-rbac -n $appDisplayName --role 'Azure Service Bus Data Owner' --scopes /subscriptions/$($SubscriptionId) --debug
```
This command begins by trying to find ANY existing service principal with that display name:
```shell
GET /v1.0/servicePrincipals?$filter=displayName%20eq%20%27MessagingTestApp%27
{"@odata.context":https://graph.microsoft.com/v1.0/$metadata#servicePrincipals,"value":[]}
```
This is already a huge problem, there can be many service principals already existing. This command is meant to create a new application, not find some arbitrary identity in the customer directory and start messing with its configuration and potentially grant it new permissions!
In our case, no service principal was found with this displayname, so the command next proceeds to try and find ANY application with that display name:
```shell
GET /v1.0/applications?$filter=startswith%28displayName%2C%27MessagingTestApp%27%29 HTTP/1.1
{"@odata.context":https://graph.microsoft.com/v1.0/$metadata#applications,"value":[{
"id":"cfbf39d0-df91-4ea9-95f3-f6931f6d282a","createdDateTime":"2025-10-16T23:50:48.7381945Z","deletedDateTime":null,"appId":"2f1
5aec7-a6e3-43f9-b802-94627dc4e511","description":null,"displayName":"MessagingTestApp","identifierUris":[],"notes":null,"signInA
udience":null,"tags":[],"additionalData":[],"api":{"requestedAccessTokenVersion":null},"appRoles":[],"info":{"logoUrl":null,"mar
ketingUrl":null,"privacyStatementUrl":null,"supportUrl":null,"termsOfServiceUrl":null},"keyCredentials":[],"passwordCredentials"
:[{"customKeyIdentifier":"","displayName":"rbac","endDateTime":"2026-10-16T23:53:17Z","hint":"Q2O","keyId":"6dd17918-2845-4121-8
fd4-35c0ffc7e235","secretText":null,"startDateTime":"2025-10-16T23:53:17Z"}],"publicClient":{"redirectUris":[]},"spa":{"redirect
Uris":[]},"web":{"homePageUrl":null,"logoutUrl":null,"redirectUris":[],"implicitGrantSettings":{"enableIdTokenIssuance":false,"e
nableAccessTokenIssuance":false}}}]}
```
In our case, it has found an already-existing application, that someone else could've created for a completely different application / test environment! It then proceeds to patch the display name on this application for no reason, since it just looked this up by display name:
```shell
PATCH /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a HTTP/1.1
{"displayName": "MessagingTestApp"}
```
Next, it does another GET request for the same application it already found (why?):
```shell
GET /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a HTTP/1.1
{"@odata.context":https://graph.microsoft.com/v1.0/$metadata#applications/$entity,"i
d":"cfbf39d0-df91-4ea9-95f3-f6931f6d282a","createdDateTime":"2025-10-16T23:50:48.7381945Z","deletedDateTime":null,"appId":"2f15a
ec7-a6e3-43f9-b802-94627dc4e511","description":null,"displayName":"MessagingTestApp","identifierUris":[],"notes":null,"signInAud
ience":null,"tags":[],"additionalData":[],"api":{"requestedAccessTokenVersion":null},"appRoles":[],"info":{"logoUrl":null,"marke
tingUrl":null,"privacyStatementUrl":null,"supportUrl":null,"termsOfServiceUrl":null},"keyCredentials":[],"passwordCredentials":[
{"customKeyIdentifier":"","displayName":"rbac","endDateTime":"2026-10-16T23:53:17Z","hint":"Q2O","keyId":"6dd17918-2845-4121-8fd
4-35c0ffc7e235","secretText":null,"startDateTime":"2025-10-16T23:53:17Z"}],"publicClient":{"redirectUris":[]},"spa":{"redirectUr
is":[]},"web":{"homePageUrl":null,"logoutUrl":null,"redirectUris":[],"implicitGrantSettings":{"enableIdTokenIssuance":false,"ena
bleAccessTokenIssuance":false}}}
```
Then, it proceeds to just remove the password credentials from this application completely! This breaks whatever was actually using that!!
```shell
POST /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a/removePassword HTTP/1.1
```
It then places a new one on the application:
```shell
POST /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a/addPassword HTTP/1.1
{"passwordCredential": {"displayName": "rbac", "endDateTime": "2026-10-16T23:53:49Z",
"startDateTime": "2025-10-16T23:53:49Z"}}
{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.password
Credential","customKeyIdentifier":null,"displayName":"rbac","endDateTime":"2026-10-16T23:53:49Z","hint":"65x","keyId":"29fadcbe-
70e1-4a45-8fc3-e97c093b701a","secretText":"[redacted]","startDateTime":"2025-10-16T23:53:49Z"}
```
Lasty, it attempts to create a service principal for this application, but one already exists, so it fails:
```shell
POST /v1.0/servicePrincipals HTTP/1.1
{"appId": "2f15aec7-a6e3-43f9-b802-94627dc4e511", "accountEnabled": true}
{"error":{"code":"Request_MultipleObjectsWithSameKeyValue","message":"Another object with the
same value for property servicePrincipalNames already exists.","details":[{"code":"ObjectConflict","message":"The service
principal cannot be created, updated, or restored because the service principal name 2f15aec7-a6e3-43f9-b802-94627dc4e511 is
already in use.","target":"servicePrincipalNames"}]}}
```
### Expected behavior
This command should NEVER assume modification for an existing application or service principal, and none of these commands should ever do lookups based on display name. Let the customer grep by display name in a separate call and be responsible to pass-in the specific and discrete appId or objectId for an existing identity to modify, otherwise, this command and its counterpart(s) should ONLY create net-new identities.
### Environment Summary
azure-cli 2.67.0 *
### Additional context
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.