Can ExtensionDataService be used from a PowerShell-based VSTS build task? - powershell

I have created a PowerShell-based build task for Visual Studio Team Services (formerly Visual Studio Online). I have implemented the majority of the functionality I need, but for the last bit of functionality I need to be able to persist a small amount of data between builds.
The ExtensionDataService seems like exactly what I want (in particular, the setValue and getValue methods), but the documentation and examples I have found are for node.js-based build tasks:
VSS.getService(VSS.ServiceIds.ExtensionData).then(function(dataService) {
// Set a user-scoped preference
dataService.setValue("pref1", 12345, {scopeType: "User"}).then(function(value) {
console.log("User preference value is " + value);
});
The previous page also has a partial example of calling the REST API, but I have gotten 404 errors when trying to use it to either save or retrieve values:
GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents
{
"id": "myKey",
"__etag": -1,
"value": "myValue"
}
Can PowerShell be used to access the ExtensionDataService, either by using a library or by calling the REST API directly?

You can call REST API through PowerShell.
Set value (Put request):
https://[vsts name].extmgmt.visualstudio.com/_apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extension id}/Data/Scopes/User/Me/Collections/%24settings/Documents?api-version=3.1-preview.1
Body (Content-Type:application/json)
{
"id": "myKey",
"__etag": -1,
"value": "myValue"
}
Get value (Get request):
https://[vsts name].extmgmt.visualstudio.com/_apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extension id}/Data/Scopes/User/Me/Collections/%24settings/Documents/mykey?api-version=3.1-preview.1
The publisher name and extension id could be get in package json file (e.g. vss-extension.json)
Regarding call REST API through PowerShell, you can refer to this article: Calling VSTS APIs with PowerShell
Simple sample to call REST API:
Param(
[string]$vstsAccount = "<VSTS-ACCOUNT-NAME>",
[string]$projectName = "<PROJECT-NAME>",
[string]$buildNumber = "<BUILD-NUMBER>",
[string]$keepForever = "true",
[string]$user = "",
[string]$token = "<PERSONAL-ACCESS-TOKEN>"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$uri = "https://$($vstsAccount).visualstudio.com/DefaultCollection/$($projectName)/_apis/build/builds?api-version=2.0&buildNumber=$($buildNumber)"
$result = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
PowerShell script to get the base URL:
Function GetURL{
param([string]$url)
$regex=New-Object System.Text.RegularExpressions.Regex("https:\/\/(.*).visualstudio.com")
$match=$regex.Match($url)
if($match.Success)
{
$vstsAccount=$match.Groups[1]
$resultURL="https://$vstsAccount.extmgmt.visualstudio.com"
}
}
GetURL "https://codetiger.visualstudio.com/"

Related

Differentiate invoke-WebRequest status codes

I am attempting to validate URLs prior to downloading those files, and I want to differentiate between things like a path that requires credentials vs a bad path.
I have this at the moment
function Get-UrlStatusCode([string] $Url) {
try {
return (Invoke-WebRequest -Uri $Url -UseBasicParsing -DisableKeepAlive -Method:head).StatusCode
} catch [Net.WebException] {
return "$([Int]$_.Exception.Response.StatusCode) $($_.Exception.Response.statusDescription)"
}
}
(Get-UrlStatusCode 'http://AWSBUCKETPATH/Test_Public.xml')
(Get-UrlStatusCode 'http://AWSBUCKETPATH/Test_Private.xml')
(Get-UrlStatusCode 'http://AWSBUCKETPATH/Test_Missing.xml')
For the public path I am getting the expected StatusCode of 200, but both the private and the missing examples are returning 403 Forbidden. Is this a failure in my code, or something that needs to be configured at AWS to provide a 404 Not Found for the bad path, or is this just not something that is possible despite the existence of 404 as a code?

Teams Webhook error: Invoke-RestMethod: Bad payload received by generic incoming webhook

I'm using configured Teams webhook in my Powershell script and keep encountering the mentioned error message. What's strange, is that this exact method of configuring Webhook worked a few months ago on a different script.
Here's what I'm trying to do:
#Set URI of the Teams channel Webhook
$URI = 'https:....'
#Define Rest Method Parameters for the Teams Webhook sending
$RestMethodParameters = #{
"URI" = $URI
"Method" = 'POST'
"Body" = $null
"ContentType" = 'application/json'
}
$JSONBody = #{
"#type" = "MessageCard"
"#context" = "http://schema.org/extensions"
"themeColor" = '0078D7'
}
#Adding text to title and body
$JSONBody += #{
"title" = "'costReport-func' Function for connecting AzAccount has failed"
"text" = "Function failed at connection to AzAccount step."
}
#Sending the message to Teams
($RestMethodParameters).Body += ConvertTo-Json $JSONBody
Invoke-RestMethod #RestMethodParameters
And with this I'm getting "Bad payload received by generic incoming webhook." error message. What is causing the issue here?
Update: Microsoft has released a preview version (2.1.0) of the Teams PowerShell module which works properly with modern authentication. It’s likely that this version will be pushed through to general availability quite quickly.
Please go through this link for more information.

