'Invoke-RestMethod : The remote server returned an error: (401) Unauthorized' only in Azure DevOps release pipeline - azure

I have a PowerShell script that works from a local PowerShell but when it runs in a PowerShell task in an Azure DevOps release pipeline it returns Invoke-RestMethod : The remote server returned an error: (401) Unauthorized.
The user is added as a project member within Azure DevOps and I have also created a Personal Access Token with full access.
function Get-HttpBasicHeader([string]$username, [string]$password)
{
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userName, $password)))
return #{Authorization=("Basic {0}" -f $base64AuthInfo)}
}
$headers = Get-HttpBasicHeader $userName $password
$url = "https://transpa.vsrm.visualstudio.com/transPA/_apis/release/releases?definitionId=42&definitionEnvironmentId=61&api-version=5.0"
$response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
What am I doing wrong? Let me know if I should provide any additional information.

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 {
....

WebJobs not stopping by using the Rest API Post method within powershell script - Getting Error 403 - This web app is stopped

When switching slots withing azure on my application, i am trying to stop the webjobs on the "stopped" server as they seem to keep running within azure. However when i use the invoke-restmethod within my powershell script, it comes back with Error 403 - This web app is stopped, but on my azure site it is still running. Frustratingly i can use the method when using postman, but when it comes to my script with the identical request it is not stopping the webjobs.
I have tried running this particular script within powershell, but it wont run due to the stated error. The Get method works fine in powershell, bringing back the application, but the stop rest call does not.
function StopWebJob($slotToStop) {
# Get an access token to authenticate the web jobs API
Write-Verbose -Verbose "Getting an access token..."
$stopToken = Get-BasicAuthToken $slotToStop
Write-Verbose -Verbose "Got access token."
#Generate a header to start/stop the web jobs using the access token
$stopHeader = #{ 'Authorization'= $stopToken }
Write-Verbose -Verbose "Checking job status..."
$getJob = "https://$WebAppName-$slotToStop.scm.azurewebsites.net/api/continuouswebjobs/{myWebJob}/"
$getJobResult = Invoke-RestMethod -Uri $getJob -Headers $stopHeader -Method Get
If($getJobResult.status -ne "Stopped") {
Write-Verbose -Verbose "Stopping job..."
$stop = "https://$WebAppName-$slotToStop.scm.azurewebsites.net/api/continuouswebjobs/{MyWebJob}/stop"
Invoke-RestMethod -Uri $stop -Headers $stopHeader -Method Post
Write-Verbose -Verbose "Job stopped."
} else {
Write-Verbose -Verbose "Job is already stopped."
}
}
When the slot switches, I expect the Job on the "down" slot to be stopped via the rest api.
I can reproduce your issue, looks you missed the -UserAgent in the Invoke-RestMethod.
It should be like below, add the -UserAgent "powershell/1.0" to your command.
Invoke-RestMethod -Uri $stop -Headers $stopHeader -Method Post -UserAgent "powershell/1.0"
Then it will work fine, you could refer to my complete sample.
$username = "`$website"
$password = "pwd"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
Write-Verbose -Verbose "Checking job status..."
$getJob = "https://<webappname>-slot1.scm.azurewebsites.net/api/continuouswebjobs/webjob1"
$getJobResult = Invoke-RestMethod -Uri $getJob -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method Get
If($getJobResult.status -ne "Stopped") {
Write-Verbose -Verbose "Stopping job..."
$stop = "https://<webappname>-slot1.scm.azurewebsites.net/api/continuouswebjobs/webjob1/stop"
Invoke-RestMethod -Uri $stop -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method Post -UserAgent "powershell/1.0"
Write-Verbose -Verbose "Job stopped."
} else {
Write-Verbose -Verbose "Job is already stopped."
}

Outlook rest-api from powershell returning oath request (401)

I'm trying to create an auto notification app from outlook,
which I tried to use outlook rest API to do that.
for now I'm just making a simple code, but the result always returning error code 401
I've tried to register my apps into app registration on my Azure tenants. but have no luck nor I understand what to do next...
$uri = "https://outlook.office365.com/api/v2.0/me/sendmail"
$userName = << my username >>
$password = << my password >>
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName,$password
$body = "{
""Subject"":""rest API test"",
""Importance"":""High"",
""Body"":{
""ContentType"":""HTML"",
""Content"":""test test 1 2 3""
},
""ToRecipients"":[{
""Address"":""<< my recipients >>""
}]
}"
Invoke-RestMethod -Uri $uri -Method Post -Credential $credb -ContentType "application/json" -Body $body
the result from api said :
{"error":{"code":"OAuthMissingForThisApiVersion","message":"Authentication for this API version requires OAuth."}}
which is why I tried to register my app to azure app registration to get my token.
do anyone knows how to solve this problem?
or even some tutorial to show me how to set an app registration until I can get the token to my powershell app.
regards,
https://outlook.office.com/api/v1.0 doesn't support Basic auth as OAuth is the recommended auth mechanism, but you can continue to use https://outlook.office365.com/api/v1.0 if you need to keep using Basic auth.
Try something like below, it should work
$uri = "https://outlook.office365.com/api/v1.0/me/sendmail"
$UserName = "mv.v#my.domain"
$Password = cat C:\MydomainCreds.txt | convertto-securestring
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username,$password
$body = "{
""Message"":{
""Subject"": ""This is a send test"",
""Importance"": ""High"",
""Body"": {
""ContentType"": ""HTML"",
""Content"": ""How about this for a surprise!""
},
""ToRecipients"": [
{
""EmailAddress"":{
""Address"": ""mytestmailbox#anywhere.com""
}
}
]
}}"
Invoke-RestMethod -Uri $uri -Method Post -Credential $cred -ContentType "application/json" -Body $Body
Hope it helps.

