Carbon.FileSystem.psm1
using namespace System.Security.AccessControl using namespace System.Collections # Copyright WebMD Health Services # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License #Requires -Version 5.1 Set-StrictMode -Version 'Latest' # Functions should use $moduleRoot as the relative root from which to find # things. A published module has its function appended to this file, while a # module in development has its functions in the Functions directory. $script:moduleRoot = $PSScriptRoot $psModulesPath = Join-Path -Path $script:moduleRoot -ChildPath 'Modules' -Resolve Import-Module -Name (Join-Path -Path $psModulesPath -ChildPath 'Carbon.Security' -Resolve) ` -Function @('Get-CPermission', 'Grant-CPermission', 'Revoke-CPermission', 'Test-CPermission') ` -Verbose:$false Import-Module -Name (Join-Path -Path $psModulesPath -ChildPath 'Carbon.Accounts' -Resolve) ` -Function @('Resolve-CPrincipal', 'Resolve-CPrincipalName') ` -Verbose:$false Add-Type @' using System; using System.Text; using System.Collections.Generic; using System.Runtime.InteropServices; namespace Carbon.FileSystem { public static class Kernel32 { #region WinAPI P/Invoke declarations [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr FindFirstFileNameW(string lpFileName, uint dwFlags, ref uint StringLength, StringBuilder LinkName); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool FindNextFileNameW(IntPtr hFindStream, ref uint StringLength, StringBuilder LinkName); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool FindClose(IntPtr hFindFile); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool GetVolumePathName(string lpszFileName, [Out] StringBuilder lpszVolumePathName, uint cchBufferLength); public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); // 0xffffffff; public const int MAX_PATH = 65535; // Max. NTFS path length. #endregion } } '@ # Store each of your module's functions in its own file in the Functions # directory. On the build server, your module's functions will be appended to # this file, so only dot-source files that exist on the file system. This allows # developers to work on a module without having to build it first. Grab all the # functions that are in their own files. $functionsPath = Join-Path -Path $moduleRoot -ChildPath 'Functions\*.ps1' if( (Test-Path -Path $functionsPath) ) { foreach( $functionPath in (Get-Item $functionsPath) ) { . $functionPath.FullName } } function ConvertTo-CarbonSecurityApplyTo { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [AllowEmptyString()] [AllowNull()] [ValidateSet('FolderOnly', 'FolderSubfoldersAndFiles', 'FolderAndSubfolders', 'FolderAndFiles', 'SubfoldersAndFilesOnly', 'SubfoldersOnly', 'FilesOnly')] [String] $ApplyTo ) process { $map = @{ 'FolderOnly' = 'ContainerOnly'; 'FolderSubfoldersAndFiles' = 'ContainerSubcontainersAndLeaves'; 'FolderAndSubfolders' = 'ContainerAndSubcontainers'; 'FolderAndFiles' = 'ContainerAndLeaves'; 'SubfoldersAndFilesOnly' = 'SubcontainersAndLeavesOnly'; 'SubfoldersOnly' = 'SubcontainersOnly'; 'FilesOnly' = 'LeavesOnly'; } if (-not $ApplyTo) { return } return $map[$ApplyTo] } } function Get-CNtfsHardLink { <# .SYNOPSIS Retrieves hard link targets from a file. .DESCRIPTION Get-CNtfsHardLink retrieves hard link targets from a file given a file path. This fixes compatibility issues between Windows PowerShell and PowerShell Core when retrieving targets from a hard link. .EXAMPLE Get-CNtfsHardLink -Path $Path Demonstrates how to retrieve a hard link given a file path. #> [CmdletBinding()] param( # The path whose hard links to get/return. Must exist. [Parameter(Mandatory)] [String] $Path ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState if( -not (Resolve-Path -LiteralPath $Path) ) { return } try { $sbPath = [Text.StringBuilder]::New([Carbon.FileSystem.Kernel32]::MAX_PATH) $charCount = [uint32]$sbPath.Capacity; # in/out character-count variable for the WinAPI calls. # Get the volume (drive) part of the target file's full path (e.g., @"C:\") [void][Carbon.FileSystem.Kernel32]::GetVolumePathName($Path, $sbPath, $charCount) $volume = $sbPath.ToString(); # Trim the trailing "\" from the volume path, to enable simple concatenation # with the volume-relative paths returned by the FindFirstFileNameW() and FindFirstFileNameW() functions, # which have a leading "\" $volume = $volume.Substring(0, $volume.Length - 1); # Loop over and collect all hard links as their full paths. [IntPtr]$findHandle = [IntPtr]::Zero $findHandle = [Carbon.FileSystem.Kernel32]::FindFirstFileNameW($Path, 0, [ref]$charCount, $sbPath) if( [Carbon.FileSystem.Kernel32]::INVALID_HANDLE_VALUE -eq $findHandle) { $errorCode = [Runtime.InteropServices.Marshal]::GetLastWin32Error() $msg = "Failed to find hard links to path ""$($Path | Split-Path -Relative)"": the system error code is ""$($errorCode)""." Write-Error $msg -ErrorAction $ErrorActionPreference return } do { Join-Path -Path $volume -ChildPath $sbPath.ToString() | Write-Output # Add the full path to the result list. $charCount = [uint32]$sbPath.Capacity; # Prepare for the next FindNextFileNameW() call. } while( [Carbon.FileSystem.Kernel32]::FindNextFileNameW($findHandle, [ref]$charCount, $sbPath) ) [void][Carbon.FileSystem.Kernel32]::FindClose($findHandle); } catch { Write-Error -Message $_ -ErrorAction $ErrorActionPreference } } function Get-FileHardLink { <# .SYNOPSIS ***OBSOLETE.*** Use Get-CNtfsHardLink instead. .DESCRIPTION ***OBSOLETE.*** Use Get-CNtfsHardLink instead. .EXAMPLE Get-CNtfsHardLink -Path $Path Demonstrates that you should use `Get-CNtfsHardLink` instead. #> [CmdletBinding()] param( # The path whose hard links to get/return. Must exist. [Parameter(Mandatory)] [String] $Path ) $msg = 'The Get-FileHardLink function is obsolete and will removed in the next major version of ' + 'Carbon.FileSystem. Please use Get-CNtfsHardLink instead.' Write-Warning -Message $msg Get-CNtfsHardLink @PSBoundParameters } function Get-CNtfsPermission { <# .SYNOPSIS Gets the permissions (access control rules) for a file or directory. .DESCRIPTION The `Get-CNtfsPermission` function gets permissions on a file or directory. Permissions returned are the `[Security.AccessControl.FileSystemAccessRule]` objects from the file/directory's ACL. By default, all non-inherited permissions are returned. Pass the path to the file/directory whose permissions to get to the `Path` parameter. To also get inherited permissions, use the `Inherited` switch. To get the permissions a specific identity has on the file/directory, pass that username/group name to the `Identity` parameter. If the identity doesn't exist, or it doesn't have any permissions, no error is written and nothing is returned. .OUTPUTS System.Security.AccessControl.FileSystemAccessRule. .LINK Get-CNtfsPermission .LINK Grant-CNtfsPermission .LINK Revoke-CNtfsPermission .LINK Test-CNtfsPermission .EXAMPLE Get-CNtfsPermission -Path 'C:\Windows' Returns `System.Security.AccessControl.FileSystemAccessRule` objects for all the non-inherited rules on `C:\windows`. .EXAMPLE Get-CNtfsPermission -Path 'C:\Windows' -Inherited Returns `System.Security.AccessControl.RegistryAccessRule` objects for all the inherited and non-inherited rules on `hklm:\software`. .EXAMPLE Get-CNtfsPermission -Path 'C:\Windows' -Idenity Administrators Returns `System.Security.AccessControl.FileSystemAccessRule` objects for all the `Administrators'` rules on `C:\windows`. #> [CmdletBinding()] [OutputType([Security.AccessControl.FileSystemAccessRule])] param( # The path to the file/directory whose permissions (i.e. access control rules) to return. Wildcards supported. [Parameter(Mandatory, ValueFromPipeline)] [String] $Path, # The identity whose permissiosn (i.e. access control rules) to return. By default, all non-inherited # permissions are returned. [String] $Identity, # Return inherited permissions in addition to explicit permissions. [switch] $Inherited ) process { Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState Get-CPermission @PSBoundParameters } } function Grant-CNtfsPermission { <# .SYNOPSIS Grants permission on folders and files. .DESCRIPTION The `Grant-CNtfsPermission` functions grants permissions to folders and files. Pass the folder/file path to the `Path` parameter, the user/group name to the `Identity` parameter, and the permissions to the `Permission` parameter. By default, the permissions are applied to the folder and inherited to all its subfolders and files. To control how the permissions are applied, use the `ApplyTo` parameter. If you want permissions to only apply to child files and folders, use the `OnlyApplyToChildFilesAndFolders` switch. By default, an "Allow" permission is granted. To add a "Deny" permission, set the value of the `Type` parameter to `Deny`. All existing, non-inherited permissions for the given identity are removed first. If you want to preserve a user/group's existing permissions, use the `Append` switch. To remove *all* non-inherited permissions except the permission being granted, use the `Clear` switch. The permission is only granted if it doesn't exist. To always grant the permission, use the `Force` switch. To get the permission back as a `[System.Security.AccessControl.FileSystemAccessRule]` object, use the `PassThru` switch. .OUTPUTS System.Security.AccessControl.FileSystemAccessRule. .LINK Get-CNtfsPermission .LINK Revoke-CNtfsPermission .LINK Test-CNtfsPermission .LINK http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx .LINK http://msdn.microsoft.com/en-us/magazine/cc163885.aspx#S3 .EXAMPLE Grant-CNtfsPermission -Identity ENTERPRISE\Engineers -Permission FullControl -Path C:\EngineRoom Grants the Enterprise's engineering group full control on the engine room. Very important if you want to get anywhere. .EXAMPLE Grant-CNtfsPermission -Identity ENTERPRISE\Engineers -Permission FullControl -Path C:\EngineRoom -Clear Grants the Enterprise's engineering group full control on the engine room. Any non-inherited, existing access rules are removed from `C:\EngineRoom`. .EXAMPLE Grant-CNtfsPermission -Identity BORG\Locutus -Permission FullControl -Path 'C:\EngineRoom' -Type Deny Demonstrates how to grant deny permissions on an objecy with the `Type` parameter. .EXAMPLE Grant-CNtfsPermission -Path C:\Bridge -Identity ENTERPRISE\Wesley -Permission 'Read' -ApplyTo ContainerAndSubContainersAndLeaves -Append Grant-CNtfsPermission -Path C:\Bridge -Identity ENTERPRISE\Wesley -Permission 'Write' -ApplyTo ContainerAndLeaves -Append Demonstrates how to grant multiple access rules to a single identity with the `Append` switch. In this case, `ENTERPRISE\Wesley` will be able to read everything in `C:\Bridge` and write only in the `C:\Bridge` directory, not to any sub-directory. #> [Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '')] [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName='DefaultAppliesToFlags')] [OutputType([Security.AccessControl.FileSystemAccessRule])] param( # The folder/file path on which the permissions should be granted. [Parameter(Mandatory)] [String] $Path, # The user or group getting the permissions. [Parameter(Mandatory)] [String] $Identity, # The permissions to grant. See # [System.Security.AccessControl.FileSystemRights](http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx) # for the list of rights with descriptions. [Parameter(Mandatory)] [FileSystemRights[]] $Permission, # How to apply the permissions. The default is `FolderSubfoldersAndFiles`. Valid values are: # # * FolderOnly # * FolderSubfoldersAndFiles # * FolderAndSubfolders # * FolderAndFiles # * SubfoldersAndFilesOnly # * SubfoldersOnly # * FilesOnly [Parameter(Mandatory, ParameterSetName='SetAppliesToFlags')] [ValidateSet('FolderOnly', 'FolderSubfoldersAndFiles', 'FolderAndSubfolders', 'FolderAndFiles', 'SubfoldersAndFilesOnly', 'SubfoldersOnly', 'FilesOnly')] [String] $ApplyTo, # Only apply the permissions to files and/or folders within the folder. Don't set this if the Path parameter is # to a file. [Parameter(ParameterSetName='SetAppliesToFlags')] [switch] $OnlyApplyToChildFilesAndFolders, # The type of rule to apply, either `Allow` or `Deny`. The default is `Allow`, which will allow access to the # item. The other option is `Deny`, which will deny access to the item. [AccessControlType] $Type = [AccessControlType]::Allow, # Removes all non-inherited permissions on the item. [switch] $Clear, # Returns an object representing the permission created or set on the `Path`. The returned object will have a # `Path` propery added to it so it can be piped to any cmdlet that uses a path. [switch] $PassThru, # Grants permissions, even if they are already present. [switch] $Force, # When set, adds the permissions as a new access rule instead of replacing any existing access rules. [switch] $Append ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if (-not $ApplyTo -and (Test-Path -Path $Path -PathType Container)) { $ApplyTo = 'FolderSubfoldersAndFiles' } if ($ApplyTo) { $PSBoundParameters['ApplyTo'] = $ApplyTo | ConvertTo-CarbonSecurityApplyTo } if ($PSBoundParameters.ContainsKey('OnlyApplyToChildFilesAndFolders')) { $PSBoundParameters.Remove('OnlyApplyToChildFilesAndFolders') $PSBoundParameters['OnlyApplyToChildren'] = $OnlyApplyToChildFilesAndFolders } Grant-CPermission @PSBoundParameters } function Revoke-CNtfsPermission { <# .SYNOPSIS Revokes *explicit* permissions on folders and files. .DESCRIPTION Revokes all of user/group's *explicit* permissions on a folder or file. Only explicit permissions are considered; inherited permissions are ignored. If the identity doesn't have permission, nothing happens, not even errors written out. .LINK Get-CNtfsPermission .LINK Grant-CNtfsPermission .LINK Test-CNtfsPermission .EXAMPLE Revoke-CNtfsPermission -Identity ENTERPRISE\Engineers -Path 'C:\EngineRoom' Demonstrates how to revoke all of the 'Engineers' permissions on the `C:\EngineRoom` directory. #> [Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '')] [CmdletBinding(SupportsShouldProcess)] param( # The folder or file path on which the permissions should be revoked. [Parameter(Mandatory)] [String] $Path, # The identity losing permissions. [Parameter(Mandatory)] [String] $Identity ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState Revoke-CPermission @PSBoundParameters } function Set-CNtfsOwner { <# .SYNOPSIS Sets the owner of an NTFS file or directory. .DESCRIPTION The `Set-CNtfsOwner` function sets the owner of an NTFS file or directory. Pass the path of the file or directory to the `Path` parameter. Pass the new owner to the `Identity` parameter. If the file or directory isn't owned by the new owner, its ACL is updated. Otherwise, nothing happens. You can also pipe file system objects to the function in place of passing a path. This function requires administrative privileges. .EXAMPLE Set-CNtfsOwner -Path $Path -Identity $username Demonstrates how to set the owner of a file system object to a specific principal. In this example, the file or directory at `$Path` will be owned by `$username`. .EXAMPLE Get-ChildItem -Path $directory | Set-CNtfsOwner -Identity $username Demonstrates that you can pipe items to `Set-CNtfsOwner` to mass change the owner. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName')] [String] $Path, [Parameter(Mandatory)] [String] $Identity ) process { Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if (-not (Test-Path -Path $Path)) { $msg = "Failed to set owner on ""${Path}"" to ""${Identity}"" because that path does not exist." Write-Error -Message $msg -ErrorAction $ErrorActionPreference return } $newOwner = Resolve-CPrincipal -Name $Identity if (-not $newOwner) { Write-Error -Message "Principal ""${Identity}"" not found." -ErrorAction $ErrorActionPreference return } $paths = Resolve-Path -Path $Path foreach ($pathItem in $paths) { $acl = Get-Acl -LiteralPath $pathItem $currentOwner = Resolve-CPrincipalName -Name $acl.Owner if ($currentOwner -eq $newOwner.FullName) { Write-Verbose "Principal ""$($newOwner.FullName)}"" already owns ""${pathItem}""." return } Write-Information "Changing owner of ""${pathItem}"" from ""${currentOwner}"" to ""$($newOwner.FullName)""." $acl.SetOwner($newOwner.Sid) Set-Acl -LiteralPath $pathItem -AclObject $acl } } } function Test-CNtfsPermission { <# .SYNOPSIS Tests if permissions are set on a folder or file. .DESCRIPTION The `Test-CNtfsPermission` function tests if an identity has a permission on a file/folder. Pass the path to check to the `Path` parameter, the user/group name to the `Identity` parameter, and the permission to check for to the `Permission` parameter. If the user/group has the given permission on the given path, the function returns `$true`, otherwise it returns `$false`. Inherited permissions are *not* checked by default. To check inherited permission, use the `-Inherited` switch. By default, the permission check is not exact, i.e. the user may have additional permissions to what you're checking. If you want to make sure the user has *exactly* the permission you want, use the `-Strict` switch. Please note that by default, NTFS will automatically add/grant `Synchronize` permission on an item, which is handled by this function. You can also test how the permission is inherited by using the `ApplyTo` and `OnlyApplyToChildFilesAndFolders` parameters. .OUTPUTS System.Boolean. .LINK Get-CNtfsPermission .LINK Grant-CNtfsPermission .LINK Revoke-CNtfsPermission .LINK http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx .EXAMPLE Test-CNtfsPermission -Identity 'STARFLEET\JLPicard' -Permission 'FullControl' -Path 'C:\Enterprise\Bridge' Demonstrates how to check that Jean-Luc Picard has `FullControl` permission on the `C:\Enterprise\Bridge`. .EXAMPLE Test-CNtfsPermission -Identity 'STARFLEET\Worf' -Permission 'Write' -ApplyTo 'FolderOnly' -Path 'C:\Enterprise\Brig' Demonstrates how to test for inheritance/propogation flags, in addition to permissions. #> [CmdletBinding(DefaultParameterSetName='SkipAppliesToFlags')] param( # The path to a folder/file on which the permissions should be checked. [Parameter(Mandatory)] [String] $Path, # The user or group name whose permissions to check. [Parameter(Mandatory)] [String] $Identity, # The permission to test for: e.g. FullControl, Read, etc. See # [System.Security.AccessControl.FileSystemRights](http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights.aspx) # for the list of rights with descriptions. [Parameter(Mandatory)] [FileSystemRights[]] $Permission, # Checks how the permission is inherited. By default, the permission's inheritance is ignored. # # Valid values are: # # * FolderOnly # * FolderSubfoldersAndFiles # * FolderAndSubfolders # * FolderAndFiles # * SubfoldersAndFilesOnly # * SubfoldersOnly # * FilesOnly [Parameter(Mandatory, ParameterSetName='TestAppliesToFlags')] [ValidateSet('FolderOnly', 'FolderSubfoldersAndFiles', 'FolderAndSubfolders', 'FolderAndFiles', 'SubfoldersAndFilesOnly', 'SubfoldersOnly', 'FilesOnly')] [String] $ApplyTo, # Checks that the permissions are only applied to child files and folders. By default, the permission's # inheritnace is ignored. [Parameter(ParameterSetName='TestAppliesToFlags')] [switch] $OnlyApplyToChildFilesAndFolders, # Include inherited permissions in the check. [switch] $Inherited, # Check for the exact permissions and how the permission is applied, i.e. make sure the identity has # *only* the permissions you specify. [switch] $Strict ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if ($PSCmdlet.ParameterSetName -eq 'TestAppliesToFlags') { if ($ApplyTo) { $PSBoundParameters['ApplyTo'] = $ApplyTo | ConvertTo-CarbonSecurityApplyTo } $PSBoundParameters.Remove('OnlyApplyToChildFilesAndFolders') | Out-Null $PSBoundParameters['OnlyApplyToChildren'] = $OnlyApplyToChildFilesAndFolders } Test-CPermission @PSBoundParameters } function Use-CallerPreference { <# .SYNOPSIS Sets the PowerShell preference variables in a module's function based on the callers preferences. .DESCRIPTION Script module functions do not automatically inherit their caller's variables, including preferences set by common parameters. This means if you call a script with switches like `-Verbose` or `-WhatIf`, those that parameter don't get passed into any function that belongs to a module. When used in a module function, `Use-CallerPreference` will grab the value of these common parameters used by the function's caller: * ErrorAction * Debug * Confirm * InformationAction * Verbose * WarningAction * WhatIf This function should be used in a module's function to grab the caller's preference variables so the caller doesn't have to explicitly pass common parameters to the module function. This function is adapted from the [`Get-CallerPreference` function written by David Wyatt](https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d). There is currently a [bug in PowerShell](https://connect.microsoft.com/PowerShell/Feedback/Details/763621) that causes an error when `ErrorAction` is implicitly set to `Ignore`. If you use this function, you'll need to add explicit `-ErrorAction $ErrorActionPreference` to every `Write-Error` call. Please vote up this issue so it can get fixed. .LINK about_Preference_Variables .LINK about_CommonParameters .LINK https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d .LINK http://powershell.org/wp/2014/01/13/getting-your-script-module-functions-to-inherit-preference-variables-from-the-caller/ .EXAMPLE Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState Demonstrates how to set the caller's common parameter preference variables in a module function. #> [CmdletBinding()] param ( [Parameter(Mandatory)] #[Management.Automation.PSScriptCmdlet] # The module function's `$PSCmdlet` object. Requires the function be decorated with the `[CmdletBinding()]` # attribute. $Cmdlet, [Parameter(Mandatory)] # The module function's `$ExecutionContext.SessionState` object. Requires the function be decorated with the # `[CmdletBinding()]` attribute. # # Used to set variables in its callers' scope, even if that caller is in a different script module. [Management.Automation.SessionState]$SessionState ) Set-StrictMode -Version 'Latest' # List of preference variables taken from the about_Preference_Variables and their common parameter name (taken # from about_CommonParameters). $commonPreferences = @{ 'ErrorActionPreference' = 'ErrorAction'; 'DebugPreference' = 'Debug'; 'ConfirmPreference' = 'Confirm'; 'InformationPreference' = 'InformationAction'; 'VerbosePreference' = 'Verbose'; 'WarningPreference' = 'WarningAction'; 'WhatIfPreference' = 'WhatIf'; } foreach( $prefName in $commonPreferences.Keys ) { $parameterName = $commonPreferences[$prefName] # Don't do anything if the parameter was passed in. if( $Cmdlet.MyInvocation.BoundParameters.ContainsKey($parameterName) ) { continue } $variable = $Cmdlet.SessionState.PSVariable.Get($prefName) # Don't do anything if caller didn't use a common parameter. if( -not $variable ) { continue } if( $SessionState -eq $ExecutionContext.SessionState ) { Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false } else { $SessionState.PSVariable.Set($variable.Name, $variable.Value) } } } |