Skip to content

Conversation

@hydraxman
Copy link

@hydraxman hydraxman commented Dec 31, 2025

Fix [BUG] AzureCliCredential fails in non-interactive environments due to hardcoded /bin/sh PATH resolution

#47614

Add ensureCliPath method to set Azure CLI installation paths in the process environment.

Description

Please add an informative description that covers that changes made by the pull request and link all relevant issues.

If an SDK is being regenerated based on a new swagger spec, a link to the pull request containing these swagger spec changes has been included above.

All SDK Contribution checklist:

General Guidelines and Best Practices

  • Title of the pull request is clear and informative.
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.

Testing Guidelines

  • Pull request includes test coverage for the included changes.

…e to hardcoded /bin/sh PATH resolution

Add ensureCliPath method to set Azure CLI installation paths in the process environment.
Copilot AI review requested due to automatic review settings December 31, 2025 03:30
@hydraxman hydraxman requested review from a team, g2vinay and joshfree as code owners December 31, 2025 03:30
@github-actions github-actions bot added Azure.Identity Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. labels Dec 31, 2025
@github-actions
Copy link
Contributor

Thank you for your contribution @hydraxman! We will review the pull request and get back to you soon.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where AzureCliCredential and AzureDeveloperCliCredential fail in non-interactive environments due to the Azure CLI not being found in the PATH. The fix adds an ensureCliPath method that prepends common Homebrew installation directories (/usr/local/bin and /opt/homebrew/bin) to the PATH environment variable when executing CLI commands on non-Windows platforms.

Key changes:

  • Added ensureCliPath method to ensure Azure CLI installation paths are available in the process environment
  • Applied the path fix to both getTokenFromAzureCLIAuthentication and getTokenFromAzureDeveloperCLIAuthentication methods

Comment on lines +689 to +697
private void ensureCliPath(ProcessBuilder builder) {
if (!isWindowsPlatform()) {
Map<String, String> env = builder.environment();
String path = env.getOrDefault("PATH", "");
if (!path.contains("/usr/local/bin") && !path.contains("/opt/homebrew/bin")) {
env.put("PATH", "/usr/local/bin:/opt/homebrew/bin:" + path);
}
}
}
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new ensureCliPath method lacks test coverage. There are no tests validating that:

  1. The PATH is correctly modified when the directories are not present
  2. The PATH is not modified when the directories are already present
  3. The method correctly handles edge cases like empty PATH or PATH with trailing colons
  4. The method is correctly invoked for both Azure CLI and Azure Developer CLI commands

Since the existing test file AzureCliCredentialTest.java has comprehensive test coverage for other behaviors, tests should be added for this new functionality to maintain the same quality standard.

Copilot uses AI. Check for mistakes.
Comment on lines +684 to +688
* Ensures that the process environment PATH contains common Azure CLI installation directories
* when invoking CLI commands in non-interactive environments.
* Azure CLI is commonly installed via Homebrew on macOS and Linux, which places the `az` executable
* under locations such as {@code /usr/local/bin} (Intel) or {@code /opt/homebrew/bin} (Apple Silicon).
*/
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JavaDoc comment uses an asterisk (*) at the start of line 684 instead of the standard JavaDoc format with proper indentation and asterisk alignment. The opening /** should be on its own line, and each subsequent line should start with a space, asterisk, and space pattern for consistency with JavaDoc conventions used elsewhere in the codebase.

Suggested change
* Ensures that the process environment PATH contains common Azure CLI installation directories
* when invoking CLI commands in non-interactive environments.
* Azure CLI is commonly installed via Homebrew on macOS and Linux, which places the `az` executable
* under locations such as {@code /usr/local/bin} (Intel) or {@code /opt/homebrew/bin} (Apple Silicon).
*/
* Ensures that the process environment PATH contains common Azure CLI installation directories
* when invoking CLI commands in non-interactive environments.
* Azure CLI is commonly installed via Homebrew on macOS and Linux, which places the `az` executable
* under locations such as {@code /usr/local/bin} (Intel) or {@code /opt/homebrew/bin} (Apple Silicon).
*/

