Azure Automation Account: How to Pass Webhook data to a runbook? - powershell

I have an Azure Runbook in an Azure Automation account, which I would like to trigger with a webhook which contains some parameters.
The runbook looks like this
workflow do-something
{
param
(
[object]$WebhookData
)
inlinescript {
if ($WebhookData -ne $null) {
$WebhookName = $WebhookData.WebhookName
$WebhookBody = $WebhookData.RequestBody
$webhookBodyObject = $WebhookBody | ConvertFrom-JSON
$customerEmail = $webhookBodyObject.customerEmail
$customerName = $webhookBodyObject.customerName
$dataLocation = $webhookBodyObject.dataLocation
} else {
"The WebhookData is totally and completely null"
exit (0)
}
$webhookjson = $WebhookData | ConvertTo-JSON
"The webhookdata is $webhookjson"
"The webhook name is $WebhookName"
"The customer email is $customerEmail"
"The body s $WebhookBody"
}
}
I then saved it and published it, and then got a webhook for it. As per instructions, i wrote a little Powershell script to trigger the webhook:
#Not the real URI, but similar in structure
$uri = "https://s10events.azure-automation.net/webhooks?token=Qt%xyxyxyxyxyxyxyxyxyxyxyxy%ababababab%3d"
$headers = #{"From"="babu#bhatt.com";"Date"="05/28/2015 15:47:00"}
$params = #{"customerName"="Jay Godse"; "customerEmail"="jaygodse#exmple.com"; "dataLocation"="Canada"}
$body = ConvertTo-Json -InputObject $params
#$response = Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -Body $body
$webresp = Invoke-WebRequest -Method Post -Uri $uri -Headers $headers -Body $body -Verbose
When I invoked the request, i got a 202 response code which suggested that the request was successfully queued.
Then I went to the Jobs section in the Runbook, and and looked at the input and output of the job. The input looked like this:
{"WebhookName":"test1","RequestBody":"{\r\n \"customerEmail\": \"jaygodse#exmple.com\",\r\n \"customerName\": \"Jay Godse\",\r\n \"dataLocation\": \"Canada\"\r\n}","RequestHeader":{"Connection":"Keep-Alive","Date":"Thu, 28 May 2015 19:47:00 GMT","From":"babu#bhatt.com","Host":"s10events.azure-automation.net","User-Agent":"Mozilla/5.0","x-ms-request-id":"d8995f98-1344-4822-af69-ababababababa"}}
The output looked like this:
The WebhookData is totally and completely null
What do I have to do to pass data successfully from the webhook to the my Azure Automation runbook? I couldn't find any examples on the web which actually worked.

You should use $using: scope inside the inlinescript {} block like that:
workflow do-something
{
param
(
[object]$WebhookData
)
inlinescript {
if ($using:WebhookData -ne $null) {
$WebhookName = $using:WebhookData.WebhookName
$WebhookBody = $using:WebhookData.RequestBody
$webhookBodyObject = $WebhookBody | ConvertFrom-JSON
$customerEmail = $webhookBodyObject.customerEmail
$customerName = $webhookBodyObject.customerName
$dataLocation = $webhookBodyObject.dataLocation
} else {
"The WebhookData is totally and completely null"
exit (0)
}
$webhookjson = $using:WebhookData | ConvertTo-JSON
"The webhookdata is $webhookjson"
"The webhook name is $WebhookName"
"The customer email is $customerEmail"
"The body s $WebhookBody"
}
}
See the explanation here: https://technet.microsoft.com/en-us/library/jj574197(v=ws.11).aspx ("Variables in InlineScript" section).

Related

How to trigger Jenkins job by using Octopus deploy with powershell?

