Azure Devops REST API SendMail - azure

I'm trying to send mail after the successful stage on my release definition.
Following the docs
OAuth box is checked in my stage
Project Collection Service Account is added to Build Administrators and Release Administrators.
But the response from REST API is "Azure DevOps Login Page"
Here is my script:
$OrganizationName = "myorg"
$ProjectName = "myproj"
$sendmailto = "example#mail.com"
$mysubject = "Test Mail Subjcet"
$mailbody = "Mail body to test it works with azure rest api"
$PAT="MYPAT"
$Token = "$(System.AccessToken)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$Token"))
$HeaderMail = #{
Authorization = "Bearer $encodedCreds"
}
##send mail
$urimail = "https://${OrganizationName}.vsrm.visualstudio.com/${ProjectName}/_apis/Release/sendmail/$($env:RELEASE_RELEASEID)?api-version=3.2-preview.1"
$requestBody =
#"
{
"senderType":1,
"to":{"tfsIds":[$sendmailto]},
"body":"${mailbody}",
"subject":"${mysubject}"
}
"#
Try {
Invoke-RestMethod -Uri $urimail -Body $requestBody -Method POST -ContentType "application/json" -Headers $HeaderMail
}
Catch {
$_.Exception
}
Tested with:
Tried with 3.2 version and 7.1
PAT Token and authorization to Basic return 400 with Bearer return 401.
Switch $(System.AccessToken) to $($env:System_AccessToken) trygin to convert to base64 and without.
What I'm missing?
Response from
ConsoleLog

It's caused by the $requestBody. The request body requires valid Azure DevOps users referenced by their tfsIds.
Below PS script works for me, if your are running it in pipeline, then please use the $(System.AccessToken) instead of $PAT.
Running locally and authenticate with PAT:
$OrganizationName = "organization"
$ProjectName = "Project"
$sendmailto = "xxx#microsoft.com"
$mysubject = "Test Mail Subjcet"
$PAT="xxx"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$PAT"))
$HeaderMail = #{
Authorization = "Basic $encodedCreds"
}
#Get the tfsid
$userentitlementurl = "https://vsaex.dev.azure.com/${OrganizationName}/_apis/userentitlements?api-version=7.1-preview.1"
$response = Invoke-RestMethod -Uri $userentitlementurl -Method Get -Headers $HeaderMail
#Filter by sendmailto
$tfsid = ($response.value| where {$_.user.mailAddress -eq $sendmailto}).id
Write-Host $tfsid
##send mail
$urimail = "https://vsrm.dev.azure.com/${OrganizationName}/${ProjectName}/_apis/Release/sendmail/168?api-version=7.1-preview.1"
$requestBody =
#"
{
"senderType": 1,
"to": {
"tfsIds": [
"$tfsid"
],
"emailAddresses": []
},
"subject": "$mysubject",
"sections": [
5,
0,
1,
2,
4
]
}
"#
Try {
Invoke-RestMethod -Uri $urimail -Body $requestBody -Method POST -ContentType "application/json" -Headers $HeaderMail
}
Catch {
$_.Exception
}
Running in release pipeline and authenticate with $(System.AccessToken): (Please note that, because this script is being run during the release, the summary email will show the environment as IN PROGRESS even if it is run as the last step in the Release.)
$OrganizationName = "organization"
$ProjectName = "project"
$sendmailto = "xxx#microsoft.com"
$mysubject = "Test Mail Subjcet"
$HeaderMail = #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
#Get the tfsid
$userentitlementurl = "https://vsaex.dev.azure.com/${OrganizationName}/_apis/userentitlements?api-version=7.1-preview.1"
$response = Invoke-RestMethod -Uri $userentitlementurl -Method Get -Headers $HeaderMail
#Filter by sendmailto
$tfsid = ($response.value| where {$_.user.mailAddress -eq $sendmailto}).id
Write-Host $tfsid
##send mail
$urimail = "$env:SYSTEM_TEAMFOUNDATIONSERVERURI$env:SYSTEM_TEAMPROJECT/_apis/Release/sendmail/$($env:RELEASE_RELEASEID)?api-version=7.1-preview.1"
$requestBody =
#"
{
"senderType": 1,
"to": {
"tfsIds": [
"$tfsid"
],
"emailAddresses": []
},
"subject": "$mysubject",
"sections": [
5,
0,
1,
2,
4
]
}
"#
Try {
Invoke-RestMethod -Uri $urimail -Body $requestBody -Method POST -ContentType "application/json" -Headers $HeaderMail
}
Catch {
$_.Exception
}

