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

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)"}]

Related

Using PowerShell to call the GraphAPI and create a folder in a SharePoint document library

So I am trying to create a new folder in a SharePoint library using Graph API. I can get the access token just fine, but whenever I send a request to create a new folder, I get (400) bad request. Here is my code, any help would be much appreciated.
#header containing access token
$header = #{
Authorization = "Bearer " + $resp.access_token
}
$CreateFolderURL = "https://graph.microsoft.com/v1.0/drives/(Here I write the drive ID)/items/root/children"
$uploadFolderRequestBody = #{
name= "NewFolder"
folder = $null
"#microsoft.graph.conflictBehavior"= "rename"
} | ConvertTo-Json
Invoke-RestMethod -Headers $header -Method Post -Body $uploadFolderRequestBody -ContentType "application/json" -Uri $CreateFolderURL
Try to use folder = #{} instead of folder = $null.
It produces a json where "folder" : null but it should be "folder" : {}
$uploadFolderRequestBody = #{
name= "NewFolder"
folder = #{}
"#microsoft.graph.conflictBehavior"= "rename"
} | ConvertTo-Json
It will produce the expected json
{
"name": "NewFolder",
"folder": {},
"#microsoft.graph.conflictBehavior": "rename"
}

Permissions to access to MS Graph API via PowerShell

