Link to Parent: PowerShell - Deep Dive and Best Practice



Using $args in script blocks can be problematic if you assume that it will behave like other variables.  It does not (or, more accurately, the script block will not treat it like other variables).

The documentation on exactly what the rules are for $args seems sparse.

The following is a distillation of what I have been able to determine about the rules for using $args in script blocks.
I cannot point any documentation to back it up, it is purely inferential from experimentation, but I have found no case where it does not ring true.

In any given script block, $args can only be populated by an argument list explicitly passed to that script block.

This seems to be an inherent property of script blocks. 

It is not a scope issue - you cannot  dot source the script block into a current scope and use $args that was passed to that scope.  It will not use those arguments.
Further, you cannot force it to use those arguments by using .getnewclosure().  It will not close over $args.

$args can be useful, but you have to use it as designed, and it was designed to work with an argument list. 

A consequence of this, and as a  general rule for using $args in a script block:

In any given PowerShell code construct that uses a script block, if there is no provision in the syntax for passing an argument list to the script block,
 then you cannot use $args in that script block.

All PowerShell script blocks can use $args, provided you can get an argument list to them.
If you cannot give them an argument list, then any reference to $args in that script block will be null.

There are some PowerShell constructs  that use script blocks and run them in the current scope.

Foreach-Object
Where-Object
For, Foreach, Do, and While loops

While these script blocks will  use any other variables in the local scope, they cannot use $args in the local scope, and have no provision for providing them with an argument list.
Consequently , you cannot use $args in the script block of any of these constructs.

The Invoke-Command cmdlet lets you specify both a scriptblock and initialization script as parameters to the cmdlet.  There is also an argument list parameter that is provided to supply arguments to the script block used in the scriptblock parameter.  In this script block you can use $args, and populate that variable using arguments provided via the argument list parameter.  There is no provision for providing an argument list to the script block of the initialization script parameter, so you cannot use $args in that script block - there is no mechanism to provide it with the argument list it needs to populate that variable.

Being able to take an argument list and use $args is built into the design of script blocks. 
You can create and execute arbitrary script blocks in your code, and provide them with an argument list, so you can use $args in them. 
You can assign those script blocks to variables, and use them like anonymous functions, and here again the syntax rules allow you to provide an argument list, and you can use $args.

The function provider also allows you to pass an argument list to the script blocks of functions and filters, so you can use $args in them.

Any script block you can provide an argument list to, you can use $args in.  If you can not (or do not) provide a script block with an argument list and you use $args in that script block, you can achieve the same effect by simply replacing all instances of $args in the script block with $null, because that's what it's going to be.

See Also

Other Languages