Using Azure Yaml pipeline to call powershell RestMethod with secret variable in body - azure

I am trying to automate a step in our Azure yaml pipeline to delete some users from Auth0 using PowerShell and RestMethod, but for some reason the secret is either not used correctly or changed or something.
I have set the secret as a variable and then tried using it either directly in the script or through setting it on the task as well as an environment variable, but no luck.
Here is my last iteration, but still no joy, could anyone shed some light on the issue?
$api = "api/v2/"
$audience = $audience + $api
$secret = "$env:AUTH0_CLIENT_SECRET"
$test = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($secret))
Write-Host $clientSecret.substring(0,10)
Write-Host $test.substring(0,10)
Write-Host $audience
Write-Host $clientId
$Body = #{
client_id = $clientId
client_secret = $test OR $secret OR $clientSecret
audience = $audience
grant_type = "client_credentials"
}
Write-Host 'Getting token'
$response = Invoke-RestMethod https://mytenant.eu.auth0.com/oauth/token `
-Method 'POST' `
-ContentType 'application/json; charset=utf-8' `
-Body ($Body | ConvertTo-Json)
As shown, I have tried using test, secret and clientSecret as the secret, but also no luck. The write host at the start does show the correct starting characters for $clientSecret
Am I missing something basic or is this just not possible using RestMethod?

