Atlassian JIRA Integration (PowerShell)
Secret Server can integrate with Atlassian JIRA via PowerShell. This integration includes validating ticket numbers and their status, and adding comments.
For more information about integrating ticket systems with PowerShell, see PowerShell Ticketing Integration.
Requirements
-
PowerShell, see Creating and Using PowerShell Scripts.
Ticket Number Validation Pattern (Regex)
Before making a call to the ticket validation script, you can have Secret Server validate that the number matches a pattern. For example, you will probably have multiple JIRA projects and will want your users to specify these correctly. One regex would be to simply allow any project name followed by a dash and numbers:
^\w{3}-\d+$
Or perhaps you want to specifically match projects that you know are real followed by a dash and numbers:
^((PRO1)|(PRO2))-\d+$
For more information, see Setting a Ticket Pattern Regex on the Ticketing System Integration page.
Validating Ticket Status
You need to create a PowerShell script to retrieve and validate tickets. This integration assumes that the user will pass in the full ticket name, including the project name. For example: (PROJ-123)
. This could easily be extended so that multiple JIRA instances could be made for each project. In that case, you could have the user provide only the ticket number and pass in an argument to the script that specifies the project. This implementation also assumes that any ticket not in "Closed" status is invalid.
$ticket = $args[0]
$user = $args[1]
$password = $args[2]
$url = $args[3]
$closedStatus = "Closed"
$fields = "status"
$p = $password | ConvertTo-SecureString -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PsCredential($user,$p)
$getStatusMethod = "$url/rest/api/latest/issue/$ticket"
function ConvertTo-UnsecureString([System.Security.SecureString][parameter(mandatory=$true)]$SecurePassword)
{
$unmanagedString = [System.IntPtr]::Zero;
try
{
$unmanagedString = [Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($SecurePassword)
return [Runtime.InteropServices.Marshal]::PtrToStringUni($unmanagedString)
}
finally
{
[Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($unmanagedString)
}
}
function ConvertTo-Base64($string)
{
$bytes = [System.Text.Encoding]::UTF8.GetBytes($string);
$encoded = [System.Convert]::ToBase64String($bytes);
return $encoded;
}
function ConvertFrom-Base64($string)
{
$bytes = [System.Convert]::FromBase64String($string);
$decoded = [System.Text.Encoding]::UTF8.GetString($bytes);
return $decoded;
}
function Get-HttpBasicHeader($Credentials, $Headers = @{})
{
$b64 = ConvertTo-Base64 "$($Credentials.UserName):$(ConvertTo-UnsecureString $Credentials.Password)"
$Headers["Authorization"] = "Basic $b64"
return $Headers
}
try
{
$headers = Get-HttpBasicHeader $credentials
$response = Invoke-RestMethod -Method Get -uri $getStatusMethod -Headers $headers -ContentType 'application/json'
if($response.fields.status.name -eq $closedStatus)
{
throw "JIRA ticket ($ticket) is closed."
}
}
catch
{
$exception = $_.Exception
if ($exception.Response.StatusCode.value__ -eq 404)
{
throw "JIRA ticket ($ticket) does not exist."
}
}
Adding Comments to Tickets
To add comments to tickets, you will need to create the script below.
$ticket = $args[0]
$comment = $args[1]
$user = $args[2]
$password = $args[3]
$url = $args[4]
$p = $password | ConvertTo-SecureString -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PsCredential($user,$p)
function ConvertTo-UnsecureString([System.Security.SecureString][parameter(mandatory=$true)]$SecurePassword)
{
$unmanagedString = [System.IntPtr]::Zero;
try
{
$unmanagedString = [Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($SecurePassword)
return [Runtime.InteropServices.Marshal]::PtrToStringUni($unmanagedString)
}
finally
{
[Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($unmanagedString)
}
}
function ConvertTo-Base64($string)
{
$bytes = [System.Text.Encoding]::UTF8.GetBytes($string);
$encoded = [System.Convert]::ToBase64String($bytes);
return $encoded;
}
function ConvertFrom-Base64($string)
{
$bytes = [System.Convert]::FromBase64String($string);
$decoded = [System.Text.Encoding]::UTF8.GetString($bytes);
return $decoded;
}
function Get-HttpBasicHeader($Credentials, $Headers = @{})
{
$b64 = ConvertTo-Base64 "$($Credentials.UserName):$(ConvertTo-UnsecureString $Credentials.Password)"
$Headers["Authorization"] = "Basic $b64"
return $Headers
}
try
{
$updateObject = @{'body'=$comment}
$body = $updateObject | ConvertTo-Json
$addComment = "$url/rest/api/latest/issue/$ticket/comment"
$headers = Get-HttpBasicHeader $credentials
$response = Invoke-RestMethod -uri $addComment -Headers $headers -Method Post -ContentType "application/json" -Body $body
if ($response.body -ne $comment)
{
throw "There was an issue adding a comment to the ticket ($ticket)."
}
}
catch
{
$exception = $_.Exception
if ($exception.Response.StatusCode.value__ -eq 404)
{
throw "The ticket ($ticket) does not exist.";
}
if ($exception.Response.StatusCode.value__ -eq 400)
{
throw "There was an issue adding a comment to the ticket ($ticket)."
}
throw "There was an unhandled issue with adding a comment: " + $exception.ToString()
}