InstallModule.psm1
# -------------------------- Load Script Files ---------------------------- # # Do not add Export-ModuleMember logic. All functions in .\Public are added to the manifest during build. # If you need to import from \src\ run the build.ps1 in \tools\ with 'ExportFunctionsToSrc' as the task. # $ModuleScriptFiles = @(Get-ChildItem -Path $PSScriptRoot -Filter *.ps1 -Recurse | Where-Object { $_.Name -notlike "*.ps1xml" } ) foreach ($ScriptFile in $ModuleScriptFiles) { try { Write-Verbose "Loading script file $($ScriptFile.Name)" . $ScriptFile.FullName } catch { Write-Error "Error loading script file $($ScriptFile.FullName)" } } #--------------------------------------------------------------------------------- #The sample scripts are not supported under any Microsoft standard support #program or service. The sample scripts are provided AS IS without warranty #of any kind. Microsoft further disclaims all implied warranties including, #without limitation, any implied warranties of merchantability or of fitness for #a particular purpose. The entire risk arising out of the use or performance of #the sample scripts and documentation remains with you. In no event shall #Microsoft, its authors, or anyone else involved in the creation, production, or #delivery of the scripts be liable for any damages whatsoever (including, #without limitation, damages for loss of business profits, business interruption, #loss of business information, or other pecuniary loss) arising out of the use #of or inability to use the sample scripts or documentation, even if Microsoft #has been advised of the possibility of such damages #--------------------------------------------------------------------------------- #requires -version 2.0 <# .SYNOPSIS The PowerShell script which can be used to add trusted sites in Internet Explorer. .DESCRIPTION The PowerShell script which can be used to add trusted sites in Internet Explorer. .PARAMETER TrustedSites Spcifies the trusted site in Internet Explorer. .PARAMETER HTTP Once you use the HTTP switch parameter, the domain will be use the http:// prefix. .PARAMETER PrimaryDomain Spcifies the primary domain in Internet Explorer. .PARAMETER SubDomain Spcifies the sub domain in Internet Explorer. .EXAMPLE C:\PS> C:\AddingTrustedSites.ps1 -TrustedSites "contoso1.com","contoso2.com" -HTTP Successfully added 'contoso1.com' and 'contoso2.com' domain to trusted sites in Internet Explorer. This command will add 'contoso1.com' and 'contoso2.com' domain to trusted sites in Internet Explorer respectively. .EXAMPLE C:\PS> C:\AddingTrustedSites.ps1 -PrimaryDomain "contoso.com" -SubDomain "test.domain" Successfully added 'test.domain.contoso.com' domain to trusted sites in Internet Explorer. This command will add 'test.domain.contoso.com' domain to trusted sites in Internet Explorer. #> function Add-TrustedSite { Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "SingleDomain")] [Alias('Sites')][ValidateNotNullOrEmpty()] [String[]]$TrustedSites, [Parameter(Mandatory = $false)] [Switch]$HTTP, [Parameter(Mandatory = $true, ParameterSetName = "CombineDomain")] [Alias('pdomain')][ValidateNotNullOrEmpty()] [String]$PrimaryDomain, [Parameter(Mandatory = $true, ParameterSetName = "CombineDomain")] [Alias('sdomain')][ValidateNotNullOrEmpty()] [String]$SubDomain ) #Initialize key variables $UserRegPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains" $DWord = 2 #Main function Function CreateKeyReg { Param ( [String]$KeyPath, [String]$Name ) If (Test-Path -Path $KeyPath) { Write-Verbose "Creating a new key '$Name' under $KeyPath." New-Item -Path "$KeyPath" -ItemType File -Name "$Name" ` -ErrorAction SilentlyContinue | Out-Null } Else { Write-Warning "The path '$KeyPath' not found." } } Function SetRegValue { Param ( [Boolean]$blnHTTP = $false, [String]$RegPath ) Try { If ($blnHTTP) { Write-Verbose "Creating a Dword value named 'HTTP' and set the value to 2." Set-ItemProperty -Path $RegPath -Name "http" -Value $DWord ` -ErrorAction SilentlyContinue | Out-Null } Else { Write-Verbose "Creating a Dword value named 'HTTPS' and set the value to 2." Set-ItemProperty -Path $RegPath -Name "https" -Value $DWord ` -ErrorAction SilentlyContinue | Out-Null } } Catch { Write-Verbose "Failed to add trusted sites in Internet Explorer." -BackgroundColor Red } } If ($TrustedSites) { #Adding trusted sites in the registry Foreach ($TruestedSite in $TrustedSites) { #If user does not specify the user type. By default,the script will add the trusted sites for the current user. If ($HTTP) { CreateKeyReg -KeyPath $UserRegPath -Name $TruestedSite SetRegValue -RegPath "$UserRegPath\$TruestedSite" -blnHTTP $true -DWord $DWord Write-Verbose "Successfully added '$TruestedSite' domain to trusted Sites in Internet Explorer." } Else { CreateKeyReg -KeyPath $UserRegPath -Name $TruestedSite SetRegValue -RegPath "$UserRegPath\$TruestedSite" -blnHTTP $false -DWord $DWord Write-Verbose "Successfully added '$TruestedSite' domain to to trusted Sites in Internet Explorer." } } } Else { #Setting the primary domain and sub-domain If ($HTTP) { CreateKeyReg -KeyPath $UserRegPath -Name $PrimaryDomain CreateKeyReg -KeyPath "$UserRegPath\$PrimaryDomain" -Name $SubDomain SetRegValue -RegPath "$UserRegPath\$PrimaryDomain\$SubDomain" -blnHTTP $true -DWord $DWord Write-Verbose "Successfully added $SubDomain.$PrimaryDomain' domain to trusted Sites in Internet Explorer." } Else { CreateKeyReg -KeyPath $UserRegPath -Name $PrimaryDomain CreateKeyReg -KeyPath "$UserRegPath\$PrimaryDomain" -Name $SubDomain SetRegValue -RegPath "$UserRegPath\$PrimaryDomain\$SubDomain" -blnHTTP $false -DWord $DWord Write-Verbose "Successfully added '$SubDomain.$PrimaryDomain' domain to trusted Sites in Internet Explorer." } } } function Compare-Hashtable { <# .SYNOPSIS Compare two Hashtable and returns an array of differences. .DESCRIPTION The Compare-Hashtable function computes differences between two Hashtables. Results are returned as an array of objects with the properties: "key" (the name of the key that caused a difference), "side" (one of "<=", "!=" or "=>"), "lvalue" an "rvalue" (resp. the left and right value associated with the key). .PARAMETER left The left hand side Hashtable to compare. .PARAMETER right The right hand side Hashtable to compare. .EXAMPLE Returns a difference for ("3 <="), c (3 "!=" 4) and e ("=>" 5). Compare-Hashtable @{ a = 1; b = 2; c = 3 } @{ b = 2; c = 4; e = 5} .EXAMPLE Returns a difference for a ("3 <="), c (3 "!=" 4), e ("=>" 5) and g (6 "<="). $left = @{ a = 1; b = 2; c = 3; f = $Null; g = 6 } $right = @{ b = 2; c = 4; e = 5; f = $Null; g = $Null } Compare-Hashtable $left $right #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [Hashtable]$Left, [Parameter(Mandatory = $true)] [Hashtable]$Right ) function NewResult($Key, $LValue, $Side, $RValue) { New-Object -Type PSObject -Property @{ key = $Key lvalue = $LValue rvalue = $RValue side = $Side } } [Object[]]$Results = $Left.Keys | ForEach-Object { if ($Left.ContainsKey($_) -and !$Right.ContainsKey($_)) { NewResult -Key $_ -LValue $Left[$_] -Side "<=" -RValue $Null } else { $LValue, $RValue = $Left[$_], $Right[$_] if ($LValue -ne $RValue) { NewResult -Key $_ -LValue $LValue -Side "!=" -RValue $RValue } } } $Results += $Right.Keys | ForEach-Object { if (!$Left.ContainsKey($_) -and $Right.ContainsKey($_)) { NewResult -Key $_ -LValue $Null -Side "=>" -RValue $Right[$_] } } $Results } function ConvertTo-PlainText { param ( [SecureString]$EncryptedString ) $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($EncryptedString) Return [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) } function Get-CredentialManagerCredential { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] param ( [String]$Target, [String]$User='User' ) ConvertFrom-CredMan(credman -GetCred -Target "$(if ($User){"$User@$Target"} else {$Target})") } <# .SYNOPSIS Function to return the download folder .DESCRIPTION Function to return the download folder since it is not a standard well known folder .EXAMPLE Get-DownloadFolder .NOTES Author: Mark Evans General notes #> function Get-DownloadFolder { Get-KnownFolderPath -KnownFolder 'Downloads' } function Get-EnvironmentVariable { param ( [string]$Name ) foreach ($item in [enum]::getvalues([System.EnvironmentVariableTarget])) { $result = [System.Environment]::GetEnvironmentVariable($Name,$item) if ($result){ break } } $result } function Get-InstalledSoftware { <# .SYNOPSIS Retrieves a list of all software installed on a Windows computer. .EXAMPLE PS> Get-InstalledSoftware This example retrieves all software installed on the local computer. .PARAMETER ComputerName If querying a remote computer, use the computer name here. .PARAMETER Name The software title you'd like to limit the query to. .PARAMETER Guid The software GUID you'e like to limit the query to #> [CmdletBinding()] param ( [Parameter()] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter()] [guid]$Guid ) process { #$args[0].GetEnumerator() | ForEach-Object { New-Variable -Name $_.Key -Value $_.Value } $UninstallKeys = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ) New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null $UninstallKeys += Get-ChildItem HKU: | Where-Object { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+$' } | ForEach-Object { "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall" } if (-not $UninstallKeys) { Write-Warning -Message 'No software registry keys found' } else { foreach ($UninstallKey in $UninstallKeys) { $friendlyNames = @{ 'DisplayName' = 'Name' 'DisplayVersion' = 'Version' } Write-Verbose -Message "Checking uninstall key [$($UninstallKey)]" if ($Name) { $WhereBlock = { $_.GetValue('DisplayName') -like "$Name*" } } elseif ($GUID) { $WhereBlock = { $_.PsChildName -eq $Guid.Guid } } else { $WhereBlock = { $_.GetValue('DisplayName') } } $SwKeys = Get-ChildItem -Path $UninstallKey -ErrorAction SilentlyContinue | Where-Object $WhereBlock if (-not $SwKeys) { Write-Verbose -Message "No software keys in uninstall key $UninstallKey" } else { foreach ($SwKey in $SwKeys) { $output = @{ } foreach ($ValName in $SwKey.GetValueNames()) { if ($ValName){ if ($ValName -ne 'Version') { $output.InstallLocation = '' if ($ValName -eq 'InstallLocation' -and ($SwKey.GetValue($ValName)) -and (@('C:', 'C:\Windows', 'C:\Windows\System32', 'C:\Windows\SysWOW64') -notcontains $SwKey.GetValue($ValName).TrimEnd('\'))) { $output.InstallLocation = $SwKey.GetValue($ValName).TrimEnd('\') } [string]$ValData = $SwKey.GetValue($ValName) if ($friendlyNames[$ValName]) { $output[$friendlyNames[$ValName]] = $ValData.Trim() ## Some registry values have trailing spaces. } else { $output[$ValName] = $ValData.Trim() ## Some registry values trailing spaces } }} } $output.GUID = '' if ($SwKey.PSChildName -match '\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b') { $output.GUID = $SwKey.PSChildName } if ($output.name -eq 'Microsoft .NET Framework 4 Multi-Targeting Pack'){ Write-Output 'Test' } New-Object -TypeName PSObject -Prop $output } } } } } } Function Get-ListeningTCPConnections { [cmdletbinding()] param( ) try { $TCPProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() $Connections = $TCPProperties.GetActiveTcpListeners() foreach ($Connection in $Connections) { if ($Connection.address.AddressFamily -eq "InterNetwork" ) { $IPType = "IPv4" } else { $IPType = "IPv6" } $OutputObj = New-Object -TypeName PSobject $OutputObj | Add-Member -MemberType NoteProperty -Name "LocalAddress" -Value $connection.Address $OutputObj | Add-Member -MemberType NoteProperty -Name "ListeningPort" -Value $Connection.Port $OutputObj | Add-Member -MemberType NoteProperty -Name "IPV4Or6" -Value $IPType $OutputObj } } catch { Write-Error "Failed to get listening connections. $_" } } function Get-NewPassword { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] param ( [Int]$Length = 15 ) try { [reflection.assembly]::loadwithpartialname("system.web") | Out-Null } catch { Write-Log -Level ERROR -Message 'Unable to load module to create new password' } do { $Password = [System.Web.Security.Membership]::GeneratePassword($Length, 3) } until (!($Password.Contains(';') -or ($Password.Contains("'")))) Return ConvertTo-SecureString -String "$Password" -AsPlainText -Force } function Get-WebHostingURI ($Channel) { $VersionURL = "https://dotnetcli.blob.core.windows.net/dotnet/aspnetcore/Runtime/$Channel/latest.version" $CurrentProgressPreference = $ProgressPreference $ProgressPreference = 'SilentlyContinue' # Subsequent calls do not display UI. $RequestResult = Invoke-WebRequest -UseBasicParsing -Uri $VersionURL if ($RequestResult.StatusCode -eq 200) { $LatestVersion = $RequestResult.Content } else { Write-Error -Message "Error Checking Version $($RequestResult.StatusDescription)" Exit } $URL = "https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-aspnetcore-$LatestVersion-windows-hosting-bundle-installer" #ensure we get a response even if an error's returned $response = try { $RequestResult = Invoke-WebRequest -Uri $URL -UseBasicParsing -ErrorAction Stop $RequestResult.BaseResponse } catch [System.Net.Http.HttpRequestException] { Write-Verbose "An exception was caught: $($_.Exception.Message)" $_.Exception.Response } $ProgressPreference = $CurrentProgressPreference # Subsequent calls do display UI. if ($response.StatusCode -eq 200) { $FileUri = [Uri]($RequestResult.content | select-string "window.open\("".*?""" | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value).Replace("window.open(", '').replace('"', '') } else { Write-Error -Message "Error Checking Version $($response)" Return $null } Return $FileUri } Function New-ScriptParameterFolder { <# .SYNOPSIS Script to Allow user to input values for script parameters .NOTES Name: New-ScriptParameterFolder Author: Mark Evans <mark@madspaniels.co.uk> Version: 1.0 DateCreated: 2021-06-23 .EXAMPLE New-ScriptParameterFolder .LINK #> [CmdletBinding(SupportsShouldProcess)] param( [String]$CommandPath = $MyInvocation.PSCommandPath ) $script = Get-ChildItem -Path $CommandPath $scriptParameters = (Get-Command $Script).Parameters.GetEnumerator() | Where-Object { $_.Value.ParameterType -eq [System.IO.DirectoryInfo] } foreach ($item in $scriptParameters) { $Param = $item.Value [System.IO.DirectoryInfo]$Folder = $PSCmdlet.SessionState.PSVariable.GetValue($Param.Name) If ($Folder -and !($Folder.Exists) -and $PSCmdlet.ShouldProcess($Directory.Name, "Create Folder")) { Write-Log -Level WARNING -Message "Creating Folder {0}" -Arguments $Directory.FullName $Folder = New-Item $Folder.FullName -ItemType Directory -Force $PSCmdlet.SessionState.PSVariable.Set($Param.Name, $Folder) } } } function New-Shortcut { [CmdletBinding(SupportsShouldProcess)] param ( [System.IO.FileInfo] [Parameter(Position = 0, Mandatory = $true, ParameterSetName = "Item")] [IO.FileInfo] $Target, [Parameter(Position = 0, Mandatory = $true, ParameterSetName = "Name")] [string] $TargetPath, [Parameter(Position = 0, Mandatory = $true, ParameterSetName = "Link")] [System.Uri]$TargetLink, [String] $ShortcutName, [String] $ShortcutFolder, [switch] $Desktop, [switch] $AllUsers ) if ($PSCmdlet.ParameterSetName -eq "Link") { $TargetPath = $TargetLink.AbsoluteUri if (!($ShortcutName)) { $ShortcutName = $TargetLink.Segments | Select-Object -Last 1 } } else { if ($PSCmdlet.ParameterSetName -eq "Item") { $TargetPath = Get-ChildItem $Target.FullName } if (!(Test-Path -Path $TargetPath -PathType Leaf)) { throw [System.IO.FileNotFoundException] "$TargetPath not found." } if ($PSCmdlet.ParameterSetName -eq 'Name') { $Target = Get-ChildItem $TargetPath } if (!($ShortcutName)) { $ShortcutName = $Target.BaseName } } if (!($ShortcutName.EndsWith('.lnk'))) { $ShortcutName += '.lnk' } if ($AllUsers) { if ($Desktop) { $ShortcutRoot = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonDesktopDirectory) } else { $ShortcutRoot = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonStartMenu) } } else { if ($Desktop) { $ShortcutRoot = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::DesktopDirectory) } else { $ShortcutRoot = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::StartMenu) } } If (!($Desktop)) { $ShortcutRoot = Join-Path $ShortcutRoot 'Programs' } if ($ShortcutFolder) { $ShortcutRoot = Join-Path $ShortcutRoot $ShortcutFolder New-Item -Path $ShortcutRoot -ItemType Directory -Force | Out-Null } $ShortcutLink = Join-Path $ShortcutRoot $ShortcutName if ($PSCmdlet.ShouldProcess("$ShortcutLink", "Create")) { $WshShell = New-Object -comObject WScript.Shell $Shortcut = $WshShell.CreateShortcut($ShortcutLink) $Shortcut.TargetPath = $TargetPath $Shortcut.Save() return (Get-ChildItem $ShortcutLink) } return } Function Read-ScriptParameter { <# .SYNOPSIS Script to Allow user to input values for script parameters .NOTES Name: Read-ScriptParameter Author: Mark Evans <mark@madspaniels.co.uk> Version: 1.1 DateCreated: 2021-02-05 .EXAMPLE Read-ScriptParameter .LINK #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Scope = 'Function', Target = '*')] [CmdletBinding()] param( [switch]$Reset, [String]$CommandPath = $MyInvocation.PSCommandPath ) $script = Get-ChildItem -Path $CommandPath $scriptParameters = (Get-Command $Script).Parameters.GetEnumerator() | Where-Object { $_.Key -notin ([System.Management.Automation.Cmdlet]::CommonParameters) } $ast = (Get-Command $Script).ScriptBlock.Ast foreach ($item in $scriptParameters) { $Param = $item.Value #$Param.Attributes.Add([ValueAttribute]::new('Test','One','two')) $CurrentValue = $PSCmdlet.SessionState.PSVariable.GetValue($Param.Name) $ParamDefinition = ($ast.ParamBlock.Parameters | Where { $_.Name.VariablePath -like $Param.Name }) if ($ParamDefinition.DefaultValue) { $CurrentDefaultValue = (Invoke-Expression $ParamDefinition.DefaultValue) -As $ParamDefinition.StaticType } if ($CurrentValue -and $CurrentValue.ToString() -eq $CurrentDefaultValue.ToString()){ $PSCmdlet.SessionState.PSVariable.Set($Param.Name,$null) } } foreach ($item in $scriptParameters) { $Param = $item.Value Write-Log -Level Verbose -Message "Updating Parameter {0}" -Arguments $Param.Name $value = $null # Get Help Message $Message = ($Param.Attributes | Where-Object { $_.TypeId -eq [System.Management.Automation.ParameterAttribute] }).HelpMessage # If validateion Set get allowed values $Set = ($Param.Attributes | Where-Object { $_.TypeId -eq [System.Management.Automation.ValidateSetAttribute] } ).ValidValues If ($Set) { $SetString = "($($Set -join ','))" } else { $SetString = "" } # Load default value from Parameter File if exists or use default parameter $DefaultValues = @{} $ParamDataFile = Join-Path (Split-Path $PROFILE.CurrentUserAllHosts -Parent) "$($Param.Name).xml" If ($Reset -and (Test-Path $ParamDataFile)) { Remove-Item $ParamDataFile -Force Write-Log -Level DEBUG -Message "Parameter {0} Saved Value reset" -Arguments $Param.Name } If ($Reset) { if ($Param.Name -like 'Env*') { Set-EnvironmentVariable -Name $param.Name.Remove(0, 3).Replace('PATH', '') -Value $null -Scope Machine Set-EnvironmentVariable -Name $param.Name.Remove(0, 3).Replace('PATH', '') -Value $null -Scope User [System.Environment]::SetEnvironmentVariable($param.Name.Remove(0, 3).Replace('PATH', ''), $null) Write-Log -Level DEBUG -Message "Parameter {0} Environment Value reset" -Arguments $Param.Name } } # If Param DataFile exists retrieve saved value If (Test-Path $ParamDataFile -PathType Leaf) { $DefaultValues["Saved"] = Import-Clixml $ParamDataFile } else { $DefaultValues["Saved"] = $null } # Retrieve any value from the calling script $DefaultValues["Script"] = $PSCmdlet.SessionState.PSVariable.GetValue($Param.Name) If ($Param.Name.ToLower().StartsWith('env')) { $DefaultValues["Environment"] = [System.Environment]::GetEnvironmentVariable($param.Name.Remove(0, 3).Replace('PATH','')) } else { $DefaultValues["Environment"] = $null } # If default value defined in script retrieve expression and determine value $ParamDefinition = ($ast.ParamBlock.Parameters | Where { $_.Name.VariablePath -like $Param.Name }) if ($ParamDefinition.DefaultValue) { $DefaultValues["Definition"] = Invoke-Expression $ParamDefinition.DefaultValue } else { if ($Param.SwitchParameter) { $DefaultValues["Definition"] = $false } else { $DefaultValues["Definition"] = $null } } # For Secure String retrieve value from credential manager If ($Param.ParameterType -eq [securestring]) { #Retrieve Password from credential manager assuming user parameter also exists $UserParamName = $Param.Name.Replace('Password', 'User') if ($scriptParameters.Key -contains $UserParamName) { $UserName = $PSCmdlet.SessionState.PSVariable.GetValue($UserParamName) If (!($UserName)) { $UserName = $Param.Name } $Target="PowerShell" $UserCredential = Get-CredentialManagerCredential -Target $Target -User $UserName $DefaultValues["Credential"] = $UserCredential.SecurePass If ($Message -and $UserCredential.SecurePass) { $Message = "$Message - Default from Credential Manager" } } } $DefaultValues.Keys | ForEach-Object { Write-Log -Level Verbose -Message "[{0}]`t{1}" -Arguments $_, $DefaultValues[$_] } # Select Default Value if ($DefaultValues["Credential"]) { $defaultset = "Credential" } elseif (($DefaultValues["Script"]) -and $DefaultValues["Script"] -ne $DefaultValues["Definition"]) { $defaultset = "Script" } elseif ($Param.Name.ToLower().StartsWith('env') -and $DefaultValues["Environment"]) { $defaultset = "Environment" } elseif ($DefaultValues["Saved"]) { $defaultset = "Saved" } else { $defaultset = "Definition" } $default = $DefaultValues[$defaultset] Write-Log -Level VERBOSE -Message "[{0}] default {1} from {2}" -Arguments $param.Name, $default, $defaultset If (!([string]::IsNullOrEmpty($Message))) { If (Test-Interactive) { # If running interactively ask User to enter value - repeat until valid value if validate set do { Wait-Logging if ($Param.ParameterType -eq [securestring] -and $default) { $defaultString = '**********' } else { $defaultString = $default } if (!($value = Read-Host -Prompt "$Message $SetString [$defaultString]" -AsSecureString:($Param.ParameterType -eq [securestring]))) { $value = $default -as $Param.ParameterType } if ($value.GetType() -eq [securestring] -and $value.Length -eq 0){ $value = $default } } while ($value -notin $set -and $set) } else { # If not interactive set default value $value = $default } $UpdateDefault = ($value -ne $default) # For switch parameters convert value to boolean if ($Param.SwitchParameter -and $value) { if ($value.ToString() -eq "False" -or $value.ToString() -eq '0') { $value = $false } else { $value = $true } } # Set calling script variable to value $PSCmdlet.SessionState.PSVariable.Set($Param.Name, $Value) Set-Variable $Param.Name -Value $value } # If Script Variable changed from default save to XML if ($UpdateDefault) { Write-Log -Level WARNING -Message "Updating Default for {0} to {1}" -Arguments $Param.Name, "$(if ($Param.ParameterType -eq [securestring]){'******'}else{$value})" if ($Param.ParameterType -eq [securestring] -and ($UserParamName) -and ($value)) { $null = Set-CredentialManagerCredential -Target $Target -Pass $value -User $UserName -Comment "Set by install script $(Get-Date)" } elseif ($Param.Name -like 'ENV*') { Set-EnvironmentVariable -Name $param.Name.Remove(0, 3).Replace('PATH', '') -Value $value -Scope Machine } elseif ($PSCmdlet.SessionState.PSVariable.GetValue($Param.Name) -ne $DefaultValues["Definition"]) { if ((Test-Path $ParamDataFile) -and (get-item $ParamDataFile -Force).Attributes.HasFlag([System.IO.FileAttributes]::Hidden)) { (get-item $ParamDataFile -force).Attributes -= 'Hidden' } Export-Clixml $ParamDataFile -InputObject $PSCmdlet.SessionState.PSVariable.GetValue($Param.Name) -Force (get-item $ParamDataFile -force).Attributes += 'Hidden' } } } } function Read-ScriptParam { [CmdletBinding()] param ( # Specifies a path to one or more locations. [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Path to one or more locations.")] [Alias("PSPath")] [ValidateNotNullOrEmpty()] [string[]] $ScriptPath = $MyInvocation.PSCommandPath, [switch] $NoSaveParameters ) begin { Write-Log -Level VERBOSE -Message "BEGIN" $NonInteractive = Test-IsNonInteractiveShell Write-Log -Level WARNING -Message "Non Interactive : $NonInteractive" } process { $script = Get-ChildItem -Path $ScriptPath $scriptParameters = (Get-Command $Script).Parameters.GetEnumerator() | Where-Object { $_.Key -notin ([System.Management.Automation.Cmdlet]::CommonParameters) } $MaxLength = $scriptParameters.Key | Sort-Object -Property Length | Select-Object -Last 1 | Select-Object -ExpandProperty Length $ast = (Get-Command $Script).ScriptBlock.Ast $paramtoremove = @() foreach ($item in $scriptParameters) { $Param = $item.Value $CurrentValue = $PSCmdlet.SessionState.PSVariable.GetValue($Param.Name) Set-Variable -Name $Param.Name $CurrentValue $ParamDefinition = ($ast.ParamBlock.Parameters | Where-Object { $_.Name.VariablePath -like $Param.Name }) if ($ParamDefinition.DefaultValue) { $CurrentDefaultValue = (Invoke-Expression $ParamDefinition.DefaultValue) -As $ParamDefinition.StaticType } if ($CurrentValue -and $CurrentValue.ToString() -eq $CurrentDefaultValue.ToString()) { $paramtoremove += $Param.Name } } foreach ($item in $paramtoremove) { Remove-Variable -Name $Item -ErrorAction SilentlyContinue try { $PSCmdlet.SessionState.PSVariable.Remove($item) } catch { } } foreach ($item in $scriptParameters) { $Param = $item.Value Write-Log -Level Verbose -Message "Updating Parameter {0}" -Arguments $Param.Name # Get Help Message $Message = ($Param.Attributes | Where-Object { $_.TypeId -eq [System.Management.Automation.ParameterAttribute] }).HelpMessage # If default value defined in script retrieve expression and determine value $ParamDefinition = $ast.paramblock.parameters | Where-Object { $_.Name.ToString().Replace('$', '') -like $Param.Name } $default = Get-ParameterDefault ($ParamDefinition) $count = 0 if ($default) { if ($Param.ParameterType -eq [securestring]) { $defaultString = ''.PadRight($default.Length, '*') } else { $defaultString = $default.ToString() } } else { $defaultString = '' } do { If (($Message)) { Wait-Logging } If (!($Message) -or $NonInteractive -or !($value = Read-Host -Prompt "$Message $SetString [$defaultString]" -AsSecureString:($Param.ParameterType -eq [securestring]))) { $value = $default } if ($value.GetType() -eq [securestring] -and $value.Length -eq 0) { $value = $default } if ($Param.SwitchParameter) { $type = [bool] } else { $type = $Param.ParameterType } $value = $value -as $type $count++ } until ((Test-ParameterValidation $Param $value) -or $count -gt 3) if ($count -gt 3) { throw [System.Management.Automation.ValidationMetadataException]::new("$($Param.Name) -> $value") } $PSCmdlet.SessionState.PSVariable.Set($Param.Name, $Value) Set-Variable -Name $Param.Name -Value $value Write-Log -Level INFO "Arg:{0} = <{1}>" -Arguments $Param.Name.PadRight($MaxLength, " "), $Value if (($value -ne $default -or (Test-ParameterIsEnvironmentValue $ParamDefinition)) -and !($NoSaveParameters)) { Write-Log -Level WARNING -Message "Saving new default value for {0}" -Arguments $Param.Name Save-ParameterValue ($ParamDefinition) } if ($param.ParameterType -eq [System.IO.DirectoryInfo] -and !($value.exists)) { $f = New-Item -Path $value.FullName -ItemType Directory -Force Write-Log -Level WARNING -Message "Creating Folder {0}" -Arguments $f.FullName } } } end { } } Function Remove-DBTableData { <# .SYNOPSIS Runs a script containing statements supported by the SQL Server SQLCMD utility. .DESCRIPTION The Invoke-Sqlcmd cmdlet runs a script containing the languages and commands supported by the SQL Server SQLCMD utility. The commands supported are Transact-SQL statements and the subset of the XQuery syntax that is supported by the database engine. This cmdlet also accepts many of the commands supported natively by SQLCMD, such as GO and QUIT. This cmdlet also accepts the SQLCMD scripting variables, such as SQLCMDUSER. By default, this cmdlet does not set SQLCMD scripting variables. This cmdlet does not support the use of commands that are primarily related to interactive script editing. The commands not supported include :!!, :connect, :error, :out, :ed, :list, :listvar, :reset, :perftrace, and :serverlist. When this cmdlet is run, the first result set that the script returns is displayed as a formatted table. If subsequent result sets contain different column lists than the first, those result sets are not displayed. If subsequent result sets after the first set have the same column list, their rows are appended to the formatted table that contains the rows that were returned by the first result set. You can display SQL Server message output, such as those that result from the SQL PRINT statement, by specifying the Verbose parameter. .PARAMETER AbortOnError Indicates that this cmdlet stops the SQL Server command and returns an error level to the Windows PowerShell ERRORLEVEL variable if this cmdlet encounters an error. The error level returned is 1 if the error has a severity higher than 10, and the error level is 0 if the error has a severity of 10 or less. If the ErrorLevel parameter is also specified, this cmdlet returns 1 only if the error message severity is also equal to or higher than the value specified for ErrorLevel. .PARAMETER AccessToken A valid access token to be used to authenticate to SQL Server, in alternative to user/password or Windows Authentication. This can be used, for example, to connect to `SQL Azure DB` and `SQL Azure Managed Instance` using a `Service Principal` or a `Managed Identity` (see references at the bottom of this page) Do not specify UserName , Password , or Credential when using this parameter. .PARAMETER ConnectionString Specifies a connection string to connect to the server. .PARAMETER ConnectionTimeout Specifies the number of seconds when this cmdlet times out if it cannot successfully connect to an instance of the Database Engine. The timeout value must be an integer value between 0 and 65534. If 0 is specified, connection attempts do not time out. .PARAMETER Credential The PSCredential object whose Username and Password fields will be used to connect to the SQL instance. .PARAMETER Database Specifies the name of a database. This cmdlet connects to this database in the instance that is specified in the ServerInstance parameter. If the Database parameter is not specified, the database that is used depends on whether the current path specifies both the SQLSERVER:\SQL folder and a database name. If the path specifies both the SQL folder and a database name, this cmdlet connects to the database that is specified in the path. If the path is not based on the SQL folder, or the path does not contain a database name, this cmdlet connects to the default database for the current login ID. If you specify the IgnoreProviderContext parameter switch, this cmdlet does not consider any database specified in the current path, and connects to the database defined as the default for the current login ID. .PARAMETER DedicatedAdministratorConnection Indicates that this cmdlet uses a Dedicated Administrator Connection (DAC) to connect to an instance of the Database Engine. DAC is used by system administrators for actions such as troubleshooting instances that will not accept new standard connections. The instance must be configured to support DAC. If DAC is not enabled, this cmdlet reports an error and will not run. .PARAMETER DisableCommands Indicates that this cmdlet turns off some sqlcmd features that might compromise security when run in batch files. It prevents Windows PowerShell variables from being passed in to the Invoke-Sqlcmd script. The startup script specified in the SQLCMDINI scripting variable is not run. .PARAMETER DisableVariables Indicates that this cmdlet ignores sqlcmd scripting variables. This is useful when a script contains many INSERT statements that may contain strings that have the same format as variables, such as $(variable_name). .PARAMETER EncryptConnection Indicates that this cmdlet uses Secure Sockets Layer (SSL) encryption for the connection to the instance of the Database Engine specified in the ServerInstance parameter. If this parameter is specified, SSL encryption is used. If you do not specify this parameter, specified encryption is not used. .PARAMETER ErrorLevel Specifies that this cmdlet display only error messages whose severity level is equal to or higher than the value specified. All error messages are displayed if this parameter is not specified or set to 0. Database Engine error severities range from 1 to 24. .PARAMETER HostName Specifies a workstation name. The workstation name is reported by the sp_who system stored procedure and in the hostname column of the sys.processes catalog view. If this parameter is not specified, the default is the name of the computer on which Invoke-Sqlcmd is run. This parameter can be used to identify different Invoke-Sqlcmd sessions. .PARAMETER IgnoreProviderContext Indicates that this cmdlet ignores the database context that was established by the current SQLSERVER:\SQL path. If the Database parameter is not specified, this cmdlet uses the default database for the current login ID or Windows account. .PARAMETER IncludeSqlUserErrors Indicates that this cmdlet returns SQL user script errors that are otherwise ignored by default. If this parameter is specified, this cmdlet matches the default behavior of the sqlcmd utility. .PARAMETER InputFile Specifies a file to be used as the query input to this cmdlet. The file can contain Transact-SQL statements, XQuery statements, and sqlcmd commands and scripting variables. Specify the full path to the file. Spaces are not allowed in the file path or file name. The file is expected to be encoded using UTF-8. You should only run scripts from trusted sources. Ensure all input scripts are secured with the appropriate NTFS permissions. .PARAMETER MaxBinaryLength Specifies the maximum number of bytes returned for columns with binary string data types, such as binary and varbinary. The default value is 1,024 bytes. .PARAMETER MaxCharLength Specifies the maximum number of characters returned for columns with character or Unicode data types, such as char, nchar, varchar, and nvarchar. The default value is 4,000 characters. .PARAMETER NewPassword Specifies a new password for a SQL Server Authentication login ID. This cmdlet changes the password and then exits. You must also specify the Username and Password parameters, with Password that specifies the current password for the login. .PARAMETER OutputAs Specifies the type of the results this cmdlet gets. If you do not specify a value for this parameter, the cmdlet sets the value to DataRows. .PARAMETER OutputSqlErrors Indicates that this cmdlet displays error messages in the Invoke-Sqlcmd output. .PARAMETER Password Specifies the password for the SQL Server Authentication login ID that was specified in the Username parameter. Passwords are case-sensitive. When possible, use Windows Authentication. Do not use a blank password, when possible use a strong password. If you specify the Password parameter followed by your password, the password is visible to anyone who can see your monitor. If you code Password followed by your password in a .ps1 script, anyone reading the script file will see your password. Assign the appropriate NTFS permissions to the file to prevent other users from being able to read the file. .PARAMETER QueryTimeout Specifies the number of seconds before the queries time out. If a timeout value is not specified, the queries do not time out. The timeout must be an integer value between 1 and 65535. .PARAMETER ServerInstance Specifies a character string or SQL Server Management Objects (SMO) object that specifies the name of an instance of the Database Engine. For default instances, only specify the computer name: MyComputer. For named instances, use the format ComputerName\InstanceName. .PARAMETER SeverityLevel Specifies the lower limit for the error message severity level this cmdlet returns to the ERRORLEVEL Windows PowerShell variable. This cmdlet returns the highest severity level from the error messages generated by the queries it runs, provided that severity is equal to or higher than specified in the SeverityLevel parameter. If SeverityLevel is not specified or set to 0, this cmdlet returns 0 to ERRORLEVEL. The severity levels of Database Engine error messages range from 1 to 24. This cmdlet does not report severities for informational messages that have a severity of 10 .PARAMETER SuppressProviderContextWarning Indicates that this cmdlet suppresses the warning that this cmdlet has used in the database context from the current SQLSERVER:\SQL path setting to establish the database context for the cmdlet. .PARAMETER Username Specifies the login ID for making a SQL Server Authentication connection to an instance of the Database Engine. The password must be specified through the Password parameter. If Username and Password are not specified, this cmdlet attempts a Windows Authentication connection using the Windows account running the Windows PowerShell session. When possible, use Windows Authentication. .PARAMETER Variable Specifies, as a string array, a sqlcmd scripting variable for use in the sqlcmd script, and sets a value for the variable. Use a Windows PowerShell array to specify multiple variables and their values. .EXAMPLE Get-DBTableRowCount -Database 'DatabaseName' Returns the row counts for all tables in the database DatabaseName .EXAMPLE Get-DBTableRowCount -Database 'MNPCalendar' -TableName 'Table' Returns the row count for the Table .OUTPUTS .LINK SQLServer_Cmdlets .LINK https://docs.microsoft.com/azure/azure-sql/database/authentication-aad-service-principal .LINK Service Principal .LINK https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi .LINK Managed Identity #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUsernameAndPasswordParams", "")] [CmdletBinding(DefaultParameterSetName = 'ByConnectionParameters')] param( [Parameter(ParameterSetName = 'ByConnectionParameters', ValueFromPipeline = $true)] [psobject] ${ServerInstance}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [ValidateNotNullOrEmpty()] [string] ${Database}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [switch] ${EncryptConnection}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [ValidateNotNullOrEmpty()] [string] ${Username}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [ValidateNotNullOrEmpty()] [string] ${AccessToken}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [ValidateNotNullOrEmpty()] [string] ${Password}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [ValidateNotNullOrEmpty()] [pscredential] [System.Management.Automation.CredentialAttribute()] ${Credential}, [ValidateRange(0, 65535)] [int] ${QueryTimeout}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [int] ${ConnectionTimeout}, [ValidateRange(-1, 255)] [int] ${ErrorLevel}, [ValidateRange(-1, 25)] [int] ${SeverityLevel}, [ValidateRange(1, 2147483647)] [int] ${MaxCharLength}, [ValidateRange(1, 2147483647)] [int] ${MaxBinaryLength}, [switch] ${AbortOnError}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [switch] ${DedicatedAdministratorConnection}, [switch] ${DisableCommands}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [ValidateNotNullOrEmpty()] [string] ${HostName}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [string] ${NewPassword}, [string] ${TableName}, [ValidateNotNullOrEmpty()] [string] ${InputFile}, [bool] ${OutputSqlErrors}, [switch] ${IncludeSqlUserErrors}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [switch] ${SuppressProviderContextWarning}, [Parameter(ParameterSetName = 'ByConnectionParameters')] [switch] ${IgnoreProviderContext}, [Alias('As')] [Microsoft.SqlServer.Management.PowerShell.OutputType] ${OutputAs}, [Parameter(ParameterSetName = 'ByConnectionString', Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] ${ConnectionString}) begin { DATA Query { @" DECLARE @tbl varchar(30) = '`$(TableName)' DECLARE @dynSQL varchar(50) SET @dynSQL = 'TRUNCATE TABLE ' + @tbl EXEC (@dynSQL) "@ } if ($TableName) { $Variable = "TableName=$TableName" } else { $Variable = "TableName=%" } try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $PSBoundParameters.Remove('TableName') | Out-Null $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('SqlServer\Invoke-Sqlcmd', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = { & $wrappedCmd @PSBoundParameters -Query $Query -Variable $Variable } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } } function Restart-ComputerAndContinue { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter()] [String] $LogParameterName = 'LogFile', # Parameter to hold arguments to pass to script [Parameter()] [String[]] $ScriptArguments ) $script = Get-ChildItem -Path $MyInvocation.PSCommandPath $scriptParameters = (Get-Command $Script).Parameters [System.Collections.ArrayList]$Arguments = @("&", "'$($script.FullName)'") if ($scriptParameters.ContainsKey($LogParameterName)) { $null = $Arguments.Add("-$LogParameterName '$((Get-LoggingTarget -Name File).Path)'") } foreach ($argument in $ScriptArguments) { $null = $Arguments.Add("$argument") } if ($PSVersionTable.PSEdition -eq 'Core') { $pstart = "pwsh.exe" } else { $pstart = "powershell.exe" } $actionParameters = @{ Execute = $pstart Argument = "-NonInteractive -WindowStyle Normal -NoLogo -NoProfile -NoExit -Command ""$($Arguments -join ' ')""" } $Trigger = (New-ScheduledTaskTrigger -AtLogOn -User "$($Env:USERDOMAIN)\\$($Env:USERNAME)" -RandomDelay (New-Timespan -Seconds 10)) $Trigger.Delay = 'PT30S' $ScheduledTaskParameters = @{ TaskName = "Logon Script - $($script.BaseName)" Action = (New-ScheduledTaskAction @actionParameters) Trigger = $Trigger Settings = (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -RunOnlyIfNetworkAvailable) RunLevel = [Microsoft.PowerShell.Cmdletization.GeneratedTypes.ScheduledTask.RunLevelEnum]'Highest' } if ((Test-PendingReboot)) { if ($PSCmdlet.ShouldProcess("ScheduledTask [$($ScheduledTaskParameters.TaskName)]", "Create")) { try { $null = Register-ScheduledTask @ScheduledTaskParameters -Force Write-Log -Level WARNING -Message "Created Scheduled Task {0}" -Arguments $ScheduledTaskParameters.TaskName Write-Log -Level WARNING "Restarting Computer" Wait-Logging if (!(Test-Interactive)) { Restart-Computer -Force } else { Write-Output "Press any key to reboot (ESC to ignore)..." $key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") if ($key.VirtualKeyCode -ne 27) { Restart-Computer -Force } else { Write-Log -Level Warning -Message "Restart Cancelled by user" } } } catch { Write-Log -Level ERROR "Unable to create task {0}" -ExceptionInfo $_ -Arguments $ScheduledTaskParameters.TaskName throw $_ } } } else { Write-Log -Level DEBUG -Message "No Restart Required" $Tasks = Get-ScheduledTask | Where-Object { ($_ | Select-Object -ExpandProperty Actions | Where-Object { $_.Arguments -like "*$($Script.FullName)*" -and ($_.Execute -like '*powershell*' -or $_.Execute -like '*pwsh*') }) } foreach ($task in $Tasks) { Write-Log -Level WARNING -Message "Removing Scheduled Task [{0}]" -Arguments $task.TaskName $task | Unregister-ScheduledTask -Confirm:$false } } } function Set-CredentialManagerCredential { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] param ( [String]$Target, [Parameter(ParameterSetName='USER+PASS')] [String]$User='User', [Parameter(ParameterSetName='USER+PASS')] [SecureString]$Pass, [Parameter(ParameterSetName='CREDENTIAL')] [PSCredential]$UserCredential, [String]$Comment ) IF($PSCmdlet.ParameterSetName -eq 'CREDENTIAL'){ $User = $UserCredential.UserName $Pass = $UserCredential.Password } $TextPass = ConvertTo-PlainText $Pass $Target = if ($User){"$User@$Target"} else {$Target} ConvertFrom-CredMan(credman -AddCred -Target $Target -User $User -Pass $TextPass -Comment "$Comment") } function Set-EnvironmentVariable { #.Synopsis # Set an environment variable at the highest scope possible [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Position = 0)] [String]$Name, [Parameter(Position = 1)] [String]$Value, [System.EnvironmentVariableTarget] $Scope = "Machine", [Switch]$FailFast ) Write-Information "Set-EnvironmentVariable $Name $Value -Scope $Machine" -Tags "Trace", "Enter" if ((Get-Content "ENV:$Name" -ErrorAction SilentlyContinue) -ne $Value) { Set-Content "ENV:$Name" $Value Write-Log -Level Verbose -Message "Set {2} Environment Variable {0} = {1}" -Arguments $Name,$Value,'Session' } $Success = $False do { try { if ($PSCmdlet.ShouldProcess("ENV:$Name $Scope", "Set $Value") -and ([System.Environment]::GetEnvironmentVariable($Name,$Scope) -ne $Value)) { [System.Environment]::SetEnvironmentVariable($Name, $Value, $Scope) Write-Log -Level WARNING -Message "Set {2} Environment Variable {0} = {1}" -Arguments $Name,$Value,$Scope } $Success = $True } catch [System.Security.SecurityException] { if ($FailFast) { $PSCmdlet.ThrowTerminatingError( (New-Object System.Management.Automation.ErrorRecord ( New-Object AccessViolationException "Can't set environment variable in $Scope scope" ), "FailFast:$Scope", "PermissionDenied", $Scope) ) } else { Write-Log -Level WARNING -Message "Cannot set environment variables in the $Scope scope" } $Scope = [int]$Scope - 1 } } while (!$Success -and $Scope -gt "Process") Write-Information "Set-EnvironmentVariable $Name $Value -Scope $Machine" -Tags "Trace", "Exit" } function Start-DefaultLogging { [CmdletBinding(SupportsShouldProcess)] param ( $LogFile, $LogPath, $LogLevel = 'INFO' ) $script = Get-ChildItem -Path $MyInvocation.PSCommandPath If (!($LogPath)) { $LogPath = Join-Path $env:ProgramData $script.BaseName } else { $CurrentFileLogging = (Get-LoggingTarget)['File'] } if ($PSCmdlet.ShouldProcess("$LogPath", "CreateFolder")) { $null = New-Item $LogPath -ItemType Directory -Force } If (!($LogFile) ) { $wildcardBasePath = "$(($script).BaseName) $( Get-Date -Format "yyyy-MM-dd") *.log" If (Test-Path $LogPath) { $FileCount = ([System.IO.Directory]::GetFiles($logPath, $wildcardBasePath)).Count } else { $FileCount = 0 } $logFileNumber = "{0:d2}" -f $FileCount $logFile = Join-Path $logPath ($wildcardBasePath.Replace('*', $logFileNumber)) } if ($PSCmdlet.ShouldProcess("Logging", "Configure")) { Add-LoggingLevel -LevelName 'VERBOSE' -Level 5 Set-LoggingDefaultLevel $LogLevel Add-LoggingTarget -Name Console -Configuration @{ColorMapping = @{ 'VERBOSE' = 'Gray' 'DEBUG' = 'Blue' 'INFO' = 'Green' 'WARNING' = 'Yellow' 'ERROR' = 'Red' } } if ($CurrentFileLogging) { Wait-Logging If (Test-Path $CurrentFileLogging.Path) { Get-Content $CurrentFileLogging.Path | Add-Content $LogFile -Force Remove-Item $CurrentFileLogging.Path } $CurrentFileLogging.Path = $LogFile Add-LoggingTarget -Name File -Configuration $CurrentFileLogging } else { Add-LoggingTarget -Name File -Configuration @{Path = $logFile } } If (!($CurrentFileLogging)) { if (Test-Interactive) { $Type = "Interactive" } else { $Type = "Non-Interactive" } Write-Log -Level DEBUG -Message 'Starting Script {0} {1}' -Arguments $Script.Name, $Type } Write-Log -Level WARNING -Message "Logging to file {0}" -Arguments (Get-LoggingTarget -Name File).Path } } function Test-Interactive { !([bool]([Environment]::GetCommandLineArgs() -like '-noni*') -and ! ($env:TERM_PROGRAM -eq 'vscode')) } function Test-IsAdmin { ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") } |