Rest URL Is coming back as a Dead Link

Issue #15 resolved
Julian Governale created an issue

Did the REST Method URI change for JIRA? Im able to do my automated backups for Confluence but starting a couple weeks ago the JIRA backups have been failing via the powershell script. They were working for about a month before that then just stopped and now i get a dead link error. Seems its the reference to the css that is failing?

Error:

Invoke-RestMethod : <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Oops, you've found a dead link. - JIRA</title><script type="text/javascript">contextPath = "";</script><link type='text/css' rel='stylesheet' href='/static-assets/metal-all.css' media='all'><script src='/static-assets/metal-all.js'></script><!--[if lt IE 9]><link type='text/css' rel='stylesheet' href='/static-assets/metal-all-ie.css' media='all'><script src='/static-assets/metal-all-ie.js'></script><![endif]--><!--[if IE 9]><link type='text/css' rel='stylesheet' href='/static-assets/metal-all-ie9.css' media='all'><![endif]--><meta name="decorator" content="none" /></head><body class=" error-page error404"><script type="text/javascript">document.body.className += " js-enabled";</script><div id="page"><header id="header" role="banner"></header><!-- #header --><section id="content" role="main"><div class="aui-page-panel"><div class="aui-page-panel-inner"><section class="aui-page-panel-content lowerContent"><div id="error-state"><span class="error-type"></span><h1>Oops, you've found a dead link.</h1><ul><li>Go back to the <a href="javascript:window.history.back()">previous page</a></li><li>Go to the <a href="/secure/MyJiraHome.jspa">Home Page</a></li></ul></div></section><!-- .aui-page-panel-content --></div><!-- .aui-page-panel-inner --></div><!-- .aui-page-panel --></section><!-- #content --><footer id="footer" role="contentinfo"><section class="footer-body"><div id="footer-logo"><a href="http://www.atlassian.com/" rel="nofollow">Atlassian</a></div></section></footer><!-- #footer --></div><!-- #page --></body></html> At C:\scripts\JIRA\jirabackup.ps1:59 char:1 + Invoke-RestMethod -Method Post -Uri "https://$Account.atlassian.net/rest/obm/1.0 ... + ~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Write-Log "$($ScriptName)" "Triggering backup."
Invoke-RestMethod -Method Post -Uri "https://$Account.atlassian.net/rest/obm/1.0/runbackup" -WebSession $session -ContentType 'application/json' -Body (@{cbAttachments = $Attachments} | ConvertTo-Json -Compress) | Out-Null

