Skip to content

Azure Credential to provide OIDC token for playbooks#193

Open
p3ck wants to merge 5 commits into
ansible:develfrom
p3ck:azure_oidc
Open

Azure Credential to provide OIDC token for playbooks#193
p3ck wants to merge 5 commits into
ansible:develfrom
p3ck:azure_oidc

Conversation

@p3ck

@p3ck p3ck commented Jul 9, 2026

Copy link
Copy Markdown

Requires Client Secret Credentials or a Host under Managed Identity.

Summary by CodeRabbit

  • New Features
    • Added an Azure OIDC token lookup credential plugin.
    • Supports token retrieval using either managed identity or client credentials.
    • Requires Azure cloud selection and token request scope.
  • Bug Fixes
    • Improved credential resolution logic and provided clearer, more actionable error messages for missing cloud matches and authentication failures.
  • Tests
    • Extended plugin import/discovery smoke tests to cover the new Azure OIDC credential entry point.

Requires Client Secret Credentials or a Host under Managed Identity.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 0d54c07b-6eda-4e07-aa57-14164787969b

📥 Commits

Reviewing files that changed from the base of the PR and between 6701e5d and 81bc5fc.

📒 Files selected for processing (1)
  • src/awx_plugins/credentials/azure_token.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/awx_plugins/credentials/azure_token.py

📝 Walkthrough

Walkthrough

This PR adds an Azure OIDC credential lookup plugin with required Azure inputs, service-principal or managed-identity selection, token retrieval and error mapping, plugin wiring, and entry-point discovery coverage.

Changes

Azure OIDC credential flow

Layer / File(s) Summary
Credential inputs and token retrieval
src/awx_plugins/credentials/azure_token.py
Defines Azure cloud inputs, selects ClientSecretCredential or ManagedIdentityCredential, resolves one cloud, retrieves tokens, and maps Azure exceptions to RuntimeError.
Plugin wiring and discovery
src/awx_plugins/credentials/azure_token.py, pyproject.toml, tests/importable_test.py
Creates azure_oidc_plugin, registers the azure_token entry point, and adds it to importability checks.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant azure_oidc_backend
  participant _initialize_credential
  participant AzureCredential

  Caller->>azure_oidc_backend: provide url and cloud_name
  azure_oidc_backend->>_initialize_credential: initialize credential for selected cloud
  _initialize_credential-->>azure_oidc_backend: ClientSecretCredential or ManagedIdentityCredential
  azure_oidc_backend->>AzureCredential: get_token(url)
  AzureCredential-->>azure_oidc_backend: token or Azure exception
  azure_oidc_backend-->>Caller: token string or RuntimeError
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding Azure credential support to provide OIDC tokens for playbooks.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/awx_plugins/credentials/azure_token.py`:
- Around line 95-109: The credential initialization in _initialize_ currently
falls back to ManagedIdentityCredential whenever tenant, client, or secret is
missing, which can hide partially configured service-principal settings. Update
_initialize_ to explicitly detect when some but not all of tenant/client/secret
are provided and reject that configuration with an error instead of using
managed identity. Keep the existing ClientSecretCredential path for the fully
populated case, and only return ManagedIdentityCredential when none of the three
values are supplied.
- Around line 73-77: `cloud_name` is currently only exposed in the Azure
credential field definitions and is not propagated into the credential setup, so
the selected cloud never affects authentication. Update the Azure credential
flow by threading the chosen `cloud_name` from the credential constructors into
`_initialize_credential` (or through `azure_oidc_backend`), and use it to select
the correct Azure authority host instead of always defaulting to the public
cloud. Make sure the selection is wired through the relevant credential creation
path so `ClientSecretCredential` uses the matching cloud environment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 83cdb174-872d-423c-a128-9b3a027d7d9f

📥 Commits

Reviewing files that changed from the base of the PR and between 7c6a7cf and 12e1f44.

📒 Files selected for processing (1)
  • src/awx_plugins/credentials/azure_token.py

Comment thread src/awx_plugins/credentials/azure_token.py
Comment on lines +95 to +109
def _initialize_credential(
tenant: str = '',
client: str = '',
secret: str = '',
) -> TokenCredential:
explicit_credentials_provided = all((tenant, client, secret))

if explicit_credentials_provided:
return ClientSecretCredential(
tenant_id=tenant,
client_id=client,
client_secret=secret,
)

return ManagedIdentityCredential()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject partial service-principal configuration instead of falling back to managed identity.

Line 100 treats any missing tenant/client/secret as “use managed identity”. If a user partially configures a service principal, this can silently authenticate as the host identity instead.

Proposed fix
 def _initialize_credential(
     tenant: str = '',
     client: str = '',
     secret: str = '',
 ) -> TokenCredential:
+    if any((tenant, client, secret)) and not all((tenant, client, secret)):
+        raise RuntimeError(
+            'Client ID, Client Secret, and Tenant ID must be provided together, '
+            'or all left empty to use Managed Identity.',
+        )
+
     explicit_credentials_provided = all((tenant, client, secret))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _initialize_credential(
tenant: str = '',
client: str = '',
secret: str = '',
) -> TokenCredential:
explicit_credentials_provided = all((tenant, client, secret))
if explicit_credentials_provided:
return ClientSecretCredential(
tenant_id=tenant,
client_id=client,
client_secret=secret,
)
return ManagedIdentityCredential()
def _initialize_credential(
tenant: str = '',
client: str = '',
secret: str = '',
) -> TokenCredential:
if any((tenant, client, secret)) and not all((tenant, client, secret)):
raise RuntimeError(
'Client ID, Client Secret, and Tenant ID must be provided together, '
'or all left empty to use Managed Identity.',
)
explicit_credentials_provided = all((tenant, client, secret))
if explicit_credentials_provided:
return ClientSecretCredential(
tenant_id=tenant,
client_id=client,
client_secret=secret,
)
return ManagedIdentityCredential()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/awx_plugins/credentials/azure_token.py` around lines 95 - 109, The
credential initialization in _initialize_ currently falls back to
ManagedIdentityCredential whenever tenant, client, or secret is missing, which
can hide partially configured service-principal settings. Update _initialize_ to
explicitly detect when some but not all of tenant/client/secret are provided and
reject that configuration with an error instead of using managed identity. Keep
the existing ClientSecretCredential path for the fully populated case, and only
return ManagedIdentityCredential when none of the three values are supplied.

