-
-
Save mwkoehler/8c6b93f7c6bb75d5664b65758023bc9d to your computer and use it in GitHub Desktop.
Powershell script to backup Asana
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Creates a backup of Asana projects | |
.DESCRIPTION | |
The script will get all Asana projects and writes the projects, tasks, and comments in XML format as a backup. | |
.PARAMETER FilePath | |
The file to which the backup will be written. | |
.PARAMETER Key | |
An Asana Personal Access Token that has rights to the data. | |
.EXAMPLE | |
Get-AsanaBackup -FilePath c:\temp\asana.xml -Key 'abcdefg' | |
This will save the backup to c:\temp\asana.xml | |
.EXAMPLE | |
Get-AsanaBackup -FilePath c:\temp\asana.xml -Key 'abcdefg' -Verbose | |
Enabling verbose output will display status on how the backup is proceeding. | |
#> | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory=$true,ValueFromPipeline=$true)] | |
[ValidateScript({Test-Path $(Split-Path $_ -Parent) -PathType 'Container'})] | |
[string] $FilePath, | |
[Parameter(Mandatory=$true,ValueFromPipeline=$false)] | |
[string] $Key | |
) | |
function Get-Asana($url, $apikey) | |
{ | |
$baseurl = "https://app.asana.com/api/1.0/" | |
$fullUrl = $baseurl + $url | |
$authorization = "Authorization: Bearer " + $apikey | |
$fastAPI = 'Asana-Fast-Api: true' | |
$request = [System.Net.WebRequest]::Create($fullUrl) | |
$request.Headers.Add($authorization) | |
$request.Headers.Add($fastAPI) | |
try | |
{ | |
$response = $request.GetResponse() | |
if ($response -ne $null) | |
{ | |
$StreamReader = New-Object System.IO.StreamReader $response.GetResponseStream() | |
$json = $StreamReader.ReadToEnd() | |
ConvertFrom-Json -InputObject $json | |
} | |
} | |
catch | |
{ | |
# Likely a time out. Sleep and re-try but first let people know. | |
$message = $_.Exception.Message | |
write-error "Exception $message" | |
write-error $url | |
start-sleep -seconds 1 | |
Get-Asana $url $apikey | |
} | |
} | |
function StripInvalidXmlCharacters([string] $str) | |
{ | |
$invalidXmlCharactersRegex = [Regex] "[^\u0009\u000a\u000d\u0020-\ufffd]|([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])" | |
$invalidXmlCharactersRegex.Replace($str, "") | |
} | |
function EscapeInvalidXmlCharacters([string] $str) | |
{ | |
$invalidXmlCharactersRegex = [Regex] "[^\u0009\u000a\u000d\u0020-\ufffd]|([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])" | |
$invalidXmlCharactersRegex.Replace($str, {CharToUnicodeSequence($args[0].Value)}) | |
} | |
function CharToUnicodeSequence([char] $symbol) | |
{ | |
"\u$(([int] $symbol).ToString("x4"))" | |
} | |
function ConvertTo-XMLString($object) | |
{ | |
$string = $object | convertto-xml -Depth 30 -NoTypeInformation -As String | |
$string -replace '<.xml version="1.0".>','' | |
} | |
# Tell .Net what the current directory is so the next call will work with relative paths | |
[System.IO.Directory]::SetCurrentDirectory(((Get-Location -PSProvider FileSystem).ProviderPath)) | |
$stream = [System.IO.StreamWriter] $FilePath | |
if ($stream -eq $null) | |
{ | |
write-error "$FilePath stream returned null. Aborting." | |
return # Something went wrong. The exception should have displayed. | |
} | |
if (-not $stream.BaseStream.CanWrite) | |
{ | |
write-error "$FilePath stream cannot be written to. Aborting." | |
return # Something went wrong. The exception should have displayed. | |
} | |
# Tell .Net to use TLS 1.2 | |
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 | |
$stream.WriteLine('<projects>') | |
$projects = Get-Asana 'projects' $Key | |
$projects.data | ForEach-Object { | |
$stream.WriteLine('<project>') | |
# each has an id and a name | |
$id = $_.id | |
$projectName = $_.name | |
Write-Verbose "Project $projectName" | |
$details = Get-Asana "projects/$id`?opt_expand=owner" $Key | |
$stream.WriteLine($(ConvertTo-XMLString $details)) | |
$stream.WriteLine('<sections>') | |
$sections = Get-Asana "projects/$id/sections" $Key | |
if ($sections -ne $null) | |
{ | |
$stream.WriteLine($(convertto-xmlstring $sections)) | |
} | |
$stream.WriteLine('</sections>') | |
$stream.WriteLine('<tasks>') | |
$tasks = Get-Asana "projects/$id/tasks" $Key | |
$tasks.data | ForEach-Object { | |
$stream.WriteLine("`n<task>") | |
$tid = $_.id | |
$tname = $_.name | |
Write-Verbose " Task $tname" | |
$task = Get-Asana "tasks/$tid" $Key | |
if ($task -ne $null) | |
{ | |
$stream.WriteLine($(convertto-xmlstring $task)) | |
} | |
$stream.WriteLine("<comments>") | |
$stories = Get-Asana "tasks/$tid/stories" $Key | |
if ($stories -ne $null) | |
{ | |
foreach ($story in $stories.data) | |
{ | |
$story.text = EscapeInvalidXmlCharacters $story.text | |
} | |
$stream.WriteLine($(convertto-xmlstring $stories)) | |
} | |
$stream.WriteLine('</comments>') | |
$stream.WriteLine('</task>') | |
} | |
$stream.WriteLine('</tasks>') | |
$stream.WriteLine('</project>') | |
} | |
$stream.WriteLine('</projects>') | |
$stream.Close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment