Authenticate external clients using Bearer Token using PowerShell script - azure

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

Related

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.

Programmatically authenticate into AAD with MFA via powershell

I have a PowerShell script that logs into Azure subscription with the command Connect-AzAccount using user's credentials.
The code is the following:
$userPassword='password'
$userName="username"
$tenantId="########-####-####-####-############"
$subscriptionId="########-####-####-####-############"
$azureSecpassword = $userPassword | ConvertTo-SecureString -asPlainText -Force
$azureCredential = New-Object System.Management.Automation.PSCredential($userName, $azureSecpassword)
Connect-AzAccount -Credential $azureCredential -Tenant $tenantId -SubscriptionId $subscriptionId
The code above works without any user interaction.
Few days ago the customer enabled the multi-factor authentication for the users.
How can I keep a fully automated login process (without user interactions) with the multi-factor authentication?
Best Regards.
This is a common question. Unfortunately, the answer is No. If the account is MFA-enabled, you could just login with an interactive way.
In such a case, we choose to use the service principal to login with non-interactive in general.
$azureAplicationId ="Azure AD Application Id"
$azureTenantId= "Your Tenant Id"
$azurePassword = ConvertTo-SecureString "client secret" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($azureAplicationId , $azurePassword)
Connect-AzAccount -Credential $psCred -TenantId $azureTenantId -ServicePrincipal
Reference - Sign in with a service principal.
If you must log in as a user, there might be 2 optional approaches.
1. If you will run the script locally or in a specific PC
You can Persist Azure user credentials. You can enable auto save, or manually save the context to a file, and then use it in another PS session.
If you enabled auto save, then you can directly get the context as following:
Get-AzContext
# If you have more than one contexts, you can choose one by specifing the name
Get-AzContext -Name 'CSP Azure (e5b0****-****-****-****-5e5f****4c68) - jack#h****a.onmicrosoft.com'
If you want to manually do it, here is the sample:
# Interactively log for one time
Connect-AzAccount
# Save the context
Save-AzContext -Path D:\ctx.dat
And in another PS session, you can:
Import-AzContext -Path D:\ctx.dat
2. Use refresh token to acquire token, and connect to Azure
You can get the refresh token from the auto saved Azure context (usually at C:\Users\<UserName>\.Azure\TokenCache.dat).
Open the dat file with notepad, and you will get the refresh token:
Then you can get a new token in PowerShell with that refresh token, and connect to Azure:
Clear-AzContext
$tenantId = "e4c9ab4e-****-****-****-230b****57fb"
$subscriptionId = "e5b0fcfa-****-****-****-5e5f****4c68"
$refreshToken = 'AQABAAAAAAAP0****a lot of characters here*****0A9FWoB8mvDtoWRJHBVO7GJzodLKYmNIAA'
$url = "https://login.microsoftonline.com/" + $tenantId + "/oauth2/token"
$body = "grant_type=refresh_token&refresh_token=" + $refreshToken
$response = Invoke-RestMethod $url -Method POST -Body $body
$AccessToken = $response.access_token
Connect-AzAccount -AccountId "the user id, jack#h****a.onmicrosoft.com" -AccessToken $AccessToken -Tenant $tenantId -SubscriptionId $subscriptionId
How can I keep a fully automated login process (without user interactions) with the multi-factor authentication?
You can't do this with a user account--that's the whole point of multi-factor authentication.
Instead, Azure AD supports authenticating with a service principal (instead of a user principal, like you're doing currently), and Azure supports granting access to Azure resources to service principals.
MFA requirements (and other conditional access policies) do not apply to service principals (often referred to as an Azure AD "app"), and service principals support more secure methods of authentication for automation scenarios (e.g. public/private key pairs).
So, what you should do:
Ensure the machine running this script is secure. Anyone with access to the machine has the same amount of access as the script.
Create an application identity and associate credentials with it.
Note: It is strongly recommend you use certificate-based authentication for your service principal, instead of password-based. It is a very insecure practice to have any kind of secret stored in a PowerShell script!
Grant the service principal the minimum level of access to Azure resources, to allow it to complete the required task.
Update your script to use the app's identity (service principal) instead of the user's identity. It's even simpler than using a user account:
$tenantId = "########-####-####-####-############"
$subscriptionId = "########-####-####-####-############"
$appId = "########-####-####-####-############"
$thumbprint= "##############"
Connect-AzAccount -ServicePrincipal -TenantId $tenantId -ApplicationId $appId -CertificateThumbprint $thumbprint
Note: If this script is running on a VM in Azure, you should forget step 2, and simply enable a managed identity and use that.

Method to get list of AD users using application password

In an application, I'm currently using PowerShell and MSOnline module (Connect-MsolService and Get-MsolUser) to get a list of AD users. A global admin provides his username and password and the application is able to get a list of all users under that tenant.
That works fine... As long as the password is not an app password. When an app password is used then the following is what the global admin gets to see:
Authentication Error: Bad username or password
My question is: Is there any other method, which uses PowerShell, but doesn't have to, to get the list of users in AD, but which works with app password? I know of Graph API, but that's not a fit for the project right now.
If I understand you correctly, you want to use the AD App and its password(secret) to list the users.
You could use the Az powershell module to do that, login with the service principal and list users via Get-AzADUser. Also, make sure your AD App(service principal) has the admin role like User administrator or Global administrator.
$azureAplicationId ="<AD App Application id>"
$azureTenantId= "<tenant id>"
$azurePassword = ConvertTo-SecureString "<password>" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($azureAplicationId , $azurePassword)
Connect-AzAccount -Credential $psCred -TenantId $azureTenantId -ServicePrincipal
Get-AzADUser
Update:
Currently, use app password of MFA enabled user to connect MSOL powershell is not supported, for more details see this link.
App passwords are NOT supported, simply use Connect-MsolService without any parameter to trigger the ADAL dialog and complete the 2FA challenge as normal.

How to perform a Non-Interactive MFA Login to Azure Subscription using Powershell

Previously, I was able to login to the azure subscription via powershell using the below code.
$azureAccountName ="username"
$azurePassword = ConvertTo-SecureString "Password" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($azureAccountName, $azurePassword)
Login-AzureRmAccount -Credential $psCred
Recently, Microsoft introduced MFA (Multi-Factor Authentication) and now the above code fails as we now have to verify the login via a code received on the mobile number registered at the time of profile creation.
I do not want the interactive login but the automatic one which my code was earlier able to do.
Any suggestions?
As a workaround, I think you can use service principal instead of your Microsoft account.
About create Azure service principal, we can follow this article via Azure portal to create it.
Then use PowerShell like this:
$subscriptionId="5384xxxx-xxxx-xxxx-xxxx-xxxxe29axxxx"
$tenantid="1fcf418e-66ed-4c99-9449-d8e18bf8737a"
$clientid="1498b171-e1ca-451f-9d7a-8ef56a178b89" #appid
$password="7db814b1-xxxx-4654-xxxx-1d210cb546f9"
$userPassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$userCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $clientid, $userPassword
Add-AzureRmAccount -TenantId $tenantid -ServicePrincipal -SubscriptionId $subscriptionId -Credential $userCredential
In this way, we can use powershell to login it.
This is not posible (at least to my knowledge).
Use an application password.
I've highlighted a couple things from the Microsoft documentation here, but the short version is that an app password is basically your personal back door to bypass MFA. Note that for that reason an organization can restrict users from being able to create app passwords.
An app password is a long, randomly generated password that you provide only once instead of your regular password when signing in to an app or device that doesn't support two-step verification.
https://learn.microsoft.com/en-us/azure/active-directory/user-help/multi-factor-authentication-end-user-app-passwords
Certain non-browser apps, such as Outlook 2010, doesn't support two-step verification. This lack of support means that if you're using two-step verification, the app won't work. To get around this problem, you can create an auto-generated password to use with each non-browser app, separate from your normal password.
You're given an app password during your initial two-step verification registration. If you need more than that one password, you can create additional passwords, based on how you use two-step verification
Use one app password per device, not per app. For example, create a single password for all the apps on your laptop, and then another single password for all the apps on your desktop.
There's a limit of 40 passwords per user. If you try to create one after that limit, you'll be prompted to delete an existing password before being allowed to create the new one.

Running New-AzureRmResourceGroupDeployment from within a Function App

I need to wire up a stateless worker ad-hoc to perform a long running job based off a user action that self destructs when its done. I am trying to run New-AzureRmResourceGroupDeployment from within a PoSh Function App and cannot figure out how to authenticate to Azure from within the PoSh script.
I tried this:
$accountName = "myID#mydomain.com"
$pwd = ConvertTo-SecureString "password" -AsPlainText -Force
$cred = new-object PSCredential($accountName, $pwd)
Add-AzureRmAccount -Credential $cred
New-AzureResourceGroupDeployment -ResourceGroupName yadda yadda
And I get an error message that I need to use an Organization ID (which I am, our Azure AD is federated and we use AD Sync (and SiteMinder w/o WS-* if that matters)):
Add-AzureRmAccount : -Credential parameter can only be used with Organization ID credentials. For more information, please refer to http://go.microsoft.com/fwlink/?linkid=331007&clcid=0x409 for more information about the difference between an organizational account and a Microsoft account.
I tried "Login-AzureRMAccount -Credential $cred" with similar results.
If I do the Add- or Login- cmdlets from a PoSh window on my local machine (which is member joined to AD) with the -Credential flag I get a similar error. If I run the cmdlets without the credential I am prompted for credentials through an interactive ID/PW window (I do not have to enter my password once I type in my ID).
Does anyone know how I can do the authentication? I would be okay with authenticating like above, some sort of pass through credential from our web layer, or even an Option C I don't know about.
You will need to use service principal for authentication. A sample with instructions can be found here.
Azure Function role like permissions to Stop Azure Virtual Machines
For that you would need to use Service Principal auth. I don't think there is any sense of copypasting Azure Doc's to this answer, just consult this document:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal

Resources