{"id":638,"date":"2022-04-01T07:06:22","date_gmt":"2022-04-01T14:06:22","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/powershell-community\/?p=638"},"modified":"2022-04-01T07:19:35","modified_gmt":"2022-04-01T14:19:35","slug":"how-to-have-more-control-of-preferences-in-functions-and-the-role-of-modules-on-inheritance","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell-community\/how-to-have-more-control-of-preferences-in-functions-and-the-role-of-modules-on-inheritance\/","title":{"rendered":"On Preferences and Scopes"},"content":{"rendered":"<h2>Progress in PowerShell: a tale of Verbosity and other preferences with lessons in Scopes and Proxies thrown in<\/h2>\n<p>It started, as these things often do, with someone complaining. In PowerShell Version 7.2 the output of <code>Invoke-WebRequest -Verbose<\/code> and <code>Invoke-RestMethod -Verbose<\/code> look like this:<\/p>\n<pre><code class=\"language-powershell-console\">VERBOSE: GET with 0-byte payload<\/code><\/pre>\n<p>In all the earlier versions they look like the version below, which is more helpful when you&#8217;re trying to debug code that builds URIs:<\/p>\n<pre><code class=\"language-powershell-console\">VERBOSE: GET https:\/\/google.com\/ with 0-byte payload<\/code><\/pre>\n<p>A <em>proxy function<\/em> will fix that. If two commands have the same name an alias beats a function, which beats a cmdlet, which beats an external program. You can specify the full path to an external program or cmdlet &#8211; for example <code>Microsoft.PowerShell.UtilityInvoke-RestMethod<\/code> so an <code>Invoke-RestMethod<\/code> <em>function<\/em> can act as a <em>proxy<\/em> for the cmdlet, anything which calls <code>Invoke-RestMethod<\/code> will go to the function, which calls the cmdlet with its fully qualified name. PowerShell even has a mechanism to create the function&#8217;s code:<\/p>\n<pre><code class=\"language-powershell\">$cmd      = Get-Command Invoke-RestMethod\n$MetaData = New-Object System.Management.Automation.CommandMetaData $cmd\n[System.Management.Automation.ProxyCommand]::create($MetaData) | clip<\/code><\/pre>\n<p>(I don&#8217;t carry those 3 lines in my head, when I need them I refer to a script Jeffrey Snover wrote long ago, a newer version is <a href=\"https:\/\/www.powershellgallery.com\/packages\/MetaProgramming\/1.0.0.2\">on the PowerShell Gallery<\/a>.)<br \/>\nI added extra <code>Write-Verbose<\/code> calls and tidied up the autogenerated code and posted the result <a href=\"https:\/\/gist.github.com\/jhoneill\/f8ddd4e4e0a25c22d77749166d6f14fe\">as a gist<\/a>.<br \/>\nA module I&#8217;m working has lots of calls to <code>Invoke-RestMethod<\/code> but the proxy function wouldn&#8217;t see that I&#8217;d specified <code>-Verbose<\/code>. So I needed to investigate.<\/p>\n<p>The <code>-Verbose<\/code> switch sets the value of <code>$VerbosePreference<\/code> in the function being called; if you thought setting the global <code>$VerbosePreference<\/code> to <code>continue<\/code> was the same as a running everything with <code>-Verbose<\/code>, trying the following might surprise you:<\/p>\n<pre><code class=\"language-powershell\">$VerbosePreference=\"Continue\"\nInvoke-WebRequest \"https:\/\/google.com\" -OutFile delete.me\nCopy-Item delete.me delete.too<\/code><\/pre>\n<p><code>Invoke-WebRequest<\/code> heeds the preference, but <code>Copy-Item<\/code> only prints a message if the <code>-Verbose<\/code> <em>switch<\/em> is specified.<\/p>\n<p>My proxy function would print a verbose message if run with <code>-Verbose<\/code>, it <em>would<\/em> heed the global preference-variable but not the switch passed to the function that called it. The <code>-Confirm<\/code>, <code>-Debug<\/code>, <code>-ErrorAction<\/code>, <code>-InformationAction<\/code>, <code>-Verbose<\/code>, <code>-WarningAction<\/code>, and <code>-WhatIf<\/code> switches <em>all<\/em> set the corresponding preference-variable inside a function but that wasn&#8217;t being inherited when the functions in my module called the proxy function.<br \/>\nI could reproduce this with the two functions below. First, I loaded them from a single <code>.PSM1<\/code> file (and things were the same if I pasted them in at a PowerShell prompt)<\/p>\n<pre><code class=\"language-powershell\">function one {\n  [cmdletBinding()]\n  param()\n  Write-Verbose \"One calls two\"\n  two\n  Write-Verbose \"Two returned\"\n}\n\nfunction two {\n  [cmdletBinding()]\n  param()\n  Write-Verbose \"Two has a message\"\n}<\/code><\/pre>\n<p>If I load this, I get three messages:<\/p>\n<pre><code class=\"language-powershell-console\">VERBOSE: One calls two\nVERBOSE: Two has a message\nVERBOSE: Two returned<\/code><\/pre>\n<p>Things change if <code>One<\/code> is in its own module<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-content\/uploads\/sites\/69\/2022\/04\/jon-1.png\" alt=\"Screen shot showing the difference if the calling function is a module\" \/><\/p>\n<p><code>One -verbose<\/code> now just produces two messages:<\/p>\n<pre><code class=\"language-powershell-console\">VERBOSE: One calls two\nVERBOSE: two returned<\/code><\/pre>\n<p>Setting the global preference-variable at the prompt returns all 3 &#8211; because function <code>two<\/code> sees it.<br \/>\nIt is common to assume that <em>a function inherits the variables from whatever called it<\/em>; we can see that working with simpler functions<\/p>\n<pre><code class=\"language-powershell\">function three {\n    $e=\"ewe\"\n    four\n}\n\nfunction four {\n    $e\n} <\/code><\/pre>\n<p>If I set <code>$e<\/code> and run <code>three<\/code> from the prompt<\/p>\n<pre><code class=\"language-powershell-console\">&gt; $e = \"eye\"  \n&gt; three\newe<\/code><\/pre>\n<p>the assumption holds; and a lot of documentation stops there, but if I import <code>three<\/code> from a module<\/p>\n<pre><code class=\"language-powershell-console\">&gt; three\neye<\/code><\/pre>\n<p>The inheritance assumption is qualified by a rule that says <em>what happens in a module stays in the module<\/em>.<br \/>\nThe question is <em>what can we do about it?<\/em><\/p>\n<p>Before it dawned on me that this was a <em>scopes<\/em> thing and not just <code>-Verbose<\/code>, a search brought me <a href=\"https:\/\/deangrant.me\/2015\/10\/03\/inheriting-verbose-preference-in-windows-powershell-module-functions\/\">a clue<\/a>; the solution is to change function <code>two<\/code> (or my proxy function) as follows:<\/p>\n<pre><code class=\"language-powershell\">function two {\n  [cmdletBinding()]\n  param($VerbosePreference = $PSCmdlet.GetVariableValue('VerbosePreference')\n  Write-Verbose \"Two has a message\"\n}<\/code><\/pre>\n<p>We can only use <code>$PSCmdlet<\/code> if we have either <code>[cmdletBinding()]<\/code> or a <code>[parameter()]<\/code> decoration &#8211; just a side note on that, if you paste in<\/p>\n<pre><code class=\"language-powershell\">function five { param ($p) }\nfunction six  { param ([parameter()]$p) }<\/code><\/pre>\n<p>when you try to tab-complete parameters for <code>five<\/code> and <code>six<\/code>, you\u2019ll see <code>six<\/code> gets all the common parameters but <code>five<\/code> does not &#8211; <code>[parameter()]<\/code> is an implicit <code>[cmdletBinding()]<\/code>, although it&#8217;s still good to write the latter explicitly.<\/p>\n<p><code>$PSCmdlet<\/code> is available <em>when the function is setting up its variables<\/em>. Inside the function we&#8217;d never replace <code>$x<\/code> with the long-winded <code>$PSCmdlet.GetVariableValue(\"x\")<\/code>, but <em>in a parameter<\/em> it is \u201cuse the value from the scope that called you &#8211; even if that scope <em>is<\/em> a module\u201d.<\/p>\n<p><em>Now<\/em> the called function inherits the preference from its caller. If we specify <code>-Verbose<\/code> it takes precedence, so nothing breaks copying in <code>$VerbosePreference<\/code>. The same thing applies to <code>-Confirm<\/code> and <code>-WhatIf<\/code>.<\/p>\n<pre><code class=\"language-powershell\">function seven {\n  [cmdletBinding(SupportsShouldProcess=$true)]\n  param()\n  eight\n  Write-Host \"Something Safe \"\n}\n\nfunction eight {\n  [cmdletBinding(SupportsShouldProcess=$true)]\n  param()\n  if ($PSCmdlet.ShouldProcess(\"This\",\"Do you want to do\")) {\n    Write-Host \"Something dangerous\"\n  }\n}<\/code><\/pre>\n<p>If <code>seven<\/code> and <code>eight<\/code> are loaded from the same psm1 file<\/p>\n<pre><code class=\"language-powershell-console\">seven -Confirm\n\nConfirm\nAre you sure you want to perform this action?\nPerforming the operation \"Do you want to do\" on target \"This\".\n[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is\"Y\"): n\n\nSomething Safe<\/code><\/pre>\n<p>But, if <code>seven<\/code> loads from its own module \u2026<\/p>\n<pre><code class=\"language-powershell-console\">seven -Confirm\nSomething dangerous\nSomething Safe<\/code><\/pre>\n<p>Modifying the parameters in <code>eight<\/code> with the code below restores the confirmation<\/p>\n<pre><code class=\"language-powershell\">param($ConfirmPreference = $PSCmdlet.GetVariableValue('ConfirmPreference'))<\/code><\/pre>\n<p><code>-confirm<\/code> sets <code>$confirmPreference<\/code> to <code>low<\/code> which triggers <code>$PSCmdlet.ShouldProcess<\/code> to prompt the user. I treat <code>-Force<\/code> as an extra preference and my functions typically have<br \/>\n<code>if ($Force -or $PSCmdlet.ShouldProcess(...<\/code><br \/>\nto ensure the a command can be run non-interactively, even if its impact is set higher than <code>$ConfirmPreference<\/code>.<\/p>\n<p>The example above might lead you to think <code>-Confirm<\/code> and <code>-WhatIf<\/code> <em>should<\/em> be inherited, but this would cause a problem with scopes at the <code>script<\/code> level (which apply module-wide). Suppose the module that contained seven read like this:<\/p>\n<pre><code class=\"language-powershell\">$ConfirmPreference = 'None'\n\nfunction seven {\n...\n}<\/code><\/pre>\n<p>Any cmdlet that would normally ask for confirmation inside <code>seven<\/code> will run silently. But what if the module containing <code>eight<\/code> sets that option to something different? If a function overrides something from its own script-scope, then only things which share that scope see the change, which seems logical. Thinking of functions&#8217; scopes as <em>children<\/em> of their module&#8217;s scope which in turn is usually a child of the global scope means we can say things pass down their branch of the scope \u201ctree\u201d but don&#8217;t jump <em>between<\/em> branches.<\/p>\n<p>Before leaving <code>$ConfirmPreference<\/code>, I have been think about changing it inside a function, so it drops to <code>low<\/code> if many items are being updated. I\u2019m not sure that&#8217;s a great idea because coming to <em>rely<\/em> on a prompt and which isn&#8217;t there <em>reliably<\/em>, will lead to trouble.<\/p>\n<p>The title said this is about <em>Progress<\/em>. PowerShell 7.2 has an optional new way of displaying the progress bar, but the things it relies on are a bit flaky in the Integrated-Shell in Visual Studio Code (things improve if you use <code>Write-Progress -completed<\/code>) and <code>Invoke-WebRequest<\/code> downloading 100 log files becomes a mess. There is no <code>-Progress<\/code> common parameter, but the day before I started on the <code>-Verbose<\/code> problem, I had found that adding <code>$ProgressPreference<\/code> as a parameter worked, so it makes sense to do it the same way. I inserted the following parameter into the function that calls <code>Invoke-WebRequest<\/code>. Unlike the examples above, which might be tagged with <code>[Parameter(DontShow)]<\/code> I want this to tab-complete the possible values, so it is marked with the <code>ActionPreference<\/code> type:<\/p>\n<pre><code class=\"language-powershell\">[ActionPreference]$ProgressPreference = $PSCmdlet.GetVariableValue('ProgressPreference')<\/code><\/pre>\n<p>And I will also want <em>that<\/em> to inherit into my proxy function.<br \/>\nWith that in place, I can use the code below to call my function and replace the byte-by-byte progress indicator that <code>Invoke-WebRequest<\/code> displays with my own file-by-file one:<\/p>\n<pre><code class=\"language-powershell\">$count = 0\nForEach ($f in $file) {\n  Write-Progress \"Downloading\" -PercentComplete ($count\/$file.Count)\n  Myfunction $f -ProgressPreference SilentlyContinue\n  $count += 100\n}\nWrite-Progress \"Downloading\" -Completed<\/code><\/pre>\n<p>Quick tip: adding 100 to the counter each time (instead of adding 1) removes the need to multiply by 100 for a percentage.<\/p>\n<p>Since I mentioned <code>ErrorAction<\/code> above, before finishing I wanted to share one last tip about preferences:<\/p>\n<pre><code class=\"language-powershell\">function nine {\n  [cmdletBinding()]\n  param ($Name)\n  if (-not $Name) {throw \"Name is required\"}\n  Write-Host \"Deleting $Name*\"\n}<\/code><\/pre>\n<p>The \u201cdangerous\u201d line in the example above never runs if <code>$n<\/code> is empty, right? Wrong, actually.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-content\/uploads\/sites\/69\/2022\/04\/jon-2.png\" alt=\"Screen shot showing the effect of -ErrorAction on throw\" \/><\/p>\n<p>Specifying <code>-ErrorAction<\/code> prevents the <code>throw<\/code> statement throwing so<\/p>\n<p><code>nine \"\" -ErrorAction SilentlyContinue<\/code> would mean the dangerous code <em>is<\/em> run.<\/p>\n<p>When it is acting as a \u201cfence\u201d around dangerous code, it is worth putting a <code>return<\/code> after <code>throw<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Progress in PowerShell: a tale of Verbosity and other preferences with lessons in Scopes and Proxies thrown in It started, as these things often do, with someone complaining. In PowerShell Version 7.2 the output of Invoke-WebRequest -Verbose and Invoke-RestMethod -Verbose look like this: VERBOSE: GET with 0-byte payload In all the earlier versions they look [&hellip;]<\/p>\n","protected":false},"author":6479,"featured_media":77,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13],"tags":[66,3,63,65,64],"class_list":["post-638","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-erroraction","tag-powershell","tag-preference-variables","tag-progress","tag-verbose"],"acf":[],"blog_post_summary":"<p>Progress in PowerShell: a tale of Verbosity and other preferences with lessons in Scopes and Proxies thrown in It started, as these things often do, with someone complaining. In PowerShell Version 7.2 the output of Invoke-WebRequest -Verbose and Invoke-RestMethod -Verbose look like this: VERBOSE: GET with 0-byte payload In all the earlier versions they look [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts\/638","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/users\/6479"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/comments?post=638"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts\/638\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/media\/77"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/media?parent=638"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/categories?post=638"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/tags?post=638"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}