How to protect your PowerShell Functions
Creating functions in PowerShell is relatively a simple operation. You declare the function’s name and value, press the Enter key and you’re good to go. It is also very simple to override the function just by re-creating it; that’s all it takes, simple and easy.
What if you need to prevent that from happening? You don’t want to allow someone or something from overriding your functions?
PowerShell gives us the option to do that via a dynamic parameter. Dynamic parameters are cmdlet parameters that are added by a Windows PowerShell provider and are available only when the cmdlet is being used in the provider-enabled drive. The Options dynamic parameter is supported by New-Item and Set-Item when you use them with the Alias,Variable or Function providers.
The Options parameter <System.Management.Automation.ScopedItemOptions> possible values:
- None - No options. "None" is the default.
- Private - The function is visible only in the current scope (not in child scopes).
- ReadOnly - The properties of the function cannot be changed except by using the Force parameter. You can use Remove-Item to delete the function.
- Constant - The function cannot be deleted, and its properties cannot be changed. Constant is available only when you are creating a function. You cannot change the option of an existing function to Constant.
- AllScope - The function is copied to any new scopes that are created.
Let’s see how we can protect a function, we’ll start by marking it Read only.
# create a function
PS> function test { "I'm a read-only function" }
# set it as readonly
PS> Set-Item –Path Function:test –Options ReadOnly
# try to update the function definition
PS> function test { "new value" }
Cannot write to function test because it is read-only or constant.
At line:8 char:1
+ function test { "new value" }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (test:String) [], SessionStateUnauthorizedAccessException
+ FullyQualifiedErrorId : FunctionNotWritable
We get an error when we try to update the function. Let’s try to override it with the Force switch:
PS> Set-Item –Path Function:test -Value { "new value" } -Force
PS> test
new value
This time we don’t get the error and the function has been updated. If ReadOnly is not enough and doesn’t really protect the function like we want to, then we can set another value. This time let’s try to make it constant.
PS> Set-Item –Path Function:test -Value { "I'm constant" } -Options Constant -Force
Set-Item : Existing function test cannot be made constant. Functions can be made constant only at creation time.
At line:1 char:1
+ Set-Item –Path Function:test -Value { "I'm constant" } -Options Constant -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (test:String) [Set-Item], SessionStateUnauthorizedAccessException
+ FullyQualifiedErrorId : FunctionCannotBeMadeConstant,Microsoft.PowerShell.Commands.SetItemCommand
PS>
We get an error that functions can be made constant only at creation time. Let’s create a new function and then try to override it and remove it:PS C:\> # create a constant function
# # create a constant function
PS> New-Item –Path Function:test2 -Value { "I'm constant" } -Options Constant
Capability Name ModuleName
---------- ---- ----------
Script test2
# try to update the function
PS> Set-Item –Path Function:test2 -Value { "I'm constant 2" } -Force
# try to remove it
PS> Remove-Item Path Function:test2 -Force
Set-Item : Cannot write to function test2 because it is read-only or constant.
At line:5 char:1
+ Set-Item –Path Function:test2 -Value { "I'm constant 2" } -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (test2:String) [Set-Item], SessionStateUnauthorizedAccessException
+ FullyQualifiedErrorId : FunctionNotWritable,Microsoft.PowerShell.Commands.SetItemCommand
Remove-Item : A positional parameter cannot be found that accepts argument 'Function:test2'.
At line:8 char:1
+ Remove-Item Path Function:test2 -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Remove-Item], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
PS>
As you can see none of the above changes the function. It is protected and the only option to get rid of it is by closing the shell. In addition to Aliases and Functions, we can also protect other objects, like: variables and modules. For variables use the Set-Variable cmdlet, it has its own options parameter. To protect modules from being unloaded we need to set the module’s AccessMode property. Here’s an example using dynamic module.
PS> New-Module -Name CantTouchThis -ScriptBlock {
$ExecutionContext.SessionState.Module.AccessMode = 'constant'
} | Import-Module
Remove-Module -Name CantTouchThis -Force
Remove-Module : Unable to remove module 'CantTouchThis' because it is marked as constant. A module cannot be removed if it is marked constant.
At line:5 char:1
+ Remove-Module -Name CantTouchThis -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (CantTouchThis:PSModuleInfo) [Remove-Module], InvalidOperationException
+ FullyQualifiedErrorId : Modules_ModuleIsConstant,Microsoft.PowerShell.Commands.RemoveModuleCommand
Remove-Module : No modules were removed. Verify that the specification of modules to remove is correct and those modules exist in the runspace.
At line:5 char:1
+ Remove-Module -Name CantTouchThis -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (:) [Remove-Module], InvalidOperationException
+ FullyQualifiedErrorId : Modules_NoModulesRemoved,Microsoft.PowerShell.Commands.RemoveModuleCommand
PS>
I would like to have another way to create constant functions or modules so I logged two suggestions on Microsoft's Connect website, one for functions and another one for modules, vote them up and maybe we'll get them in v4 :)