{"id":3301,"date":"2013-07-01T00:01:00","date_gmt":"2013-07-01T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/07\/01\/use-powershell-3-0-to-get-more-out-of-windows-live\/"},"modified":"2013-07-01T00:01:00","modified_gmt":"2013-07-01T00:01:00","slug":"use-powershell-3-0-to-get-more-out-of-windows-live","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-3-0-to-get-more-out-of-windows-live\/","title":{"rendered":"Use PowerShell 3.0 to Get More Out of Windows Live"},"content":{"rendered":"<p><strong style=\"font-size: 12px\">Summary<\/strong><span style=\"font-size: 12px\">: Microsoft PFE, Chris Wu, talks about using Windows PowerShell 3.0 to automate Windows Live.<\/span><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Welcome back to guest blogger, <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/chris+wu\/\" target=\"_blank\">Chris Wu<\/a>. Take it away Chris&hellip;<\/p>\n<p>One advantage of Windows PowerShell that I&rsquo;ve really enjoyed is the ability to test various APIs at ease. There is no need for Visual Studio, serious UI design, or commitment to a project. Windows PowerShell makes it possible to experiment and understand the core logics of different API systems through line-by-line executions.<\/p>\n<p>Windows Live is one such system I always wanted to play with&mdash;more specifically, SkyDrive and Outlook contacts and calendar. Let&rsquo;s say I want to share a script with the public, but keep the configuration file that&rsquo;s specific to my environment (such as my machine name, IP addresses, and credentials) safe and portable through SkyDrive. Although the desktop SkyDrive application can fill the bill by syncing files from the cloud to computers I own (hence they become easily accessible), there are also cases where this practice is inconvenient or unsafe.<\/p>\n<p>Microsoft released <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/live\/ff621314.aspx\" target=\"_blank\">Live SDK 5.3<\/a> in 2012. It provides a set of APIs for a variety of application platforms to integrate Windows Live services. Windows Store apps, Windows Phone apps, iOS, and Android apps have their own APIs. Desktop and mobile applications (the category that Windows PowerShell scripts fall in) can use the Live Connect Representational State Transfer (REST) API to programmatically achieve the same.<\/p>\n<p>Before any coding work, we need to register our script as an application on the <a href=\"http:\/\/go.microsoft.com\/fwlink\/p\/?LinkId=193157\" target=\"_blank\">Live Connect app management site<\/a>. Follow these three simple steps:<\/p>\n<ul>\n<li>Go to the site and sign in with your Microsoft account credentials.<\/li>\n<li>Click <strong>Create application<\/strong>, give the application a display name and primary language, and read and accept Terms of Use and privacy statement.<\/li>\n<li>In the application&rsquo;s API setting, leave the <strong>Redirect domain<\/strong> field blank, and select <strong>Yes<\/strong> for <strong>Mobile client app<\/strong>.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4035.hsg-7-1-13-1.png\"><img decoding=\"async\" title=\"Image of menu\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4035.hsg-7-1-13-1.png\" alt=\"Image of menu\" \/><\/a><\/p>\n<p>That last step is very important because our script will use a special redirect URL after user authentication, and a different OAuth 2.0 authentication flow than other applications.<\/p>\n<p>When you register, you will get the following IDs:<\/p>\n<ul>\n<li>A client ID that looks like this: 00000000603E0BFE<\/li>\n<li>A secret ID that looks like this: qXipuPomaauItsIsmwtKZ2YacGZtCyXD<\/li>\n<\/ul>\n<p>Both are unique to the application. The secret ID should be kept in a safe place, or it may be misused by malicious scripts disguising to be yours. (You can change the client ID afterwards.) Keep these two pieces of information handy, and we will use them in a moment.<\/p>\n<p>Our script needs to access user information; so first, we need to allow the user to sign in and give consent. We&rsquo;ll do so by hosting a WebBrowser control to direct the user to the Microsoft authorization page. After the user successfully signs in and accepts the scope of information the script can access, we get an access token by reading the URI that the user is redirected to. More information about this process can be found on the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/live\/hh826528.aspx\" target=\"_blank\">Live SDK Core Concepts<\/a> site. Depending on the scenario, one of the following OAuth 2.0 grant flows can be used:<\/p>\n<ul>\n<li><strong>Implicit grant flow<\/strong>: ideal for a public environment where explicit user sign-in and consent is required<\/li>\n<li><strong>Authorization code grant flow<\/strong>: ideal for automation in a safe environment<\/li>\n<\/ul>\n<h3>Implicit grant flow<\/h3>\n<p>Specifying <strong>response_type=token<\/strong> when sending the user to the authorization page starts an implicit grant flow. After the user signs in, the Live Connect authorization server redirects the WebBrowser control to a special URI with an access token and its period of validity (usually 3600 seconds). A script can then access authorized information by using the access token.<\/p>\n<p style=\"padding-left: 30px\">$ClientID = &#8220;00000000603E0BFE&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$RedirectUri = &#8220;https:\/\/login.live.com\/oauth20_desktop.srf&#8221;<\/p>\n<p style=\"padding-left: 30px\">$AuthorizeUri = &#8220;https:\/\/login.live.com\/oauth20_authorize.srf&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$Scope = &#8220;wl.skydrive&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">#region &#8211; Implicit grant flow<\/p>\n<p style=\"padding-left: 30px\">Add-Type -AssemblyName System.Windows.Forms<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$OnDocumentCompleted = {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; if($web.Url.AbsoluteUri -match &#8220;access_token=([^&amp;]*)&#8221;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $script:AccessToken = $Matches[1]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; if($web.Url.AbsoluteUri -match &#8220;expires_in=([^&amp;]*)&#8221;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $script:ValidThru = (get-date).AddSeconds([int]$Matches[1])<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $form.Close()<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp; elseif($web.Url.AbsoluteUri -match &#8220;error=&#8221;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $form.Close()<\/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\">$web = new-object System.Windows.Forms.WebBrowser -Property @{Width=400;Height=500}<\/p>\n<p style=\"padding-left: 30px\">$web.Add_DocumentCompleted($OnDocumentCompleted)<\/p>\n<p style=\"padding-left: 30px\">$form = new-object System.Windows.Forms.Form -Property @{Width=400;Height=500}<\/p>\n<p style=\"padding-left: 30px\">$form.Add_Shown({$form.Activate()})<\/p>\n<p style=\"padding-left: 30px\">$form.Controls.Add($web)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$web.Navigate(&#8220;$AuthorizeUri`?client_id=$ClientID&amp;scope=$Scope&amp;response_type=token&amp;redirect_uri=$RedirectUri&#8221;)<\/p>\n<p style=\"padding-left: 30px\">$null = $form.ShowDialog()<\/p>\n<p style=\"padding-left: 30px\">#endregion<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4454.hsg-7-1-13-2.png\"><img decoding=\"async\" title=\"Image of menu\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4454.hsg-7-1-13-2.png\" alt=\"Image of menu\" \/><\/a><\/p>\n<p>The previous script snippet defines a scope of <strong>wl.skydrive<\/strong>, which will request access to user&rsquo;s SkyDrive data in Read-only mode. The previous image show what the user will see when running the code. If a script wants to have Write access to SkyDrive and also utilize a single sign-on (so users don&rsquo;t have to type a user name and password if they are already signed-in with other applications that use a Microsoft ID), the scope can be changed to:<\/p>\n<p style=\"padding-left: 30px\">$Scope = &#8220;wl.skydrive_update&#8221;,&#8221;wl.signin&#8221; -join &#8220;%20&#8221;<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong>&nbsp;&nbsp;&nbsp;The space between multiple scope and permission strings is encoded because it will be used as part of the URI.<\/p>\n<p>After we get an access token (saved in the <strong>$AccessToken<\/strong> variable), we can start browsing SkyDrive and so on:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7444.hsg-7-1-13-3.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7444.hsg-7-1-13-3.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<h3>Authorization code grant flow<\/h3>\n<p>In this work flow, two steps are required to retrieve an access token. First, sign in the user with <strong>response_type=code<\/strong>, which will return an authorization code.<\/p>\n<p>In the second step, a script needs to invoke a <strong>Rest<\/strong> method with the client secret and authorization code to get a temporary access token. This access token, like that in the previous scenario, is valid for only one hour. However, if the script is granted offline access permission, <strong>refresh_token<\/strong> will also be provided in the response, which enables a script to renew the access token and visit Windows Live services for a longer period of time without a user&rsquo;s explicit consent on every run.<\/p>\n<p>The following script snippet shows how it&rsquo;s done.<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong>&nbsp;&nbsp;&nbsp;This snippet requires Windows PowerShell&nbsp;3.0 because it uses the <strong>Invoke-RestMethod<\/strong> cmdlet.<\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: 12px\">$ClientID = &#8220;00000000603E0BFE&#8221;<\/span><\/p>\n<p style=\"padding-left: 30px\">$Secret = &#8220;qXipuPomaauItsIsmwtKZ2YacGZtCyXD&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$RedirectUri = &#8220;https:\/\/login.live.com\/oauth20_desktop.srf&#8221;<\/p>\n<p style=\"padding-left: 30px\">$AuthorizeUri = &#8220;https:\/\/login.live.com\/oauth20_authorize.srf&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$Scope = &#8220;wl.skydrive_update&#8221;,&#8221;wl.signin&#8221;,&#8221;wl.offline_access&#8221; -join &#8220;%20&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">#region &#8211; Authorization code grant flow&#8230;<\/p>\n<p style=\"padding-left: 30px\">Add-Type -AssemblyName System.Windows.Forms<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$OnDocumentCompleted = {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; if($web.Url.AbsoluteUri -match &#8220;code=([^&amp;]*)&#8221;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $script:AuthCode = $Matches[1]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $form.Close()<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp; elseif($web.Url.AbsoluteUri -match &#8220;error=&#8221;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $form.Close()<\/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\">$web = new-object System.Windows.Forms.WebBrowser -Property @{Width=400;Height=500}<\/p>\n<p style=\"padding-left: 30px\">$web.Add_DocumentCompleted($OnDocumentCompleted)<\/p>\n<p style=\"padding-left: 30px\">$form = new-object System.Windows.Forms.Form -Property @{Width=400;Height=500}<\/p>\n<p style=\"padding-left: 30px\">$form.Add_Shown({$form.Activate()})<\/p>\n<p style=\"padding-left: 30px\">$form.Controls.Add($web)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># Request Authorization Code<\/p>\n<p style=\"padding-left: 30px\">$web.Navigate(&#8220;$AuthorizeUri`?client_id=$ClientID&amp;scope=$Scope&amp;response_type=code&amp;redirect_uri=$RedirectUri&#8221;)<\/p>\n<p style=\"padding-left: 30px\">$null = $form.ShowDialog()<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># Request AccessToken<\/p>\n<p style=\"padding-left: 30px\">$Response = Invoke-RestMethod -Uri &#8220;https:\/\/login.live.com\/oauth20_token.srf&#8221; -Method Post -ContentType &#8220;application\/x-www-form-urlencoded&#8221; -Body &#8220;client_id=$ClientID&amp;redirect_uri=$RedirectUri&amp;client_secret=$Secret&amp;code=$AuthCode&amp;grant_type=authorization_code&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$AccessToken = $Response.access_token<\/p>\n<p style=\"padding-left: 30px\">$ValidThru = (get-date).AddSeconds([int]$Response.expires_in)<\/p>\n<p style=\"padding-left: 30px\">$RefreshToken = $Response.refresh_token<\/p>\n<p style=\"padding-left: 30px\">#endregion<\/p>\n<p style=\"padding-left: 30px\">Apparently, the previous snippet discloses the client secret, so it should only be used in a secure environment. When the time comes to refresh the access token, another <strong>Rest<\/strong> method needs to be called:<\/p>\n<p style=\"padding-left: 30px\"># Refresh AccessToken<\/p>\n<p style=\"padding-left: 30px\">$Response = Invoke-RestMethod -Uri &#8220;https:\/\/login.live.com\/oauth20_token.srf&#8221; -Method Post -ContentType &#8220;application\/x-www-form-urlencoded&#8221; -Body &#8220;client_id=$ClientID&amp;redirect_uri=$RedirectUri&amp;grant_type=refresh_token&amp;refresh_token=$RefreshToken&#8221;<\/p>\n<p style=\"padding-left: 30px\">$AccessToken = $Response.access_token<\/p>\n<p style=\"padding-left: 30px\">$ValidThru = (get-date).AddSeconds([int]$Response.expires_in)<\/p>\n<p style=\"padding-left: 30px\">$RefreshToken = $Response.refresh_token<\/p>\n<p>One benefit of authorization code grant flow is that once the initial authorization code is acquired, the rest of the process doesn&rsquo;t need to host a WebBrowser control. This makes it possible to run on the Windows RT operating system. Due to restrictions in Windows RT, Windows PowerShell cannot host a console.<\/p>\n<p>A feasible workaround is to launch an Internet Explorer window to complete the sign-in and authorization process, and then manually copy the redirected URI that contains the authorization code to a Windows PowerShell window so that the script can continue. Admittedly, this involves manual switching between applications; hence, it is not an automated process. However, after we pass that step, refreshing the access token can be fully scripted&mdash;as long as you remember to always save the ever-changing refresh token.<\/p>\n<p>The following script snippet shows how to get a valid <strong>$AccessToken<\/strong> and the initial <strong>$RefreshToken<\/strong> in Windows RT:<\/p>\n<p style=\"padding-left: 30px\">$ClientID = &#8220;00000000603E0BFE&#8221;<\/p>\n<p style=\"padding-left: 30px\">$Secret = &#8220;qXipuPomaauItsIsmwtKZ2YacGZtCyXD&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$RedirectUri = &#8220;https:\/\/login.live.com\/oauth20_desktop.srf&#8221;<\/p>\n<p style=\"padding-left: 30px\">$AuthorizeUri = &#8220;https:\/\/login.live.com\/oauth20_authorize.srf&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$Scope = &#8220;wl.skydrive_update&#8221;,&#8221;wl.signin&#8221;,&#8221;wl.offline_access&#8221; -join &#8220;%20&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">#region &#8211; Authorization code grant flow for RT&#8230;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># Request Authorization Code in IE<\/p>\n<p style=\"padding-left: 30px\">Start-Process &#8220;$AuthorizeUri`?client_id=$ClientID&amp;scope=$Scope&amp;response_type=code&amp;redirect_uri=$RedirectUri&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$Uri = Read-Host &#8220;After authorization, copy Uri from IE and paste here&#8221;<\/p>\n<p style=\"padding-left: 30px\">if ($Uri -match &#8220;code=([^&amp;]*)&#8221;) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $AuthCode = $Matches[1]<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># Request AccessToken<\/p>\n<p style=\"padding-left: 30px\">$Response = Invoke-RestMethod -Uri &#8220;https:\/\/login.live.com\/oauth20_token.srf&#8221; -Method Post -ContentType &#8220;application\/x-www-form-urlencoded&#8221; `<\/p>\n<p style=\"padding-left: 30px\">&nbsp; -Body &#8220;client_id=$ClientID&amp;redirect_uri=$RedirectUri&amp;client_secret=$Secret&amp;code=$AuthCode&amp;grant_type=authorization_code&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$AccessToken = $Response.access_token<\/p>\n<p style=\"padding-left: 30px\">$ValidThru = (get-date).AddSeconds([int]$Response.expires_in)<\/p>\n<p style=\"padding-left: 30px\">$RefreshToken = $Response.refresh_token<\/p>\n<p style=\"padding-left: 30px\">#endregion<\/p>\n<p>We should end up with a valid access token that opens the door to Windows Live. In my next blog posts, we will see how to perform common tasks.<\/p>\n<p>But before I conclude this post, here is a best practice reminder: A script should properly sign out a user at the end of a session. Sending the following web request can help ensure that the user&rsquo;s data isn&rsquo;t left unattended:<\/p>\n<p style=\"padding-left: 30px\">Invoke-WebRequest &#8220;https:\/\/login.live.com\/oauth20_logout.srf?client_id=$ClientID&amp;redirect_uri=$RedirectUri&#8221;<\/p>\n<p>~Chris<\/p>\n<p>Thank you, Chris, Join us tomorrow when Chris will be back for more exciting Windows PowerShell fun.<\/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 using Windows PowerShell 3.0 to automate Windows Live. Microsoft Scripting Guy, Ed Wilson, is here. Welcome back to guest blogger, Chris Wu. Take it away Chris&hellip; One advantage of Windows PowerShell that I&rsquo;ve really enjoyed is the ability to test various APIs at ease. There is no need [&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,435,3,167,45],"class_list":["post-3301","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-chris-wu","tag-guest-blogger","tag-invoke-webrequest","tag-scripting-guy","tag-using-the-internet","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft PFE, Chris Wu, talks about using Windows PowerShell 3.0 to automate Windows Live. Microsoft Scripting Guy, Ed Wilson, is here. Welcome back to guest blogger, Chris Wu. Take it away Chris&hellip; One advantage of Windows PowerShell that I&rsquo;ve really enjoyed is the ability to test various APIs at ease. There is no need [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3301","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=3301"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3301\/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=3301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=3301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=3301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}