Publishing release notes to TFS/VSTS Wiki during Release

My mission is to generate and publish release notes on WIKI automatically when ever the release triggered, for this I am following this Blog, its very handy blog, but my bad luck still not able to create wiki page with release template. (using both Azure DevOps and TFS)
Template:
**Build Number** : $($build.buildnumber)
**Build started** : $("{0:dd/MM/yy HH:mm:ss}" -f [datetime]$build.startTime)
**Source Branch** : $($build.sourceBranch)
###Associated work items
##WILOOP##
* #$($widetail.id)
##WILOOP##
###Associated change sets/commits
##CSLOOP##
* **ID $($csdetail.changesetid)$($csdetail.commitid)**
>$($csdetail.comment)
##CSLOOP##
PowerShell Script
$content = [IO.File]::ReadAllText("$(System.DefaultWorkingDirectory)\releasenotes.md")
$data = #{content=$content;} | ConvertTo-Json;
$params = #{uri = '$(WikiPath)';
Method = 'PUT';
Headers = #{Authorization = "Bearer $(System.AccessToken)" };
ContentType = "application/json";
Body = $data;
}
Invoke-WebRequest #params
Please guide me what I am doing wrong
After testing, we find that the PowerShell script in your mentioned blog uses this Rest API: Pages - Create Or Update, thus the wikipath is the requested url like below format:
https://dev.azure.com/{organization}/{project}/_apis/wiki/wikis/{wikiIdentifier}/pages?path={path}&api-version=6.0
For example, we create a project wiki named scrum-test.wiki, and want to create a new wiki page named Release notes, the url would like below
https://dev.azure.com/{organization}/{project}/_apis/wiki/wikis/scrum-test.wiki/pages?path=Release notes&api-version=6.0
If we now want to create a child wiki page named 0.1.0 under Release notes page, the url would be like below
https://dev.azure.com/{organization}/{project}/_apis/wiki/wikis/scrum-test.wiki/pages?path=Release notes/0.1.0&api-version=6.0
In addition, using AccessToken we always get error which says that "The wiki page operation failed with message : User does not have write permissions for this wiki." even we grant full wiki permissions for identity: {project name} Build Service ({organization name}), so we use PAT authentication with full access and it works fine with below PowerShell script.
$content = [IO.File]::ReadAllText("$(System.DefaultWorkingDirectory)\releasenotes.md")
$data = #{content=$content;} | ConvertTo-Json;
$connectionToken="PAT here"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$params = #{uri = '$(WikiPath)';
Method = 'PUT';
Headers = #{Authorization = "Basic $base64AuthInfo" };
ContentType = "application/json";
Body = $data;
}
Invoke-WebRequest #params
Check access to System.AccessToken on the job level: https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=classic
Additionally, check permissions on your wiki

Getting restrictions from Confluence page

