From c# code, I want to call Azure Devops Pipeline Url and get the job/build ids for that url. And then get the result if the job/build succeds/fail.
How to make REST call from c# code my Azure pipelines (I have the pipeline url)?
You can use this sample through HttpClient. Additionally, you can use Microsoft.TeamFoundationServer.Client and BuildHttpClient.GetBuildsAsync: the example is here.
There are 2 ways to do authentication for Azure DevOps API.
Private PAT
Environment Variable: System.AccessToken
For PAT you can review Shamrai Aleksander's code, if you want to call Azure DevOps API in your pipeline, you can conside use System.AccessToken.
Here is Powershell Script for your reference, the field of Authorization is different.
function Http-Request {
param (
[Parameter(Mandatory=$true)]
[string] $Url,
[Parameter(Mandatory=$false)]
[string] $SystemToken
)
if([string]::IsNullOrEmpty($SystemToken)) {
$pat = 'YOUR_PAT'
$username = 'YOUR_USERNAME'
$auth = '{0}:{1}' -f $username, $pat
$bytes = [System.Text.Encoding]::UTF8.GetBytes($auth)
$encoded = [Convert]::ToBase64String($bytes)
$auth = 'Basic ' + $encoded
} else {
$auth = 'Bearer ' + $SystemToken
}
$headers = #{
'Content-Type' = 'application/json'
'Authorization' = $auth
}
Write-Host -ForegroundColor Green 'Http Request:'$Url
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
$response = Invoke-RestMethod -Uri $Url -Method Get -Headers $headers
Write-Host ($reponse | Format-List | Out-String)
return $response
}
Related
I am trying to export all the url endpoints of my APIs which are stored in Azure APIM.
I need them listed in the format [POST]: https://apim-resource-name.azure-api.net/api-name/api-function, or something similar, so I am trying to enumerate all the endpoints first and the VERB that can be used much like what SwaggerUI or one of the OpenAPI docs might show.
I have this code so far but cannot get the output I would like, am I on the right track or is there any easier way to do this even via UI? I have also tried exporting the template and parsing the script, but it seemed overly complicated for this task.
#az login
$token = az account get-access-token | ConvertFrom-Json
$token = $token.accessToken -replace "`n","" -replace "`r",""
$headers = #{Authorization="Bearer $token.accessToken"}
$subscriptionId = 'subscriptionid'
$resourceGroupName = 'resourcegroupname'
$resourceName = 'resourcename'
$apiName = 'apiname'
$url_getapiexport = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.ApiManagement/service/$resourceName/apis/$apiName`?format=swagger-link&export=true&api-version=2021-08-01" #GET
$url_getapiexport
#Invoke-RestMethod -Uri $url_getapiexport -Method GET -Headers #{Authorization="Bearer $token"} -ContentType "application/json"
$url_getapibytags = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.ApiManagement/service/$resourceName/apisByTags?&includeNotTaggedApis=true&api-version=2021-08-01" #GET
$url_getapibytags
$api_tags = Invoke-RestMethod -Uri $url_getapibytags -Method GET -Headers #{Authorization="Bearer $token"} -ContentType "application/json"
$api_tags
foreach ($api in $api_tags) {
write-host API: $api.value.api.name
}
So if you want to export all APIM APIs and their service URLs you can do like this (replace xxx with correct values). Example in PHP
<?php
echo "[INFO] API Names and Service Urls \n\n";
$apiList = shell_exec('az apim api list --resource-group "xxx" --service-name "xxx" ');
$apis = json_decode($apiList, true);
$cli = "az rest --method get --url";
foreach ($apis as $api)
{
$name = $api["name"];
$apiRequest = "$cli " . "\"https://management.azure.com/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ApiManagement/service/xxx/apis/$name?api-version=2021-08-01\"";
$json = shell_exec($apiRequest);
$apiInfo = json_decode($json, true);
$displayName = $apiInfo["properties"]["displayName"];
$serviceUrl = $apiInfo["properties"]["serviceUrl"];
printf("%s: %s \n", $displayName, $serviceUrl);
}
?>
I've been trying to get access to a storage blob (and table in future) with a managed identity in Azure Automation, but unfortunately I can't get it to work.
The Managed Identity has the following permissions on the Blob:
Contributor
Managed Application Operator Role
Storage Blob Data Contributor
Storage Table Data Contributor
I've tried several resources to get my accesstoken:
'https://storage.azure.com/'
'https://STORAGE.blob.core.windows.net/'
I do get the AccessToken for both resources, yet I still get the famous 403 forbidden errors.
This is the Uri I use to access the blob:
'https://{0}.blob.core.windows.net/{1}{2}' -f $($Storage), $Container, $FileName
My invoke uses the Get method:
$InvokeSplatting = #{
Uri = $Uri
Method = 'Get'
headers = $Headers
}
The header contains the AccessToken: "Bearer ~"
Am I doing something wrong?
PS: the script did work with a SASToken, so something must be up with the AccessToken.
function New-ManagedIdentityAccessToken {
<#
.SYNOPSIS
Short description
.DESCRIPTION
Resources:
'https://vault.azure.net'
'https://management.azure.com'
'https://storage.azure.com/'
.PARAMETER Resource
Parameter description
.EXAMPLE
An example
.NOTES
General notes
#>
[CmdletBinding()]
param (
[parameter(mandatory = $true)]
$Resource
)
begin {
$url = $env:IDENTITY_ENDPOINT
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("X-IDENTITY-HEADER", $env:IDENTITY_HEADER)
$headers.Add("Metadata", "True")
$body = #{resource = $Resource }
}
process {
$accessToken = Invoke-RestMethod $url -Method 'POST' -Headers $headers -ContentType 'application/x-www-form-urlencoded' -Body $body
$Headers = #{
Authorization = "Bearer $($accessToken.access_token)"
}
}
end {
return $Headers
}
}
I've tried to authenticate to Azure Batch using REST API, to do so I wrote following PowerShell code
$Key = 'key'
$region = "region"
$sharedKey = [System.Convert]::FromBase64String($Key)
$date = [System.DateTime]::UtcNow.ToString("R")
$stringToSign = "GET`n`n`n`n`n`n`n`n`n`n`n`nocp-date:$date`n /$batchAccount/jobs`napi-version:2019-08-01.10.0`ntimeout:20"
[byte[]]$dataBytes = ([System.Text.Encoding]::UTF8).GetBytes($stringToSign)
$hmacsha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha256.Key = [Convert]::FromBase64String($key)
$sig = [Convert]::ToBase64String($hmacsha256.ComputeHash($dataBytes))
$authhdr = "SharedKey $BatchAccount`:$sig"
$headers = #{
"ocp-date" = $date;
"Authorization" = "$authhdr";
}
Invoke-restmethod -Headers $headers -Uri 'https://$BatchAccount.$region.batch.azure.com/jobs?api-version=2019-08-01.10.0'
please note that I know that I can
use OAuth2 as alternative authentication mechanism
use Az.Batch powershell modules
I just wanted to do this using REST and SharedKey scheme as described here
https://learn.microsoft.com/en-us/rest/api/batchservice/authenticate-requests-to-the-azure-batch-service
for this API
https://learn.microsoft.com/en-us/rest/api/batchservice/job/list
But for some reason it doesn't work
I get this error but everything seems to be folowing the docs
"message":{
"lang":"en-US",
"value":"Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:eb5134f2-2821-4244-ac5b-066bf19bec10\nTime:2019-11-24T21:08:20.3223384Z"
},
"values":[
{
"key":"AuthenticationErrorDetail",
"value":"The MAC signature found in the HTTP request 'signature-goes-here' is not the same as any computed signature. Server used following string to sign: 'GET\n\n\n\n\napplication/json; odata=minimalmetadata; charset=utf-8\n\n\n\n\n\n\nocp-date:Sun, 24 Nov 2019 21:08:20 GMT\n/name-goes-here/jobs\napi-version:2019-08-01.10.0'."
}
]
There is something wrong with $stringToSign . Try this :
$Key = "your key"
$region = "your region"
$BatchAccount = "your account name"
$BatchAccountURL = "Https://$BatchAccount.$region.batch.azure.com"
$sharedKey = [System.Convert]::FromBase64String($Key)
$date = [System.DateTime]::UtcNow.ToString("R")
$stringToSign = "GET`n`n`n`n`n`n`n`n`n`n`n`nocp-date:$date`n/$BatchAccount/jobs`napi-version:2019-08-01.10.0"
[byte[]]$dataBytes = ([System.Text.Encoding]::UTF8).GetBytes($stringToSign)
$hmacsha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha256.Key = [Convert]::FromBase64String($key)
$sig = [Convert]::ToBase64String($hmacsha256.ComputeHash($dataBytes))
$authhdr = "SharedKey $BatchAccount`:$sig"
$headers = #{
"ocp-date" = $date;
"Authorization" = "$authhdr";
}
Invoke-restmethod -Headers $headers -Uri "$BatchAccountURL/jobs?api-version=2019-08-01.10.0"
Result :
I'm trying to deploy stored procedures to a collection within an Azure Cosmos DB account as part of my deployment pipeline in Azure DevOps. Due to security reasons, I have to use the REST API (cannot use or import PowerShell modules to do this).
The build agents that I'm using are on-premise agents. Again, security reasons.
In order to generate an authorization token to make requests, I have the following PowerShell function:
Add-Type -AssemblyName System.Web
# Generate Authorization Key for REST calls
Function Generate-MasterKeyAuthorizationSignature
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$verb,
[Parameter(Mandatory=$true)][String]$resourceLink,
[Parameter(Mandatory=$true)][String]$resourceType,
[Parameter(Mandatory=$true)][String]$dateTime,
[Parameter(Mandatory=$true)][String]$key,
[Parameter(Mandatory=$true)][String]$keyType,
[Parameter(Mandatory=$true)][String]$tokenVersion
)
$hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacSha256.Key = [System.Convert]::FromBase64String($key)
$payLoad = "$($verb.ToLowerInvariant())`n$($resourceType.ToLowerInvariant())`n$resourceLink`n$($dateTime.ToLowerInvariant())`n`n"
$hashPayLoad = $hmacSha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($payLoad))
$signature = [System.Convert]::ToBase64String($hashPayLoad);
[System.Web.HttpUtility]::UrlEncode("type=$keyType&ver=$tokenVersion&sig=$signature")
}
I then call this function within the POST function that I'm using to POST the stored procedure to Azure Cosmos DB:
Function Post-StoredProcToCosmosDb
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$EndPoint,
[Parameter(Mandatory=$true)][String]$DataBaseId,
[Parameter(Mandatory=$true)][String]$CollectionId,
[Parameter(Mandatory=$true)][String]$MasterKey,
[Parameter(Mandatory=$true)][String]$JSON
)
$Verb = 'Post'
$ResourceType = "sprocs"
$ResourceLink = "dbs/$DataBaseId/colls/$CollectionId"
$dateTime = [DateTime]::UtcNow.ToString("r")
$authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb -resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime
$header = #{authorization=$authHeader;"x-ms-version"="2017-02-22";"x-ms-date"=$dateTime;"xs-ms-session-token"="28"}
$contentType = "application/json"
$queryUri = "$EndPoint$ResourceLink/sprocs"
$result = Invoke-RestMethod -Headers $header -Method $Verb -ContentType $contentType -Uri $queryUri -Body $JSON
return $result.statuscode
}
I see from the documentation that I need to pass my stored procedure in the body as a string, so I set the path of my stored procedure to a variable like so:
$HelloWorld = Get-Content -Path '.\Databases\MyCosmosDB\MyCosmosCollection\Stored Procedures\HelloWorld.js' | Out-String
I then call my POST function like so:
Post-StoredProcToCosmosDb -EndPoint $env:COSMOSENDPOINT -DataBaseId $env:MYCOSMOSDB -CollectionId $MyCollection -MasterKey $env:MASTERKEY -JSON $HelloWorld
However, when I run the task, I get the following error:
Invoke-RestMethod : The remote server returned an error: (400) Bad Request
At D:_workAzure\r12\a_Cosmos\Deployment\scripts\postStoredProcedures.ps1:61 char:15
$result = Invoke-RestMethod -Headers $header -Method $Verb -Content ...
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I have followed the example request body as outlined in the documentation for my stored procedure.
Here is what it looks like for your info:
"function () {\r\n var context = getContext();\r\n var response = context.getResponse();\r\n\r\n response.setBody(\"Hello, World\");\r\n}"
I'm fairly new to PowerShell, so I'm wondering where I am going wrong. I've tried setting the contentType to both application/json and application/query+json but otherwise, I'm not sure where I am going wrong?
If anyone can provide any guidance on this, I'd be most grateful.
So turns out the request body was wrong. It should be like this:
{
"body":"function HelloWorld() {\r\n var context = getContext();\r\n var response = context.getResponse();\r\nresponse.setBody(\"Hello, World\");\r\n}",
"id": "HelloWorld"
}
That's the acceptable request body for Stored Procedures. So what I should have done is set my $HelloWorld variable to:
$HelloWorld = Get-Content -Path '.\Databases\MyCosmosDB\MyCosmosCollection\Stored Procedures\HelloWorld.json' | Out-String
Hope my stupidity helps someone someday :/
I would look to automate process of creating build, releases in azure devops. I do understand that rest api's exist. But will they help to automate the process and can this be done in node.js?
yes. Azure Devops do have API. I do not have experiences in node.js.
Docu for Azure DevOps REST API here:
https://learn.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-5.0
You may find node.js libraries here:
https://github.com/microsoft/azure-devops-node-api
Can you give a few more details about your desired automation process?
If you mean also the creation of definitions, then I would also have a look into YAML. In the future, there will be few more update especially for the Release Definitions:
https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema
I implemented a Powershell (but not with Node.js) wrapper for the Azure DevOps Api . Here are some code snippets to create a new release:
SET UP CONNECTION
<#
.SYNOPSIS
Sets the environment parameter for the current session so that the commandlets can access Azure DevOps.
#>
function Set-AzureDevOpsEnvironment {
Param(
<# The account name of the Azure DevOps tenant to access. If not set, this is set to "vanstelematics" #>
[Parameter(Mandatory=$true)]
$AzureDevOpsAccountName,
<# The project name of Azure DevOps to work with. If not set, this is set to "ScaledPilotPlatform" #>
[Parameter(Mandatory=$true)]
$AzureDevOpsProjectName,
<# The PAT to access the Azure DevOps REST API. If not set, the function tries to read the PAT from a
textfile called "AzureDevOpsPat.user" either in the current working directory or in the profile
directory of the current user. The textfile must contain only that PAT as plain string. #>
[Parameter(Mandatory=$false)]
$AzureDevOpsPat
)
if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) {
$Script:Verbose = $true
} else {
$Script:Verbose = $false
}
if (!$AzureDevOpsPat) {
$paths = #("AzureDevOpsPat.user", (JOin-Path $env:USERPROFILE "AzureDevOpsPat.user"))
foreach ($path in $paths) {
if (Test-Path $path -ErrorAction SilentlyContinue) {
$AzureDevOpsPat = Get-Content $path
break
}
}
if (!$AzureDevOpsPat) {
Write-Host "AzureDevOpsPat is empty and file AzureDevOpsPat.user not found." -ForegroundColor Red
return
}
}
Write-Host "The Azure DevOps project '$($AzureDevOpsProjectName)' inside the Azure DevOps account '$($AzureDevOpsAccountName)' will be used."
$Script:AzureDevOpsAccountName = $AzureDevOpsAccountName
$Script:AzureDevOpsProjectName = $AzureDevOpsProjectName
$Script:AzureDevOpsPat = $AzureDevOpsPat
}
REST CALLER
function Call-AzureDevOpsApi($Url, $JsonBody, [ValidateSet("GET", "DELETE", "POST", "PUT", "PATCH")]$Method, $ContentType) {
if ($Script:Verbose) {
Write-Host "Calling $($Method) $($Url)"
}
if (!$ContentType) {
$ContentType = "application/json"
}
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(("{0}:{1}" -f "", $Script:AzureDevOpsPat)))
$parameters = #{
Headers = #{Authorization = ("Basic {0}" -f $base64AuthInfo)};
Method = $Method;
Uri = $Url;
}
if ($Method -in #("POST", "PUT", "PATCH")) {
if (!$JsonBody) {
Write-Error "A JsonBody is required for method $($Method)."
return
}
$JsonBodyUtf8 = [Text.Encoding]::UTF8.GetBytes($JsonBody)
$parameters["Body"] = $JsonBodyUtf8
$parameters["ContentType"] = $ContentType
}
$result = Invoke-RestMethod #parameters
return $result
}
function Call-AzureDevOpsApiPost($Url, $JsonBody, [Parameter(Mandatory=$False)][ValidateSet("application/json", "application/json-patch+json")]$ContentType) {
return Call-AzureDevOpsApi -Url $Url -JsonBody $JsonBody -ContentType $ContentType -Method POST
}
function Call-AzureDevOpsApiPut($Url, $JsonBody, [Parameter(Mandatory=$False)][ValidateSet("application/json", "application/json-patch+json")]$ContentType) {
return Call-AzureDevOpsApi -Url $Url -JsonBody $JsonBody -Method PUT
}
function Call-AzureDevOpsApiPatch($Url, $JsonBody, [Parameter(Mandatory=$False)][ValidateSet("application/json", "application/json-patch+json")]$ContentType) {
return Call-AzureDevOpsApi -Url $Url -JsonBody $JsonBody -Method PATCH
}
function Call-AzureDevOpsApiGet($Url, [Parameter(Mandatory=$False)][ValidateSet("application/json", "application/json-patch+json")]$ContentType) {
return Call-AzureDevOpsApi -Url $Url -Method GET
}
function Call-AzureDevOpsApiDelete($Url, [ValidateSet("application/json", "application/json-patch+json")]$ContentType) {
return Call-AzureDevOpsApi -Url $Url -Method DELETE
}
NEW RELEASE
<#
.SYNOPSIS
Creates a new release for a given Release Definition and artifact (p.e. build)
#>
function New-Release {
Param(
<# The id of the release definition to create the release for. #>
[Parameter(Mandatory=$true)]
$ReleaseDefinition,
<# The alias of the artifact of the release definition to create the release for #>
[Parameter(Mandatory=$true)]
$ArtifactAlias,
<# The version of the artifact (p.e. the id of the build)#>
[Parameter(Mandatory=$true)]
$ArtifactVersion,
<# The description/name of the release #>
[Parameter(Mandatory=$true)]
$Description
)
$url = "https://vsrm.dev.azure.com/$($Script:AzureDevOpsAccountName)/$($Script:AzureDevOpsProjectName)/_apis/release/releases?api-version=4.1-preview.6"
$releaseData = #{
"definitionId" = $ReleaseDefinition.id;
"description" = $Description;
"artifacts" = #(
#{
"alias" = $ArtifactAlias;
"instanceReference" = $ArtifactVersion
}
);
"isDraft" = $false;
"reason" = "none";
"manualEnvironments" = $ReleaseDefinition.environments | select -ExpandProperty name
}
$result = Call-AzureDevOpsApiPost -Url $url -JsonBody ($releaseData | ConvertTo-Json -Depth 100)
return $result
}
Hope this gives an idea how to use it.