I'm attempting to write some PowerShell functions to interface with our Atlassian JIRA system (JIRA 5.2, download version). Unfortunately, I've found through trial and error that the Invoke-RestMethod doesn't seem to work (doesn't support authentication headers), so I've written up a simple function called Invoke-JiraMethod. I can confirm that this method works for GET requests; I've been able to use it to get Jira objects to my heart's desire. As soon as I tried to create an issue, though, I started getting a HTTP 400 / Bad request error.
I've followed the steps here to get my issue metadata, and I'm filling out all the required fields in my input object. Could anyone help me figure out how to solve the 400 error? I can provide more information if needed - I just didn't want to overflow the description of the question. :)
Function Invoke-JiraMethod
{
<#
.Synopsis
Low-level function that directly invokes a REST method on JIRA
.Description
Low-level function that directly invokes a REST method on JIRA. This is designed for
internal use.
#>
[CmdletBinding()]
param
(
[ValidateSet("Get","Post")] [String] $Method,
[String] $URI,
[String] $Username,
[String] $Password
)
process
{
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("${Username}:${Password}"))
$webRequest = [System.Net.WebRequest]::Create($URI)
$webRequest.Method = $Method.ToUpper()
$webRequest.AuthenticationLevel = "None"
$webRequest.Headers.Add('Authorization', "Basic $token")
#$webRequest.Headers.Add('Authorization', $token)
$webRequest.PreAuthenticate = $true
$webRequest.ContentType = "application/json"
Write-Verbose "Invoking JIRA method $Method with URI $URI"
$response = $webRequest.GetResponse()
$requestStream = $response.GetResponseStream()
$readStream = New-Object -TypeName System.IO.StreamReader -ArgumentList $requestStream
$json = $readStream.ReadToEnd()
$readStream.Close()
$response.Close()
$result = $json | ConvertFrom-Json
Write-Output $result
}
}
Function New-JiraIssue
{
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string] $ProjectKey,
[Parameter(Mandatory = $true,
Position = 1)]
[string] $IssueType,
[Parameter(Mandatory = $false)]
[string] $Priority = 3,
[Parameter(Mandatory = $true,
Position = 2)]
[string] $Summary,
[Parameter(Mandatory = $true,
Position = 3)]
[string] $Description,
[Parameter(Mandatory = $true,
Position = 4)]
[string] $Location,
[Parameter(Mandatory = $true,
Position = 5)]
[string] $Phone,
[Parameter(Mandatory = $false)]
[string] $Reporter,
[Parameter(Mandatory = $false)]
[PSCredential] $Credential
)
process
{
$ProjectObject = New-Object -TypeName PSObject -Property #{"key"=$ProjectKey}
$IssueTypeObject = New-Object -TypeName PSObject -Property #{"id"=$IssueType}
if ( -not ($Reporter))
{
Write-Verbose "Reporter not specified; defaulting to $JiraDefaultUser"
$Reporter = $JiraDefaultUser
}
$ReporterObject = New-Object -TypeName PSObject -Property #{"name"=$Reporter}
$fields = New-Object -TypeName PSObject -Property ([ordered]#{
"project"=$ProjectObject;
"summary"=$Summary;
"description"=$Description;
"issuetype"=$IssueTypeObject;
"priority"=$Priority;
"reporter"=$ReporterObject;
"labels"="";
$CustomFields["Location"]=$Location;
$CustomFields["Phone"]=$Phone;
})
$json = New-Object -TypeName PSObject -Property (#{"fields"=$fields}) | ConvertTo-Json
Write-Verbose "Created JSON object:`n$json"
# https://muwebapps.millikin.edu/jira/rest/api/latest/issue/IT-2806
# $result = Invoke-RestMethod -Uri $JiraURLIssue -Method Post -ContentType "application/json" -Body $json -Credential $Credential
if ($Username -or $Password)
{
$result = (Invoke-JiraMethod -Method Post -URI "${JiraURLIssue}" -Username $Username -Password $Password)
} else {
$result = (Invoke-JiraMethod -Method Post -URI "${JiraURLIssue}" -Username $JiraDefaultUser -Password $JiraDefaultPassword)
}
Write-Output $result
}
}
Thanks in advance!
I was receiving the error 400 because the issue type id number I had put into the json data wasn't mapped to anything. fiddler helped me diagnose that.
I used this bit of code to figure out authenticating to jira via invoke-restmethod's -Headers option: http://poshcode.org/3461
then I put the json data into a here string, and ran invoke-restmethod.
code bits below. replace "any valid... type id number" with actual project and issue type ids you get from your jira admin.
$uri = "$BaseURI/rest/api/2/issue"
$jsonString = #'
{
"fields": {
"project":
{
"id": "any valid project id number"
},
"summary": "No REST for the Wicked.",
"description": "Creating of an issue using ids for projects and issue types using the REST API",
"issuetype": {
"id": "any valid issue type id number"
}
}
}
'#
$headers = Get-HttpBasicHeader $Credentials
Invoke-RestMethod -uri $uri -Headers $headers -Method Post -Body $jsonString -ContentType "application/json"
Related
I am trying to create a user story and task in Azure DevOps with this RESTapi
There are multiple backlogs.
POST https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/${type}?api-version=6.0
My code
function Set-pbiStuff {
param
(
[Parameter(Mandatory = $true)] [string] $Organization,
[Parameter(Mandatory = $true)] [string] $Project,
[Parameter(Mandatory = $true)] [hashtable] $Token
)
$Base = "https://dev.azure.com/$($organization)/$($project)/_apis/wit/workitems"
$workItemType = 'task'
$URL = "$($Base)/$($workItemType)?api-version=6.0"
$Json = #(
#{
op = 'add'
path = '/fields/System.Title'
value = $workItemType
}
)
$Body = (ConvertTo-Json $Json)
$response = Invoke-RestMethod `
-Method Post `
-Uri $URL `
-ContentType 'application/json' `
-Body $Body `
-Headers $Token
Write-Host $URL
Write-Host $response
}
$Token= #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($env:SYSTEM_ACCESSTOKEN)")) }
$tt = Set-pbiStuff -Organization 'myOrganization' -Project 'myProject' -Token $Token
return $tt
But the response I get is that the page was not found.
What have I missed?
The problem is that the type ${type} is incorrect in your script, the character $ is missing.
Please try this:
$URL = "$($Base)/"+"$"+"$($workItemType)?api-version=6.0"
And the content type should be "application/json-patch+json"
Below script works for me : (PAT used for the test, you can change it back to SYSTEM_ACCESSTOKEN if you run it in pipeline)
function Set-pbiStuff {
param
(
[Parameter(Mandatory = $true)] [string] $Organization ,
[Parameter(Mandatory = $true)] [string] $Project ,
[Parameter(Mandatory = $true)] [string] $Token
)
$Base = "https://dev.azure.com/$($organization)/$($project)/_apis/wit/workitems"
$workItemType = 'task'
$URL = "$($Base)/"+"$"+"$($workItemType)?api-version=6.0"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f '',$Token)))
$Json = #(
#{
op = 'add'
path = '/fields/System.Title'
value = $workItemType
}
)
$Body = (ConvertTo-Json $Json)
$response = Invoke-RestMethod `
-Method Post `
-Uri $URL `
-ContentType 'application/json-patch+json' `
-Body $Body `
-Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
Write-Host $URL
Write-Host $response
}
$tt = Set-pbiStuff -Organization 'orgname' -Project 'ProjectName' -Token Tokenhere
return $tt
You're trying to call the API with a PATCH verb, see line two:
$response = Invoke-RestMethod `
-Method Patch `
-Uri $URL `
-ContentType 'application/json' `
-Body $Body `
-Headers $AzAuthHeader
The API endpoint is, like the documentation shows and you stated in your question, a POST endpoint.
Try this URL:
$URL = "$($Base)/`$$($workItemType)?api-version=6.0"
Check this sample: https://arindamhazra.com/create-azure-devops-task-using-powershell/
I had a previous issue where sending an excel file wouldn't work because it got corrupted but got that working thanks to some help. Now a new issue I have is that I don't know how to send to multiple recipients. The main issue is that my sendgrid code is within a loop that reads from a file, and the number of recipients is dynamic. And I get the structure of the code from an existing github source.
MAINLY: Need to know how to send to multiple recipients via variable using the structure below.
function Send-SendGridEmail {
param(
[Parameter(Mandatory = $true)]
[String] $destEmailAddress,
[Parameter(Mandatory = $true)]
[String] $fromEmailAddress,
[Parameter(Mandatory = $true)]
[String] $subject,
[Parameter(Mandatory = $false)]
[string]$contentType = 'text/plain',
[Parameter(Mandatory = $true)]
[String] $contentBody,
[parameter(Mandatory = $false)]
[string]$FileName,
[parameter(Mandatory = $false)]
[string]$FileNameWithFilePath,
[parameter(Mandatory = $false)]
[string]$AttachementType
)
<#
.Synopsis
Function to send email with SendGrid
.Description
A function to send a text or HTML based email
See https://sendgrid.com/docs/API_Reference/api_v3.html for API details
This script provided as-is with no warranty. Test it before you trust it.
www.ciraltos.com
.Parameter apiKey
The SendGrid API key associated with your account
.Parameter destEmailAddress
The destination email address
.Parameter fromEmailAddress
The from email address
.Parameter subject
Email subject
.Parameter type
The content type, values are “text/plain” or “text/html”. "text/plain" set by default
.Parameter content
The content that you'd like to send
.Example
Send-SendGridEmail
#>
############ Update with your SendGrid API Key ####################
$apiKey = "key"
$headers = #{
'Authorization' = 'Bearer ' + $apiKey
'Content-Type' = 'application/json'
}
$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($FileNameWithFilePath))
$body = #{
personalizations = #(
#{
to = #(
#{
email = $destEmailAddress
}
)
}
)
from = #{
email = $fromEmailAddress
}
subject = $subject
content = #(
#{
type = $contentType
value = $contentBody
}
)
attachments = #(
#{
content=$base64string
filename=$FileName
type= $AttachementType
disposition="attachment"
}
)
}
try {
$bodyJson = $body | ConvertTo-Json -Depth 4
}
catch {
$ErrorMessage = $_.Exception.message
write-error ('Error converting body to json ' + $ErrorMessage)
Break
}
try {
Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method
Post -Headers $headers -Body $bodyJson
}
catch {
$ErrorMessage = $_.Exception.message
write-error ('Error with Invoke-RestMethod ' + $ErrorMessage)
Break
}
}
# Call the function
# Splat the input
#Send-SendGridEmail #splat
# Sample code with HTML body
$htmlBody = #"
//stuff
"#
$splat2 = #{
destEmailAddress = '""
fromEmailAddress = 'noreply#domaint.com'
subject = 'Test Email'
contentType = 'text/html'
contentBody = $htmlBody
FileName = "filename"
FileNameWithFilePath = "path\excel.xlsx"
AttachementType = "application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet"
}
Send-SendGridEmail #splat2
I am using the following powershell code to send a message to an Azure Service Bus Topic with a property set:
function Send-AzServiceBusMessage {
param(
[Parameter(Mandatory = $true)]
[string] $ResourceGroupName,
[Parameter(Mandatory = $true)]
[string] $NamespaceName,
[Parameter(Mandatory = $true)]
[string] $TopicName,
[Parameter(Mandatory = $false)]
[string] $PolicyName = 'RootManageSharedAccessKey',
[Parameter(Mandatory = $false)]
[string] $Property1
)
$message = [PSCustomObject] #{ "Body" = "Test message"; "Property1" = $Property1 }
$namespace = (Get-AzServiceBusNamespace -ResourceGroupName $ResourceGroupName -Name $namespacename).Name
$key = (Get-AzServiceBusKey -ResourceGroupName $ResourceGroupName -Namespace $namespacename -Name $PolicyName).PrimaryKey
$message.psobject.properties.Remove("Body")
$token = New-AzServiceBusSasToken -Namespace $namespace -Policy $PolicyName -Key $Key
#set up the parameters for the Invoke-WebRequest
$headers = #{ "Authorization" = "$token"; "Content-Type" = "application/atom+xml;type=entry;charset=utf-8" }
$uri = "https://$namespace.servicebus.windows.net/$TopicName/messages"
$headers.Add("BrokerProperties", $(ConvertTo-Json -InputObject $Message -Compress))
$result = Invoke-WebRequest -Uri $uri -Headers $headers -Method Post -Body $body
if ($result.StatusCode -ne 201) {
$result
}
I have a rule set up on a topic subscription such that:
Property1 in ('somevalue')
However, if I set up a catch all subscription with no rules, I can see the message is being received by that and not by the subscription with the rule. So my question is how do I send messages with properties set using powershell. Similar to this in C#:
var message = new BrokeredMessage { MessageId = Guid.NewGuid().ToString()};
message.Properties["Property1"] = "somevalue";
Based on the documentation it looks like you have to nest your custom properties in a Properties member:
$headers.Add("BrokerProperties", $(ConvertTo-Json -InputObject #{ Properties = $message } -Compress))
The solution was as above with the following modifications:
$message = [PSCustomObject] #{ "Body" = "Test message" }
$headers = #{ "Authorization" = "$token"; "Content-Type" = "application/atom+xml;type=entry;charset=utf-8"; "Property1" = $Property1 }
$headers.Add("BrokerProperties", $(ConvertTo-Json -InputObject $Message -Compress))
I am trying to create build configurations via the restapi and powershell and keep getting the following error:
Exception calling "GetResponse" with "0" argument(s): "The remote server returned an error: (405) Method Not Allowed."
It seems that I can use GET fine, the issue appears to be with the PUT command
Code Snippet
$url = http://%teamcityServer%:8111/app/rest/buildTypes/id:%projectname%"
$req = [System.Net.WebRequest]::Create($url)
$req.ContentType = "text/plain"
$req.UseDefaultCredentials = $true
$req.Credentials = Get-Credential("username")
$req.Method ="PUT"
$req.ContentLength = 0
$req.Accept = "*/*"
$resp = $req.GetResponse()
$results = [xml]$resp.ReadToEnd()
Output from the Team City log
2015-09-10 09:14:30,582] WARN [io-8111-exec-70] - est.jersey.ExceptionMapperUtil - Error has occurred during request processing (405). Error: javax.ws.rs.WebApplicationException. Not supported request. Please check URL, HTTP method and transfered data are correct. metadata: [Allow:[HEAD,DELETE,GET,OPTIONS],] Request: PUT '/app/rest/buildTypes/id:%project%'
Team City Version is 9.1.1 so I believe this is possible.
I'm fairly new to the restapi so any input is appreciated.
Its possible but you need to post xml data to create builds/projects. For example if you need to create a project you can POST XML something like
<newProjectDescription name='New Project Name' id='newProjectId' copyAllAssociatedSettings='true'><parentProject locator='id:project1'/><sourceProject locator='id:project2'/></newProjectDescription>
to http://teamcity:8111/httpAuth/app/rest/projects.
Check more info on Teamcity REST documentation.
You didn't mention your powershell version, but if you are using 3.0 or later you can call Invoke-WebRequest cmdlet and your code should look something like:
Invoke-WebRequest -Uri $uri -Credential $cred -Method Post -Body $body -ContentType "Application/xml"
For PowerShell 2.0 You can write your own Web-Request method like:
function Web-Request{
param(
[Parameter(Mandatory=$true)]
[string]
$Uri,
[Parameter(Mandatory=$false)]
[string]
$Username = $null,
[Parameter(Mandatory=$false)]
[string]
$Password = $null,
[Parameter(Mandatory=$true)]
[string]
$ContentType,
[Parameter(Mandatory=$true)]
[string]
$Method,
[Parameter(Mandatory=$false)]
[string]
$PostString
)
$webRequest = [System.Net.WebRequest]::Create($Uri)
$webRequest.ContentType = $ContentType
$webRequest.Method = $Method
$webRequest.Accept = "*/*"
if ($Username)
{
$webRequest.Credentials = new-object system.net.networkcredential($Username, $Password)
}
try
{
switch -regex ($Method)
{
"PUT|POST" # PUT and POST behaves similar ways except that POST is only used for creation while PUT for creation/modification
{
if ($PostString -ne "")
{
$PostStringBytes = [System.Text.Encoding]::UTF8.GetBytes($PostString)
$webrequest.ContentLength = $PostStringBytes.Length
$requestStream = $webRequest.GetRequestStream()
$requestStream.Write($PostStringBytes, 0,$PostStringBytes.length)
}
else
{
$requestStream = $webRequest.GetRequestStream()
}
}
"DELETE"
{
$requestStream = $webRequest.GetRequestStream()
}
default
{
# GET requests usually don't have bodies, default will behave like a GET
}
}
if ($requestStream -ne $null)
{
$requestStream.Close()
}
[System.Net.WebResponse] $resp = $webRequest.GetResponse();
$rs = $resp.GetResponseStream();
[System.IO.StreamReader] $sr = New-Object System.IO.StreamReader -argumentList $rs;
[string] $results = $sr.ReadToEnd();
}
catch
{
$results = "Error : $_.Exception.Message"
}
finally
{
if ($sr -ne $null) { $sr.Close(); }
if ($resp -ne $null) { $resp.Close(); }
$resp = $null;
$webRequest = $null;
$sr = $null;
$requestStream = $null;
}
return $results
}
What I am currently doing:
Invoke-WebRequest -Uri https://coolWebsite.com/ext/ext -ContentType application/json -Method POST -Body $someJSONFile
I am looking for a way to POST this same .json file in Powershell without using Invoke-WebRequest, if it is possible. This new method would preferably allow me to get the server output content and parse through it in powershell.
Maybe by calling an outside cURL method? I really am not sure and all my internet research has proved fruitless.
How can I achieve this above result without Invoke-WebRequest?
You can try this :
# RestRequest.ps1
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization, System.Web.Extensions
$utf8 = [System.Text.Encoding]::UTF8
function Request-Rest
{
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)]
[String] $URL,
[Parameter(Mandatory=$false)]
[System.Net.NetworkCredential] $credentials,
[Parameter(Mandatory=$true)]
[String] $JSON)
# Remove NewLine from json
$JSON = $JSON -replace "$([Environment]::NewLine) *",""
# Create a URL instance since the HttpWebRequest.Create Method will escape the URL by default.
# $URL = Fix-Url $Url
$URI = New-Object System.Uri($URL,$true)
try
{
# Create a request object using the URI
$request = [System.Net.HttpWebRequest]::Create($URI)
# Build up a nice User Agent
$UserAgent = "My user Agent"
$request.UserAgent = $("{0} (PowerShell {1}; .NET CLR {2}; {3})" -f $UserAgent, $(if($Host.Version){$Host.Version}else{"1.0"}),
[Environment]::Version,
[Environment]::OSVersion.ToString().Replace("Microsoft Windows ", "Win"))
$request.Credentials = $credentials
$request.KeepAlive = $true
$request.Pipelined = $true
$request.AllowAutoRedirect = $false
$request.Method = "POST"
$request.ContentType = "application/json"
$request.Accept = "application/json"
$utf8Bytes = [System.Text.Encoding]::UTF8.GetBytes($JSON)
$request.ContentLength = $utf8Bytes.Length
$postStream = $request.GetRequestStream()
$postStream.Write($utf8Bytes, 0, $utf8Bytes.Length)
#Write-String -stream $postStream -string $JSON
$postStream.Dispose()
try
{
#[System.Net.HttpWebResponse] $response = [System.Net.HttpWebResponse] $request.GetResponse()
$response = $request.GetResponse()
}
catch
{
$response = $Error[0].Exception.InnerException.Response;
Throw "Exception occurred in $($MyInvocation.MyCommand): `n$($_.Exception.Message)"
}
$reader = [IO.StreamReader] $response.GetResponseStream()
$output = $reader.ReadToEnd()
$reader.Close()
$response.Close()
Write-Output $output
}
catch
{
$output = #"
{
"error":1,
"error_desc":"Error : Problème d'accès au serveur $($_.Exception.Message)"
}
"#
Write-Output $output
}
}
Edited 19-10-2015
Here is an example usage :
#$urlBase = "http://192.168.1.1:8080/"
#######################################################################
# Login #
#######################################################################
$wsLogin = "production/login"
Function login
{
[CmdletBinding()]
PARAM
(
[ValidateNotNullOrEmpty()]
[String] $login,
[String] $passwd
)
Write-Verbose $wsLogin
#$jsonIn = [PSCustomObject]#{"login"=$login;"passwd"=$passwd} | ConvertTo-Json
$jsonIn = #"
{
"login":"$login",
"passwd":"$passwd"
}
"#
Write-Verbose $jsonIn
$jsonOut = Request-Rest -URL "$urlBase$wsLogin" -JSON $jsonIn -credentials $null
Write-Verbose $jsonOut
#return $jsonOut | ConvertFrom-Json
return $jsonOut
}
It is easy to convert that code to cURL
curl -v --insecure -X POST -H "Content-Type: application/json" --data-binary someJSONFile.js https://coolWebsite.com/ext/ext/