Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ def _msi_operations_operations(cli_ctx, _):


def _msi_federated_identity_credentials_operations(cli_ctx, _):
return _msi_client_factory(cli_ctx).federated_identity_credentials
return _msi_client_factory(cli_ctx, api_version='2025-01-31-preview').federated_identity_credentials
39 changes: 30 additions & 9 deletions src/azure-cli/azure/cli/command_modules/identity/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,42 +39,63 @@
helps['identity federated-credential'] = """
type: group
short-summary: Manage federated identity credentials under user assigned identities.
long-summary: |
Federated identity credentials enable user-assigned managed identities to be authenticated using external identity providers.
Credentials can be configured using either a subject identifier or a claims matching expression for more complex token validation scenarios.
"""

helps['identity federated-credential create'] = """
type: command
short-summary: Create a federated identity credential under an existing user assigned identity.
examples:
- name: Create a federated identity credential under a specific user assigned identity.
- name: Create a federated identity credential using subject identifier
text: |
az identity federated-credential create --name myFicName --identity-name myIdentityName --resource-group myResourceGroup --issuer myIssuer --subject mySubject --audiences myAudiences
az identity federated-credential create --fc-name myFedCredential --name myIdentity --resource-group myResourceGroup --issuer 'https://aks.azure.com/issuerGUID' --subject 'system:serviceaccount:ns:svcaccount' --audiences 'api://AzureADTokenExchange'
Copy link
Copy Markdown
Member

@jiasli jiasli May 7, 2025

Choose a reason for hiding this comment

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

Changing --identity-name to --name is incorrect and not allowed.

- name: Create a federated identity credential using claims matching expression (long form)
text: |
az identity federated-credential create --fc-name myFedCredential --name myIdentity --resource-group myResourceGroup --issuer 'https://aks.azure.com/issuerGUID' --cme-value "claims['sub'] startswith 'repo:contoso-org/contoso-repo:ref:refs/heads/'" --cme-version 1 --audiences 'api://AzureADTokenExchange'
- name: Create a federated identity credential using claims matching expression (short form)
text: |
az identity federated-credential create -f myFedCredential --name myIdentity --resource-group myResourceGroup --issuer 'https://aks.azure.com/issuerGUID' -v "claims['sub'] startswith 'repo:contoso-org/contoso-repo:ref:refs/heads/'" -e 1 --audiences 'api://AzureADTokenExchange'
"""

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.
- name: Update a federated identity credential using subject identifier
text: |
az identity federated-credential update --fc-name myFedCredential --name myIdentity --resource-group myResourceGroup --issuer 'https://aks.azure.com/issuerGUID' --subject 'system:serviceaccount:ns:svcaccount' --audiences 'api://AzureADTokenExchange'
- name: Update a federated identity credential using claims matching expression (long form)
text: |
az identity federated-credential update --name myFicName --identity-name myIdentityName --resource-group myResourceGroup --issuer myIssuer --subject mySubject --audiences myAudiences
az identity federated-credential update --fc-name myFedCredential --name myIdentity --resource-group myResourceGroup --issuer 'https://aks.azure.com/issuerGUID' --cme-value "claims['sub'] startswith 'repo:contoso-org/contoso-repo:ref:refs/heads/'" --cme-version 1 --audiences 'api://AzureADTokenExchange'
- name: Update a federated identity credential using claims matching expression (short form)
text: |
az identity federated-credential update -f myFedCredential --name myIdentity --resource-group myResourceGroup --issuer 'https://aks.azure.com/issuerGUID' -v "claims['sub'] startswith 'repo:contoso-org/contoso-repo:ref:refs/heads/'" -e 1 --audiences 'api://AzureADTokenExchange'
"""

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.
- name: Delete a federated identity credential using long form
text: |
az identity federated-credential delete --fc-name myFedCredential --name myIdentity --resource-group myResourceGroup
- name: Delete a federated identity credential using short form
text: |
az identity federated-credential delete --name myFicName --identity-name myIdentityName --resource-group myResourceGroup
az identity federated-credential delete -f myFedCredential --name myIdentity --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.
- name: Show a federated identity credential using long form
text: |
az identity federated-credential show --fc-name myFedCredential --name myIdentity --resource-group myResourceGroup
- name: Show a federated identity credential using short form
text: |
az identity federated-credential show --name myFicName --identity-name myIdentityName --resource-group myResourceGroup
az identity federated-credential show -f myFedCredential --name myIdentity --resource-group myResourceGroup
"""

helps['identity federated-credential list'] = """
Expand All @@ -83,5 +104,5 @@
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
az identity federated-credential list --name myIdentity --resource-group myResourceGroup
"""
8 changes: 5 additions & 3 deletions src/azure-cli/azure/cli/command_modules/identity/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ 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:
c.argument('federated_credential_name', options_list=('--name', '-n'), help='The name of the federated identity credential resource.')
with self.argument_context('identity federated-credential', min_api='2025-01-31-preview') as c:
c.argument('federated_credential_name', options_list=['--fc-name', '-f'], help='The name of the federated identity credential resource.')
Copy link
Copy Markdown
Member

@jiasli jiasli May 7, 2025

Choose a reason for hiding this comment

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

Renaming federated_credential_name from --name/-n to --fc-name/-f is incorrect as --name argument should match the command noun/subject - federated-credential. This is also a breaking change and not allowed.

