Automatically update Service Principal client secret on expiry? - azure

I have a Service Principal for a Power Platform environment which will be used by a DevOps platform to make deployments to this environment.
The service principal requires me to set a client secret which will be referenced by my Service Connection in DevOps
You must set a client secret expiry date of up to 2 years and after that time, it won't work. So I would need to go into the Azure portal, update the client secret and then into DevOps and update the service connection.
Is there a way I can do this automatically?

You must set a client secret expiry date of up to 2 years and after that time, it won't work.
Actually, no need to do that, in azure portal, the maximum is 2 years, but you could use azure powershell to create a near-permanent secret, e.g. 100 years.
If you want to custom the secret value, use Az module, login with Connect-AzAccount, then use New-AzADAppCredential as below.
$SecureStringPassword = ConvertTo-SecureString -String "password" -AsPlainText -Force
New-AzADAppCredential -ApplicationId <ApplicationId of the App Registration> -CustomKeyIdentifier "test" -Password $SecureStringPassword -EndDate (Get-Date).AddYears(100)
If you want to generate a secret value automatically, use AzureAD module, login with Connect-AzureAD, then use as New-AzureADApplicationPasswordCredential below.
New-AzureADApplicationPasswordCredential -ObjectId <ObjectId of the App Registration> -EndDate (Get-Date).AddYears(100)

Related

Authenticate external clients using Bearer Token using PowerShell script

Scenario: I have a Function App deployed in Azure that needs to be called from a PowerShell client in a non-Microsoft domain. My logic is to authenticate the user via their onMicrosoft domain email address and password. I am using MSAL to do the same. But this needs clientId and tenantId to work, as per my current knowledge, we cannot ask users to put in client id and client secret here.
Problem 1: Is using the MSAL the best way to achieve this? I created an App Registration in my Azure cloud and for testing, I am using the clientId and tenantId of the above App registration in the below PowerShell and I use my own Microsoft email/password + 2FA for getting the token. I cannot propagate the same clientId and tenantId to our actual non-Microsoft client.
Problem 2: I might be not fully leveraging MSAL here, since I am unable to persist the token (System/env variables are not allowed), and every time the PS script is invoked the interactive login window opens up, which is not a pleasant user experience.
Sample script code:
Assume $clientId and $tenantId bieng user input arguments
if (-not(Get-Module -ListAvailable -Name MSAL.ps))
{
Install-Module -Name MSAL.ps -AllowClobber -Confirm:$False -Force -AcceptLicense
}
Import-Module MSAL.ps
$Token = Get-MsalToken -ClientId $clientId -TenantId $tenantId -Interactive -Scope 'https://graph.microsoft.com/User.Read'
$bearerToken = $Token.AccessToken

AAS Unable to obtain authentication token using the credentials provided

Currently, we are using Service account to refresh the Azure Analysis Service Tabular model using the Azure Automation account and now planning to move to Service principal
Below are the steps I followed to do the required setup:
Create a Run as accounts from the Automation Account screen
The above step created a Service Principal in Azure Active Directory and using this I created a Client Secret. After this, I picked up the below values from the Service Principal:
Application (client) ID
Object ID
Directory (tenant) ID
Client Secret ID
Then, I have created a Credential in the Automation Account page using the <Application (client) ID> and <in the Azure Automation account
Username: <Application (client) ID>
Password:
Edited the Automation account Runbook to fetch the Service Principal credentials and then refresh the Azure Analysis Service. Below is the script:
#Tls 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
#Base Values
$databaseName = "Reporting-Tabular"
$server = "asazure://.asazure.windows.net/AASServer"
#Get Creds
$Credential = Get-AutomationPSCredential -Name 'service-principal'
Write-Output "Credential Obtained, invoking refresh"
Invoke-ProcessASDatabase -DatabaseName $databaseName -RefreshType "Full" -Server $server - ServicePrincipal -Credential $Credential
Write-Output "Done!"*
Added the app:<Application (client) ID>#<Directory (tenant) ID> to the Azure Analysis Services admin list using SSMS
I am getting the below error when I test the run book using the Test pane in the Automation account:
Unable to obtain authentication token using the credentials provided.
Please help how to resolve this issue.
in the Automation Credential password try pasting app registration's secret value instead of secret id.

Reset the client secret of Azure Service Principal using powershell

