Carbon.Windows.psm1
# 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 $script:backConnHostNamesKeyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' $script:backConnHostNamesValueName = 'BackConnectionHostNames' $psModulesRoot = Join-Path -Path $script:moduleRoot -ChildPath 'Modules' -Resolve Import-Module -Name (Join-Path -Path $psModulesRoot -ChildPath 'Carbon.Registry') # 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 $script:moduleRoot -ChildPath 'Functions\*.ps1' if( (Test-Path -Path $functionsPath) ) { foreach( $functionPath in (Get-Item $functionsPath) ) { . $functionPath.FullName } } function Get-CBackConnectionHostName { <# .SYNOPSIS Gets the back connection hostnames configured for the local computer. .DESCRIPTION The `Get-CBackConnectionHostName` function gets the current list of configured back connection hostnames. .EXAMPLE Get-CBackConnectionHostName Demonstrates how to get the back connection hostnames. #> [CmdletBinding()] param( ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState return Get-CRegistryKeyValue -Path $script:backConnHostNamesKeyPath -Name $script:backConnHostNamesValueName } function Get-CEventLog { <# .SYNOPSIS Gets logs from Windows Event Viewer. .DESCRIPTION The `Get-CEventLog` function gets logs from the Windows Event Viewer. Use the `List` parameter to list all of the available event logs. Use the `LogName` parameter to get all of the available event log entries in a specific log. If the log doesn't exist, an error will be written. The `Newest` parameter can be used to get the latest logs. The `EntryType` parameter can be used to filter by the type of log entry. The `Message` parameter can be used to filter by the message of the log entry by matching the text within the log. .EXAMPLE Get-CEventLog -List Demonstrates listing all of the available Windows Event Logs. .EXAMPLE Get-CEventLog -LogName 'Application' Demonstrates getting all of the available event logs in the 'Application' log. .EXAMPLE Get-CEventLog -LogName 'System' -Newest 5 Demonstrates getting the latest 5 logs from the 'System' event logs. .EXAMPLE Get-CEventLog -LogName 'Security' -EntryType 'Error' -Message '*SQL*' Demonstrates getting the event logs from the 'Security' log that have are of type 'Error' and contain 'SQL' in the message. #> [CmdletBinding(DefaultParameterSetName='list')] param( # List all event log categories. [Parameter(Mandatory, ParameterSetName='list')] [switch] $List, # The name of the log to view. [Parameter(Mandatory, ParameterSetName='logs')] [String] $LogName, # The number of latest logs to return. [Parameter(ParameterSetName='logs')] [int] $Newest, # The entry type to filter for. [Parameter(ParameterSetName='logs')] [Diagnostics.EventLogEntryType] $EntryType, # The message to filter for. [Parameter(ParameterSetName='logs')] [String] $Message ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if ($List) { return [Diagnostics.EventLog]::GetEventLogs() } if ($LogName) { if (-not (Test-CEventLog -LogName $LogName)) { $msg = "Failed to get log messages from ${LogName} event log because that event log doesn't exist." Write-Error -Message $msg -ErrorAction $ErrorActionPreference return } $log = [Diagnostics.EventLog]::New($LogName) $entries = $log.Entries if ($EntryType) { $entries = $entries | Where-Object EntryType -Like $EntryType } if ($Message) { $entries = $entries | Where-Object Message -Like $Message } if ($Newest) { $entries = $entries | Select-Object -Last $Newest } return $entries } } function Install-CEventLog { <# .SYNOPSIS Creates an event log if it does not exist. .DESCRIPTION The `Install-CEventLog` function creates an event log on the local computer. If the event log already exists, this function does nothing. .EXAMPLE Install-CEventLog -LogName 'TestApp' -Source 'TestLog' Demonstrates creating a `TestLog` event log and registers `TestApp` as a source for the log. #> [CmdletBinding(SupportsShouldProcess)] param( # The name of the log to be created. [Parameter(Mandatory)] [String] $LogName, # The source of the log to be created. [Parameter(Mandatory)] [String[]] $Source ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState $logExists = Test-CEventLog -LogName $LogName $missingSources = $Source | Where-Object { -not (Test-CEventLog -Source $_) } if ($logExists -and -not $missingSources) { return } New-CEventLog -LogName $LogName -Source $missingSources } function New-CEventLog { <# .SYNOPSIS Creates an event log. .DESCRIPTION The `New-CEventLog` creates a new Windows Event Log on the local computer and registers event sources for the log. This function will write an error if any of the event source already exists. No changes will be made to the system. .EXAMPLE New-CEventLog -LogName 'TestApp' -Source 'TestLog' Demonstrates creating a `TestLog` event log and registers `TestApp` as a source for the log. #> [CmdletBinding(SupportsShouldProcess)] param( # The name of the log to be created. [Parameter(Mandatory)] [String] $LogName, # The source of the events to be written to the log. [Parameter(Mandatory)] [String[]] $Source ) #Requires -RunAsAdministrator Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState foreach ($s in $Source) { if (Test-CEventLog -Source $s) { Write-Error -Message "Event log source '${s}' already exists." -ErrorAction $ErrorActionPreference return } } foreach ($s in $Source) { if ($PSCmdlet.ShouldProcess("$s event log source '${LogName}'", "install")) { [Diagnostics.EventLog]::CreateEventSource($s, $LogName) } } } function Register-CBackConnectionHostName { <# .SYNOPSIS Adds hostnames to the back connection hostname list. .DESCRIPTION The `Register-CBackConnectionHostName` function adds a hostname to the list of back connection hostnames. If the hostname is already in the list, it does nothing. You can pass a single hostname to the `HostName` parameter or pipe in multiple hostnames. .EXAMPLE Register-CBackConnectionHostName -HostName 'example.com' Demonstrates how to add an item to the back connection hostnames list by passing a single hostname to the `HostName` parameter. .EXAMPLE 'example.com', 'example2.com' | Register-CBackConnectionHostName Demonstrates how to add multiple hostnames to the back connction hostnames list by pipeling them to the `Register-CBackConnectionHostName` function. #> [CmdletBinding()] param( # The hostname to add to the list. [Parameter(Mandatory, ValueFromPipeline)] [String] $HostName ) begin { Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState $curHostNames = Get-CBackConnectionHostName $hostnamesToAdd = [Collections.Generic.List[String]]::New() } process { if ($curHostNames -contains $HostName) { return } [void]$hostnamesToAdd.Add($HostName) } end { if ($hostnamesToAdd.Count -eq 0) { return } $newValue = $curHostNames + $hostnamesToAdd Set-CRegistryKeyValue -Path $script:backConnHostNamesKeyPath ` -Name $script:backConnHostNamesValueName ` -Strings $newValue } } function Remove-CEventLog { <# .SYNOPSIS Removes an event log. .DESCRIPTION The `Remove-CEventLog` function removes an event log from the local computer and unregisters all of the associated event sources. If the event log does not exist, an error is written. If the `Source` parameter is specified, only the provided sources will be unregistered. The log associated with the sources will not be removed. If the source does not exist, an error is thrown. .EXAMPLE Remove-CEventLog -LogName 'TestApp' Demonstrates removing the `TestApp` event log. .EXAMPLE Remove-CEventLog -Source 'TestLog' Demonstrates unregistering the `TestLog` event source regardless of the Log it belongs to. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName='LogName')] param( # The name of the log to remove. [Parameter(Mandatory, ParameterSetName='LogName')] [String[]] $LogName, # The name of the event source to remove. [Parameter(Mandatory, ParameterSetName='Source')] [String[]] $Source ) #Requires -RunAsAdministrator Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if ($LogName) { foreach ($log in $LogName) { if (-not (Test-CEventLog -LogName $log)) { Write-Error -Message "Event log '${LogName}' does not exist." -ErrorAction $ErrorActionPreference return } if ($PSCmdlet.ShouldProcess("event log '$LogName'", "delete")) { [Diagnostics.EventLog]::Delete($LogName) } } return } foreach ($s in $Source) { if (-not (Test-CEventLog -Source $s)) { Write-Error -Message "Event log source '${s}' does not exist." -ErrorAction $ErrorActionPreference return } if ($PSCmdlet.ShouldProcess("event log source '${s}'", "uninstall")) { [Diagnostics.EventLog]::DeleteEventSource($s) } } } function Test-CEventLog { <# .SYNOPSIS Tests if an event log exists. .DESCRIPTION The `Test-CEventLog` function checks to see if an event log or event source exists. Use the `LogName` parameter to test if an event log exists. Use the `Source` parameter to test if an event source exists. .EXAMPLE Test-CEventLog -LogName 'TestApp' Demonstrates testing if the `TestApp` event log exists. .EXAMPLE Test-CEventLog -Source 'TestLog' Demonstrate testing if the `TestLog` event source exists. #> [CmdletBinding(DefaultParameterSetName='LogName')] param( [Parameter(Mandatory, ParameterSetName='LogName')] [String] $LogName, [Parameter(Mandatory, ParameterSetName='Source')] [String] $Source ) #Requires -RunAsAdministrator Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if ($LogName) { return [Diagnostics.EventLog]::Exists($LogName) } return [Diagnostics.EventLog]::SourceExists($Source) } function Uninstall-CEventLog { <# .SYNOPSIS Removes an event log if it exists. .DESCRIPTION The Uninstall-CEventLog function removes an event log from the local computer, if it exists. If the event log doesn't exist, the function does nothing. .EXAMPLE Uninstall-CEventLog -LogName 'TestApp' Demonstrates removing the `TestApp` event log. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName='LogName')] param( [Parameter(Mandatory, ParameterSetName='LogName')] [String[]] $LogName, [Parameter(Mandatory, ParameterSetName='Source')] [String[]] $Source ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if ($LogName) { foreach ($log in $LogName) { if (Test-CEventLog -LogName $log) { Remove-CEventLog -LogName $log } } return } foreach ($s in $source) { if (Test-CEventLog -Source $s) { Remove-CEventLog -Source $s } } } function Unregister-CBackConnectionHostName { <# .SYNOPSIS Removes hostnames from the back connection hostname list. .DESCRIPTION The `Unregister-CBackConnectionHostName` function removes a hostname from the list of back connection hostnames. If the hostname is not in the list, it does nothing. You can pass a single hostname to the `HostName` parameter or pipe in multiple hostnames. .EXAMPLE Unregister-CBackConnectionHostName -HostName 'example.com' Demonstrates how to remove an item from the back connection hostnames list by passing a single hostname to the `HostName` parameter. .EXAMPLE 'example.com', 'example2.com' | Unregister-CBackConnectionHostName Demonstrates how to remove multiple hostnames from the back connction hostnames list by pipeling them to the `Register-CBackConnectionHostName` function. #> [CmdletBinding()] param( # The hostname to remove from the back connection hostname list. [Parameter(Mandatory, ValueFromPipeline)] [String] $HostName ) begin { Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState $hostnamesToRemove = [Collections.Generic.List[String]]::New() } process { [void]$hostnamesToRemove.Add($HostName) } end { [String[]] $currentValues = Get-CBackConnectionHostName if ($null -eq $currentValues) { $currentValues = @() } [String[]] $newValues = $currentValues | Where-Object { $_ -notin $hostnamesToRemove } if ($null -eq $newValues) { $newValues = @() } if ($newValues.Count -eq $currentValues.Count) { return } Set-CRegistryKeyValue -Path $script:backConnHostNamesKeyPath ` -Name $script:backConnHostNamesValueName ` -Strings $newValues } } 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) } } } |