{"id":6661,"date":"2008-02-22T15:29:00","date_gmt":"2008-02-22T15:29:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/2008\/02\/22\/how-to-write-interdependent-scripts\/"},"modified":"2019-02-18T13:16:12","modified_gmt":"2019-02-18T20:16:12","slug":"how-to-write-interdependent-scripts","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/how-to-write-interdependent-scripts\/","title":{"rendered":"How to Write Interdependent Scripts"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">A lot of the time, I want to build scripts that depend on other scripts that I\u2019ve already written.&nbsp; Sometimes I will just want to be able to use a custom Cmdlet or function I\u2019ve written.&nbsp; Sometimes, I\u2019ll simply want to always call the underlying script without changing anything and sometimes I\u2019ll want to change to make the default arguments to a command customizable (like giving a custom Copy-Item script a default destination). <\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">There are two ways you can approach this.&nbsp; You can reference the script, by .\\ or by absolute path, and pass it\u2019s arguments normally, or you can determine the location of the current script and create aliases or functions that call the script.&nbsp; Of the two ways to go, the second, while a little trickier, is the recommended approach.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">The first approach has a couple of drawbacks.&nbsp; If you use .\\ to run the scripts, then they have to really be in the current directory.&nbsp; If you use this approach, you would have to switch the location where the scripts are (with Push-Location) just before you run the script, and then revert it when you\u2019re done.&nbsp; This approach works for making one command that depends on another, but if you tried to have another command that used that command, it wouldn\u2019t work (because the new scripts would change the location, and then your script would change the location, and then the new scripts would be looking for scripts in the wrong place).&nbsp; It is not a sustainable approach.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><strong>If you ever write production scripts that call other scripts with .\\, you should try the approach below instead, because your scripts can conflict with others that take the same approach, or can have trouble working if scripts are called from another directory.<\/strong><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><font size=\"3\"><font face=\"Calibri\">&nbsp;<\/font><\/font><\/b><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">The second approach is pretty simple, and can be done in 3 steps:<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoListParagraph\"><span><span><font face=\"Calibri\" size=\"3\">1.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">Find the root location of the script<\/font><\/p>\n<p class=\"MsoListParagraph\"><span><span><font face=\"Calibri\" size=\"3\">2.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">Set an Alias or create a function to call the script that the script depends on<\/font><\/p>\n<p class=\"MsoListParagraph\"><span><span><font face=\"Calibri\" size=\"3\">3.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">In each of the script files, check for all of the required commands before running<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">I\u2019ll demonstrate the technique below.&nbsp; In this case, I\u2019m writing a script that depends on two other scripts, Get-MyData \u2013Id &lt;int&gt; -DataSet&lt;Object&gt; and Set-MyData \u2013Id &lt;int&gt; -DataSet&lt;Object&gt;, which depend on a third and fourth command, Test-MyData (used by both) and Add-MyData (used by just Set-MyData)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Part 1 can be done in this simple line<span>, <\/span><span>which should be at the top of the script (not within a function or script block)<\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/font><span>$scriptRoot = Split-Path (Resolve-Path $myInvocation.MyCommand.Path)<\/span><\/font><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">To do part 2:<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">If you don\u2019t want to change the arguments you\u2019re passing, you can just alias the script:<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">Set-Alias \u201cSet-MyData\u201d $scriptRoot\\Set-MyData.ps1<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">Now let\u2019s suppose I\u2019ll almost always want to get the same set of data.&nbsp; I can omit that parameter by writing a function that uses a different default and calls Get-MyData<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">function Get-MyData($id, $dataSet = \u201cMyDataSet\u201d) {<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;&nbsp; $scriptRoot\\Get-MyData \u2013id $id `<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;&nbsp; &nbsp;&nbsp;-dataSet $dataSet <\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">}<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">To do part 3, you would put this little snippet of code after the parameter block in Set-MyData.ps1.&nbsp; In Get-MyData.ps1, $requiredCommands would just be \u201cTest-MyData\u201d.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">$requiredCommands = \u201cTest-MyData\u201d, \u201cAdd-MyData\u201d<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">$null = get-command $requiredcommands -ea SilentlyContinue -ev missingRequiredcommands<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">foreach ($cmd in $missingRequiredCommands) {<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\"><span>&nbsp;&nbsp;&nbsp; <\/span>Write-Warning \u201cMissing $($cmd.Exception.CommandName)\u201d<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">} <\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">if ($missingRequiredCommands.Count) { throw \u201cMissing Required Commands, Exiting.\u201d }<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">By following the approach outlined above (finding the root, aliasing the dependent scripts, and checking for dependencies in each script), you can reuse your PowerShell code quickly and easily.<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">Hope this helps,<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">&nbsp;<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\">James Brundage [MSFT]<\/font><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A lot of the time, I want to build scripts that depend on other scripts that I\u2019ve already written.&nbsp; Sometimes I will just want to be able to use a custom Cmdlet or function I\u2019ve written.&nbsp; Sometimes, I\u2019ll simply want to always call the underlying script without changing anything and sometimes I\u2019ll want to change [&hellip;]<\/p>\n","protected":false},"author":600,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-6661","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell"],"acf":[],"blog_post_summary":"<p>A lot of the time, I want to build scripts that depend on other scripts that I\u2019ve already written.&nbsp; Sometimes I will just want to be able to use a custom Cmdlet or function I\u2019ve written.&nbsp; Sometimes, I\u2019ll simply want to always call the underlying script without changing anything and sometimes I\u2019ll want to change [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/6661","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/600"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=6661"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/6661\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=6661"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=6661"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=6661"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}