I have a Jenkins Job, and that needs to get triggered by Octopus deploy. For that I have used the step template - Jenkins -Queue Job from Octopus library Installed Community Step Templates.
In the existing powershell script of the template, I have updated my parameters to run the Jenkins job. And finally during execution, I am facing the below error:
Exception in jenkins job: The remote server returned an error: (403) Forbidden.
The remote script failed with exit code 1.
I tried the ways to authenticate Octopus with Jenkins, and still couldn't find a way. Can someone provide inputs with this ? Thanks in advance!
I encountered the same issue just now. I ended up just copying their template and making changes. The issue is related to not keeping the session cookie across requests. Note: I had to move their authentication code out of the function because the session variable data would not work for some reason.
$jenkinsServer = $OctopusParameters['jqj_JenkinsServer']
$jenkinsUserName = $OctopusParameters['jqj_JenkinsUserName']
$jenkinsUserPassword = $OctopusParameters['jqj_JenkinsUserPasword']
$jobURL = $jenkinsServer + $OctopusParameters['jqj_JobUrl']
$failBuild = [System.Convert]::ToBoolean($OctopusParameters['jqj_FailBuild'])
$jobTimeout = $OctopusParameters['jqj_JobTimeout']
$buildParam = $OctopusParameters['jqj_BuildParam']
$checkIntervals = $OctopusParameters['jqj_checkInterval']
$jobUrlWithParams = "$jobURL$buildParam"
Write-Host "job url: " $jobUrlWithParams
try {
$params = #{}
if ($jenkinsUserName -ne "") {
$securePwd = ConvertTo-SecureString $jenkinsUserPassword -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($jenkinsUserName, $securePwd)
$head = #{"Authorization" = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($jenkinsUserName + ":" + $jenkinsUserPassword ))}
$params = #{
Headers = $head;
Credential = $credential;
ContentType = "text/plain";
}
}
# If your Jenkins uses the "Prevent Cross Site Request Forgery exploits" security option (which it should),
# when you make a POST request, you have to send a CSRF protection token as an HTTP request header.
# https://wiki.jenkins.io/display/JENKINS/Remote+access+API
try {
$tokenUrl = $jenkinsServer + "crumbIssuer/api/json?tree=crumbRequestField,crumb"
$crumbResult = Invoke-WebRequest -Uri $tokenUrl -Method Get #params -UseBasicParsing -SessionVariable session | ConvertFrom-Json
Write-Host "CSRF protection is enabled, adding CSRF token to request headers"
$params.Headers += #{$crumbResult.crumbRequestField = $crumbResult.crumb}
} catch {
Write-Host $Error[0]
}
Write-Host "Start the build"
$returned = Invoke-WebRequest -Uri $jobUrlWithParams -WebSession $session -Method Post -UseBasicParsing #params
Write-Host "Job URL Link: $($returned.BaseResponse.Headers['Location'])"
$jobResult = "$($returned.BaseResponse.Headers['Location'])/api/json"
$response = Invoke-RestMethod -Uri $jobResult -Method Get #params -WebSession $session
$buildUrl = $Response.executable.url
while ($buildUrl -eq $null -or $buildUrl -eq "") {
$response = Invoke-RestMethod -Uri $jobResult -Method Get #params -WebSession $session
$buildUrl = $Response.executable.url
}
Write-Host "Build Number is: $($Response.executable.number)"
Write-Host "Job URL Is: $($buildUrl)"
$buildResult = "$buildUrl/api/json?tree=result,number,building"
$isBuilding = "True"
$i = 0
Write-Host "Estimate Job Duration: " $jobTimeout
while ($isBuilding -eq "True" -and $i -lt $jobTimeout) {
$i += 5
Write-Host "waiting $checkIntervals secs for build to complete"
Start-Sleep -s $checkIntervals
$retyJobStatus = Invoke-RestMethod -Uri $buildResult -Method Get #params -WebSession $session
$isBuilding = $retyJobStatus[0].building
$result = $retyJobStatus[0].result
$buildNumber = $retyJobStatus[0].number
Write-Host "Retry Job Status: " $result " BuildNumber: " $buildNumber " IsBuilding: " $isBuilding
}
if ($failBuild) {
if ($result -ne "SUCCESS") {
Write-Host "BUILD FAILURE: build is unsuccessful or status could not be obtained."
exit 1
}
}
}
catch {
Write-Host "Exception in jenkins job: $($_.Exception.Message)"
exit 1
}

