Skip to content
Closed
107 changes: 67 additions & 40 deletions src/azure-cli/azure/cli/command_modules/identity/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,40 @@
from knack.help_files import helps

helps['identity'] = """
type: group
short-summary: Managed Identities
type: group
short-summary: Managed Identities
long-summary: These commands are in preview and under development. Reference and support levels are not guaranteed, and these commands might be changed or removed in the future.
"""

helps['identity create'] = """
type: command
short-summary: Create Identities.
examples:
- name: Create an identity.
text: |
az identity create --name MyIdentity --resource-group MyResourceGroup
type: command
short-summary: Create a user assigned managed identity.
long-summary: You can create a user assigned managed identity to use across multiple Azure resources.
examples:
- name: Create a user assigned managed identity.
text: |
az identity create --name MyIdentity --resource-group MyResourceGroup
"""

helps['identity list'] = """
type: command
short-summary: List Managed Identities.
type: command
short-summary: List user assigned managed identities.
"""

helps['identity list-operations'] = """
type: command
short-summary: List available operations for the Managed Identity provider.
type: command
short-summary: List operations for managed identities.
"""

helps['identity list-resources'] = """
type: command
short-summary: List the associated resources for the identity.
type: command
short-summary: List resources associated with a managed identity.
"""

helps['identity federated-credential'] = """
type: group
short-summary: Manage federated identity credentials under user assigned identities.
type: group
short-summary: Manage federated identity credentials for user assigned managed identities.
long-summary: These commands are in preview and use API version 2025-01-31-PREVIEW.
"""

helps['identity federated-credential create'] = """
Expand All @@ -48,40 +51,64 @@
- name: Create a federated identity credential under a specific user assigned identity.
text: |
az identity federated-credential create --name myFicName --identity-name myIdentityName --resource-group myResourceGroup --issuer myIssuer --subject mySubject --audiences myAudiences
- name: Create a federated identity credential with claims matching expressions.
text: |
az identity federated-credential create --name myFicName --identity-name myIdentityName --resource-group myResourceGroup --issuer https://tokens.githubusercontent.com --audiences api://AzureADTokenExchange --claims-matching-expression-value "claims['sub'] startswith 'repo:contoso-org/contoso-repo:ref:refs/heads'" --claims-matching-expression-version 1
"""

helps['identity federated-credential update'] = """
type: command
short-summary: Update a federated identity credential under an existing user assigned identity.
examples:
- name: Update a federated identity credential under a specific user assigned identity.
text: |
az identity federated-credential update --name myFicName --identity-name myIdentityName --resource-group myResourceGroup --issuer myIssuer --subject mySubject --audiences myAudiences
type: command
short-summary: Update a federated identity credential under an existing user assigned identity.
examples:
- name: Update a federated identity credential under a specific user assigned identity.
text: |
az identity federated-credential update --name myFicName --identity-name myIdentityName --resource-group myResourceGroup --issuer myIssuer --subject mySubject --audiences myAudiences
- name: Update a federated identity credential with claims matching expression for GitHub
text: |
az identity federated-credential update --name myFicName --identity-name myIdentityName --resource-group myResourceGroup --issuer https://token.actions.githubusercontent.com --audiences api://AzureADTokenExchange --claims-matching-expression-value "claims['sub'] startswith 'repo:contoso-org/contoso-repo:ref:refs/heads'" --claims-matching-expression-version 1
"""

helps['identity federated-credential delete'] = """
type: command
short-summary: Delete a federated identity credential under an existing user assigned identity.
examples:
- name: Delete a federated identity credential under a specific user assigned identity.
text: |
az identity federated-credential delete --name myFicName --identity-name myIdentityName --resource-group myResourceGroup
type: command
short-summary: Delete a federated identity credential.
examples:
- name: Delete a federated identity credential under a user assigned managed identity.
text: |
az identity federated-credential delete --name myFicName --identity-name myIdentityName --resource-group myResourceGroup
"""