Related

Azure DevOps SendMail Rest API returns 204 but doesn't send an email

I am trying to use the following SendMail Azure DevOps Rest API route to send out e-mails in PowerShell:
https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/send-mail/send-mail?view=azure-devops-rest-7.1#emailrecipients
This is the JSON body I am using for this request:
{
"message" : {
"body": "<p>Test Email</p>",
"to" : {
"tfIds" : ["mytfsid here"]
},
"cc" : {},
"replyTo" : {},
"inReplyTo" : "",
"subject" : "test"
},
"projectId" : "my project id here"
}
Postman shows me my request passes with 204; however, I am not receiving any e-mails. I'm at a loss. Can anyone tell me what I'm doing wrong, or why the e-mail would fail to deliver?
Thanks in advance.
What I've tried:
- Tried using the tfId of a colleague; still no e-mail received
- Tried using emailAddresses instead within the "to" block; still does not work.
- Tried using emailAddresses AND tfIds within the "to" block; still does not work.
Please make sure the notification works well, otherwise you have to troubleshoot the notification issue first. See Why am I not receiving notification emails?
If the notification works well, then please try below PowerShell script to send email for the selected work items: (works for me)
Param(
[string]$orgname = "{org-name}",
[string]$project = "{project-name}",
[string]$sendmailto = "xxx#xxx.com",
[string]$mysubject = "Test-Subject",
[string]$mailbody = "Body test",
[string]$witids = "1536,1537",
[string]$user = "",
[string]$token = "PAT"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
#Get the tfsid
$userentitlementurl = "https://vsaex.dev.azure.com/$orgname/_apis/userentitlements?api-version=7.1-preview.1"
$response = Invoke-RestMethod -Uri $userentitlementurl -Method Get -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
#Filter by sendmailto
$tfsid = ($response.value| where {$_.user.mailAddress -eq $sendmailto}).id
#Create Jason body
function CreateJsonBody
{
$value = #"
{
"message": "{\"to\":{\"tfIds\":[\"$tfsid\"],\"emailAddresses\":[],\"unresolvedEntityIds\":[]},\"subject\":\"$mysubject\",\"body\":\"$mailbody\",\"cc\":{\"tfids\":[\"$tfsid\"]},\"replyTo\":{\"tfids\":[\"$tfsid\"]}}",
"ids": [
$witids
],
"fields": [
"System.Id",
"System.Title",
"System.AssignedTo",
"System.State",
"System.AreaPath",
"System.Tags",
"System.CommentCount"
],
"format": 1
}
"#
return $value
}
$json = CreateJsonBody
$baseurl = "https://dev.azure.com/$orgname/$project/_api/_wit/sendMail?api-version=7.1-preview.1"
#Send mail
Invoke-RestMethod -Uri $baseurl -Method POST -Body $json -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}

Getting 'Bad Request' and 'Invalid query definition' when calling Azure REST API

