How to get Key Vault Secret under Web App's (with a user-assigned managed identity) Kudu powershell environment? - azure

If I have a web app which has a system-assigned managed identity and the web app has been added to a Key Vault's Access Policies, I can use the following code the get secret value from the Key Vault under Kudu
powershell Environment:
function GetSecret {
param (
[parameter(Mandatory=$True, Position=1)] [String] $keyVaultSecretUri
)
$apiVersion="2017-09-01"
$resourceURI = "https://vault.azure.net"
$tokenResponse = Invoke-RestMethod `
-Method Get `
-Headers #{"Secret"="$env:MSI_SECRET";"Content-Type"="application/json"} `
-Uri "${env:MSI_ENDPOINT}?resource=${resourceURI}&api-version=${apiVersion}"
$accessToken = $tokenResponse.access_token
$headers = #{'Authorization'="Bearer $accessToken"}
$keyVaultApiVersion="7.1"
$secret=Invoke-RestMethod -Method Get -Headers $headers -Uri "${keyVaultSecretUri}?api-version=${keyVaultApiVersion}"
return $secret
}
GetSecret -keyVaultSecretUri $SecreteUri
But if I give the web app a user-assigned managed identity (without system-assigned managed identity) and add that managed identity to the Key Vault's Access Policies (with enough permissions), the above code doesn't work.
Actually even the following three lines gets a runtime exception:
$apiVersion="2017-09-01"
$resourceURI = "https://vault.azure.net"
$tokenResponse = Invoke-RestMethod `
-Method Get `
-Headers #{"Secret"="$env:MSI_SECRET";"Content-Type"="application/json"} `
-Uri "${env:MSI_ENDPOINT}?resource=${resourceURI}&api-version=${apiVersion}"
The exception is (CorrelationId is hidden):
Invoke-RestMethod : {"StatusCode":400,"Message":"No MSI found for specified
ClientId/ResourceId.","CorrelationId":"0000000-0000-0000-0000-000000000000"}
At line:1 char:18
+ $tokenResponse = Invoke-RestMethod `
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:Htt
pWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShe
ll.Commands.InvokeRestMethodCommand
So how to get Key Vault Secret under Web App's (with a user-assigned managed identity) Kudu powershell environment??
(Probably the Headers for user-assigned identity web app is wrong)
PS: This answer How can I give access to key vault to a user assigned identity? doesn't resolve my question.

I actually wrote an article on this topic just a while ago: https://joonasw.net/view/get-managed-identity-access-token-in-azure-app-service-through-kudu.
There I link to the docs for the REST protocol that Managed Identity uses: https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=dotnet#using-the-rest-protocol.
If you use a user-assigned identity, you must identify which identity you want to use.
Since an App Service etc. can have multiple user-assigned identities.
In the docs they give the following options:
You only need to specify one of these if I recall correctly.
Tested this script and got a token for the user-assigned identity:
$resource = "https://vault.azure.net"
$clientId = "00000000-0000-0000-0000-000000000000"
$endpoint = $env:IDENTITY_ENDPOINT
$header = $env:IDENTITY_HEADER
$apiVersion = "2019-08-01"
$headers = #{ 'X-Identity-Header' = $header }
$url = "$($endpoint)?api-version=$apiVersion&resource=$resource&client_id=$clientId"
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
$response.access_token

Related

Azure AutomationAccount DSC scripts debug

I am running a DSC script from an Azure automation account to configure Windows VMs. It downloads files from a storage account in Azure to hosts for more configurations. If I hardcode the storage SAS token, it works fine. But I would like to get the SAS token in the DSC script. I use managed identity of the Automation account and assigned proper IAM access in storage account. I am able to get the SAS token in a test runbook script, but not in DSC script.
I got the main part of the code from
https://learn.microsoft.com/en-us/azure/automation/enable-managed-identity-for-automation
The error I am getting indicates that the SAS token is not generated correctly, but I can't find a way to see what the error msgs are from this part of the code in DSC when it executed.
any help/suggestion is appreciated!
Configuration DSCtest {
Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
$resource= "?resource=https://management.azure.com/"
$url = $env:IDENTITY_ENDPOINT + $resource
$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$Headers.Add("X-IDENTITY-HEADER", $env:IDENTITY_HEADER)
$Headers.Add("Metadata", "True")
$accessToken = Invoke-RestMethod -Uri $url -Method 'GET' -Headers $Headers
$Atoken = $accessToken.access_token
Write-Output $Atoken
$toDate = (Get-Date).AddDays(4).toString("yyyy-MM-ddT00:00:00Z")
$params = #{canonicalizedResource="/file/storageacnt/exec";signedResource="c";signedPermission="rcw";signedProtocol="https";signedExpiry=$toDate}
$jsonParams = $params | ConvertTo-Json
$sasResponse = Invoke-WebRequest -Uri https://management.azure.com/subscriptions/xxxxxxxxxxxx/resourceGroups/rg-xxxxx/providers/Microsoft.Storage/storageAccounts/storageaccnt/listServiceSas/?api-version=2017-06-01 -Method POST -Body $jsonParams -Headers #{Authorization="Bearer $Atoken"} -UseBasicParsing
$sasContent = $sasResponse.Content | ConvertFrom-Json
$sasCred = $sasContent.serviceSasToken
write-host $sasCred
$sasToken = "?$sasCred"
Node LocalHost {
....

Unable to create Azure B2C User Flow via powershell with b2cIdentityUserFlow

I am trying to create a user flow with PowerShell, but I receiveThe remote server returned an error: (403) Forbidden.. I was reading the documentation from Microsoft but with no success.
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\azdeploy\flows\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
JSON Content(Default From Microsoft Docs):
{
"id": "Customer",
"userFlowType": "signUpOrSignIn",
"userFlowTypeVersion": 3
}
Connect-AzAccount is made with a user who is Global Administrator, also tried with Lifecycle Workflows Administrator permissions. I don't know what to do, trying the old API but it is deprecated. I need to create a few User Flows with а few Application Claims. How can I achieve this?
Thanks!
I tried to reproduce the same in my environment and got below results:
I created one json file with same parameters as you like below:
I have one user named Sritest having Global Administrator role like below:
When I ran the same code as you by signing in with above user, I got same error as below:
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
Response:
You need to have IdentityUserFlow.ReadWrite.All permission to create userflow.
To resolve the error, I registered one Azure AD application and added that API permission like below:
Make sure to grant admin consent after adding API permissions in application. Now, I created one client secret and added all these details in getting access token by modifying PowerShell code.
When I ran below modified code, userflow created successfully as below:
Connect-AzureAD -TenantId "c6d99123-0cf9-4b64-bde3-xxxxxxxxx"
$graphtokenBody = #{
grant_type = "client_credentials"
scope = "https://graph.microsoft.com/.default"
client_id = "appID"
client_secret = "secret"
}
$graphToken = Invoke-RestMethod -Uri "https://login.microsoftonline.com/c6d99123-0cf9-4b64-bde3-xxxxxxxxx/oauth2/v2.0/token" -Method POST -Body $graphtokenBody
$token = $graphToken.access_token
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($token)";
} `
-Body $signinFlowContent
Response:
To confirm that, I checked the same in Portal where B2C_1_Customer userflow is present like below:

How to get the access token to Azure API Management programmatically?

I'm trying to implement Azure Active Directory in my API Management instance using the Protect an API by using OAuth 2.0 with Azure Active Directory and API Management doc. The doc suggests that in order to get the access token I need to use the Developer Portal.
My problem is: An external application is going to communicate with API Management. Is there a way to omit the Developer Portal and get the access token programmatically?
It's a pain but thanks to Jos Lieben I am able to do it with this Powershell function
It's specifically for granting API access on behalf of the Org, but as you can see you can extract the commands to get and use the API token.
Original Author Link: https://www.lieben.nu/liebensraum/2018/04/how-to-grant-oauth2-permissions-to-an-azure-ad-application-using-powershell-unattended-silently/
Function Grant-OAuth2PermissionsToApp{
Param(
[Parameter(Mandatory=$true)]$Username, #global administrator username
[Parameter(Mandatory=$true)]$Password, #global administrator password
[Parameter(Mandatory=$true)]$azureAppId #application ID of the azure application you wish to admin-consent to
)
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($Username, $secpasswd)
$res = login-azurermaccount -Credential $mycreds
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$refreshToken = #($context.TokenCache.ReadItems() | where {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken
$body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
$apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded'
$header = #{
'Authorization' = 'Bearer ' + $apiToken.access_token
'X-Requested-With'= 'XMLHttpRequest'
'x-ms-client-request-id'= [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()
}
$script:url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $header -Method POST -ErrorAction Stop
}

List Cloud Service Classic use PowerShell and Azure Rest API

I Have a problem. Could you please help me view list Cloud Service Classic use PowerShell and Azure Rest API. When I used script for Web APP I show list Web APP, but when I used scrip for Cloud Service Classic I show error.
# Variables
$TenantId = "" # Enter Tenant Id.
$ClientId = "" # Enter Client Id.
$ClientSecret = "" # Enter Client Secret.
$Resource = "https://management.core.windows.net/"
$SubscriptionId = "" # Enter Subscription Id.
$RequestAccessTokenUri = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$body = "grant_type=client_credentials&client_id=$ClientId&client_secret=$ClientSecret&resource=$Resource"
$Token = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $body -ContentType 'application/x-www-form-urlencoded'
Write-Host "Print Token" -ForegroundColor Green
Write-Output $Token
# Get Azure Resource Groups
$ResourceGroupApiUri = "https://management.core.windows.net/$SubscriptionId/services/hostedservices"
$Headers = #{}
$Headers.Add("Authorization","$($Token.token_type) "+ " " + "$($Token.access_token)")
$ResourceGroups = Invoke-RestMethod -Method Get -Uri $ResourceGroupApiUri -Headers $Headers
Write-Host "Print Resource groups" -ForegroundColor Green
Write-Output $ResourceGroups
Invoke-RestMethod : ForbiddenErrorThe server failed to authenticate the request. Verify that the certificate is valid and
is associated with this subscription.
Actually, there is a built-in ASM PowerShell to list the cloud services associated with the current subscription.
Get-AzureService
Reference - https://learn.microsoft.com/en-us/powershell/module/servicemanagement/azure/get-azureservice?view=azuresmps-4.0.0
Besides, if you insist on calling the ASM rest api with powershell, you could refer to this article, the sample calls the Get Deployment api, just change it to List Cloud Services.
#Request Headers required to invoke the GET DEPLOYMENT REST API
$method
=
“GET”
$headerDate
= ‘2009-10-01’
$headers
= #{“x-ms-version”=“$headerDate“}
#Retrieving the subscription ID
$subID
= (Get-AzureSubscription
-Current).SubscriptionId
$URI
=
https://management.core.windows.net/$subID/services/hostedservices/kaushalz/deployments/4f006bb7d2874dd4895f77a97b7938d0
#Retrieving the certificate from Local Store
$cert
= (Get-ChildItem
Cert:\CurrentUser\My
|
?{$_.Thumbprint -eq
“B4D460D985F1D07A6B9F8BFD67E36BC53A4490FC”}).GetRawCertData()
#converting the raw cert data to BASE64
body
=
“<Binary>—–BEGIN CERTIFICATE—–`n$([convert]::ToBase64String($cert))`n—–END CERTIFICATE—–</Binary>”
#Retrieving the certificate ThumbPrint
$mgmtCertThumb
= (Get-AzureSubscription
-Current).Certificate.Thumbprint
#Passing all the above parameters to Invoke-RestMethod cmdlet
Invoke-RestMethod
-Uri
$URI
-Method
$method
-Headers
$headers
-CertificateThumbprint
” B4D460D985F1D07A6B9F8BFD67E36BC53A4490FC”
-ContentType
$ContentType

Azure AD Access Token Without ADAL

I am trying to edit a user's AD profile on the server side which requires an access token. I can do this with the native app example which uses only a client id and secret. The docs mention that this is possible with only http requests (see Get Access Token), but I can't find any example or way to do this.
You can use OAuth2 to request an access token. Here is an example on how you use OAuth2 with Azure AD. It's written in PowerShell Script.
$tenantID = "<the Tenant ID of your Azure AD>"
$loginEndpoint = "https://login.windows.net/"
$graphResourceURI = "https://graph.windows.net/"
$clientId = "<the client id of your AD application>"
$key = "<the client secret of your AD application>"
# the URL for requesting access token.
$tokenURL = $loginEndpoint+$tenantID+"/oauth2/token"
# the token request body.
$body = "grant_type=client_credentials&client_id="+$clientId `
+"&client_secret="+[system.uri]::EscapeDataString($key) `
+"&resource="+[system.uri]::EscapeDataString($graphResourceURI)
# the token request headers.
$headers = #{"Content-Type"="application/x-www-form-urlencoded"}
$authenticationResult = Invoke-RestMethod -Method POST `
-Uri $tokenURL -Headers $headers -Body $body
$authenticationResult.access_token
The AD application you use here must be "Web application and/or web API", because a Native AD application does not have client secret.
I got this to work using Jack's answer but with a couple of changes. Here is the complete working example:
$tenantID = "<the name of your tenant of your Azure AD>"
$loginEndpoint = "https://login.windows.net/"
$graphResourceURI = "https://graph.windows.net/"
$clientId = "<the client id of your AD application>"
$key = "<the client secret of your AD application>"
# the URL for requesting access token.
$tokenURL = $loginEndpoint+$tenantID+"/oauth2/token"
# the token request body.
$body = "grant_type=client_credentials&client_id="+$clientId `
+"&client_secret="+[system.uri]::EscapeDataString($key) `
+"&resource="+[system.uri]::EscapeDataString($graphResourceURI)
# the token request headers.
$headers = #{"Content-Type"="application/x-www-form-urlencoded"}
$authenticationResult = Invoke-RestMethod -Method POST `
-Uri $tokenURL -Headers $headers -Body $body
$authenticationResult.access_token

Resources