Question
I am trying to write a PowerShell script to get report data via the MS Graph API /reports/credentialUserRegistrationDetails.
When I use Graph Explorer it works just fine, as long as I enable Reports.Read.All on the Modify permissions (Preview) tab.
But, when I try to do it with my script, I just get the error "Calling principal does not have required MSGraph permissions Reports.Read.All"
In all my searches, I can only find how to assign permissions to apps.
Is there some way to make it so I can do it from my script?
My Script
$azContext = Get-AzContext
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
$azContext.Account,
$azContext.Environment,
$azContext.Tenant.Id,
$null,
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never,
$null,
"https://graph.microsoft.com"
)
$params = #{
Method = "GET"
Uri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
Headers = #{
Authorization = "Bearer $($token.AccessToken)"
"Content-Type" = "application/json"
}
}
Invoke-RestMethod #params
Response
{
"error": {
"code":"Authentication_MSGraphPermissionMissing",
"message":"Calling principal does not have required MSGraph permissions Reports.Read.All",
"innerError": {
"date":"2021-10-19T01:18:36",
"request-id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client-request-id":"6b8cc3a3-b93b-44bb-b1d4-190c618aa52a"
}
}
}
When I use Graph Explorer it works just fine, as long as I enable
Reports.Read.All on the Modify permissions (Preview) tab.
Its because Microsoft Graph Explorer is a Enterprise Application of Microsoft which is present on every Azure AD tenant just you need to sign in and use it by providing the required permissions.
But when you are writing running your Powershell script it uses Microsoft Azure Powershell . You can verify it by checking the access_token received in JWT Token.So, you need to provide the Reports.Read.All API Permission to the same app in your tenant with appid : 1950a258-227b-4e31-a9cf-717495945fc2 in Enterprise Application >> Permissions and grant the admin consent .After providing the required permissions only it will work.
Another Way will be to create a App registration ,Create a client secret for it and then provide the Reports.Read.All API permission and use the below script:
$TenantName = "tenantname.onmicrosoft.com"
$clientID = "d344e3xxx-xxx-xxxx-xxxx-9c861d363244" # app registration clientId
$clientSecret = "fNc7Q~UNHBgv_xxxxxxxxxxxxxxxxxxxxxx-PD"
$Scope = "https://graph.microsoft.com/.default"
$Body = #{
Grant_Type = "client_credentials"
Scope = $Scope
client_Id = $clientID
Client_Secret = $clientSecret
}
$authUri = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
$TokenResponse = Invoke-RestMethod -Uri $authUri -Method POST -Body $Body
$Headers = #{
"Authorization" = "Bearer $($TokenResponse.access_token)"
"Content-type" = "application/json"
}
$apiUri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
$response = Invoke-RestMethod -Headers $Headers -Uri $apiUri -Method GET
$response.value
Output:
Note: In Some Tenants Microsoft Azure PowerShell might not be visible from portal , so in that case please use the above solution it will be easier.
For Authorization code flow, try something like this -
#region Auth1
#With User Interaction for Delegated Permission
Add-Type -AssemblyName System.Web
Function Get-AuthCode {
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object -TypeName System.Windows.Forms.Form -Property #{Width = 440; Height = 640 }
$web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property #{Width = 420; Height = 600; Url = ($url -f ($Scope -join "%20")) }
$DocComp = {
$Global:uri = $web.Url.AbsoluteUri
if ($Global:uri -match "error=[^&]*|code=[^&]*") { $form.Close() }
}
$web.ScriptErrorsSuppressed = $true
$web.Add_DocumentCompleted($DocComp)
$form.Controls.Add($web)
$form.Add_Shown( { $form.Activate() })
$form.ShowDialog() | Out-Null
$queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query)
$output = #{}
foreach ($key in $queryOutput.Keys) {
$output["$key"] = $queryOutput[$key]
}
#$output
}
Get-AuthCode
#Extract Access token from the returned URI
$regex = '(?<=code=)(.*)(?=&)'
$authCode = ($uri | Select-string -pattern $regex).Matches[0].Value
Write-output "Received an authCode, $authCode"
$tokenBody = #{
Grant_Type = "authorization_code"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
redirect_uri = $redirectUri
code = $authCode
ressource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth1
For delegated permissions use something like below -
$tokenBody = #{
Grant_Type = "password"
Scope = "user.read%20openid%20profile%20offline_access"
Client_Id = $clientId
username = $User
password = $pw
resource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth2
For Application permissions (using client credential flow) use something like this
$tokenBody = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$Tenantid/oauth2/v2.0/token" -Method POST -Body $tokenBody
#endregion Auth3
Despite what Method you have chosen, the tokenRepsonse Variable is holding our Key to Query against the Microsoft GRAPH API.
We want a list of all Teams in our Tenant, so this require propriate Application Permission. So for example- our Powershell to get a Full List of all Teams look like this -
$headers = #{
"Authorization" = "Bearer $($tokenResponse.access_token)"
"Content-type" = "application/json"
}
$URL = "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')"
$AllTeams = (Invoke-RestMethod -Headers $headers -Uri $URL -Method GET).value
Thanks.
I finally gave up on using the REST APIs and started using Microsoft.Graph PowerShell Modules. I find the documentation pretty sparse, but at least it works for what I need. :)
Import-Module "Microsoft.Graph.Identity.Signins"
Import-Module "Microsoft.Graph.Users"
Import-Module "Microsoft.Graph.Groups"
Connect-MgGraph -TenantId $TenantId -Scopes "Directory.Read.All", "UserAuthenticationMethod.Read.All" -ForceRefresh
Select-MgProfile -Name "beta"
$report = Get-MgReportCredentialUserRegistrationDetail

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.

how to get information from Azure DevOps using powershell script and show to power BI service?

There might be some methods to get access to RESTful APIs from my research.
Httpclient? using id and password might be not secure.
What is the way to get access to Azure Devops with a personal token?
any example code?
the code below is what I have tried and it showed credential failed.
thank you.
powershell code
function InputToPowerBI {
param ([string]$type,[string]$title,[string]$assignedTo,[string]$state)
$endpoint = "address of the end point"
$payload = #{
"Type" =$type
"Title" =$title
"Assigned To" =$assignedTo
"State" =$state
"Area Path" =$areaPath
"Tags" =$tag
"Comment Count" =$commentCount
}
$json = ConvertTo-Json #($payload);
Invoke-RestMethod -Method Post -Uri "$endpoint" -Body $json
}
GetData;
function GetData
{
try
{
$personalAccessToken="Personal token such as eiejr22837482";
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalToken)"))
$header = #{authorization = "Basic $token"}
$projectsUrl = "address of the current sprint from Azure DevOps"
$projects = Invoke-RestMethod -Uri $projectsUrl -Method Get -ContentType "application/json" -Headers $header
$projects.value | ForEach-Object {
}
}
catch
{
}
}

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