I'm trying to call an Azure REST API endpoint to get information my daily usage cost.
I'm using Powershell 7.2 and here's my code:
$uri = 'https://management.azure.com/subscriptions/{subscription id}/providers/Microsoft.CostManagement/query?api-version=2021-10-01'
$token = '{generated bearer token string}'
$securetoken = ConvertTo-SecureString $token -AsPlainText -Force
$body = #{
type = 'Usage'
timeframe = 'MonthToDate'
dataset = #{
granularity = 'Daily'
aggregation = #{
totalCost = #{
name = 'PreTaxCost'
function = 'Sum'
}
}
grouping = #(
#{
type = 'Dimension'
name = 'ServiceName'
}
)
}
}
$costresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication Bearer -Token $securetoken -Body $body
Write-Host $costresponse
Here's the request body example from the Microsoft documentation I'm trying to emulate:
{
"type": "Usage",
"timeframe": "TheLastMonth",
"dataset": {
"granularity": "None",
"aggregation": {
"totalCost": {
"name": "PreTaxCost",
"function": "Sum"
}
},
"grouping": [
{
"type": "Dimension",
"name": "ResourceGroup"
}
]
}
}
When I run the code I get this error message:
Line |
27 | … tresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| {"error":{"code":"BadRequest","message":"Invalid query definition: Missing dataset granularity; valid
| values: 'Daily'.\r\n\r\n (Request ID: c6ead005-85b3-4ebe-9b46-........)"}}
I think the error has to do with the body syntax but I can't figure out what is the issue. I'm following this Microsoft documentation: https://learn.microsoft.com/en-us/rest/api/cost-management/query/usage
EDIT:
I tried converting the body to JSON and adding a depth parameter like this:
$costresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication Bearer -Token $securetoken -Body ($body|ConvertTo-Json -Depth 5)
And now I get a slightly different error message:
Line |
26 | … tresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| {"error":{"code":"BadRequest","message":"Invalid query definition, Dataset is invalid or not supplied.
| (Request ID: cf6a4b8f-88e8-4037-aa33-904......)"}}
I needed to add -ContentType 'application/json' to my Invoke-WebRequest function to get it to work. Thanks to #bluuf for the suggestion.

How to use "Pull Request Query"?

I want to return a pull request based on a commit. I found this > https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20query/get?view=azure-devops-rest-5.1
This API is used to find what pull requests are related to a given commit.
I'm using the following code:
$body = #"
{
"items": [
{
"59c1c31397b266116ff6d735e5638ef5d1b598a0"
}
]
}
"#
$someLink = "https://dev.something.com/embrace/somethingSomething/_apis/git/repositories/****-bf64-47d9-8b10-53f21220d54d/pullrequestquery?api-version=5.1"
Invoke-RestMethod -Uri $someLink -Headers #{Authorization = $pat } -Body $body -Method Post -ContentType 'application/json'
When I run the release I get a:
The remote server returned an error: (400) Bad Request.
Try using the following body:
$body = #"
{
"queries": [{
"items": [
"59c1c31397b266116ff6d735e5638ef5d1b598a0"
],
"type": "commit"
}]
}
"#
This is what worked for me. Powershell doesn't seem to like arrays unless they are wrapped in #().
$body = #{
queries = #(
#{
items = #(
"59c1c31397b266116ff6d735e5638ef5d1b598a0"
)
type = "commit"
}
)
}

Azure DevOps REST API returns a 403 when using the system OAuth token during a build