Ended up going scorched earth and completely deleted the pipelines and started again, this is the powershell script I ended up with that worked.
$token = "/oauth/token"
$https = "https://"
$api = "/api/v2/"
$audience = $https + $domain + $api
$tokenUrl = $https + $domain + $token
Write-Host 'Getting token'
$response = Invoke-RestMethod $tokenUrl `
-Method 'POST' `
-ContentType 'application/json; charset=utf-8' `
-Body (#{
client_id = $clientId
client_secret = "$env:AUTH0_CLIENT_SECRET"
audience = $audience
grant_type = "client_credentials"
} | ConvertTo-Json)

Related

Add redirectUris for Azure application from PowerShell

This answer describes how to set a redirectUri to an Azure application using the Azure CLI:
az rest `
--method PATCH `
--uri 'https://graph.microsoft.com/v1.0/applications/{id}' `
--headers 'Content-Type=application/json' `
--body "{spa:{redirectUris:['http://localhost:3000']}}"
That request will overwrite the current list of redirect URIs. How can I add an item to the list instead?
We can use this below Powershell script to do the same using App Registration object id, client id and secrets .
$url = "http://localhost:4000"
$objectId = "<objectid>"
$clientId = "<clientID>"
$tenantValue ="<tenantID>"
$clientSecret ="<client secret value>"
$serviceAccountEmail = "yourusername.onmicrosoft.com"
$serviceAccountPassword = "your password"
$webServiceURL = $url
Write-Host "$webServiceURL"
Write-Host "Done creating the webServiceURL"
Write-Host "Convert password to Secure string"
$SecurePassword = ConvertTo-SecureString $serviceAccountPassword -AsPlainText -Force
Write-Host "Done converting password to Secure string"
$Credential = New-Object System.Management.Automation.PSCredential($serviceAccountEmail, $SecurePassword)
Write-Host "Logging in"
Login-AzAccount -Credential $Credential
$tid = (Get-AzTenant).Id
Write-Host "Getting token"
$tokenBody = #{
'tenant' = $tid
'client_id' = $clientId
'scope' = 'https://graph.microsoft.com/.default'
'client_secret' = $clientSecret
'grant_type' = 'client_credentials'
}
$Params = #{
'Uri' = "https://login.microsoftonline.com/$tid/oauth2/v2.0/token"
'Method' = 'Post'
'Body' = $tokenBody
'ContentType' = 'application/x-www-form-urlencoded'
}
$AuthResponse = Invoke-RestMethod #Params
$AuthResponse
$header = #{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $($AuthResponse.access_token)"
}
$header
$redirectUris = (Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/beta/applications/$objectId" -Headers $header).spa.redirectUris
if ($redirectUris -notcontains "$webServiceURL") {
$redirectUris += "$webServiceURL"
Write-Host "Adding $webServiceURL to redirect URIs";
}
$body = #{
'spa' = #{
'redirectUris' = $redirectUris
}
} | ConvertTo-Json
Invoke-RestMethod -Method Patch -Uri "https://graph.microsoft.com/beta/applications/$objectId" -Headers $header -Body $body
Here is the OUTPUT for Reference:-
You can fetch the current values with --method get, convert it to an ArrayList and then add your new value:
$appdata = az rest --method get --uri 'https://graph.microsoft.com/v1.0/applications/{id}' | ConvertFrom-Json
$uris = [System.Collections.ArrayList]$appdata.web.redirectUris
$uris.Add('abc')

Azure Databricks API

Trying to use the Databricks API to work with resources programmatically. I am using this microsoft documentto authenticate with a service principal.
https://learn.microsoft.com/en-us/azure/databricks/dev-tools/api/latest/aad/service-prin-aad-token
But I'm getting the following error
"Invoke-RestMethod : {"error":"invalid_resource","error_description":"AADSTS500011: The resource principal named
https://management.core.azure.com was not found in the tenant"
This is my full script. What am I missing?
$ApiCommand = "clusters/get"
$DataBrick = "https://adb-3522222096750220.0.azuredatabricks.net"
$DataBricksResourceID = ""
$VaultName = ""
$KeyName = ""
$apiEndpointUri = "https://management.core.azure.com"
$tenantId = ""
$applicationId = ""
$secret = Get-AzKeyVaultSecret -VaultName $VaultName -Name $KeyName -AsPlainText
$RequestAccessTokenUri = "https://login.microsoftonline.com/$tenantId/oauth2/token"
$body = "grant_type=client_credentials&client_id=$applicationId&client_secret=$secret&resource=2ff814a6-3304-4ab8-85cb-cd0e6f879c1d"
$Managementbody = "grant_type=client_credentials&client_id=$applicationId&client_secret=$secret&resource=$apiEndpointUri"
$contentType = 'application/x-www-form-urlencoded'
$AccessToken = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $body -ContentType $contentType
Write-Output $AccessToken
$ManagementToken = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $Managementbody -ContentType $contentType
Write-Output $ManagementToken
$apiuri = $DataBrick +"/api/2.0/$ApiCommand"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer " + $AccessToken.access_token)
$headers.Add("X-Databricks-Azure-SP-Management-Token", $ManagementToken.access_token)
$headers.Add("X-Databricks-Azure-Workspace-Resource-Id", $DataBricksResourceID)
Invoke-RestMethod -Uri $apiuri -Headers $headers
The trailing / character in the management endpoint URI is really important - you need to specify it as in the documentation: https://management.core.windows.net/
You can also add this SP into the workspace itself, then you will need to get only one AAD token (see the docs).

Storage REST API Returns Remote Name Could Not be Resolve Often

I am calling the storage REST API to get container names using
Invoke-WebRequest -Method GET -Uri $storage_url -Headers $headers
This command often returns 'remote name could not be resolved error', even when the storage account exists and is reachable. Just running the command again gives correct result.
Invoke-WebRequest : The remote name could not be resolved: '<storageAccountName>.blob.core.windows.net'
At line:1 char:1
+ Invoke-WebRequest -Method GET -Uri $storage_url -Headers $headers #In ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
From the information you provided, you use the client credential flow to get the access token, then use the token to call the Storage Rest API - List Containers.
You could use the script below, it works for me.
Make sure the service principal you used has a RBAC role e.g. Contributor/Owner in your storage account -> Access Control, if not, click the Add to add it.
$ClientID = "xxxxxxx"
$ClientSecret = "xxxxxxx"
$tennantid = "xxxxxxx"
$storageaccountname = "joystoragev2"
$TokenEndpoint = {https://login.microsoftonline.com/{0}/oauth2/token} -f $tennantid
$Resource = "https://storage.azure.com/"
$Body = #{
'resource'= $Resource
'client_id' = $ClientID
'grant_type' = 'client_credentials'
'client_secret' = $ClientSecret
}
$params = #{
ContentType = 'application/x-www-form-urlencoded'
Headers = #{'accept'='application/json'}
Body = $Body
Method = 'Post'
URI = $TokenEndpoint
}
$token = Invoke-RestMethod #params
$accesstoken = $token.access_token
$url = {https://{0}.blob.core.windows.net/?comp=list} -f $storageaccountname
$header = #{
'Authorization' = 'Bearer ' + $accesstoken
'x-ms-version' = '2019-02-02'
}
$response = Invoke-WebRequest –Uri $url –Headers $header –Method GET
$response.RawContent

How to do az acr import with Az powershell?

If Az powershell does not have it, then a working code sample using the REST Api would be helpful.
This is the path I am going to pursue, but if someone has a working sample - please share.
It's impossible to import images to ACR through the Azure PowerShell, but the REST API exists. Take a look at the Import Image REST API.
So I managed to trigger the import using REST API. Here is my code (using a Service Principal to login):
function Get-AcrId($SubId, $RGName, $AcrName)
{
"/subscriptions/$SubId/resourceGroups/$RGName/providers/Microsoft.ContainerRegistry/registries/$ACRName"
}
function Get-AzureAuthenticationToken(
[Parameter(Mandatory)][String]$TenantID,
[Parameter(Mandatory)][String]$ClientID,
[Parameter(Mandatory)][String]$ClientSecret,
[Parameter(Mandatory)][String]$ResourceAppIDUri)
{
$tokenResponse = Invoke-RestMethod -Method Post -UseBasicParsing `
-Uri "https://login.windows.net/$TenantID/oauth2/token" `
-Body #{
resource = $ResourceAppIDUri
client_id = $ClientID
grant_type = 'client_credentials'
client_secret = $ClientSecret
} -ContentType 'application/x-www-form-urlencoded'
Write-Verbose "Access token type is $($tokenResponse.token_type), expires $($tokenResponse.expires_on)"
$tokenResponse.access_token
}
function Import-DockerImage(
[Parameter(Mandatory)]$SourceSubId,
[Parameter(Mandatory)]$SourceRGName,
[Parameter(Mandatory)]$SourceACRName,
[Parameter(Mandatory)]$TargetSubId,
[Parameter(Mandatory)]$TargetRGName,
[Parameter(Mandatory)]$TargetACRName,
[Parameter(Mandatory)]$ImageName,
[Parameter(Mandatory)]$ImageTag
)
{
$AzContext = Get-AzContext
if (!$AzContext)
{
throw "No Az context is found."
}
$TenantId = $AzContext.Tenant.Id
$ClientId = $AzContext.Account.Id
$ClientSecret = $AzContext.Account.ExtendedProperties.ServicePrincipalSecret
$token = Get-AzureAuthenticationToken -TenantID $TenantId -ClientID $ClientId -ClientSecret $ClientSecret -ResourceAppIDUri "https://management.core.windows.net/"
$url = "https://management.azure.com$(Get-AcrId $TargetSubId $TargetRGName $TargetACRName)/importImage?api-version=2019-05-01"
$body = #{
source = #{
resourceId = Get-AcrId $SourceSubId $SourceRGName $SourceACRName
sourceImage = "${ImageName}:$ImageTag"
}
targetTags = #(
"${ImageName}:$ImageTag"
)
mode = "NoForce"
} | ConvertTo-Json -Depth 99
$headers = #{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$r = Invoke-WebRequest $url -Method Post -Headers $headers -Body $body
$headers.Remove('Content-Type')
while ($r.StatusCode -eq 202)
{
$RetryAfter = $r.Headers.'Retry-After'
$Location = $r.Headers.Location
Start-Sleep -Seconds $RetryAfter
$r = Invoke-WebRequest $Location -Headers $headers
}

How to script External collaboration settings in Azure

Would like to be able to control two settings in Azure Active Directory user settings:
External collaboration settings > “Members can invite”
External collaboration settings > “Guests can invite”
As we already know, you can control just about everything in Azure with powershell, except these two things.
The internet, azure docs, and other resources, known to me, have been utilized to no real results.
Code: this is what we are looking to start.
Would like to say "Get-AzAADExternalCollaborationsSettings" then use the results to say "Set-AzAADExternalCollaborationsSettings".
AzAADUserSettings Picture
AFAIK, there is no built-in powershell command for the external collaboration settings, the two settings call the azure undisclosed api https://main.iam.ad.ext.azure.com/api/xxxx, the workaround I can just find is to invoke the api with powershell, here is a sample for you to refer, it calls a different api, but the logic should be similar. You can catch the requests of the two settings via fiddler and follow the sample to have a try.
Sample:http://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-Object {$_.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()}
$url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true"
Invoke-RestMethod –Uri $url –Headers $header –Method POST -ErrorAction Stop
}
Through monitoring the webbrowser network reuest i was able to get the correct API URL https://main.iam.ad.ext.azure.com/api/Directories/B2BDirectoryProperties
This should be a Powershell command. Hopfully Microsoft does't break this API.
Here are some snippets of what i am using:
# Get API Token
$mycreds = Get-Credential
if($tenantId){
$res = login-azurermaccount -Credential $mycreds -TenantId $tenantId.ToLower()
}else{
$res = login-azurermaccount -Credential $mycreds
}
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$refreshToken = $context.TokenCache.ReadItems().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'
# Get current settings
$header = #{
'X-Requested-With'= 'XMLHttpRequest'
"Origin"="https://portal.azure.com"
'Authorization' = 'Bearer ' + $apiToken.access_token
'x-ms-client-request-id'= [guid]::NewGuid()
}
$url = "https://main.iam.ad.ext.azure.com/api/Directories/B2BDirectoryProperties"
Invoke-WebRequest -Uri $url -Headers $header -ContentType "application/json" -ErrorAction Stop
# Change settings
$settings = #{
allowInvitations = $true
limitedAccessCanAddExternalUsers = $false
restrictDirectoryAccess = $true
usersCanAddExternalUsers = $false
}
$body = $settings | ConvertTo-Json
$header = #{
'X-Requested-With'= 'XMLHttpRequest'
"Origin"="https://portal.azure.com"
'Authorization' = 'Bearer ' + $apiToken.access_token
'x-ms-client-request-id'= [guid]::NewGuid()
}
$url = "https://main.iam.ad.ext.azure.com/api/Directories/B2BDirectoryProperties"
Invoke-WebRequest -Uri $url -Method "PUT" -Headers $header -ContentType "application/json" -Body $body
Thnx for pointing me in the right direction Joy.

Resources