Copilot uses AI. Check for mistakes.
Comment on lines +692 to +694
String path = env.getOrDefault("PATH", "");
if (!path.contains("/usr/local/bin") && !path.contains("/opt/homebrew/bin")) {
env.put("PATH", "/usr/local/bin:/opt/homebrew/bin:" + path);
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When constructing the new PATH value, if the original PATH is empty or not set, this will result in PATH being set to /usr/local/bin:/opt/homebrew/bin: with a trailing colon. While this is typically handled gracefully by most systems (treating the empty entry as the current directory), it's not a best practice and could lead to subtle security issues. Consider handling the empty PATH case explicitly to avoid the trailing colon.

Suggested change
String path = env.getOrDefault("PATH", "");
if (!path.contains("/usr/local/bin") && !path.contains("/opt/homebrew/bin")) {
env.put("PATH", "/usr/local/bin:/opt/homebrew/bin:" + path);
String existingPath = env.get("PATH");
String pathToCheck = existingPath == null ? "" : existingPath;
if (!pathToCheck.contains("/usr/local/bin") && !pathToCheck.contains("/opt/homebrew/bin")) {
String additionalPaths = "/usr/local/bin:/opt/homebrew/bin";
if (existingPath == null || existingPath.isEmpty()) {
env.put("PATH", additionalPaths);
} else {
env.put("PATH", additionalPaths + ":" + existingPath);
}

Copilot uses AI. Check for mistakes.
Comment on lines +693 to +695
if (!path.contains("/usr/local/bin") && !path.contains("/opt/homebrew/bin")) {
env.put("PATH", "/usr/local/bin:/opt/homebrew/bin:" + path);
}
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current logic using contains() to check if paths are already in the PATH variable is fragile and can lead to false positives. For example, if PATH contains /home/user/opt/homebrew/bin/tool, the check path.contains("/opt/homebrew/bin") would incorrectly return true even though /opt/homebrew/bin is not actually in the PATH as a separate directory entry. This could prevent the fix from working in such cases.

Consider splitting the PATH by the path separator (: on Unix systems) and checking if the specific directories exist as individual entries in the resulting array.

Suggested change
if (!path.contains("/usr/local/bin") && !path.contains("/opt/homebrew/bin")) {
env.put("PATH", "/usr/local/bin:/opt/homebrew/bin:" + path);
}
boolean hasUsrLocalBin = false;
boolean hasOptHomebrewBin = false;
if (!path.isEmpty()) {
String[] entries = path.split(Pattern.quote(java.io.File.pathSeparator));
for (String entry : entries) {
if ("/usr/local/bin".equals(entry)) {
hasUsrLocalBin = true;
} else if ("/opt/homebrew/bin".equals(entry)) {
hasOptHomebrewBin = true;
}
if (hasUsrLocalBin && hasOptHomebrewBin) {
break;
}
}
}
// Preserve original behavior: only prepend both directories when neither is present.
if (!hasUsrLocalBin && !hasOptHomebrewBin) {
StringBuilder updatedPath = new StringBuilder();
updatedPath.append("/usr/local/bin")
.append(java.io.File.pathSeparator)
.append("/opt/homebrew/bin")
.append(java.io.File.pathSeparator)
.append(path);
env.put("PATH", updatedPath.toString());
}

Copilot uses AI. Check for mistakes.
Comment on lines +689 to +697
private void ensureCliPath(ProcessBuilder builder) {
if (!isWindowsPlatform()) {
Map<String, String> env = builder.environment();
String path = env.getOrDefault("PATH", "");
if (!path.contains("/usr/local/bin") && !path.contains("/opt/homebrew/bin")) {
env.put("PATH", "/usr/local/bin:/opt/homebrew/bin:" + path);
}
}
}
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG.md file has not been updated to document this bug fix. According to the contribution guidelines and the checklist in the PR description, the CHANGELOG should be updated for bug fixes. Please add an entry in the "Bugs Fixed" section of the unreleased version (1.19.0-beta.2) describing this fix for AzureCliCredential failing in non-interactive environments.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Azure.Identity Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization.

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

1 participant