Comments (52)

  1. Julian Governale reporter

    Thanks fir the response, ill review the Issue and see if i can get it working with the updates. Much appreciated.

  2. Matthew Urbano

    I'm trying with:

    Invoke-RestMethod -Method Post -Uri "https://$Account.atlassian.net/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body (@{cbAttachments="true"; "exportToCloud"="true"}| ConvertTo-Json -Compress) | Out-Null
    

    It gives: Invoke-RestMethod : The remote server returned an error: (412) Precondition Failed.

    Removing the convertTo-Json from the line provides this: Atlassian Cloud Notifications - Page Unavailable

                                Oops, something went wrong
    
                                    Please check Atlassian Status for known problems.
    
                                    If there are no known problems and your page hasn't appeared again in 5-10 minutes then please create a support request for assistance.
    
    
    
    
                                        Atlassian Status
    
                                    &nbsp;
    
                                        Create a support request
    
    
    
    
    
    
    
    
    
    
                        Atlassian Status
                        Release Summary
                        Documentation
                        Answers
    
                    Atlassian
    
  3. Matt Roblin

    I don't know if anyone has got further with this - having played around it looks like the 412 error is actually a response but the json coming back to tell you doesn't get thrown in Powershell ..

    I have taken the variables and converted them to a json for the body:

    [$body = @{
              cbAttachments='true'
              exportToCloud='true'
             }
    $bodyjson = $body | ConvertTo-Json
    

    and then had to force the response to be caught and read fully ..

    try {
    
    $InitiateBackup = Invoke-RestMethod -Method Post -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body $bodyjson -Verbose | ConvertTo-Json -Compress | Out-Null
    
    }
    catch {
            $InitiateBackup = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($InitiateBackup)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $responseBody = $reader.ReadToEnd();
    }
    
    $responseBody
    

    Then you actually can see the response - in this case I had already managed to trigger the backup so it is telling me I can't do so again .. but with a 412 Error as the response code.

    {"error":"Backup frequency is limited. You cannot make another backup right now. Approximate time until next allowed backup: 47h 43m"}
    

    Even then the rest of the script won't work as you then have to execute

    Invoke-WebRequest -Method Get -WebSession $session https://$hostname/rest/backup/1/export/lastTaskId
    

    to get the ID of the job, then run

    Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/internal/2/task/progress/{id goes here}" -WebSession $session
    

    to get this content back (once finished)

    {"mediaFileId":"bec08d76-e33c-xxxxxxxx-3542760","fileName":"jira-export34xxxxxxxxxxxxxxxxx9.zip","fileSize":56465}
    

    then you have to get this data into fields and submit those into another request to pull the backup .. which is what I am stuck on at the moment.

  4. Matt Roblin

    Managed to get it working albeit in an ugly fashion - the response from the ID is Json so using ConvertFrom-Json allows you to parameterise the fields and push them into the Get request. I have butchered the original script enough to get it working but can't test if the error handling works because it is now produced as a file and won't trigger the validation process but if anyone is stuck this is enough to get the download working again.

    # Wait for backup to finish
    do {
        $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/internal/2/task/progress/$LatestBackupID" -WebSession $session
        $statusoutput = $status.result
        $separator = ","
        $option = [System.StringSplitOptions]::None
        $s
    
        if ($status.progress -match "(\d+)") {
            $percentage = $Matches[1]
            if ([int]$percentage -gt 100) {
                $percentage = "100"
            }
            Write-Progress -Activity 'Creating backup' -Status $status.progress -PercentComplete $percentage
        }
        Start-Sleep -Seconds 5
    } while($status.status -ne 'Success')
    
    # Download
    if ([bool]($status.PSObject.Properties.Name -match "failedMessage")) {
        throw $status.failedMessage
    }
    
    $BackupDetails = $status.result | ConvertFrom-Json
    $JiraMediaID = $BackupDetails.mediaFileId
    $JiraFileName = $BackupDetails.fileName
    $BackupURI = "https://$hostname/plugins/servlet/export/download/$JiraMediaID/$JiraFileName" 
    
    Invoke-WebRequest -Method Get -Headers @{"Accept"="*/*"} -WebSession $session -Uri $BackupURI -OutFile (Join-Path -Path $destination -ChildPath "JIRA-backup-$today.zip")
    
  5. Matthew Urbano

    @mattroblin What's your # Request backup creation look like now? I see you're using $LatestBackupID in your #wait for backup to finish, but I don't see you having assigned it. I was stuck on pulling down the backupID

  6. Matt Roblin

    @vespene56 - looks like this .. I originally tried getting it to just use $GetBackupID.content in the next request but it wasn't happening so I had to create another variable for it to pass correctly..

    $GetBackupID = Invoke-WebRequest -Method Get -WebSession $session https://$hostname/rest/backup/1/export/lastTaskId
    $LatestBackupID = $GetBackupID.content
    
  7. Niall Hannon

    Would someone mind posting a full view of the code as I am struggling to get this working. It was working ok before Atlassian changed the endpoints and now I can't seem to get it working and I don't know powershell at all. Thanks

  8. Matt Roblin

    Here you go @niallhannon - hope it helps,

    $account     = 'youratlassianjira' # Atlassian subdomain i.e. whateverproceeds.atlassian.net
    $username    = 'youratlassianusername' # username without domain
    $password    = 'youratlassianpassword'
    $destination = 'C:\Backups' # Location on server where script is run to dump the backup zip file.
    $attachments = $true # Tells the script to pull down the attachments as well
    
    $hostname    = "$account.atlassian.net"
    $today       = Get-Date -format yyyyMMdd-hhmmss
    $credential  = New-Object System.Management.Automation.PSCredential($username, (ConvertTo-SecureString $password -AsPlainText -Force))
    
    $string = "cbAttachments:true, exportToCloud:true"
    $stringbinary = [system.Text.Encoding]::Default.GetBytes($String) | %{[System.Convert]::ToString($_,2).PadLeft(8,'0') }
    
    $body = @{
              cbAttachments='true'
              exportToCloud='true'
             }
    $bodyjson = $body | ConvertTo-Json
    
    if ($PSVersionTable.PSVersion.Major -lt 4) {
        throw "Script requires at least PowerShell version 4. Get it here: https://www.microsoft.com/en-us/download/details.aspx?id=40855"
    }
    
    # Login
    #Invoke-WebRequest -Method Post -Uri "https://$hostname/login" -SessionVariable session -Body @{username = $username; password = $password} | Out-Null
    # New Session
    #Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'"
    
    Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'
    
    
    
    # Request backup
    #Invoke-RestMethod -Method Post -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body @{cbAttachments="true"; "exportToCloud"="true"} | ConvertTo-Json -Compress | Out-Null
    
    try {
    
    $InitiateBackup = Invoke-RestMethod -Method Post -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body $bodyjson -Verbose | ConvertTo-Json -Compress | Out-Null
    
    }
    catch {
            $InitiateBackup = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($InitiateBackup)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $responseBody = $reader.ReadToEnd();
    }
    
    $responseBody
    
    $GetBackupID = Invoke-WebRequest -Method Get -WebSession $session https://$hostname/rest/backup/1/export/lastTaskId
    $LatestBackupID = $GetBackupID.content
    
    
    # Wait for backup to finish
    do {
        $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/internal/2/task/progress/$LatestBackupID" -WebSession $session
        $statusoutput = $status.result
        $separator = ","
        $option = [System.StringSplitOptions]::None
        $s
    
        if ($status.progress -match "(\d+)") {
            $percentage = $Matches[1]
            if ([int]$percentage -gt 100) {
                $percentage = "100"
            }
            Write-Progress -Activity 'Creating backup' -Status $status.progress -PercentComplete $percentage
        }
        Start-Sleep -Seconds 5
    } while($status.status -ne 'Success')
    
    # Download
    if ([bool]($status.PSObject.Properties.Name -match "failedMessage")) {
        throw $status.failedMessage
    }
    
    $BackupDetails = $status.result | ConvertFrom-Json
    $JiraMediaID = $BackupDetails.mediaFileId
    $JiraFileName = $BackupDetails.fileName
    $BackupURI = "https://$hostname/plugins/servlet/export/download/$JiraMediaID/$JiraFileName" 
    
    Invoke-WebRequest -Method Get -Headers @{"Accept"="*/*"} -WebSession $session -Uri $BackupURI -OutFile (Join-Path -Path $destination -ChildPath "JIRA-backup-$today.zip")
    
  9. Niall Hannon

    There are reports that the endpoints have changed again. I think the change is on this line ""https://$hostname/rest/internal/2/task/progress/$LatestBackupID"" where it checks for the status of the backup.

    Anyone able to confirm?

    Thanks

  10. Matt Roblin

    Hi @niallhannon - looks like they have ..

    The change you refer to now has to look like this:

    https://$hostname/rest/backup/1/export/getProgress?taskId=$LatestBackupID

    Which now creates a different response - you then have to tag this to the end of the URL rather than pluck out the ID so slightly easier.

    I have amended the script I posted the other day - looks like it is working for me. Let me know if this fixes the problem for you.

    Thanks Matt

    $account     = 'youratlassianjira' # Atlassian subdomain i.e. whateverproceeds.atlassian.net
    $username    = 'youratlassianusername' # username without domain
    $password    = 'youratlassianpassword'
    $destination = 'C:\Backups' # Location on server where script is run to dump the backup zip file.
    $attachments = $true # Tells the script to pull down the attachments as well
    
    $hostname    = "$account.atlassian.net"
    $today       = Get-Date -format yyyyMMdd-hhmmss
    $credential  = New-Object System.Management.Automation.PSCredential($username, (ConvertTo-SecureString $password -AsPlainText -Force))
    
    $string = "cbAttachments:true, exportToCloud:true"
    $stringbinary = [system.Text.Encoding]::Default.GetBytes($String) | %{[System.Convert]::ToString($_,2).PadLeft(8,'0') }
    
    $body = @{
              cbAttachments='true'
              exportToCloud='true'
             }
    $bodyjson = $body | ConvertTo-Json
    
    if ($PSVersionTable.PSVersion.Major -lt 4) {
        throw "Script requires at least PowerShell version 4. Get it here: https://www.microsoft.com/en-us/download/details.aspx?id=40855"
    }
    
    # Login
    #Invoke-WebRequest -Method Post -Uri "https://$hostname/login" -SessionVariable session -Body @{username = $username; password = $password} | Out-Null
    # New Session
    #Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'"
    
    Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'
    
    
    
    # Request backup
    #Invoke-RestMethod -Method Post -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body @{cbAttachments="true"; "exportToCloud"="true"} | ConvertTo-Json -Compress | Out-Null
    
    try {
    
    $InitiateBackup = Invoke-RestMethod -Method Post -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body $bodyjson -Verbose | ConvertTo-Json -Compress | Out-Null
    
    }
    catch {
            $InitiateBackup = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($InitiateBackup)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $responseBody = $reader.ReadToEnd();
    }
    
    $responseBody
    
    $GetBackupID = Invoke-WebRequest -Method Get -WebSession $session https://$hostname/rest/backup/1/export/lastTaskId
    $LatestBackupID = $GetBackupID.content
    
    
    # Wait for backup to finish
    do {
        $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/getProgress?taskId=$LatestBackupID" -WebSession $session
        $statusoutput = $status.result
        $separator = ","
        $option = [System.StringSplitOptions]::None
        $s
    
        if ($status.progress -match "(\d+)") {
            $percentage = $Matches[1]
            if ([int]$percentage -gt 100) {
                $percentage = "100"
            }
            Write-Progress -Activity 'Creating backup' -Status $status.progress -PercentComplete $percentage
        }
        Start-Sleep -Seconds 5
    } while($status.status -ne 'Success')
    
    # Download
    if ([bool]($status.PSObject.Properties.Name -match "failedMessage")) {
        throw $status.failedMessage
    }
    
    $BackupDetails = $status.result
    $BackupURI = "https://$hostname/plugins/servlet/$BackupDetails" 
    
    Invoke-WebRequest -Method Get -Headers @{"Accept"="*/*"} -WebSession $session -Uri $BackupURI -OutFile (Join-Path -Path $destination -ChildPath "JIRA-backup-$today.zip")
    
  11. Niall Hannon

    Thanks, I'll give that a go when we can next do a backup (just did one so cant do another one for 48 hours).

    Yes, just to confirm this script works perfectly, it executed this morning. I thought Atlassian enforced a minimum of 48 hours between data backup exporting but that wasn't blocked this morning for me anyways.

    Thanks @mattroblin again.

  12. Jonah Turnquist Account Deactivated

    Thanks for the fix Matt. I hope you don't mind, I updated the source code to reflect your fix. Indeed we updated these internal APIs, which caused these scripts to break.

    If you have any more fixes, feel free to open a pull request, and put me as a reviewer :)

    Best, Jonah Turnquist Atlassian, Migration Platform Team

  13. Julian Governale reporter

    Hi @jonah_turnquist

    How come we are only allowed one backup per 48 hours. My company hasn't taken backup now in almost 5 days as this end point has changed but it already thinks i tried to execute my backup even though it failed on the endpoint. I cant even test if this works honestly without wasting my backup try. Is there a way to allow as many backups a day and only a one back can be downloaded per 48 hours?

  14. Jonah Turnquist Account Deactivated

    Hey Julian,

    The best way to do that would be to open a support request at https://support.atlassian.com/contact/#/

    They will be able to reset the 48 hour timer when you request it.

    The reason for this is that backups are a highly memory and CPU intensive process - if we had people running backups too frequently, it could have negative affects on the service performance and reliability.

    Keep in mind that Atlassian also backs up your data on your behalf: https://confluence.atlassian.com/cloud/data-storage-faq-873871367.html#DatastorageFAQ-Howoftenisdatabackedup

  15. Stefan Engeli Account Deactivated

    Hi @mattroblin , I tried the script you posted and it doesnt get the error 401, instead it has a problem with the https connection. I try to translate from german: "The basic connection has been closed. No trust position could be created with the protected SSL/TLS connection." So I found something to reply that the connection is safe, but I dontt know where to put it in correctly: add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

    Can someone help me out or add this to the existing backup script please? Thanks & regards

  16. Matt Roblin

    Hi @CeDeAdmin - it looks like it is not able to establish the trust relationship for the Atlassian cert. Not entirely sure how I would test this as I don't get any issues. Are you running this from your machine or from a server? You may be able to resolve this by adding the atlassian certs (the *.atlassian.net and the intermediary / root certs) into the Trusted Root Certificate Authorities section of your machines certification store instead of trying to tell the script to unconditionally accept the connection regardless?

  17. Stefan Engeli Account Deactivated

    Hi @mattroblin - I'm running this on my PC with admin rights. Good idea, I added the ".atlassian.net certificate into the trusted sites now and it seems that it has not the problem I mentioned anymore, great! Now I just get stuck at the last Invoke-WebRequest at line 79 with message: "Atlassian Cloud Notifications - Page Unavailable" for "https://$hostname/plugins/servlet/$BackupDetails". Did you succeed with the same URL or is there again a change of the API endpoints made of Atlassian..?

  18. Matt Roblin

    Working fine for me - when it fails can you try $BackupDetails and see what the variable shows you? it is a part url so if it didn't populate on line 78 it would be an invalid url and the error would occur.

  19. chandrasekaran

    Hi, i tried with below script ( downloaded from bit bucket) and its not working fine and getting below error. please advice what is wrong in the URL? session


    @{name=cloud.session.token; value=eyJraWQiOiJzZXNzaW9uLXNlcnZpY2VcL3Nlc3Npb24tc2VydmljZSIsImFsZyI6IlJTMjU2In0.eyJhc3... VERBOSE: POST https://myjira.atlassian.net/rest/backup/1/export/runbackup with -1-byte payload {"error":"Backup frequency is limited. You cannot make another backup right now. Approximate time until next allowed bac kup: 47h 8m"} Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again. At E:\Jira_backup\new.ps1:51 char:16 + ... tBackupID = Invoke-WebRequest -Method Get -WebSession $session https: ... + ~~~~~~~~~~~~~ + CategoryInfo : NotImplemented: (:) [Invoke-WebRequest], NotSupportedException + FullyQualifiedErrorId : WebCmdletIEDomNotSupportedException,Microsoft.PowerShell.Commands.InvokeWebRequestComman d

    Invoke-RestMethod : The remote server returned an error: (500) Internal Server Error. At E:\Jira_backup\new.ps1:57 char:15 + ... $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="applic ... + ~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc eption + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

    Invoke-RestMethod : The remote server returned an error: (500) Internal Server Error. At E:\Jira_backup\new.ps1:57 char:15 + ... $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="applic ... + ~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc eption + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

    ------------------Script--------------------------

    $account = 'mycompany' # Atlassian subdomain i.e. whateverproceeds.atlassian.net $username = 'username' # username without domain $password = 'password' $destination = 'C:\Backups' # Location on server where script is run to dump the backup zip file. $attachments = $true # Tells the script to pull down the attachments as well

    $hostname = "$account.atlassian.net" $today = Get-Date -format yyyyMMdd-hhmmss $credential = New-Object System.Management.Automation.PSCredential($username, (ConvertTo-SecureString $password -AsPlainText -Force))

    $string = "cbAttachments:true, exportToCloud:true" $stringbinary = [system.Text.Encoding]::Default.GetBytes($String) | %{[System.Convert]::ToString($_,2).PadLeft(8,'0') }

    $body = @{ cbAttachments='true' exportToCloud='true' } $bodyjson = $body | ConvertTo-Json

    if ($PSVersionTable.PSVersion.Major -lt 4) { throw "Script requires at least PowerShell version 4. Get it here: https://www.microsoft.com/en-us/download/details.aspx?id=40855" }

    # Login #Invoke-WebRequest -Method Post -Uri "https://$hostname/login" -SessionVariable session -Body @{username = $username; password = $password} | Out-Null # New Session #Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'"

    Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'

    # Request backup #Invoke-RestMethod -Method Post -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body @{cbAttachments="true"; "exportToCloud"="true"} | ConvertTo-Json -Compress | Out-Null

    try {

    $InitiateBackup = Invoke-RestMethod -Method Post -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body $bodyjson -Verbose | ConvertTo-Json -Compress | Out-Null

    } catch { $InitiateBackup = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($InitiateBackup) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); }

    $responseBody

    $GetBackupID = Invoke-WebRequest -Method Get -WebSession $session https://$hostname/rest/backup/1/export/lastTaskId $LatestBackupID = $GetBackupID.content

    Wait for backup to finish

    do { $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/getProgress?taskId=$LatestBackupID" -WebSession $session $statusoutput = $status.result $separator = "," $option = [System.StringSplitOptions]::None $s

    if ($status.progress -match "(\d+)") {
        $percentage = $Matches[1]
        if ([int]$percentage -gt 100) {
            $percentage = "100"
        }
        Write-Progress -Activity 'Creating backup' -Status $status.progress -PercentComplete $percentage
    }
    Start-Sleep -Seconds 5
    

    } while($status.status -ne 'Success')

    Download

    if (bool) { throw $status.failedMessage }

    $BackupDetails = $status.result $BackupURI = "https://$hostname/plugins/servlet/$BackupDetails"

    Invoke-WebRequest -Method Get -Headers @{"Accept"="/"} -WebSession $session -Uri $BackupURI -OutFile (Join-Path -Path $destination -ChildPath "JIRA-backup-$today.zip")

  20. Niall Hannon

    The error returned say "Backup frequency is limited. You cannot make another backup right now. Approximate time until next allowed ba ckup: 47h 8m" - so that's fairly self explanatory?

  21. Julian Governale reporter

    You can request from bitBucket support to have them reset your request frequency to allow for you to retry the script. But yes, only allowed one download try per 48 hours.

  22. chandrasekaran

    hi @niallhannon , thanks for your quick reply.

    When i run the script, its trigger the backup and return above error I am blindly using this script. please clarify below points. 1. if the backup is running/ exist, will it not download directly ? 2. there are some other error after "Backup frequency is limited" message. is it normal behavior ?

    Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again. At E:\Jira_backup\new.ps1:51 char:16 + ... tBackupID = Invoke-WebRequest -Method Get -WebSession $session https: ... + ~~~~~ + CategoryInfo : NotImplemented: (:) [Invoke-WebRequest], NotSupportedException + FullyQualifiedErrorId : WebCmdletIEDomNotSupportedException,Microsoft.PowerShell.Commands.InvokeWebRequestComman d

    Invoke-RestMethod : The remote server returned an error: (500) Internal Server Error. At E:\Jira_backup\new.ps1:57 char:15 + ... $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="applic ... + ~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc eption + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

    Invoke-RestMethod : The remote server returned an error: (500) Internal Server Error. At E:\Jira_backup\new.ps1:57 char:15 + ... $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="applic ... + ~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc eption + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand If a backup already exist, will it skip the backup trigger process and download the backup directly ? 2.

  23. Niall Hannon

    This error:

    "Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete"

    What you need to do here is log onto the server where the script is running from (as the user that the scripts runs as) and simple open Internet Explorer and carry out the first-launch configuration as per the error message.

  24. chandrasekaran

    hi @niallhannon , thanks you. this issue is resolved.

    One more point : As i checked, attachments, avatars, and logos are missing in the backup file. when trigger backup manually, there is an option to sellect "Include additional files (attachments, avatars, and logos) in the backup" do we need to do any changes in the script?

  25. chandrasekaran

    Attachments, avatars, and logos are missing in the backup file. what need to change in the script to backup all data? please advice.

  26. Matt Roblin

    Did you set the $attachments = $true parameter? Not sure if this does avatars and logos but does attachments.

  27. chandrasekaran

    Hi @mattroblin Yes, I have this parameter. but still attachments is missing in my backup file. Please check the below script and advice if need any changes. - Thank you.


    $account = 'myjira' # Atlassian subdomain i.e. whateverproceeds.atlassian.net $username = 'Username' # username without domain $password = 'Password' $destination = 'E:\Jira_backup' # Location on server where script is run to dump the backup zip file. $attachments = $true # Tells the script whether or not to pull down the attachments as well

    $hostname = "$account.atlassian.net" $today = Get-Date -format yyyyMMdd-hhmmss $credential = New-Object System.Management.Automation.PSCredential($username, (ConvertTo-SecureString $password -AsPlainText -Force))

    $string = "cbAttachments:true, exportToCloud:true" $stringbinary = [system.Text.Encoding]::Default.GetBytes($String) | %{[System.Convert]::ToString($_,2).PadLeft(8,'0') }

    $body = @{ cbAttachments='false' exportToCloud='true' } $bodyjson = $body | ConvertTo-Json

    if ($PSVersionTable.PSVersion.Major -lt 4) { throw "Script requires at least PowerShell version 4. Get it here: https://www.microsoft.com/en-us/download/details.aspx?id=40855" }

    New session

    Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'

    Request backup

    try { $InitiateBackup = Invoke-RestMethod -Method Post -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/runbackup" -WebSession $session -ContentType 'application/json' -Body $bodyjson -Verbose | ConvertTo-Json -Compress | Out-Null } catch { $InitiateBackup = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($InitiateBackup) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); }

    $responseBody

    $GetBackupID = Invoke-WebRequest -Method Get -WebSession $session https://$hostname/rest/backup/1/export/lastTaskId $LatestBackupID = $GetBackupID.content

    Wait for backup to finish

    do { $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json"} -Uri "https://$hostname/rest/backup/1/export/getProgress?taskId=$LatestBackupID" -WebSession $session $statusoutput = $status.result $separator = "," $option = [System.StringSplitOptions]::None $s

    if ($status.progress -match "(\d+)") {
        $percentage = $Matches[1]
        if ([int]$percentage -gt 100) {
            $percentage = "100"
        }
        Write-Progress -Activity 'Creating backup' -Status $status.progress -PercentComplete $percentage
    }
    Start-Sleep -Seconds 5
    

    } while($status.status -ne 'Success')

    Download

    if (bool) { throw $status.failedMessage }

    $BackupDetails = $status.result $BackupURI = "https://$hostname/plugins/servlet/$BackupDetails"

    Invoke-WebRequest -Method Get -Headers @{"Accept"="/"} -WebSession $session -Uri $BackupURI -OutFile (Join-Path -Path $destination -ChildPath "JIRA-backup-$today.zip")

  28. Julian Governale reporter

    Hi Candrasekaran, The body has the attachments set to false.

    $body = @{ cbAttachments='false'

    Thanks, Julian Governale

  29. Michael Belos

    So how can we change this script to work with Confluence? I'm guessing there will only be some URL changes?

  30. Matt Roblin

    @michaelbelos - I don't think Confluence has had the same API changes as Jira (no doubt it will at some point) but it means the script for Confluence is different ..

    I can't remember where I found this one but it still seems to work.

    $account     = 'youratlassianjira' # Atlassian subdomain i.e. whateverproceeds.atlassian.net
    $username    = 'youratlassianusername' # username without domain
    $password    = 'youratlassianpassword'
    $destination = 'C:\Backups' # Location on server where script is run to dump the backup zip file.
    $attachments = $true # Tells the script to pull down the attachments as well
    
    $hostname    = "$account.atlassian.net"
    $today       = Get-Date -format yyyyMMdd-hhmmss
    $credential  = New-Object System.Management.Automation.PSCredential($username, (ConvertTo-SecureString $password -AsPlainText -Force))
    
    
    
    if ($PSVersionTable.PSVersion.Major -lt 4) {
        throw "Script requires at least PowerShell version 4. Get it here: https://www.microsoft.com/en-us/download/details.aspx?id=40855"
    }
    
    # Login
    #Invoke-WebRequest -Method Post -Uri "https://$hostname/login" -SessionVariable session -Body @{username = $username; password = $password} | Out-Null
    # New Session
    #Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'"
    
    Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://$hostname/rest/auth/1/session" -SessionVariable session -Body (@{username = $username; password = $password} | convertTo-Json -Compress) -ContentType 'application/json'
    
    
    
    # Request backup
    Invoke-RestMethod -Method Post -Uri "https://$hostname/wiki/rest/obm/1.0/runbackup" -WebSession $session -ContentType 'application/json' -Body (@{cbAttachments = $attachments} | ConvertTo-Json -Compress) | Out-Null
    
    # Wait for backup to finish
    do {
        $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json"} -Uri "https://$hostname/wiki/rest/obm/1.0/getprogress" -WebSession $session
        if ($status.alternativePercentage -match "(\d+)") {
            $percentage = $Matches[1]
            if ([int]$percentage -gt 100) {
                $percentage = "100"
            }
            Write-Progress -Activity 'Creating backup' -Status $status.alternativePercentage -PercentComplete $percentage
        }
        Start-Sleep -Seconds 5
    } while($status.alternativePercentage -ne '100%')
    
    # Download
    if ([bool]($status.PSObject.Properties.Name -match "failedMessage")) {
        throw $status.failedMessage
    }
    
    $pathName = $status.fileName
    if ($pathName -match "temp/filestore/(.*)") {
        $fileName = $Matches[1]
        Write-Host "Downloading: $fileName to Confluence-backup-$today.zip"
        $progressPreference = 'Continue'
        Invoke-WebRequest -Method Get -Headers @{"Accept"="*/*"} -WebSession $session -Uri "https://$hostname/wiki/download/$pathName" -OutFile (Join-Path -Path $destination -ChildPath "Confluence-backup-$today.zip")
    } else {
        throw "Attempted to download from WEBDAV directory, which is no longer supported"
    }
    
  31. Michael Belos

    Thanks heaps @mattroblin that seems to mostly work, although I get a HTTP code of 406 on the Invoke-RestMethod line (with the url: /wiki/rest/obm/1.0/runbackup). The backup seemed to download, so maybe this is safe to ignore?

  32. Matt Roblin

    @michaelbelos - just tried it and got the same thing this morning but tried it again and it didn't happen. I haven't seen this before to be honest so it may have been something on the Atlassian side, but as you say it doesn't seem to affect the backup being produced.

  33. Niall Hannon

    From December 2018 we wont be able to use basic authentication (username\password) for the api and have to use an API Token to authenticate instead. Has anyone got that working with the existing script above? Thanks

  34. Niall Hannon

    Thanks @dbonotto, I did but I was looking for a Powershell script. I'll see if I can figure out the Powershell equivalent syntax.

  35. Matt Roblin

    I had a quick look after I saw your post - from the looks of the deprecation notice it will stop the script from being able to use the session variable because the /rest/auth/1/session url is being killed off as well.

    I think this means you therefore have to pass an authentication header for each of the Invoke-RestMethods with a Base64 encoded string which isn't quite as elegant but you can at least use the username and password variables and then use Powershell to convert it as a Base64 string and then pass that to the different web calls.

    I will try and update my scripts and post something in the next few days.

  36. Matt Roblin

    @dbonotto - I think there might be an issue with one of the endpoints calling it with an api?

    I can initiate the backup and I can check the backup using a Base64 encoded username and api key but the one in the middle that retrieves the lasttaskid - /rest/backup/1/export/lastTaskId - does not work that way. It responds with an 204 error.

    I thought this might be a Powershell thing but I replicated it with a curl equivalent and is seems to do the same thing for me:

    curl -s -u ${EMAIL}:${API_TOKEN} -H "Accept: application/json" -H "Content-Type: application/json" -X GET https://${HOSTNAME}.atlassian.net/rest/backup/1/export/lastTaskId
    

    Would you mind seeing if you get the same behaviour?

    Thanks Matt

  37. Matt Roblin

    Hi @dbonotto - did you get a chance to try this? I need to know if I have to raise this as a valid bug in the api .. Thanks Matt

  38. Dario B

    @mattroblin I didn't have any chance to test this but I can see that in the API token script I am not using it anymore. I just get the task-id from the response when running the backup: - https://bitbucket.org/atlassianlabs/automatic-cloud-backup/src/master/backup-jira-api-token.sh

    Actually, the best way to see the new endpoint (if any) is to run a backup from the UI while running the browsers developer tools.

    If you don't know how to do this I can test this maybe tomorrow (hopefully it will be less busy than today :) )

  39. Matt Roblin

    @dbonotto - so it looks like it is pulling back the reference now so not sure if I had a typo or a stuck session before.

    Jira Backup with API

    $account     = 'youratlassianjira' # Atlassian subdomain i.e. whateverproceeds.atlassian.net
    $username    = 'youratlassianusername' # username without domain
    $apikey    = 'Wh4t3v3ry0ur4P1K3y15'
    $destination = 'C:\Backups' # Location on server where script is run to dump the backup zip file.
    $attachments = $true # Tells the script to pull down the attachments as well
    
    $hostname    = "$account.atlassian.net"
    $today       = Get-Date -format yyyyMMdd-hhmmss
    
    $string = "cbAttachments:true, exportToCloud:true"
    $stringbinary = [system.Text.Encoding]::Default.GetBytes($String) | %{[System.Convert]::ToString($_,2).PadLeft(8,'0') }
    
    $body = @{
              cbAttachments='true'
              exportToCloud='true'
             }
    $bodyjson = $body | ConvertTo-Json
    
    if ($PSVersionTable.PSVersion.Major -lt 4) {
        throw "Script requires at least PowerShell version 4. Get it here: https://www.microsoft.com/en-us/download/details.aspx?id=40855"
    }
    
    #Create Base64 String for username and API Key (https://confluence.atlassian.com/cloud/api-tokens-938839638.html)
    
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$apikey)))
    
    
    try {
    
    $InitiateBackup = Invoke-RestMethod -Method Post -Headers @{"Content-Type"="application/json";"Accept"="application/json";"Authorization"="Basic $base64AuthInfo"} -Uri "https://$hostname/rest/backup/1/export/runbackup" -Body $bodyjson -Verbose | ConvertTo-Json -Compress | Out-Null
    
    }
    catch {
            $InitiateBackup = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($InitiateBackup)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $responseBody = $reader.ReadToEnd();
    }
    
    $responseBody
    
    $GetBackupID = Invoke-WebRequest -Method Get -Headers @{"Authorization"="Basic $base64AuthInfo"} https://$hostname/rest/backup/1/export/lastTaskId?
    $LatestBackupID = $GetBackupID.content
    
    
    # Wait for backup to finish
    do {
        $status = Invoke-RestMethod -Method Get -Headers @{"Content-Type"="application/json";"Accept"="application/json";"Authorization"="Basic $base64AuthInfo"} -Uri "https://$hostname/rest/backup/1/export/getProgress?taskId=$LatestBackupID"
        $statusoutput = $status.result
        $separator = ","
        $option = [System.StringSplitOptions]::None
        $s
    
        if ($status.progress -match "(\d+)") {
            $percentage = $Matches[1]
            if ([int]$percentage -gt 100) {
                $percentage = "100"
            }
            Write-Progress -Activity 'Creating backup' -Status $status.progress -PercentComplete $percentage
        }
        Start-Sleep -Seconds 5
    } while($status.status -ne 'Success')
    
    # Download
    if ([bool]($status.PSObject.Properties.Name -match "failedMessage")) {
        throw $status.failedMessage
    }
    
    $BackupDetails = $status.result
    $BackupURI = "https://$hostname/plugins/servlet/$BackupDetails" 
    
    Invoke-WebRequest -Method Get -Headers @{"Content-Type"="application/json";"Accept"="application/json";"Authorization"="Basic $base64AuthInfo"} -Uri $BackupURI -OutFile (Join-Path -Path $destination -ChildPath "JIRA-backup-$today.zip")
    

    Confluence Backup with API

    $account     = 'youratlassianjira' # Atlassian subdomain i.e. whateverproceeds.atlassian.net
    $username    = 'youratlassianusername' # username without domain
    $apikey    = 'Wh4t3v3ry0ur4P1K3y15'
    $destination = 'C:\Backups' # Location on server where script is run to dump the backup zip file.
    $attachments = $true # Tells the script to pull down the attachments as well
    
    $hostname    = "$account.atlassian.net"
    $today       = Get-Date -format yyyyMMdd-hhmmss
    
    if ($PSVersionTable.PSVersion.Major -lt 4) {
        throw "Script requires at least PowerShell version 4. Get it here: https://www.microsoft.com/en-us/download/details.aspx?id=40855"
    }
    
    #Create Base64 String for username and API Key (https://confluence.atlassian.com/cloud/api-tokens-938839638.html)
    
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$apikey)))
    
    # Request backup
    
    try {
    
    $InitiateBackup = Invoke-RestMethod -Method Post -Uri "https://$hostname/wiki/rest/obm/1.0/runbackup" -Headers @{"Content-Type"="application/json";"Accept"="application/json";"Authorization"="Basic $base64AuthInfo"}  -Body (@{cbAttachments = $attachments} | ConvertTo-Json -Compress) | Out-Null
    
    
    }
    catch {
            $InitiateBackup = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($InitiateBackup)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $responseBody = $reader.ReadToEnd();
    }
    
    $responseBody
    
    # Wait for backup to finish
    do {
        $status = Invoke-RestMethod -Method Get -Headers @{"Accept"="application/json";"Authorization"="Basic $base64AuthInfo"} -Uri "https://$hostname/wiki/rest/obm/1.0/getprogress" 
        if ($status.alternativePercentage -match "(\d+)") {
            $percentage = $Matches[1]
            if ([int]$percentage -gt 100) {
                $percentage = "100"
            }
            Write-Progress -Activity 'Creating backup' -Status $status.alternativePercentage -PercentComplete $percentage
        }
        Start-Sleep -Seconds 5
    } while($status.alternativePercentage -ne '100%')
    
    # Download
    if ([bool]($status.PSObject.Properties.Name -match "failedMessage")) {
        throw $status.failedMessage
    }
    
    $pathName = $status.fileName
    if ($pathName -match "temp/filestore/(.*)") {
        $fileName = $Matches[1]
        Write-Host "Downloading: $fileName to Confluence-backup-$today.zip"
        $progressPreference = 'Continue'
        Invoke-WebRequest -Method Get -Headers @{"Accept"="*/*";"Authorization"="Basic $base64AuthInfo"} -Uri "https://$hostname/wiki/download/$pathName" -OutFile (Join-Path -Path $destination -ChildPath "Confluence-backup-$today.zip")
    } else {
        throw "Attempted to download from WEBDAV directory, which is no longer supported"
    }
    

    @niallhannon - if you wouldn't mind trying these out for me?

  40. Niall Hannon

    Hi @mattroblin,

    Thanks for the script. I am seeing an error on line 44: of the JIRA Backsup script.

    $GetBackupID = Invoke-WebRequest -Method Get -Headers @{"Authorization"="Basic $base64AuthInfo"} https://$hostname/rest/backup/1/export/lastTaskId?

    I get a 401 unauthorised error, anyone else getting that?

  41. Matt Roblin

    HI @niallhannon - I am not getting a 401 - did you create the API on the same account as you were using before so it had the same permissions?

  42. Niall Hannon

    It might be due to the 24 hours (or is it 48 hour limit) before another backup can be done. I will try again later today.

    Thanks

  43. Matt Roblin

    You shouldn't get a 401 for that - you do get an irritating 400 bad request however:

    {"error":"Backup frequency is limited. You cannot make another backup right now. Approximate time until next allowed backup: 3h 10m"} Invoke-RestMethod : The remote server returned an error: (400) Bad Request.

    It should still work - 401 suggests it doesn't like the authentication on the second request strangely.

  44. Niall Hannon

    Yeah looks like I had the wrong username in my script, I thought the script had kicked off a backup so assumed the credentials were right. But I obviously missed something. Thanks for your help @mattroblin

  45. Log in to comment