How to authenticate for https://management.azure.com api?

I want to retrieve data on my DNS zones through a API call:
$api = "?api-version=2018-05-01"
$pat = "Bearer $env:System_AccessToken"
Write-Host "### PAT ###"
Write-Host $pat
$DNSInformation = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Network/dnsZones/$zoneName/$recordType/$relativeRecordSetName$api"
Write-Host "###"
Write-Host $DNSInformation
Write-Host "###"
$x = Invoke-RestMethod -Uri $DNSInformation -Headers #{Authorization = $pat } -Method Get
When I run this script I get:
The remote server returned an error: (401) Unauthorized.
When I navigate to the URL I get:
error: {
code: "AuthenticationFailed",
message: "Authentication failed. The Authorization header is missing."
}
I think the issue is that I can't use the $env:System_AccessToken token to get on the management api. But I can't find information what kind of authentication is needed.
As the error mentions, the authorization header is incorrect.
$URI = "https://management.azure.com/providers/microsoft.resources/checkresourcename?api-version=2014-01-01"
Invoke-RestMethod -Uri $URI -Method GET -Headers $authHeader
You can use a couple of approaches to create your header:
As you mentioned - Azure Powershell to check resource names
By creating Bearer token : Powershell Script to delete unused resources in Azure

Unable to access admin URL of Azure Functions

I am using powershell and trying to access Azure functions Administration using api.
I am trying to get list of all functions created under $appName
Certainly i am changing $appName with actual Azure Function name before call
I also got valid $authToken before this call.
Below URL:
$Functions = Invoke-RestMethod -Method GET -Headers #{Authorization = ("Bearer {0}" -f $authToken)} -Uri "https://$appName.azurewebsites.net/admin/functions"
and the error in my powershell execution is :
Invoke-RestMethod :
The underlying connection was closed: An unexpected error occurred on a send.
At KeyFA.ps1:36 char:18
+ ... Functions = Invoke-RestMethod -Method GET -Headers #{Authorization = ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I tried make it POST instead of GET but same error.
I tried access this url in broswer and the error in my broswer is :
http 401 means unauthorized.
Then i also tried access this URL from postman with Bearer auth correctly set but get below errors:
Could not get any response
There was an error connecting to
https://appname_comes_here.azurewebsites.net/admin/functions/
What am i not doing correctly?
Not able to fix this error. Is the url discontinued by Azure function site now?
Due to you just post the partial PowerShell code and the error information seems to be a network issue, I don't know what real issue you got is and how to fix it.
So I just post my work PowerShell script at here, you can refer to my code to fix your issue.
$appName = "<your app name>"
$userName='<your app credential user name>'
$userPWD='<your app credential user password>'
$apiBaseUrl = "https://$($appName).scm.azurewebsites.net/api"
$appBaseUrl = "https://$($appName).azurewebsites.net"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userName,$userPWD)))
$jwt = Invoke-RestMethod -Uri "$apiBaseUrl/functions/admin/token" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method GET
$Functions = Invoke-RestMethod -Method GET -Headers #{Authorization = ("Bearer {0}" -f $jwt)} -Uri "$appBaseUrl/admin/functions"
Note: you can follow the figures below to get the $userName and $userPWD values.
Fig 1. On Azure portal, open the Platform features tab of your Function App and click the Deployment Center link
Fig 2. Select the FTP option in the first step of SOURCE CONTROL and click the Dashboard button to copy the values of Username and Password, but just use the part of Username with $ prefix as $userName in my script

Resources