I'm running a script:
# Variables
$organization = "****"
$project = "****"
$repositoryId = "****"
$pullRequestId = $env:BUILD_PULLREQUEST_ID
$pat = "Bearer $env:System_AccessToken"
$featureReleaseUrl = "http://" + $env:prSourceBranchName + ".azurewebsites.net"
$body = #"
{
"comments": [
{
"content": "Link naar feature release $featureReleaseUrl"
}
]
}
"#
$createThreadInPRUrl = "https://dev.azure.com/$organization/$project/_apis/git/repositories/$repositoryId/pullRequests/$pullRequestId/threads?api-version=5.0"
if ($pullRequestId) {
Invoke-RestMethod -Uri $createThreadInPRUrl -Headers #{Authorization = $pat} -Body $body -Method Post -ContentType 'application/json'
}
When it runs it returns a:
##[error]The remote server returned an error: (403) Forbidden.
I've created a Personal Access Tokens in my personal settings.
I've also created this script:
# Variables
$organization = "****"
$project = "****"
$buildId = $****
$pat = "Bearer $env:System_AccessToken"
if (!$env:Build_PullRequest_SourceBranchName) {
$retrieveSourceBranchFromBuildURL = "https://dev.azure.com/$organization/$project/_apis/build/builds/$buildId" + "?api-version=5.0"
$buildInformation = Invoke-RestMethod -Uri $retrieveSourceBranchFromBuildURL -Headers #{Authorization = $pat } -Method Get -ContentType 'application/json'
$SourceBranchFromBuild = $buildInformation.sourceBranch.split('/')[-1]
Write-Host "### no Build PullRequest SourceBranchName available ###"
Write-Host "##vso[task.setvariable variable=prSourceBranchName;]"$SourceBranchFromBuild
}
And this runs fine. The difference between the first and second script is that the first is a POST and the second a GET. But they both use the $pat token.
Even though the token you used is System.AccessToken, if you don't have access permission of Pull Request, you will also could not operate it.
Go Project Setting--> Repositories--> Repository you want to access, locate your account or the group you are in. Check the permission state of Contribute to pull requests.
You must have this Contribute to pull requests permission allowed, so that you can add the comment to PR.

graph beta /groups/$id/channels Authorization has been denied for this request

i want to get all channels from an azure group.
Querying the groups works and return all azure groups. however, query the group channels result in an error
Authorization has been denied for this request
Code:
$appID = "f9....22"
$appSecret="FH..8="
$tokenAuthURI = "https://login.microsoftonline.com/c6...59/oauth2/token"
$requestBody = "grant_type=client_credentials" +
"&client_id=$appID" +
"&resource=https://graph.microsoft.com" +
"&client_secret=$appSecret"
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenAuthURI -body $requestBody -ContentType "application/x-www-form-urlencoded"
$accessToken = $tokenResponse.access_token
$groupsListURI = "https://graph.microsoft.com/beta/groups?`$filter=groupTypes/any(c:c+eq+`'Unified`')"
$graphResponse = Invoke-RestMethod -Method Get -Uri $groupsListURI -Headers #{"Authorization"="Bearer $accessToken"}
$TeamsList = #()
foreach ($group in $graphResponse.value)
{
if($group.groupTypes -eq "Unified") {
$id= $group.id
Try
{
$url = "https://graph.microsoft.com/beta/groups/$id/channels"
$team = Invoke-RestMethod -Method Get -Uri $url -Headers #{"Authorization"="Bearer $accessToken"}
"Channel count for " + $group.displayName + " is " + $team.value.id.count
}
Catch
{
$result = $_.Exception.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($result)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$reader.ReadToEnd()
$team = $null
}
}
}
the catch result is ""
{
"error": {
"code": "",
"message": "Authorization has been denied for this request.",
"innerError": {
"request-id": "7c...89",
"date": "2017-12-14T14:19:39"
}
}
}
thx
UPDATE
also tried adding either $scope or $scope2. same result
$scope = "https://graph.microsoft.com/Group.ReadWrite.All https://graph.microsoft.com/Group.Read.All https://graph.microsoft.com/User.ReadBasic.All https://graph.microsoft.com/Directory.AccessAsUser.All https://graph.microsoft.com/Directory.Read.All"
$scope2 = "Group.ReadWrite.All Group.Read.All Directory.Read.All Directory.AccessAsUser.All User.ReadBasic.All"
$requestBody = "grant_type=client_credentials" +
"&client_id=$appID" +
"&resource=https://graph.microsoft.com" +
"&client_secret=$appSecret" +
"&scope=$scope2"

Resources