Azure DevOps API: powershell get list of test points

Is there a way to get list of test points data via Azure DevOps API?
list
I tried this powershell script
function GetUrl() {
param(
[string]$orgUrl,
[hashtable]$header,
[string]$AreaId
)
# Area ids
# https://learn.microsoft.com/en-us/azure/devops/extend/develop/work-with-urls?view=azure-devops&tabs=http&viewFallbackFrom=vsts#resource-area-ids-reference
# Build the URL for calling the org-level Resource Areas REST API for the RM APIs
$orgResourceAreasUrl = [string]::Format("{0}/_apis/resourceAreas/{1}?api-preview=5.0-preview.1", $orgUrl, $AreaId)
# Do a GET on this URL (this returns an object with a "locationUrl" field)
$results = Invoke-RestMethod -Uri $orgResourceAreasUrl -Headers $header
# The "locationUrl" field reflects the correct base URL for RM REST API calls
if ("null" -eq $results) {
$areaUrl = $orgUrl
}
else {
$areaUrl = $results.locationUrl
}
return $areaUrl
}
$orgUrl = "https://dev.azure.com/fodservices"
$personalToken = "<my token pat>"
Write-Host "Initialize authentication context" -ForegroundColor Yellow
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalToken)"))
$header = #{authorization = "Basic $token"}
Write-Host "Demo 3"
$coreAreaId = "3b95fb80-fdda-4218-b60e-1052d070ae6b"
$tfsBaseUrl = GetUrl -orgUrl $orgUrl -header $header -AreaId $coreAreaId
$relDefUrl = "$($tfsBaseUrl)/_apis/testplan/Plans/70152/Suites/70154/TestPoint?api-version=5.0-preview.2"
try {
$output = Invoke-RestMethod -Uri $relDefUrl -Method Get -ContentType "application/json" -Headers $header
}
catch{
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
}
$output.value | ForEach-Object {
Write-Host $_.id
}
the result is:
Demo 3
StatusCode: 404
StatusDescription: Not Found
Can anyone tell me what i'm doing wrong im new to using powershell and azure devops rest api
Look at the contents of the $tfsBaseUrl variable. It includes the organization name, but not the project name. You need to include the project name in the URL. Look at the documentation and compare your URL to the documentation's URL.

Azure Automation Runbook unable to parse the webhookdata as a JSON object