Using powershell commands i want to reset the Service Principal client secret.
I followed the below steps from the article https://learn.microsoft.com/en-us/powershell/azure/create-azure-service-principal-azureps?view=azps-5.8.0
but it didnot reset the password
Remove-AzADSpCredential -DisplayName ServicePrincipalName
$newCredential = New-AzADSpCredential -ServicePrincipalName ServicePrincipalName
can you tell what i am doing wrong. I just want to reset the secret and have new one
I executed the above command and then i went to the app registration of that service principal and there i went to certificates & secrets i see it has not createed new secret.
Using bash i am able to reset the password by executing the below command but i want it to be done using powershell command
az ad sp credential reset --name
I went to the app registration of that service principal and there I went to certificates & secrets I see it has not created new secret.
Well, actually the command New-AzADSpCredential did create a new secret for you.
Firstly, you need to know the relationship between App Registration(AD App) and Service principal, see Application and service principal objects in Azure Active Directory.
In short, the service principal is the local representation for the AD App in a specific tenant. When you create the secret for the service principal, it will not appear in the Certificates & secrets blade, you can just get it with Get-AzADSpCredential.
If you want to reset the secret that you can find in the portal, you need to reset the sceret for the AD App(i.e. App Registration) via Remove-AzADAppCredential and New-AzADAppCredential.
You could refer to the sample below, it resets a secret with value ce96a0ed-5ae8-4a5a-9b3c-630da9ea3023, it is valid for one year, you can find it in the portal.
$obj = (Get-AzADApplication -DisplayName joyappv2).ObjectId
Remove-AzADAppCredential -ObjectId $obj -Force
$azurePassword = ConvertTo-SecureString "ce96a0ed-5ae8-4a5a-9b3c-630da9ea3023" -AsPlainText -Force
$date = Get-Date
$newCredential = New-AzADAppCredential -ObjectId $obj -Password $azurePassword -StartDate $date -EndDate $date.AddYears(1)
Note: You could not get the secret value again after creating it, so please store it when creating.

Why does getting a certificate from Azure Key Vault require it to be stored as a secret?

Blogs like the following
https://blogs.technet.microsoft.com/neales/2017/06/26/getting-a-private-certificate-from-key-vault/
Seem to retrive the secret? Does it not matter if it's "stored" as a certificate or not?
It depends on what you are planning to do with the certificate. You could update your question with details about the expected workflow you want to support.
But basically a certificate can be stored as a file. You can see more details (C#) to get inspired on how to do that after the certificate is loaded into a variable.
Exporting a Certificate as BASE-64 encoded .cer
Update
Security considerations to take into account. If you see the certificate stored in the azure key vault as a secret and you want to limit the access to it, then you have to consider how your PowerShell scripts will store the needed credentials for authenticate against the KeyVault.
If you plan on running the script unattended / scheduled without user interaction, you will have to store some kind of credentials on the machine that needs to run the script. BetterCredentials is a great PowerShell native module for storing credentials on the local machine.
I would recommend that you create an Azure Service Principal (App Registration / Registered App), that will get only enough permissions to get the certificate from the KeyVault. The created Service Principal details should then be stored locally on the machine and you should load those credentials first and use them for connecting to the KeyVault.
Example code that should be capable of loading a Service Principal details from the BetterCredentials and sign into Azure:
BetterCredentials\Get-Credential -UserName <application ID> -Store
$azureTenantId = <tenant ID>
$Cred = BetterCredentials\Get-Credential -UserName <application ID>
Add-AzureRmAccount -Credential $Cred -TenantId $azureTenantId -ServicePrincipal

Azure Key Vault Access Policy Doesn't Work For Groups

Access policies via groups on Azure Key Vault don't seem to work.
If I create a new key vault
New-AzureRmKeyVault -VaultName $vaultName
And check the keys (which there aren't any of currently)
Get-AzureKeyVaultKey -VaultName $vaultName
That works.
If I add access to a group that the current user is a member of
$group = (Get-AzureRmADGroup -SearchString 'All Developers')[0].Id
Set-AzureRmKeyVaultAccessPolicy -VaultName $vaultName -ResourceGroupName $resourceGroupName -ObjectId $group -PermissionsToKeys all -PermissionsToSecrets all
And remove direct access
Remove-AzureRmKeyVaultAccessPolicy -VaultName $vaultName -ResourceGroupName $resourceGroupName -UserPrincipalName $upn
The list operation now fails
Get-AzureRmKeyVault -VaultName $vaultName -ResourceGroupName $resourceGroupName
Get-AzureKeyVaultKey : Operation "list" is not allowed
How can I permission by group?
I discovered today that it works for users in permissioned group objects. Doesn't work for service principals in those groups.
In other words, if I authenticate using a client id and client secret, the associated service principal must have an access policy directly set on the key vault. If I permission a security group, a user in that group can in fact access the key vault. I guess this has something to do with how the JWT includes security groups in it with users, but not service principals...
The reason that adding an access policy to a group is that it isn't supported. If you look at the help for Set-AzureRmKeyVaultAccessPolicy there is this for ObjectId
-ObjectId <Guid>
Specifies the object ID of the user or service principal in Azure Active Directory for which to grant permissions.
Required? true
Position? named
Default value none
Accept pipeline input? true(ByPropertyName)
Accept wildcard characters? false
As you can see ObjectId only supports either Service principals or users.
This is reflected in the parameters of the source code for Set-AzureRmKeyVaultAccessPolicy and further up the chain the REST API when posting to
https://management.azure.com/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{vault-name}?api-version={api-version}
The payload contains the objectId parameter which is defined as
Specifies the object ID of a user or service principal in the Azure Active Directory tenant for the vault. The ID must be specified as a GUID.
I would imagine that this functionality will be added at some point in future, but at the moment it isn't possible.
This Access Denied / 403 Forbidden error can also happen when an app has made requests to a Key Vault before it was added to the Azure Active Directory Group.
Perhaps this has something to do with caching of service principal information on the App Service instance? I was unable to find documentation of this.
Solution: restart the App Service.

Resources