Use PowerShell to Work with SkyDrive for Powerful Automation

Use PowerShell to Work with SkyDrive for Powerful Automation

  • Comments 2
  • Likes

Summary: Microsoft PFE, Chris Wu, talks about creating powerful automation scenarios by using Windows PowerShell and SkyDrive.

Microsoft Scripting Guy, Ed Wilson, is here. Chris Wu is back with us today. I will let Chris dive right in…

On the Microsoft Download Center, you can download Live SDK v5.3, which contains APIs and a great deal of information about how to achieve common tasks. You can also find the information on the Live Connect documentation site. If you have stored a valid access token (by using wl.skydrive or wl.skydrive_update scope) in a variable (for example, $AccessToken), most of the tasks can be easily achieved with Windows PowerShell. For information about how to create a valid access token, read my previous post, Use PowerShell 3.0 to Get More Out of Windows Live.

A bit of background about the SkyDrive folder structure…

Each SkyDrive user has a unique ID, with a top-level folder usually named in the form "folder.<user id>". Files and folders also have unique IDs, which are composed of several parts including a keyword (such as "folder" or "file") and the owner’s unique ID. So, every file and folder in SkyDrive can be uniquely identified and individually accessed.

Browse SkyDrive folders

To access the current user’s top-level folder, simply invoke a Rest method. The JSON-formatted object that’s returned will show properties of the folder in a very familiar way:

$ApiUri = "https://apis.live.net/v5.0"

Invoke-RestMethod -Uri "$ApiUri/me/skydrive?access_token=$AccessToken"

Image of command output

There are some interesting properties such as ID, name, number of items, and permission. Among them, the upload_location property is actually the API URI through which we can enumerate or upload files. This example shows how to enumerate child items:

$Root = Invoke-RestMethod -Uri "$ApiUri/me/skydrive?access_token=$AccessToken"

$r = Invoke-RestMethod -Uri "$($Root.upload_location)?access_token=$AccessToken"

$r.data | ft Name, id, upload_location –AutoSize

Image of command output

This enables you to browse the hierarchy inside the SkyDrive.

Access SkyDrive folders through friendly names

Folders such as Documents, Pictures, and Public can be accessed through friendly names such as those shown in the following table:

Friendly Name

Folder

camera_roll

The camera roll folder

my_documents

The Documents folder

my_photos

The Pictures folder

public_documents

The Public folder


To get the Documents folder’s property, simply call:

$Docs = Invoke-RestMethod -Uri "$ApiUri/me/skydrive/my_documents?access_token=$AccessToken"

And to enumerate child items in Documents, call:

Invoke-RestMethod -Uri "$ApiUri/me/skydrive/my_documents/files?access_token=$AccessToken"

Upload and download files

Before uploading files, we need to make sure that the access token that originally was requested includes wl.skydrive_update scope. We can upload files only with proper permissions. It’s also good to check the available space on SkyDrive. Here is how to query the information:

Invoke-RestMethod -Uri "$ApiUri/me/skydrive/quota?access_token=$AccessToken"

The following screenshot shows that 7 GB is available:

Image of command output

Uploading a file can be accomplished through a Post request or Put request. The Put method is preferred whenever it’s applicable, although a small caveat is that the target file name on SkyDrive has to be specified in the request. The following example uploads a Windows PowerShell script from c:\temp\myscript.ps1 to the root on SkyDrive:

$fn = "myscript.ps1"

Invoke-RestMethod -Uri "$ApiUri/me/skydrive/files/$fn`?access_token=$AccessToken" -Method Put -InFile "c:\temp\$fn"

Image of command output

We can use any folder’s upload_location property as the destination to upload a file. We have a $Docs variable from previous tests, so the following script snippet uploads a binary file to the Documents folder on SkyDrive:

$fn="test.zip"

Invoke-RestMethod -Uri "$($Docs.upload_location)/$fn`?access_token=$AccessToken" -Method Put -InFile "C:\temp\$fn"

If a file with exactly the same name already exists in the target folder, it will be replaced with new content.

