{"id":4353,"date":"2013-01-09T00:01:00","date_gmt":"2013-01-09T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/01\/09\/powershell-workflows-nesting\/"},"modified":"2013-01-09T00:01:00","modified_gmt":"2013-01-09T00:01:00","slug":"powershell-workflows-nesting","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/powershell-workflows-nesting\/","title":{"rendered":"PowerShell Workflows: Nesting"},"content":{"rendered":"<p><strong>Summary:<\/strong>&nbsp;Windows PowerShell MVP Richard Siddaway talks about nesting Windows PowerShell workflows.\nMicrosoft Scripting Guy, Ed Wilson, is here. Today, we have the third in a series of guest blog posts written by Windows PowerShell MVP Richard Siddaway dealing with Windows PowerShell workflow.<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong> The first article, <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/12\/26\/powershell-workflows-the-basics.aspx\" target=\"_blank\">PowerShell Workflows: The Basics<\/a><em>, <\/em>introduced the basic concepts of Windows PowerShell workflow. The second article, <a href=\"http:\/\/blogs.technet.comhttps:\/\/devblogs.microsoft.com\/scripting\/powershell-workflows-restrictions\/\" target=\"_blank\">PowerShell Workflows: Restrictions<\/a><em>, <\/em>discussed the restrictions encountered with working with Windows PowerShell workflows. You should read those articles prior to reading today&rsquo;s article.\nRichard has written a <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/richard+siddaway\/\" target=\"_blank\">number of guest Hey, Scripting Guy! Blog posts<\/a>, and he has also written two books on Windows PowerShell. His most recent book, <a href=\"http:\/\/www.manning.com\/jones2\/\" target=\"_blank\">PowerShell in Depth<\/a>, is co-written with fellow MVPs Don Jones and Jeffrey Hicks.\nNow, take it away, Richard &hellip;<\/p>\n<h2>PowerShell Workflows: Nesting<\/h2>\n<p>So far you have seen only a single workflow being used at a time. If you think for a minute about how you use your Windows PowerShell scripts, you probably notice that you build a number of functions that you re-use and call from other functions and scripts. The whole concept of re-usability should permeate your Windows PowerShell code so that you maximize the return from the time and effort you put into developing your code.\nThe techniques you should adopt to ensure maximum re-usability are best left to another article, but for now, we&rsquo;ll concentrate on how Windows PowerShell functions and Windows PowerShell workflows can be used inside other workflows.\nThe whole topic of how you should design your workflows will be covered in a later article. Before you can do that design, you need to understand the mechanisms you can use to re-use existing functionality. That functionality breaks down into three broad groups:<\/p>\n<ul>\n<li>PowerShell workflows &ndash; (integrating with workflows you create in Visual Studio is possible but beyond the scope of most administrators and beyond the scope of this series).<\/li>\n<li>PowerShell functions &ndash; either in the same script file as the workflow or through a Windows PowerShell module.<\/li>\n<li>PowerShell scripts &ndash; on the local or remote machine.<\/li>\n<\/ul>\n<p>Let&rsquo;s start by looking at how your workflow can interact with other workflows by using a practical example from your Active Directory administration tasks. It is generally regarded as good practice to clean up the accounts in your Active Directory. You would normally look at disabled accounts, expired accounts, and accounts with passwords that never expire. A decision can be taken on what to do with each account after you&rsquo;ve identified accounts that match your criteria.\nTo find disabled accounts, run:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -AccountDisabled |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsDisabledAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nTo find expired accounts:<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -AccountExpired |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsExpiredAccounts.csv -NoTypeInformation\n&nbsp;\nTo find accounts whose passwords never expire:<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -PasswordNeverExpires |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsPsswdNeverExpireAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nThree simple scripts that will be familiar to Active Directory administrators. Using these is more efficient than trying to perform the task by hand, but you have to run them sequentially. Can workflows help us introduce some parallelism?\nThe most direct approach would be to wrap the scripts into a single work flow:<\/p>\n<p style=\"padding-left: 30px\">workflow get-ADReport {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;parallel {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -AccountDisabled |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsDisabledAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -AccountExpired |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsExpiredAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -PasswordNeverExpires |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsPsswdNeverExpireAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nThis works very well with the three CSV files being produced almost simultaneously on my test system. The drawback to this is that you don&rsquo;t have the ability to search for any of the individual type of accounts&mdash;you have to have them all.\nYou have a couple of options. First, you can create individual workflows for each search and nest them:<\/p>\n<p style=\"padding-left: 30px\">workflow get-ADReport {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; workflow get-disabled {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Search-ADAccount -AccountDisabled |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Export-Csv -Path c:ADReportsDisabledAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; workflow get-expired {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Search-ADAccount -AccountExpired |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Export-Csv -Path c:ADReportsExpiredAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; workflow get-passwordneverexpire {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Search-ADAccount -PasswordNeverExpires |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Export-Csv -Path c:ADReportsPsswdNeverExpireAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp; parallel {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; get-disabled<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; get-expired<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; get-passwordneverexpire<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nThis will load the individual workflows into memory so that you could use them individually. A simpler way that makes maintenance easier is to move the individual workflows out of the main workflow like this:<\/p>\n<p style=\"padding-left: 30px\">workflow get-disabled {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -AccountDisabled |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsDisabledAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">workflow get-expired {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -AccountExpired |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsExpiredAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">workflow get-passwordneverexpire {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Search-ADAccount -PasswordNeverExpires |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Select-Object -Property DistinguishedName |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Export-Csv -Path c:ADReportsPsswdNeverExpireAccounts.csv -NoTypeInformation<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">workflow get-ADReport {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;parallel {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; get-disabled<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; get-expired<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; get-passwordneverexpire<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}\nTake this a stage further and separate your workflows into individual files and create a .psm1 file to load them as a module. You can then add functionality in a granular manner without affecting the bulk of your code. I&rsquo;ve shown these workflows being used in parallel. This approach works if you need to ensure sequentiality&mdash;for example, if you need to create an Active Directory account before creating the mailbox.\nFunctions are handled in a similar way:<\/p>\n<p style=\"padding-left: 30px\">workflow get-computersystem {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;param([string[]]$computerName)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;function get-fcomputersystem {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;param ([string]$fcomputer)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_ComputerSystem -ComputerName $fcomputer<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># The contents of the foreach block will be executed in parallel&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; foreach -parallel($computer in $computerName) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; if (Test-Connection -ComputerName $computer -Quiet -Count 1) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get-fcomputersystem -fcomputer $computer<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; else {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;$computer unreachable&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&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\">get-computersystem -computerName $env:COMPUTERNAME\nIn this workflow, a list of computer names is passed in through the <strong>computerName<\/strong> parameter. A <strong>foreach &ndash;parallel<\/strong> loop is used to iterate over the list of computers. <strong>Test-Connection<\/strong> is used to determine if the remote system is contactable, and if so, the function is called.&nbsp;\nIn this case, the function is defined in the workflow. You could just as easily defined it outside the work flow as in the nested workflow example. Similarly, you could put the functions into a separate script and load them and the workflow as part of a module.\nThe important point is that the workflows or functions you want to call are loaded, or defined, before you want to use them.\nScripts are the third, and last, of the methods you can utilize to re-use existing code. Take the three scripts utilizing <strong>Search-ADAccount<\/strong> introduced at the top of the article and put each into a script so you will end up with three script files. I&rsquo;ve called them:<\/p>\n<p style=\"padding-left: 30px\">get-disabledaccount.ps1<\/p>\n<p style=\"padding-left: 30px\">get-expiredaccount.ps1<\/p>\n<p style=\"padding-left: 30px\">get-passwordNexpire.ps1<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nYou still want these to run in parallel, so you might try this:<\/p>\n<p style=\"padding-left: 30px\">workflow get-ADReport {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;parallel {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; c:adreportsget-disabledaccount.ps1<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; c:adreportsget-expiredaccount.ps1<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; c:adreportsget-passwordNexpire.ps1<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}<\/p>\n<p style=\"padding-left: 30px\">}\nUnfortunately, this won&rsquo;t work and you will see an error:<\/p>\n<p style=\"padding-left: 30px\">At line:3 char:4<\/p>\n<p style=\"padding-left: 30px\">+&nbsp;&nbsp;&nbsp; c:adreportsget-disabledaccount.ps1<\/p>\n<p style=\"padding-left: 30px\">+&nbsp;&nbsp;&nbsp; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<\/p>\n<p style=\"padding-left: 30px\">Cannot find the &#8216;c:adreportsget-disabledaccount.ps1&#8217; command. If this<\/p>\n<p style=\"padding-left: 30px\">command is defined as a workflow, ensure it is defined before the workflow<\/p>\n<p style=\"padding-left: 30px\">that calls it. If it is a command intended to run directly within Windows<\/p>\n<p style=\"padding-left: 30px\">PowerShell (or is not available on this system), place it in an InlineScript:<\/p>\n<p style=\"padding-left: 30px\">&#8216;InlineScript { c:adreportsget-disabledaccount.ps1 }&#8217;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; + CategoryInfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ParserError: (:) [], ParseException<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; + FullyQualifiedErrorId : CommandNotFound<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nSo, you could try this:<\/p>\n<p style=\"padding-left: 30px\">workflow get-ADReport {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;inlinescript {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; c:adreportsget-disabledaccount.ps1<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; c:adreportsget-expiredaccount.ps1<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; c:adreportsget-passwordNexpire.ps1&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}\n&nbsp;\nIt works, but are you getting the parallelism you need? The way to ensure that you do is to run each script separately:<\/p>\n<p style=\"padding-left: 30px\">workflow get-ADReport {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;parallel {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; inlinescript {c:adreportsget-disabledaccount.ps1}<\/p>\n<p style=\"padding-left: 30px\">&nbsp; inlinescript {c:adreportsget-expiredaccount.ps1}<\/p>\n<p style=\"padding-left: 30px\">&nbsp; inlinescript {c:adreportsget-passwordNexpire.ps1&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">}\n&nbsp;\nEach separate inlinescript section will be run in parallel.\nWhat about the situation where you want to run a script that exists on a remote system?\nPut the scripts in the C:ADReports folder on the remote machine and run your workflow as:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; get-ADReport -PSComputerName dc02\n&nbsp;\nThe scripts will run on the remote machine and, because we haven&rsquo;t modified them, that&rsquo;s where the output will be produced. All workflows have a number of parameters automatically added when they are created. They are documented in the About file:<\/p>\n<p style=\"padding-left: 30px\">Get-Help about_WorkflowCommonParameters<\/p>\n<h2>Summary<\/h2>\n<p>You saw in earlier articles that Windows PowerShell workflows can incorporate a number of elements:<\/p>\n<ul>\n<li>Workflow activities<\/li>\n<li>Workflow language features especially  \n<ul>\n<li>Parallel<\/li>\n<li>Foreach -parallel<\/li>\n<\/ul>\n<\/li>\n<li>Inline PowerShell scripts including cmdlets and language features for which workflow activities weren&rsquo;t created<\/li>\n<\/ul>\n<p>This article has shown you how to re-use existing code by incorporating:<\/p>\n<ul>\n<li>PowerShell workflows<\/li>\n<li>PowerShell functions<\/li>\n<li>PowerShell scripts on local and remote machines<\/li>\n<\/ul>\n<p>Workflows, as you have seen, are like Windows PowerShell but different. They are versatile enough that you should be able to perform whatever tasks you need; however, you may need to work a bit more to get to that point.\nOne of the great strengths of workflows is that they can be stopped and restarted. This is because they utilize the Windows PowerShell job engine. That&rsquo;s what we&rsquo;ll look at next time, including how to check-point workflows and how to stop and re-start them.\n~Richard\nThank you, Richard, for another awesome article about workflows. Join me tomorrow when I will talk about more cool Windows PowerShell stuff. &nbsp;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=\"http:\/\/blogs.technet.commailto: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:&nbsp;Windows PowerShell MVP Richard Siddaway talks about nesting Windows PowerShell workflows. Microsoft Scripting Guy, Ed Wilson, is here. Today, we have the third in a series of guest blog posts written by Windows PowerShell MVP Richard Siddaway dealing with Windows PowerShell workflow. Note The first article, PowerShell Workflows: The Basics, introduced the basic concepts of [&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":[56,189,3,4,45,382],"class_list":["post-4353","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-richard-siddaway","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell","tag-workflow"],"acf":[],"blog_post_summary":"<p>Summary:&nbsp;Windows PowerShell MVP Richard Siddaway talks about nesting Windows PowerShell workflows. Microsoft Scripting Guy, Ed Wilson, is here. Today, we have the third in a series of guest blog posts written by Windows PowerShell MVP Richard Siddaway dealing with Windows PowerShell workflow. Note The first article, PowerShell Workflows: The Basics, introduced the basic concepts of [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4353","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=4353"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4353\/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=4353"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=4353"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=4353"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}