Skip to content

Instantly share code, notes, and snippets.

@mwkoehler
Last active April 24, 2017 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwkoehler/8c6b93f7c6bb75d5664b65758023bc9d to your computer and use it in GitHub Desktop.
Save mwkoehler/8c6b93f7c6bb75d5664b65758023bc9d to your computer and use it in GitHub Desktop.
Powershell script to backup Asana
<#
.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