helps['identity federated-credential show'] = """
type: command
short-summary: Show a federated identity credential under an existing user assigned identity.
examples:
- name: Show a federated identity credential under a specific user assigned identity.
text: |
az identity federated-credential show --name myFicName --identity-name myIdentityName --resource-group myResourceGroup
type: command
short-summary: Show details of a federated identity credential.
examples:
- name: Show details of a federated identity credential under a user assigned managed identity.
text: |
az identity federated-credential show --name myFicName --identity-name myIdentityName --resource-group myResourceGroup
"""

helps['identity show'] = """
type: command
short-summary: Show details of a user assigned managed identity.
examples:
- name: Show details of a user assigned managed identity.
text: |
az identity show --name myIdentity --resource-group myResourceGroup
"""

helps['identity delete'] = """
type: command
short-summary: Delete a user assigned managed identity.
examples:
- name: Delete a user assigned managed identity.
text: |
az identity delete --name myIdentity --resource-group myResourceGroup
"""

helps['identity federated-credential list'] = """
type: command
short-summary: List all federated identity credentials under an existing user assigned identity.
examples:
- name: List all federated identity credentials under an existing user assigned identity.
text: |
az identity federated-credential list --identity-name myIdentityName --resource-group myResourceGroup
type: command
short-summary: List all federated identity credentials under an existing user assigned identity.
examples:
- name: List all federated identity credentials under an existing user assigned identity.
text: |
az identity federated-credential list --identity-name myIdentityName --resource-group myResourceGroup
"""
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def load_arguments(self, _):
c.argument('location', get_location_type(self.cli_ctx), required=False)
c.argument('tags', tags_type)

with self.argument_context('identity federated-credential', min_api='2022-01-31-preview') as c:
with self.argument_context('identity federated-credential', min_api='2025-01-31-PREVIEW') as c:
c.argument('federated_credential_name', options_list=('--name', '-n'), help='The name of the federated identity credential resource.')
c.argument('identity_name', help='The name of the identity resource.')

Expand All @@ -31,3 +31,5 @@ def load_arguments(self, _):
c.argument('issuer', help='The openId connect metadata URL of the issuer of the identity provider that Azure AD would use in the token exchange protocol for validating tokens before issuing a token as the user-assigned managed identity.')
c.argument('subject', help='The sub value in the token sent to Azure AD for getting the user-assigned managed identity token. The value configured in the federated credential and the one in the incoming token must exactly match for Azure AD to issue the access token.')
c.argument('audiences', nargs='+', help='The aud value in the token sent to Azure for getting the user-assigned managed identity token. The value configured in the federated credential and the one in the incoming token must exactly match for Azure to issue the access token.')
c.argument('claims_matching_expression_value', options_list=['--claims-matching-expression-value'], help='The claims expression value for the federated identity credential.')
c.argument('claims_matching_expression_version', options_list=['--claims-matching-expression-version'], help='The claims expression language version for the federated identity credential.')
26 changes: 15 additions & 11 deletions src/azure-cli/azure/cli/command_modules/identity/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,47 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------


from azure.cli.core.commands import CliCommandType
from azure.cli.core.profiles import ResourceType

from ._client_factory import _msi_user_identities_operations, _msi_operations_operations, \
_msi_federated_identity_credentials_operations

from ._client_factory import (_msi_user_identities_operations, _msi_operations_operations,
_msi_federated_identity_credentials_operations)
from ._validators import process_msi_namespace


def load_command_table(self, _):

identity_sdk = CliCommandType(
operations_tmpl='azure.mgmt.msi.operations#UserAssignedIdentitiesOperations.{}',
client_factory=_msi_user_identities_operations
client_factory=_msi_user_identities_operations,
resource_type=ResourceType.MGMT_MSI
)

msi_operations_sdk = CliCommandType(
operations_tmpl='azure.mgmt.msi.operations#Operations.{}',
client_factory=_msi_operations_operations
client_factory=_msi_operations_operations,
resource_type=ResourceType.MGMT_MSI
)

federated_identity_credentials_sdk = CliCommandType(
operations_tmpl='azure.mgmt.msi.operations#FederatedIdentityCredentialsOperations.{}',
client_factory=_msi_federated_identity_credentials_operations
client_factory=_msi_federated_identity_credentials_operations,
resource_type=ResourceType.MGMT_MSI
)