I am badly struck by this problem. I request you to answer or give a hint. I am running out of options.
I am calling an azure runbook upon high CPU utilization via a WebHook. My problem is inside runbook data is not getting decoded properly. For example, the below line is not printing anything.
Write-Output $WebHookData.RequestHeader
Wheras IF i try to explictly convert the data to JSON, like this
*$WebhookData = ConvertFrom-Json $WebhookData*
then it is a throwing error.
ConvertFrom-Json : Invalid JSON primitive: . At line:6 char:31 +
$WebhookData = $WebhookData | ConvertFrom-Json
By the way, I am trying to use the runbook available on Azure gallery {Vertically scale up an Azure Resource Manager VM with Azure Automation}
My Webhook is called from alert created on VM.
A very strange observation:
Working WebHood Example (found in an example) {"WebhookName":"test1","RequestBody":" [\r\n {\r\n \"Message\": \"Test Message\"\r\n }\r\n****]****"
Not Working(the data sent upon calling runbook from VM):
{"WebhookName":"test2","RequestBody":" {\"schemaId\":\"AzureMonitorMetricAlert\"}}
Thanks
I was getting the same error. From my testing, it appears that when performing a "Test" of the runbook, the Webhook data is received as plain text, but when invoked remotely it comes through already formatted as JSON. Here was my solution to cover both scenarios and so far has been working well...
Param (
[object] $WebhookData
)
# Structure Webhook Input Data
If ($WebhookData.WebhookName) {
$WebhookName = $WebhookData.WebhookName
$WebhookHeaders = $WebhookData.RequestHeader
$WebhookBody = $WebhookData.RequestBody
} ElseIf ($WebhookData) {
$WebhookJSON = ConvertFrom-Json -InputObject $WebhookData
$WebhookName = $WebhookJSON.WebhookName
$WebhookHeaders = $WebhookJSON.RequestHeader
$WebhookBody = $WebhookJSON.RequestBody
} Else {
Write-Error -Message 'Runbook was not started from Webhook' -ErrorAction stop
}
I tried with a webhook, the script Write-Output $WebHookData.RequestHeader should work fine.
And if I use ConvertFrom-Json $WebhookData, I can reproduce your issue, not sure why it occurred, according to the doc, the $WebhookData is also in a JSON format, if it is accepted, you could use ConvertFrom-Json -InputObject $WebhookData.RequestBody, it will work fine.
My runbook:
param
(
[Parameter (Mandatory = $false)]
[object] $WebhookData
)
if ($WebhookData) {
Write-Output $WebhookData.RequestHeader
$Body = ConvertFrom-Json -InputObject $WebhookData.RequestBody
Write-Output $Body
} else
{
Write-Output "Missing information";
exit;
}
The powershell script I used to send a webhook:
$uri = "https://s5events.azure-automation.net/webhooks?token=xxxxxxxxxxxx"
$vms = #(
#{ Name="vm01";ResourceGroup="vm01"},
#{ Name="vm02";ResourceGroup="vm02"}
)
$body = ConvertTo-Json -InputObject $vms
$header = #{ message="StartedbyContoso"}
$response = Invoke-WebRequest -Method Post -Uri $uri -Body $body -Headers $header
$jobid = (ConvertFrom-Json ($response.Content)).jobids[0]
Output:
I had the same problem use following to get webhookdata if using test pane with Alert json as input
if(-Not $WebhookData.RequestBody){
$WebhookData = (ConvertFrom-Json -InputObject $WebhookData)
}
$RequestBody = ConvertFrom-JSON -InputObject $WebhookData.RequestBody

Post Method - Changing a Nested Value

I am trying to change a value - specifically a variable - of one of my TFS 2017 builds. To my understanding, Patch is not supported at all. I can successfully queue a build with the Post method and I am trying to use the same command change a value as well.
When I run the Get method, I have:
*A bunch of text*
"variables": {
"system.debug": {
"value": "false",
"allowOverride": true
},
"BuildVersion": {
"value": "ValueIWantToChange"
}
},
*A bunch of text*
I need to change the Build Version and everything else will stay the same. My body in Postman looks like:
{
"Variables":
{
"BuildVersion":
{
"value": NewValue
}
}
}
When I run this in Postman, I get this error:
"Value cannot be null.\r\nParameter name: definition.Repository"
Could anyone tell me where I am going wrong or if this is possible using another method?
Seems you want to update the build definition base on you description.
To update the build definition with the REST API you need to use PUT method, please see Definitions - Update Definition for details.
Get the build definition first:
GET http://server:8080/tfs/DefaultCollection/ScrumProject/_apis/build/definitions/6?api-version=3.2
Copy all the json response from the first step as the request body,
then change the value of the specific variable which you want to be
modified.
PUT http://SERVER:8080/tfs/DefaultCollection/ScrumProject/_apis/build/definitions/6?api-version=3.2
Content-Type: application/json
Note that you need to provide the latest revision in request body:
UPDATE:
You can also use PowerShell by calling the REST API to update the specific variable value, just try below sample: (the variable name is lctest in below sample, you just need to replace it with your own variable name.)
Param(
[string]$collectionurl = "http://server:8080/tfs/DefaultCollection",
[string]$project = "ProjectName",
[string]$definitionid = "6",
[string]$user = "username",
[string]$token = "password"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
#Get build definition
$defurl = "$collectionurl/$project/_apis/build/definitions/$($definitionid)?api-version=3.2"
$definition = Invoke-RestMethod -Uri $defurl -Method Get -UseDefaultCredential -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
#Set new value for the specific variable
$definition.variables.lctest.value = "1.0.0.4"
$json = #($definition) | ConvertTo-Json -Depth 99
#Update the definition
$updatedef = Invoke-RestMethod -Uri $defurl -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
write-host $definition.variables.lctest.value
I figured out my problem awhile back and forgot to update. My initial task was to get the API for octopus so this is the long version. If youre only interested in the REST commands, refer to the last section of code. Just wanted to add the rest in for extra context.
#Create a folder
if(test-Path C:\Test){}
else{
new-item -path "C:\" -name "Test" -ItemType "directory"}
$encodedPAT = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$TFSAPIKeyForAutomatedBuild"))
$GetURI = "$MyURI"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic $encodedPAT")
[string]$Global:ChangeVersion = [version]$OctopusParameters["Octopus.Action[Deploy Package].Package.NuGetPackageVersion"]
write-host $ChangeVersion
$GetBuildresponse = Invoke-RestMethod -Method Get -header $headers -ContentType "application/json" -Uri $GetUri
write-host $GetBuildResponse
$y = convertTo-json $GetBuildresponse -depth 99 | Out-file -FilePath "C:\test\FromPostmanCopy.json"
$z = (get-content "C:\test\FromPostmanCopy.json") | select-string -pattern '(?<=value": "2.)(.*)(?=")' | % { $_.Matches} | % { $_.value }
Write-Host $z
$Content = (Get-Content "C:\Test\FromPostmanCopy.json")
$content -replace "2.$z", $changeVersion | out-file "C:\Test\FromPostmanCopy.json"
$Content = (Get-Content "C:\Test\FromPostmanCopy.json")
$Buildresponse = Invoke-RestMethod -URI $GetURI -Method Put -header $headers -Body $content -ContentType application/json

How to assign a group as Users on an endpoint in VSTS via the API?

I'm trying to tidy up VSTS and ensure that the AzureRM endpoints are consistent across our 40+ projects. I've written a Powershell script to call the rest API and ensure that the same endpoints are available for all projects. This works fine.
One thing I want to do though is grant the Contributors group for each project User rights on the non-prod endpoints. This doesn't seem to work and the official documentation (create or update) doesn't provide any real guidance on it.
I can get the group and pass this as being the "readersGroup" in the JSON Body of the call and this is then echoed in the response, implying it worked, but this doesn't appear to change anything on the endpoint itself.
Has anyone done this before who can give me some guidance as to where I'm going wrong?
[CmdletBinding()]
Param(
[ValidateSet("Production","NonProduction","RandD")][string]$Environment,
[string]$SubscriptionName,
[string]$SubscriptionDisplayName = $SubscriptionName,
[string]$SubscriptionId,
[string]$TenantId,
[string]$ClientId,
[string]$ClientKey,
[string]$Token #Required Scopes: Graph (read), Project and team (read), Service Endpoints (read, query and manage)
)
#Set up Endpoint data
$EndpointDisplayName = "$Environment ($SubscriptionDisplayName)"
$EndpointConfiguration = #"
{
"data": {
"SubscriptionId": "$SubscriptionId",
"SubscriptionName": "$SubscriptionName",
"creationMode" : "Manual"
},
"name": "$EndpointDisplayName",
"type": "azurerm",
"url" : "https://management.azure.com/",
"authorization": {
"parameters": {
"serviceprincipalid" : "$ClientId",
"serviceprincipalkey" : "$ClientKey",
"tenantid" : "$TenantId"
},
"scheme": "ServicePrincipal"
}
}
"#
#Set up API data
$Authentication = [Text.Encoding]::ASCII.GetBytes(":$Token")
$Authentication = [System.Convert]::ToBase64String($Authentication)
$Headers = #{
'Authorization' = "Basic $Authentication"
'Content-Type' = "application/json"
}
$BaseURI = "https://contoso.visualstudio.com"
$APIVersion = "?api-version=4.1-preview.1"
#get all vsts projects
$ListProjectsURI = "$BaseURI/DefaultCollection/_apis/projects$APIVersion"
$ProjectList = (Invoke-RestMethod -Method GET -Uri $ListProjectsURI -Headers $Headers).value
#Get VSTS Contributor groups for "user" role assignment
$ListGroupsURI = "https://Contoso.vssps.visualstudio.com/_apis/graph/groups$APIVersion"
$GroupsList = (Invoke-RestMethod -Method GET -Uri $ListGroupsURI -Headers $Headers).value
$AllContributorsGroups = $GroupsList | Where-Object -Property principalName -like "*\Contributors"
foreach($Project in $ProjectList)
{
$ProjectName = $Project.name
$ProjectId = $Project.id
#get all AzureRM SP endpoints
$ListEndpointsURI = "$BaseURI/$ProjectId/_apis/serviceendpoint/endpoints$APIVersion&type=azurerm&authschemes=ServicePrincipal"
$EndpointList = (Invoke-RestMethod -Method GET -Uri $ListEndpointsURI -Headers $Headers).value
$Exists = $false
#set up the endpoint settings for this project
if($Environment -eq "Production")
{
$EndpointJSON = $EndpointConfiguration
}
else #grant devs access to use non-prod/R&D endpoints
{
Write-Host "Setting [$ProjectName]\Contributors as Users on $EndpointDisplayName in $ProjectName"
$ReadersGroup = ($AllContributorsGroups | Where-Object -Property principalName -eq "[$ProjectName]\Contributors") | ConvertTo-Json
$ReadersConfiguration = #"
,"readersGroup" : $ReadersGroup
}
"#
$EndpointJSON = $EndpointConfiguration.TrimEnd('}') + $ReadersConfiguration #Append the readers role for this project to the base configuration
}
#Look for existing matching endpoints
foreach($Endpoint in $EndpointList)
{
$EndpointName = $Endpoint.name
$EndpointId = $Endpoint.id
#check if it uses the subscription Id we're updating,
if($Endpoint.data.subscriptionId -eq $SubscriptionId)
{
#if so, update it
Write-Host "Updating endpoint `"$EndpointName`" in Project `"$ProjectName`" (Endpoint ID: $EndpointId)"
$UpdateEndpointURI = "$BaseURI/$ProjectId/_apis/serviceendpoint/endpoints/$EndpointId$APIVersion"
Invoke-RestMethod -Method PUT -Uri $UpdateEndpointURI -Headers $Headers -Body $EndpointJSON
$Exists = $true
}
}
#if no existing endpoints match, create one
if(!$Exists)
{
Write-Output "No endpoint found for $SubscriptionName in `"$ProjectName`". Creating endpoint `"$EndpointDisplayName`"."
$CreateEndpointURI = "$BaseURI/$ProjectId/_apis/serviceendpoint/endpoints$APIVersion"
Invoke-RestMethod -Method POST -Uri $CreateEndpointURI -Headers $Headers -Body $EndpointJSON
}
}
Using this API instead:
Put
https://{account}.visualstudio.com/_apis/securityroles/scopes/distributedtask.serviceendpointrole/roleassignments/resources/{project id}_{endpoint id}?api-version=5.0-preview.1
Body (application/json)
[{"roleName":"User","userId":"{group or user id (originId)"}]