To download a file, we basically request the content of a file. Assuming a file’s ID has been determined through folder browsing or from the object that is returned when uploading it, the following command downloads the content of a text-based file:

$fileid = "file.6e8e4812f1211e8a.6E8E4812F1211E8A!205"

Invoke-RestMethod -Uri "$ApiUri/$fileid/content?access_token=$AccessToken"

Image of command output

The previous script snippet downloads a script and saves the code into a variable. This works great if the script is an ANSI-encoded text file. However, binary files and text files encoded in UTF8 may suffer data distortion due to the cmdlet’s attempt to parse the content. Saving the content directly into a local file can solve the problem:

Invoke-RestMethod -Uri "$ApiUri/$fileid/content?access_token=$AccessToken" -OutFile c:\temp\test2.zip

Other users’ files

So far, we have been accessing the folders and files of currently signed-in users. But the same syntax applies to folders and files for other user’s in SkyDrive that have been shared with you by the owner. The "me" portion in the URI used earlier is simply an alias for the current user, and it can be replaced with another user’s ID to browse those folders.

To get a current user’s ID, send a web request to "$ApiUri/me":

$UserID = (Invoke-RestMethod -Uri "$ApiUri/me?access_token=$AccessToken").id
With the user ID figured out, the following command can return all items shared by the user in the Public folder (assuming you have permission to access them):

(Invoke-RestMethod -Uri "$ApiUri/$UserID/skydrive/public_documents?access_token=$AccessToken").data | ft name, id -autosize

To wrap things up, I use the following script snippet to synchronize a SkyDrive folder (one-way sync) to a local disk:

# Function to sync all files from one SkyDrive Folder to a local Target

function Sync-SkyDriveFolder {

  Param(

    [PSObject]$Folder,

    [String]$Target,

    [Switch]$Recurse

  )

 

  (Invoke-RestMethod -Uri "$($Folder.upload_location)?access_token=$AccessToken").data | % {

    $Path = "$Target\$($_.Name)"

    if($_.type -eq "file") {

      if(!(Test-Path $Path) -or $_.size -ne (Get-Item $Path).Length) {

        Invoke-RestMethod -Uri "$($_.upload_location)?access_token=$AccessToken" -OutFile $Path

      }

    }

elseif ($Recurse) {

      if(!(Test-Path $Path)) { New-Item -Path $Path -ItemType Directory | Out-Null }

      Sync-SkyDriveFolder -Folder $_ -Target $Path -Recurse

    }

  }

}

 

$ApiUri = "https://apis.live.net/v5.0"

$Source = "Documents\Tools"  # Relative path of the SkyDrive folder

$Destination = "C:\Tools"    # Local folder to save files to

 

# Find the object representing the source folder on SkyDrive

$Folder = Invoke-RestMethod -Uri "$ApiUri/me/skydrive?access_token=$AccessToken"

foreach($s in $Source -split '[\\\/]') {

  if ($s = $s.Trim()) {

    $Folder = (Invoke-RestMethod -Uri "$($Folder.upload_location)?access_token=$AccessToken").data | ? { $_.name -eq $s }

    if(! $Folder) { return }

  }

}

 

# Start download recursively

Sync-SkyDriveFolder -Folder $Folder -Target $Destination -Recurse

Apparently, the script is pretty rough at this point. It checks identical files based only on size. It doesn’t confirm the validity of access tokens, which may lead to failure for a long-running task, and there is no error handling. But still, it serves its purpose pretty well. With further polish, it can even be a replacement desktop SkyDrive app for my Surface RT (or any computer running Windows RT).

~Chris

Thanks again for sharing, Chris. Join us tomorrow for more of Chris Wu and WindowsPowerShell.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • So, this is a good example, but doesn't work for other REST API's. How does one use Invoke-RESTMethod to upload a large binary file to a REST client supporting multipart/form-data POST?

  • Is there a way to do delta sync's? Similar to the Microsoft Office Uploader so that if we upload a file using powershell it will only sync changes if the file already exists?