with self.command_group('identity', identity_sdk, client_factory=_msi_user_identities_operations) as g:
# Base identity commands
with self.command_group('identity', identity_sdk, is_preview=True) as g:
g.custom_command('create', 'create_identity', validator=process_msi_namespace)
g.show_command('show', 'get')
g.command('delete', 'delete')
g.custom_command('list', 'list_user_assigned_identities')
g.custom_command('list-resources', 'list_identity_resources', min_api='2021-09-30-preview')
g.command('list-operations', 'list', command_type=msi_operations_sdk)

with self.command_group('identity', msi_operations_sdk, client_factory=_msi_operations_operations) as g:
g.command('list-operations', 'list')

with self.command_group('identity federated-credential', federated_identity_credentials_sdk,
client_factory=_msi_federated_identity_credentials_operations,
min_api='2022-01-31-preview') as g:
min_api='2025-01-31-PREVIEW') as g:
g.custom_command('create', 'create_or_update_federated_credential')
g.custom_command('update', 'create_or_update_federated_credential')
g.custom_show_command('show', 'show_federated_credential')
Expand Down
34 changes: 28 additions & 6 deletions src/azure-cli/azure/cli/command_modules/identity/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from azure.cli.core.profiles import ResourceType
from azure.cli.core.azclierror import (
RequiredArgumentMissingError
RequiredArgumentMissingError,
MutuallyExclusiveArgumentError
)


Expand Down Expand Up @@ -35,15 +36,36 @@ def list_identity_resources(cmd, resource_group_name, resource_name):


def create_or_update_federated_credential(cmd, client, resource_group_name, identity_name, federated_credential_name,
issuer=None, subject=None, audiences=None):
issuer=None, subject=None, audiences=None, claims_matching_expression_value=None, claims_matching_expression_version=None):
_default_audiences = ['api://AzureADTokenExchange']
audiences = _default_audiences if not audiences else audiences
if not issuer or not subject:
raise RequiredArgumentMissingError('usage error: please provide both --issuer and --subject parameters')
if not issuer:
recommendation = "Please provide the issuer URL using --issuer parameter. For GitHub Actions, use 'https://token.actions.githubusercontent.com'"
raise RequiredArgumentMissingError('Issuer URL is required for federated credential creation', recommendation)

# When subject is present, CME should be null
if subject:
claims_matching_expression_value = None
claims_matching_expression_version = None
elif not claims_matching_expression_value:
recommendation = [
"Option 1: Provide --subject to create a federated credential with direct subject matching",
"Option 2: Provide --claims-matching-expression-value and --claims-matching-expression-version to create a federated credential with claims matching expression"
]
raise RequiredArgumentMissingError('Missing required authentication criteria for federated credential', recommendation)

if subject and claims_matching_expression_value:
recommendation = [
"Option 1: Remove --subject to use claims matching expression",
"Option 2: Remove --claims-matching-expression-value and --claims-matching-expression-version to use subject"
]
raise MutuallyExclusiveArgumentError('Subject and claims matching expression cannot be used together', recommendation)

FederatedIdentityCredential = cmd.get_models('FederatedIdentityCredential', resource_type=ResourceType.MGMT_MSI,
operation_group='federated_identity_credentials')
parameters = FederatedIdentityCredential(issuer=issuer, subject=subject, audiences=audiences)
parameters = FederatedIdentityCredential(issuer=issuer, subject=subject, audiences=audiences,
claims_matching_expression_value=claims_matching_expression_value,
claims_matching_expression_version=claims_matching_expression_version)

return client.create_or_update(resource_group_name=resource_group_name, resource_name=identity_name,
federated_identity_credential_resource_name=federated_credential_name,
Expand All @@ -61,4 +83,4 @@ def show_federated_credential(client, resource_group_name, identity_name, federa


def list_federated_credential(client, resource_group_name, identity_name):
return client.list(resource_group_name=resource_group_name, resource_name=identity_name)
return client.list(resource_group_name=resource_group_name, resource_name=identity_name)
Loading
Loading