c.argument('identity_name', help='The name of the identity resource.')

for scope in ['identity federated-credential create', 'identity federated-credential update']:
with self.argument_context(scope) as c:
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('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. Cannot be used with --claims-matching-expression-value.')
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=['--cme-value', '-v'], help='A claims matching expression that is evaluated against incoming tokens for access token requests. Cannot be used with --subject.')
c.argument('claims_matching_expression_version', options_list=['--cme-version', '-e'], help='Version of the claims matching expression language. Required when using --cme-value.')
Comment on lines +34 to +35
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The best practice is to use full names, such as --claims-matching-expression-value, --claims-matching-expression-version. Using --cme-value and --cme-version can be confusing. -v and -e are worse.

35 changes: 27 additions & 8 deletions src/azure-cli/azure/cli/command_modules/identity/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,49 @@ 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:
raise RequiredArgumentMissingError('usage error: please provide --issuer parameter')

if subject and claims_matching_expression_value:
raise RequiredArgumentMissingError('usage error: cannot specify both --subject and --claims-matching-expression-value parameters')

if not subject and not claims_matching_expression_value:
raise RequiredArgumentMissingError('usage error: must provide either --subject or --claims-matching-expression-value parameter')

if claims_matching_expression_value and not claims_matching_expression_version:
raise RequiredArgumentMissingError('usage error: --claims-matching-expression-version is required when using --claims-matching-expression-value')

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,
parameters=parameters)
parameters=parameters, api_version='2025-01-31-preview')


def delete_federated_credential(client, resource_group_name, identity_name, federated_credential_name):
return client.delete(resource_group_name=resource_group_name, resource_name=identity_name,
federated_identity_credential_resource_name=federated_credential_name)
federated_identity_credential_resource_name=federated_credential_name,
api_version='2025-01-31-preview')


def show_federated_credential(client, resource_group_name, identity_name, federated_credential_name):
return client.get(resource_group_name=resource_group_name, resource_name=identity_name,
federated_identity_credential_resource_name=federated_credential_name)
federated_identity_credential_resource_name=federated_credential_name,
api_version='2025-01-31-preview')


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,
api_version='2025-01-31-preview')
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ def test_federated_identity_credential(self, resource_group):
'identity': 'ide',
'fic1': 'fic1',
'fic2': 'fic2',
'fic3': 'fic3',
'fic4': 'fic4',
'subject1': 'system:serviceaccount:ns:svcaccount1',
'subject2': 'system:serviceaccount:ns:svcaccount2',
'subject3': 'system:serviceaccount:ns:svcaccount3',
'issuer': 'https://oidc.prod-aks.azure.com/IssuerGUID',
'audience': 'api://AzureADTokenExchange',
'claims_expr1': "claims['sub'] startswith 'repo:org/repo:ref:refs/heads/'",
'claims_expr2': "claims['sub'] startswith 'repo:org/repo:ref:refs/tags/'",
'claims_expr3': "claims['sub'] startswith 'repo:org/repo:ref:refs/pulls/'",
'claims_expr4': "claims['sub'] startswith 'repo:org/repo:ref:refs/main/'",
})

self.cmd('identity create -n {identity} -g {rg}')
Expand Down Expand Up @@ -110,9 +116,43 @@ def test_federated_identity_credential(self, resource_group):
self.check('[0].subject', '{subject2}'),
])

# delete a federated identity credential
# create federated identity credentials with claims matching expression using short form
self.cmd('identity federated-credential create -f {fic3} --identity-name {identity} --resource-group {rg} '
'-v {claims_expr1} -e 1 --issuer {issuer} --audiences {audience}',
checks=[
self.check('length(audiences)', 1),
self.check('audiences[0]', '{audience}'),
self.check('issuer', '{issuer}'),
self.check('claimsMatchingExpressionValue', '{claims_expr1}'),
self.check('claimsMatchingExpressionVersion', '1')
])

# create federated identity credentials with claims matching expression using -cv/-cvr syntax
self.cmd('identity federated-credential create --fed-name {fic4} --identity-name {identity} --resource-group {rg} '
'-cv {claims_expr2} -cvr 1 --issuer {issuer} --audiences {audience}',
checks=[
self.check('length(audiences)', 1),
self.check('audiences[0]', '{audience}'),
self.check('issuer', '{issuer}'),
self.check('claimsMatchingExpressionValue', '{claims_expr2}'),
self.check('claimsMatchingExpressionVersion', '1')
])

# update federated identity credential with claims matching expression using -cv/-cvr syntax
self.cmd('identity federated-credential update -f {fic3} --identity-name {identity} --resource-group {rg} '
'-cv {claims_expr3} -cvr 1 --issuer {issuer} --audiences {audience}',
checks=[
self.check('name', '{fic3}'),
self.check('claimsMatchingExpressionValue', '{claims_expr3}')
])

# delete all federated identity credentials
self.cmd('identity federated-credential delete --name {fic2}'
' --identity-name {identity} --resource-group {rg} --yes')
self.cmd('identity federated-credential delete --name {fic3}'
' --identity-name {identity} --resource-group {rg} --yes')
self.cmd('identity federated-credential delete --name {fic4}'
' --identity-name {identity} --resource-group {rg} --yes')
self.cmd('identity federated-credential list --identity-name {identity} --resource-group {rg}',
checks=[
self.check('type(@)', 'array'),
Expand Down
Loading