{"id":3418,"date":"2013-06-11T00:01:00","date_gmt":"2013-06-11T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/06\/11\/determine-pending-reboot-statuspowershell-style-part-2\/"},"modified":"2013-06-11T00:01:00","modified_gmt":"2013-06-11T00:01:00","slug":"determine-pending-reboot-statuspowershell-style-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/determine-pending-reboot-statuspowershell-style-part-2\/","title":{"rendered":"Determine Pending Reboot Status&#8212;PowerShell Style! Part 2"},"content":{"rendered":"<p><span style=\"font-size: 12px\"><strong>Summary<\/strong>: Guest blogger, Brian Wilhite, talks about using Windows PowerShell to detect a server that is in pending reboot status.<\/span><\/p>\n<p align=\"left\">Microsoft Scripting Guy, Ed Wilson, is here. Today we have the conclusion to Brian Wilhite&rsquo;s guest blog series about detecting pending reboots via Windows PowerShell. Prior to reading today&rsquo;s post, you should read <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2013\/06\/10\/determine-pending-reboot-status-powershell-style-part-1.aspx\" target=\"_blank\">Determine Pending Reboot Status&mdash;PowerShell Style! Part&nbsp;1<\/a>. You can also check out Brian&rsquo;s <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/brian+wilhite\/\" target=\"_blank\">prior Hey, Scripting Guy! Blog posts<\/a>.<\/p>\n<h2>The Windows PowerShell &ldquo;awesomeness&rdquo;<\/h2>\n<p>Now that I&rsquo;m satisfied with my research and validation, I&rsquo;m ready to write some Windows PowerShell &ldquo;awesomeness.&rdquo; Also, I think it&rsquo;s important to determine exactly what my function will return before getting started. I decided on the following properties in a PSObject:<\/p>\n<ul>\n<li><strong>Computer<\/strong>: Target computer (string)<\/li>\n<li><strong>CBServicing<\/strong>: Representing the component-based servicing after Windows 2008 (Boolean)<\/li>\n<li><strong>WindowsUpdate<\/strong>: Representing the WindowsUpdate\\Auto Update Registry value (Boolean)<\/li>\n<li><strong>CCMClientSDK<\/strong>: Representing the result of the Service Center Configuration Manager&nbsp;2012 client WMI DetermineIfRebootPending method (Boolean)<\/li>\n<li><strong>PendFileRename<\/strong>: Representing whether the PendingFileRenameOperations REG_MULTI_SZ entry has a value (Boolean)<\/li>\n<li><strong>PendFileRenVal<\/strong>: Representing the value of the PendingFileRenameOperations REG_MULTI_SZ entry (string[])<\/li>\n<li><strong>RebootPending<\/strong>: Representing a culmination of the Boolean values from the previous properties. If any value returns <strong>$true<\/strong>, so this property will be (Boolean)<\/li>\n<\/ul>\n<p>With the object defined, I can commence developing the function. When authoring a function, I use a template that basically prepopulates the function&#8217;s framework. I will be covering the core code components of the function, mainly the contents of the Process block. Although the template is a good tool, it&#8217;s important to show how the <strong>ComputerName<\/strong> parameter is defined:<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding()]<\/p>\n<p style=\"padding-left: 30px\">param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Alias(&#8220;CN&#8221;,&#8221;Computer&#8221;)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [String[]]$ComputerName=&#8221;$env:COMPUTERNAME&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; &nbsp;)<\/p>\n<p>The previous parameter line has <strong>ValueFromPipeline<\/strong> and <strong>ValueFromPipelineByPropertyName<\/strong> defined for the <strong>ComputerName<\/strong> parameter.<\/p>\n<ul>\n<li><strong>ValueFromPipleline<\/strong> enables strings to be piped into the function and processed via the process block.<\/li>\n<li><strong>ValueFromPipelineByPropertyName<\/strong> enables the <strong>ComputerName<\/strong> property from other objects to be piped to the function and processed by the process block.<\/li>\n<\/ul>\n<p>Because <strong>ComputerName<\/strong> was typed as a string array, I&rsquo;m going to iterate through every computer in <strong>ComputerName<\/strong> and run the following code, explaining each step along the way.<\/p>\n<p>First, I set several values to <strong>$false<\/strong> to reduce the number of if\/else statements. Notice that I can use one equal sign (<strong>=<\/strong>) to assign multiple values to multiple variables on one line.<\/p>\n<p style=\"padding-left: 30px\">$PendFileRename,$Pending,$SCCM = $false,$false,$false<\/p>\n<p>Next, I assign <strong>$null<\/strong> to the <strong>$CBSRebootPend<\/strong> variable because operating systems prior to Windows Server&nbsp;2003 do not have Component-Based Servicing (CBS).<\/p>\n<p style=\"padding-left: 30px\">$CBSRebootPend = $null<\/p>\n<p>This allows the <strong>CBServicing<\/strong> property, which the function returns, to be null if the Component-Based Servicing registry key does not exist. Next, I query for the <strong>BuildNumber<\/strong> via WMI to determine whether the Component-Based Servicing key needs to be queried.<\/p>\n<p style=\"padding-left: 30px\">$WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer<\/p>\n<p>Notice that I used the <strong>Property<\/strong> parameter for <strong>Get-WmiObject<\/strong> for optimization purposes. Performing the query in this way reduces the number of properties returned&mdash;therefore, decreasing the time it takes to run against multiple systems. Next, I make a connection to the registry by using the [Microsoft.Win32.RegistryKey] type accelerator.<\/p>\n<p style=\"padding-left: 30px\">$RegCon = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]&#8221;LocalMachine&#8221;,$Computer)<\/p>\n<p>If you review the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.win32.registrykey.aspx\" target=\"_blank\">Microsoft.Win32.RegistryKey<\/a> class reference page on MSDN, you&rsquo;ll notice an <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/8zha3xws.aspx\" target=\"_blank\">OpenRemoteBaseKey<\/a> method that accepts two arguments, first the <strong>RegistryHive<\/strong>, and then a string that represents the remote computer. The following code is used to determine if the CBS key is present. If it is present, it executes the query, and performs it on computers with a BuildNumber of 6001 (post Windows Vista and Windows Server 2008 RTM versions).<\/p>\n<p style=\"padding-left: 30px\">If ($WMI_OS.BuildNumber -ge 6001)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p>The <strong>OpenSubKey <\/strong>method has a <strong>GetSubKeyNames<\/strong> method that is called and stored in the <strong>$RegSubKeysCBS<\/strong> variable.<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$RegSubKeysCBS = $RegCon.OpenSubKey(&#8220;SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\&#8221;).GetSubKeyNames()<\/p>\n<p>The $<strong>CBSRebootPend<\/strong> variable will be <strong>$true<\/strong> if the <strong>RebootPending<\/strong> key is present in the <strong>$RegSubKeysCBS<\/strong> variable; it will be <strong>$false<\/strong> if it&rsquo;s not. The <strong>$CBSRebootPend<\/strong> variable will be used later for the <strong>CBServicing<\/strong> property.<\/p>\n<p style=\"padding-left: 30px\">$CBSRebootPend = $RegSubKeysCBS -contains &#8220;RebootPending&#8221;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">}## End If ($WMI_OS.BuildNumber -ge 6001)<\/p>\n<p>Because the <strong>WindowsUpdate\/Auto Update<\/strong> and the <strong>PendingFileRenameOperations<\/strong> registry values are common on all versions of Windows (at least since Windows&nbsp;XP and Windows Server&nbsp;2003), I&rsquo;ll query those entries unconditionally.<\/p>\n<p style=\"padding-left: 30px\">$RegWUAU = $RegCon.OpenSubKey(&#8220;SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\&#8221;)<\/p>\n<p style=\"padding-left: 30px\">$RegWUAURebootReq = $RegWUAU.GetSubKeyNames()<\/p>\n<p>The <strong>$WUAURebootReq<\/strong> variable will be <strong>$true<\/strong> if the <strong>RebootRequired<\/strong> key is present. Again, it will be <strong>$false<\/strong> if it&rsquo;s not. This variable will also be used later for the <strong>WindowsUpdate <\/strong>property.<\/p>\n<p style=\"padding-left: 30px\">$WUAURebootReq = $RegWUAURebootReq -contains &#8220;RebootRequired&#8221;<\/p>\n<p style=\"padding-left: 30px\">$RegSubKeySM = $RegCon.OpenSubKey(&#8220;SYSTEM\\CurrentControlSet\\Control\\Session Manager\\&#8221;)<\/p>\n<p>Here I&rsquo;ve captured the value of the P<strong>endingFileRenameOperations<\/strong> entry. I&rsquo;ll use the <strong>$RegValuePFRO<\/strong> variable to populate the P<strong>endFileRenVal<\/strong> property:<\/p>\n<p style=\"padding-left: 30px\">$RegValuePFRO = $RegSubKeySM.GetValue(&#8220;PendingFileRenameOperations&#8221;,$null)<\/p>\n<p>The registry data collection is complete, so I&rsquo;m going to clean up the connection object. Any time you us this method to connect to the registry, you should close the connection. Think of this as opening a door before entering a room&mdash;generally you close it when you leave. Working with the registry is really no different. So the following script will close your registry connection by calling the <strong>Close<\/strong> method from the <strong>$RegCon<\/strong> object that I created when I initially connected to the registry.<\/p>\n<p style=\"padding-left: 30px\">$RegCon.Close()<\/p>\n<p>Now let&rsquo;s evaluate the <strong>$RegValuePFRO<\/strong> variable to determine if the <strong>PendingFileRenameOperations<\/strong> REG_MULTI_SZ registry entry has a value. If it has a value, I&rsquo;m going to set the <strong>$PendFileRename<\/strong> variable to <strong>$true<\/strong>, which will be used as the <strong>PendFileRename<\/strong> property.<\/p>\n<p style=\"padding-left: 30px\">If ($RegValuePFRO)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$PendFileRename = $true<\/p>\n<p style=\"padding-left: 30px\">}#End If ($RegValuePFRO)<\/p>\n<p>Here, I&rsquo;m setting the <strong>$CCMClientSDK<\/strong> to <strong>$null<\/strong> because there may be a chance that the function iterates through multiple systems and not allow the previous value to persist from another system:<\/p>\n<p style=\"padding-left: 30px\">$CCMClientSDK = $null<\/p>\n<p>Next, I&rsquo;m going to use a technique called splatting, when calling <strong>Invoke-WmiMethod<\/strong> for the <strong>DetermineIfRebootPending<\/strong> method.<\/p>\n<p><strong>Note <\/strong>&nbsp;Splatting is really cool. You can pass multiple parameters to a cmdlet or function by using a hash table. The &ldquo;key&rdquo; in the hash table will be the parameter name and the &ldquo;value&rdquo; will be the parameter&rsquo;s argument.<\/p>\n<p style=\"padding-left: 30px\">$CCMSplat = @{<\/p>\n<p style=\"padding-left: 30px\">&nbsp; NameSpace=&#8217;ROOT\\ccm\\ClientSDK&#8217;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Class=&#8217;CCM_ClientUtilities&#8217;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Name=&#8217;DetermineIfRebootPending&#8217;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; ComputerName=$Computer<\/p>\n<p style=\"padding-left: 30px\">&nbsp; ErrorAction=&#8217;SilentlyContinue&#8217;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p>To use the hash table that was just created, with all my <strong>Invoke-WmiMethod<\/strong> parameters, I&rsquo;ll have to use the &ldquo;@&rdquo; character instead of referencing the &ldquo;$&rdquo; as you might assume.<\/p>\n<p style=\"padding-left: 30px\">$CCMClientSDK = Invoke-WmiMethod @CCMSplat<\/p>\n<p>I like this technique because it allows me to clean up a one-liner in a script that may stretch across two screens. In the following image, check out how the splat technique is cleaner than the traditional one-liner.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1258.6-11-13-1.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1258.6-11-13-1.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>OK, enough about splatting. Jumping back on track&hellip;<\/p>\n<p>After the <strong>DetermineIfRebootPending<\/strong> WMI method is called, the following code is run to determine if the <strong>$SCCM<\/strong> value should be <strong>$true<\/strong>, <strong>$false<\/strong>, or <strong>$null<\/strong>. The <strong>$null<\/strong> value is returned if the CCM_ClientUtilities WMI class doesn&rsquo;t exist. I have also incorporated error handling if the return code from the WMI method call is not equal to 0.<\/p>\n<p style=\"padding-left: 30px\">If ($CCMClientSDK)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;If ($CCMClientSDK.ReturnValue -ne 0)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;Write-Warning &#8220;Error: DetermineIfRebootPending returned error code $($CCMClientSDK.ReturnValue)&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}## End If ($CCMClientSDK -and $CCMClientSDK.ReturnValue -ne 0)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;$SCCM = $true<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}## End If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending)<\/p>\n<p style=\"padding-left: 30px\">}## End If ($CCMClientSDK)<\/p>\n<p style=\"padding-left: 30px\">Else<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$SCCM = $null<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p>Next, if any of the <strong>$CBSRebootPend<\/strong>, <strong>$WUAURebootReq<\/strong>, <strong>$SCCM<\/strong>, or <strong>$PendFileRename<\/strong> variables are <strong>$true<\/strong>, I&rsquo;ll set <strong>$Pending<\/strong> to <strong>$true<\/strong>, which will in turn populate the final property in my custom object, <strong>RebootPending<\/strong>.<\/p>\n<p style=\"padding-left: 30px\">If ($CBSRebootPend -or $WUAURebootReq -or $SCCM -or $PendFileRename)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$Pending = $true<\/p>\n<p style=\"padding-left: 30px\">}## End If ($CBS -or $WUAU -or $PendFileRename)<\/p>\n<p>Finally, I create my custom object. Once again, I&rsquo;ve used the splatting technique for my select statement. Because my <strong>PSObjec<\/strong>t was created by using a hash table, I will use the <strong>Select-Object<\/strong> cmdlet to order the properties based on how I think it is best presented to the user.<\/p>\n<p><strong>Note<\/strong>&nbsp;&nbsp;&nbsp;If you use Windows PowerShell&nbsp;3.0, you can use the <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/12\/21\/powertip-the-easy-way-to-create-a-custom-powershell-object.aspx\">[PSCustomObject<\/a>] type accelerator when you create an object and bypass the need to use the <strong>Select-Object<\/strong> technique.<\/p>\n<p style=\"padding-left: 30px\">$SelectSplat = @{<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Property=(&#8216;Computer&#8217;,&#8217;CBServicing&#8217;,&#8217;WindowsUpdate&#8217;,&#8217;CCMClientSDK&#8217;,&#8217;PendFileRename&#8217;,&#8217;PendFileRenVal&#8217;,&#8217;RebootPending&#8217;)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">New-Object -TypeName PSObject -Property @{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Computer=$WMI_OS.CSName<\/p>\n<p style=\"padding-left: 30px\">&nbsp;CBServicing=$CBSRebootPend<\/p>\n<p style=\"padding-left: 30px\">&nbsp;WindowsUpdate=$WUAURebootReq<\/p>\n<p style=\"padding-left: 30px\">&nbsp;CCMClientSDK=$SCCM<\/p>\n<p style=\"padding-left: 30px\">&nbsp;PendFileRename=$PendFileRename<\/p>\n<p style=\"padding-left: 30px\">&nbsp;PendFileRenVal=$RegValuePFRO<\/p>\n<p style=\"padding-left: 30px\">&nbsp;RebootPending=$Pending<\/p>\n<p style=\"padding-left: 30px\">&nbsp;} | Select-Object @SelectSplat<\/p>\n<p>The following screenshot illustrates the output of my function when run without the <strong>ComputerName<\/strong> parameter, which defaults to $env:COMPUTERNAME.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2664.6-11-13-2.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2664.6-11-13-2.png\" alt=\"Image of command output\" width=\"400\" height=\"281\" \/><\/a><\/p>\n<p>You&rsquo;ll notice that there are two values that are null. As noted earlier, this is by design because my local workstation does not have SCCM 2012 installed, nor was the <strong>PendingFileRenameOperations<\/strong> populated at the time I ran the function.<\/p>\n<p>I&rsquo;ve rambled enough for now. If you want to download my function, you can find it in the Script Center Repository:<\/p>\n<p><a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Get-PendingReboot-Query-bdb79542\" target=\"_blank\">Get-PendingReboot &#8211; Query Computer(s) For Pending Reboot State<\/a><\/p>\n<p>If you have suggestions, questions, or concerns, please leave feedback on the download page of the Script Center Repository. Without the suggestions from others, this function wouldn&rsquo;t be as complete as it is today. So if you have an idea, don&rsquo;t hesitate to contact me. Until next time&hellip;<\/p>\n<p>~Brian<\/p>\n<p>Thank you, Brian. Awesome job. Join me tomorrow when I will talk about cool Windows PowerShell stuff.<\/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><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Guest blogger, Brian Wilhite, talks about using Windows PowerShell to detect a server that is in pending reboot status. Microsoft Scripting Guy, Ed Wilson, is here. Today we have the conclusion to Brian Wilhite&rsquo;s guest blog series about detecting pending reboots via Windows PowerShell. Prior to reading today&rsquo;s post, you should read Determine Pending [&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":[327,56,31,3,170,45],"class_list":["post-3418","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-brian-wilhite","tag-guest-blogger","tag-operating-system","tag-scripting-guy","tag-splatting","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Guest blogger, Brian Wilhite, talks about using Windows PowerShell to detect a server that is in pending reboot status. Microsoft Scripting Guy, Ed Wilson, is here. Today we have the conclusion to Brian Wilhite&rsquo;s guest blog series about detecting pending reboots via Windows PowerShell. Prior to reading today&rsquo;s post, you should read Determine Pending [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3418","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=3418"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3418\/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=3418"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=3418"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=3418"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}