p3ck added 2 commits July 16, 2026 14:02
Also add test to validate azure_token will import.
The cloud_name logic was not wired up.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pyproject.toml (1)

163-168: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add optional dependencies feature-flag for azure_token.

The new azure_token credential plugin uses azure-identity and msrestazure, but a corresponding feature-flag dependency group (e.g., credentials-azure-token) is missing in pyproject.toml. This prevents end-users from easily installing the required dependencies for this new plugin.

💻 Proposed fix

Add the following under [project.optional-dependencies]:

credentials-azure-token = [
  "awx_plugins.interfaces",
  "azure-identity",
  "msrestazure",
]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` around lines 163 - 168, Add a credentials-azure-token
optional dependency group under [project.optional-dependencies] containing
awx_plugins.interfaces, azure-identity, and msrestazure, matching the existing
feature-flag dependency group structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/awx_plugins/credentials/azure_token.py`:
- Around line 98-118: Update _initialize_credential to reject partial
service-principal configuration instead of falling back to managed identity,
while preserving ClientSecretCredential for complete tenant, client, and secret
values. Construct ManagedIdentityCredential without the unsupported
cloud_environment argument, and pass client_id only when client is non-empty,
otherwise use None.
- Line 41: Update the default_cloud assignment to retain the
azure_cloud.AZURE_PUBLIC_CLOUD object instead of its .name string, so the
existing default_cloud.name accesses later in the plugin remain valid.

---

Outside diff comments:
In `@pyproject.toml`:
- Around line 163-168: Add a credentials-azure-token optional dependency group
under [project.optional-dependencies] containing awx_plugins.interfaces,
azure-identity, and msrestazure, matching the existing feature-flag dependency
group structure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: acdc7ca2-3439-4668-af9f-7e2162c49205

📥 Commits

Reviewing files that changed from the base of the PR and between d383d92 and 6701e5d.

📒 Files selected for processing (3)
  • pyproject.toml
  • src/awx_plugins/credentials/azure_token.py
  • tests/importable_test.py


# https://github.com/Azure/msrestazure-for-python/blob/master/msrestazure/azure_cloud.py
clouds = [x[1] for x in inspect.getmembers(azure_cloud) if isinstance(x[1], azure_cloud.Cloud)]
default_cloud = azure_cloud.AZURE_PUBLIC_CLOUD.name

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix AttributeError caused by accessing .name on a string.

At line 41, default_cloud is assigned the string value of azure_cloud.AZURE_PUBLIC_CLOUD.name. Later at lines 76 and 77, the code attempts to access default_cloud.name, which will raise an AttributeError: 'str' object has no attribute 'name' and crash the plugin upon import.

💻 Proposed fix

Change the assignment so default_cloud is the object itself:

-default_cloud = azure_cloud.AZURE_PUBLIC_CLOUD.name
+default_cloud = azure_cloud.AZURE_PUBLIC_CLOUD
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
default_cloud = azure_cloud.AZURE_PUBLIC_CLOUD.name
default_cloud = azure_cloud.AZURE_PUBLIC_CLOUD
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/awx_plugins/credentials/azure_token.py` at line 41, Update the
default_cloud assignment to retain the azure_cloud.AZURE_PUBLIC_CLOUD object
instead of its .name string, so the existing default_cloud.name accesses later
in the plugin remain valid.

Comment thread src/awx_plugins/credentials/azure_token.py
Update pyproject dependencies.
Formatting changes.
Fixes for default cloud.
Fixes for ManagedAuthentication.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant