{"id":3295,"date":"2013-07-02T00:01:00","date_gmt":"2013-07-02T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/07\/02\/use-powershell-to-work-with-skydrive-for-powerful-automation\/"},"modified":"2013-07-02T00:01:00","modified_gmt":"2013-07-02T00:01:00","slug":"use-powershell-to-work-with-skydrive-for-powerful-automation","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-to-work-with-skydrive-for-powerful-automation\/","title":{"rendered":"Use PowerShell to Work with SkyDrive for Powerful Automation"},"content":{"rendered":"<p><strong style=\"font-size: 12px\">Summary<\/strong><span style=\"font-size: 12px\">: Microsoft PFE, Chris Wu, talks about creating powerful automation scenarios by using Windows PowerShell and SkyDrive.<\/span><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/chris+wu\/\" target=\"_blank\">Chris Wu<\/a> is back with us today. I will let Chris dive right in&hellip;<\/p>\n<p>On the Microsoft Download Center, you can download <a href=\"http:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=35754\" target=\"_blank\">Live SDK v5.3<\/a>, which contains APIs and a great deal of information about how to achieve common tasks. You can also find the information on the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/live\/\" target=\"_blank\">Live Connect documentation<\/a> site. If you have stored a valid access token (by using <strong>wl.skydrive<\/strong> or <strong>wl.skydrive_update scope<\/strong>) in a variable (for example, <strong>$AccessToken<\/strong>), 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, <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2013\/07\/01\/use-powershell-3-0-to-get-more-out-of-windows-live.aspx\" target=\"_blank\">Use PowerShell 3.0 to Get More Out of Windows Live<\/a>.<\/p>\n<p>A bit of background about the SkyDrive folder structure&hellip;<\/p>\n<p>Each SkyDrive user has a unique ID, with a top-level folder usually named in the form &#8220;<strong>folder.<em>&lt;user id&gt;<\/em><\/strong>&#8220;. Files and folders also have unique IDs, which are composed of several parts including a keyword (such as &#8220;folder&#8221; or &#8220;file&#8221;) and the owner&rsquo;s unique ID. So, every file and folder in SkyDrive can be uniquely identified and individually accessed.<\/p>\n<h2>Browse SkyDrive folders<\/h2>\n<p>To access the current user&rsquo;s top-level folder, simply invoke a <strong>Rest<\/strong> method. The JSON-formatted object that&rsquo;s returned will show properties of the folder in a very familiar way:<\/p>\n<p style=\"padding-left: 30px\">$ApiUri = &#8220;https:\/\/apis.live.net\/v5.0&#8221;<\/p>\n<p style=\"padding-left: 30px\">Invoke-RestMethod -Uri &#8220;$ApiUri\/me\/skydrive?access_token=$AccessToken&#8221;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8787.hsg-7-2-13-1.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8787.hsg-7-2-13-1.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>There are some interesting properties such as ID, name, number of items, and permission. Among them, the <strong>upload_location<\/strong> property is actually the API URI through which we can enumerate or upload files. This example shows how to enumerate child items:<\/p>\n<p style=\"padding-left: 30px\">$Root = Invoke-RestMethod -Uri &#8220;$ApiUri\/me\/skydrive?access_token=$AccessToken&#8221;<\/p>\n<p style=\"padding-left: 30px\">$r = Invoke-RestMethod -Uri &#8220;$($Root.upload_location)?access_token=$AccessToken&#8221;<\/p>\n<p style=\"padding-left: 30px\">$r.data | ft Name, id, upload_location &ndash;AutoSize<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1385.hsg-7-2-13-2.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1385.hsg-7-2-13-2.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>This enables you to browse the hierarchy inside the SkyDrive.<\/p>\n<h2>Access SkyDrive folders through friendly names<\/h2>\n<p>Folders such as Documents, Pictures, and Public can be accessed through friendly names such as those shown in the following table:<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"132\">\n<p><strong>Friendly Name<\/strong><\/p>\n<\/td>\n<td valign=\"top\" width=\"210\">\n<p><strong>Folder<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"132\">\n<p>camera_roll<\/p>\n<\/td>\n<td valign=\"top\" width=\"210\">\n<p>The camera roll folder<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"132\">\n<p>my_documents<\/p>\n<\/td>\n<td valign=\"top\" width=\"210\">\n<p>The Documents folder<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"132\">\n<p>my_photos<\/p>\n<\/td>\n<td valign=\"top\" width=\"210\">\n<p>The Pictures folder<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"132\">\n<p>public_documents<\/p>\n<\/td>\n<td valign=\"top\" width=\"210\">\n<p>The Public folder<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p> To get the Documents folder&rsquo;s property, simply call:<\/p>\n<p style=\"padding-left: 30px\">$Docs = Invoke-RestMethod -Uri &#8220;$ApiUri\/me\/skydrive\/my_documents?access_token=$AccessToken&#8221;<\/p>\n<p>And to enumerate child items in Documents, call:<\/p>\n<p style=\"padding-left: 30px\">Invoke-RestMethod -Uri &#8220;$ApiUri\/me\/skydrive\/my_documents\/files?access_token=$AccessToken&#8221;<\/p>\n<h2>Upload and download files<\/h2>\n<p>Before uploading files, we need to make sure that the access token that originally was requested includes&nbsp;<strong>wl.skydrive_update scope<\/strong>. We can upload files only with proper permissions. It&rsquo;s also good to check the available space on SkyDrive. Here is how to query the information:<\/p>\n<p style=\"padding-left: 30px\">Invoke-RestMethod -Uri &#8220;$ApiUri\/me\/skydrive\/quota?access_token=$AccessToken&#8221;<\/p>\n<p>The following screenshot shows that 7&nbsp;GB is available:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7827.hsg-7-2-13-3.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7827.hsg-7-2-13-3.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Uploading a file can be accomplished through a <strong>Post<\/strong> request or <strong>Put<\/strong> request. The <strong>Put<\/strong> method is preferred whenever it&rsquo;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:<\/p>\n<p style=\"padding-left: 30px\">$fn = &#8220;myscript.ps1&#8221;<\/p>\n<p style=\"padding-left: 30px\">Invoke-RestMethod -Uri &#8220;$ApiUri\/me\/skydrive\/files\/$fn`?access_token=$AccessToken&#8221; -Method Put -InFile &#8220;c:\\temp\\$fn&#8221;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4617.hsg-7-2-13-4.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4617.hsg-7-2-13-4.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>We can use any folder&rsquo;s <strong>upload_location<\/strong> property as the destination to upload a file. We have a <strong>$Docs<\/strong> variable from previous tests, so the following script snippet uploads a binary file to the Documents folder on SkyDrive:<\/p>\n<p style=\"padding-left: 30px\">$fn=&#8221;test.zip&#8221;<\/p>\n<p style=\"padding-left: 30px\">Invoke-RestMethod -Uri &#8220;$($Docs.upload_location)\/$fn`?access_token=$AccessToken&#8221; -Method Put -InFile &#8220;C:\\temp\\$fn&#8221;<\/p>\n<p>If a file with exactly the same name already exists in the target folder, it will be replaced with new content.<\/p>\n<p>To download a file, we basically request the content of a file. Assuming a file&rsquo;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:<\/p>\n<p style=\"padding-left: 30px\">$fileid = &#8220;file.6e8e4812f1211e8a.6E8E4812F1211E8A!205&#8221;<\/p>\n<p style=\"padding-left: 30px\">Invoke-RestMethod -Uri &#8220;$ApiUri\/$fileid\/content?access_token=$AccessToken&#8221;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6266.hsg-7-2-13-5.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6266.hsg-7-2-13-5.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>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&rsquo;s attempt to parse the content. Saving the content directly into a local file can solve the problem:<\/p>\n<p style=\"padding-left: 30px\">Invoke-RestMethod -Uri &#8220;$ApiUri\/$fileid\/content?access_token=$AccessToken&#8221; -OutFile c:\\temp\\test2.zip<\/p>\n<h2>Other users&rsquo; files<\/h2>\n<p>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&rsquo;s in SkyDrive that have been shared with you by the owner. The &#8220;me&#8221; portion in the URI used earlier is simply an alias for the current user, and it can be replaced with another user&rsquo;s ID to browse those folders.<\/p>\n<p>To get a current user&rsquo;s ID, send a web request to &#8220;$ApiUri\/me&#8221;:<\/p>\n<p style=\"padding-left: 30px\">$UserID = (Invoke-RestMethod -Uri &#8220;$ApiUri\/me?access_token=$AccessToken&#8221;).id<br \/> 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):<\/p>\n<p style=\"padding-left: 30px\">(Invoke-RestMethod -Uri &#8220;$ApiUri\/$UserID\/skydrive\/public_documents?access_token=$AccessToken&#8221;).data | ft name, id -autosize<\/p>\n<p>To wrap things up, I use the following script snippet to synchronize a SkyDrive folder (one-way sync) to a local disk:<\/p>\n<p style=\"padding-left: 30px\"># Function to sync all files from one SkyDrive Folder to a local Target<\/p>\n<p style=\"padding-left: 30px\">function Sync-SkyDriveFolder {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [PSObject]$Folder,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [String]$Target,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Switch]$Recurse<\/p>\n<p style=\"padding-left: 30px\">&nbsp; )<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; (Invoke-RestMethod -Uri &#8220;$($Folder.upload_location)?access_token=$AccessToken&#8221;).data | % {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $Path = &#8220;$Target\\$($_.Name)&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; if($_.type -eq &#8220;file&#8221;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!(Test-Path $Path) -or $_.size -ne (Get-Item $Path).Length) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Invoke-RestMethod -Uri &#8220;$($_.upload_location)?access_token=$AccessToken&#8221; -OutFile $Path<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">elseif ($Recurse) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!(Test-Path $Path)) { New-Item -Path $Path -ItemType Directory | Out-Null }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sync-SkyDriveFolder -Folder $_ -Target $Path -Recurse<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$ApiUri = &#8220;https:\/\/apis.live.net\/v5.0&#8221;<\/p>\n<p style=\"padding-left: 30px\">$Source = &#8220;Documents\\Tools&#8221;&nbsp; # Relative path of the SkyDrive folder<\/p>\n<p style=\"padding-left: 30px\">$Destination = &#8220;C:\\Tools&#8221;&nbsp;&nbsp;&nbsp; # Local folder to save files to<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># Find the object representing the source folder on SkyDrive<\/p>\n<p style=\"padding-left: 30px\">$Folder = Invoke-RestMethod -Uri &#8220;$ApiUri\/me\/skydrive?access_token=$AccessToken&#8221;<\/p>\n<p style=\"padding-left: 30px\">foreach($s in $Source -split &#8216;[\\\\\\\/]&#8217;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; if ($s = $s.Trim()) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $Folder = (Invoke-RestMethod -Uri &#8220;$($Folder.upload_location)?access_token=$AccessToken&#8221;).data | ? { $_.name -eq $s }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; if(! $Folder) { return }<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># Start download recursively<\/p>\n<p style=\"padding-left: 30px\">Sync-SkyDriveFolder -Folder $Folder -Target $Destination -Recurse<\/p>\n<p>Apparently, the script is pretty rough at this point. It checks identical files based only on size. It doesn&rsquo;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).<\/p>\n<p>~Chris<\/p>\n<p>Thanks again for sharing, Chris. Join us tomorrow for more of Chris Wu and WindowsPowerShell.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong><span style=\"font-size: 12px\">&nbsp;<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&hellip; On the Microsoft Download Center, you can download Live SDK v5.3, which contains APIs and a great [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[302,56,434,3,12,167,45],"class_list":["post-3295","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-chris-wu","tag-guest-blogger","tag-invoke-restmethod","tag-scripting-guy","tag-storage","tag-using-the-internet","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>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&hellip; On the Microsoft Download Center, you can download Live SDK v5.3, which contains APIs and a great [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3295","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=3295"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3295\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=3295"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=3295"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=3295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}