I'm not very savvy with web API calls, but I've been using the following powershell code (this site in this example is one I found that has some public data... my site is internal and requires I pass the credential, which has been working for me without issue):
If(-not (Get-InstalledModule -Name 'ConfluencePS')){Install-Module ConfluencePS}
Import-Module ConfluencePS
Set-ConfluenceInfo -BaseUri "https://wiki.opnfv.org"
$space = Get-confluencepage -Spacekey ds
ForEach($item in $space)
{
$splatParams = #{
Uri = "https://wiki.opnfv.org/rest/api/content/$($item.ID)/restriction"
ContentType = 'application/json'
method = 'GET'
}
#reference https://developer.atlassian.com/cloud/confluence/rest/#api-api-content-id-restriction-get
Invoke-RestMethod #splatParams
}
The documentation for the ConfluencePS shows that restrictions is still an open feature request but I need to get this working for a project.
I put a breakpoint in on line 982 from ConfluencePS.psm1 and was able to see the various calls and how the params are structured but when I try to mimic it (and change the URI based on the confluence documentation) I get an error "HTTP error 405 - MethodNotAllowed". Anyone have suggestions on how I can get this working? I'm trying to return back the permissions applied for all pages in a specific space.
Get Restrictions by Content ID
As you found out by yourself, it is required to add "byOperation".
I was able to get the restrictions of a specific page with the following code:
# for testing purposes ONLY, I've specified the URL and ID
$wikiUrl = "https://wiki.opnfv.org"
$itemId = "6820746"
$splatParams = #{
Uri = "$wikiUrl/rest/api/content/$itemId/restriction/byOperation"
ContentType = 'application/json'
method = 'GET'
}
$result = Invoke-RestMethod #splatParams
Tested on version 6.0.4 and 6.15.9
Filter by user name
If you like to filter the result by a specific username, you can use the following URI:
"$wikiUrl/rest/api/content/$itemId/restriction/byOperation/.../user?userName=".
Bt, there's an open bug on this way of action:
restriction returns ambiguous responses

Extract Response Data from Invoke-RestMethod

I have this piece of code which creates a new ticket in ServiceNow
$Response = Invoke-RestMethod -Uri $URI -Credential $SNowCreds -Method Post -Body $body -ContentType 'application/xml'
Write-IntoLog $Response.result
The output is stored in $Response and is as below
#{parent=; made_sla=true; caused_by=; watch_list=; upon_reject=Cancel all future Tasks; sys_updated_on=2018-01-11 08:49:50; child_incidents=0; hold_reason=; approval_history=; number=INC0010079; resolved_by=; sys_updated_by=Admin; opened_by=; user_input=; sys_created_on=2018-01-11 08:49:50; sys_domain=; state=In Progress; sys_created_by=Admin; knowledge=false; order=; calendar_stc=; closed_at=; cmdb_ci=; delivery_plan=; impact=2 - Medium; active=true; work_notes_list=; business_service=; priority=2 - High; sys_domain_path=/; rfc=; time_worked=; expected_start=; opened_at=2018-01-11 08:49:50; business_duration=; group_list=; work_end=; caller_id=; reopened_time=; resolved_at=; approval_set=; subcategory=Internal Application; work_notes=; short_description=Issues with Accessing Web URL; close_code=; correlation_display=; delivery_task=; work_start=; assignment_group=; additional_assignee_list=; business_stc=; description=We are facing issues with accessing the portal https://www.inmotion.com. The error Received is a 403 Error and Access to the same; calendar_duration=; close_notes=; notify=Do Not Notify; sys_class_name=Incident; closed_by=; follow_up=; parent_incident=; sys_id=2c40df9edb234300479a7e7dbf96198f; contact_type=; reopened_by=; incident_state=In Progress; urgency=1 - High; problem_id=; company=; reassignment_count=0; activity_due=UNKNOWN; assigned_to=; severity=3 - Low; comments=; approval=Not Yet Requested; sla_due=UNKNOWN; comments_and_work_notes=; due_date=; sys_mod_count=0; reopen_count=0; sys_tags=; escalation=Normal; upon_approval=Proceed to Next Task; correlation_id=; location=; category=Network}
From the output I want to extract the number(Incident ID) and also generate a link to the ServiceNow Incident.
However I am unable to extract the number(Incident ID). I tried converting the piece of output to JSON
$Response = Invoke-RestMethod -Uri $URI -Credential $SNowCreds -Method Post -Body $body -ContentType 'application/xml' | ConvertTo-Json
I tried to navigate it through the nodes. I am still not able to retrieve the IncidentID
Request your assistance in the above issue.
Without using the 'ConvertTo-Json'
Write-host "The number is $($response.result.number)"
This works.
Other useful reference:
Powershell ServiceNow API