NTS.Tools.General.psm1
function Start-FolderCleanUp { <# .Description this function can be used to remove folders and its items .Parameter FolderToRemove version of sql reporting services setup .Example Start-CleanUp -FolderToRemove $SSRSTempFolder .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $FolderToRemove ) try { Write-Verbose "removing temp files from $($FolderToRemove)" Remove-Item -Path $FolderToRemove -Recurse -Force Write-Verbose "cleanup finished" } catch { throw "error while cleanup - $($PSItem.Exception.Message)" } } function Set-Interface { <# .Description configures the network interface, ip, dns, gateway .Parameter InterfaceObject nic objects .Parameter IPAddress ipaddress .Parameter NetPrefix net prefix, e.g. 24 .Parameter DefaultGateway default gateway in the subnet .Parameter DNSAddresses dns server addresses .Parameter NewName new name of the network adapter .Example Set-Interface -InterfaceObject $SFP10G_NICs[0] -IPAddress $CLU1_IPAddress -NetPrefix $NetPrefix -DefaultGateway $CLU_DefaultGateway -DNSAddresses $CLU_DNSAddresses -NewName "Datacenter-1" .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $InterfaceObject, [Parameter(Mandatory = $true)] [string] $IPAddress, [Parameter(Mandatory = $true)] [string] $NetPrefix, [Parameter(Mandatory = $false)] [string] $DefaultGateway = "", [Parameter(Mandatory = $false)] [string[]] $DNSAddresses = "", [Parameter(Mandatory = $false)] [string] $NewName ) $ErrorActionPreference = 'Stop' try { # Correct IP Address Input try { $FixedIPArray = $IPAddress.Split(".") | ForEach-Object { if ($PSItem -like "0*") { return $PSItem.Replace("0", "") } else { return $PSItem } } $IPAddress = [IPAddress]::Parse("$($FixedIPArray[0]).$($FixedIPArray[1]).$($FixedIPArray[2]).$($FixedIPArray[3])").IPAddressToString } catch { throw "could not parse ip - $($PSItem.Exception.Message)" } Write-Verbose "configuring nic with macaddress $($InterfaceObject.MacAddress)" If (($InterfaceObject | Get-NetIPConfiguration).IPv4Address.IPAddress) { $InterfaceObject | Remove-NetIPAddress -AddressFamily "IPv4" -Confirm:$false } If (($InterfaceObject | Get-NetIPConfiguration).Ipv4DefaultGateway) { $InterfaceObject | Remove-NetRoute -AddressFamily "IPv4" -Confirm:$false } # disable dhcp Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\$($InterfaceObject.InterfaceGuid)" -Name EnableDHCP -Value 0 Start-Sleep -Seconds 2 # default gateway if ($DefaultGateway -ne "") { $InterfaceObject | New-NetIPAddress -IPAddress $IPAddress -AddressFamily "IPv4" -PrefixLength $NetPrefix -DefaultGateway $DefaultGateway | Out-Null Write-Verbose "interface $($InterfaceObject.InterfaceDescription) has the static ip $($IPAddress) now" } else { $InterfaceObject | New-NetIPAddress -IPAddress $IPAddress -AddressFamily "IPv4" -PrefixLength $NetPrefix | Out-Null Write-Verbose "interface $($InterfaceObject.InterfaceDescription) has the static ip $($IPAddress) now" } # dns settings if ($DNSAddresses -ne "") { $InterfaceObject | Set-DnsClientServerAddress -ServerAddresses $DNSAddresses } # interface friendlyname if ($null -ne $NewName -and $NewName -ne "") { $InterfaceObject | Rename-NetAdapter -NewName $NewName Write-Verbose "interface $($InterfaceObject.InterfaceDescription) renamed to $($NewName)" } $InterfaceObject | Restart-NetAdapter } catch { throw "error setting $($InterfaceObject.Name) - $($PSItem.Exception.Message)" } } function Test-FileLock { <# .Description this function test if a file is in use and returns true if so .Parameter Path file path to the file .Example Test-FileLock -Path C:\WINDOWS\CCM\Logs\PolicyAgentProvider.log .NOTES https://stackoverflow.com/questions/24992681/powershell-check-if-a-file-is-locked #> [CmdletBinding()] param ( [parameter(Mandatory = $true)] [string] $Path ) $oFile = New-Object System.IO.FileInfo $Path if ((Test-Path -Path $Path) -eq $false) { return $false } try { $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) if ($oStream) { $oStream.Close() } $false } catch { # file is locked by a process. return $true } } function Test-RegistryValue { <# .Description tests if a registry and its value .Parameter DownloadURL tests if a registry and its value .Parameter Key Registry key path .Parameter Value value .Example Test-RegistryValue -Key "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Value "RebootInProgress" .NOTES #> [OutputType('bool')] [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Key, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Value ) $ErrorActionPreference = 'Stop' if (Get-ItemProperty -Path $Key -Name $Value -ErrorAction Ignore) { $true } } function Test-RebootPending { <# .SYNOPSIS checks some if reboot is pending .DESCRIPTION checks some registry key and value for a pending reboot .EXAMPLE Test-RebootPending .NOTES https://adamtheautomator.com/pending-reboot-registry-windows/ #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [switch] $DisplayReason ) [bool]$PendingReboot = $false # Check for Keys $Keys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending", "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress", "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending", "HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts", "HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttemps" ) foreach ($Key in $Keys) { if (Get-Item -Path $Key -ErrorAction Ignore) { if ($DisplayReason) { Write-Output $Key } $PendingReboot = $true } } # Pending File Rename Operations "PendingFileRenameOperations", "PendingFileRenameOperations2" | ForEach-Object { $TempObject = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\" -Name "$($PSItem)" -ErrorAction SilentlyContinue if ($null -ne $TempObject) { if ($null -ne $TempObject.$($PSItem)) { if ($DisplayReason) { Write-Output "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\ > $($PSItem)" } $PendingReboot = $true $PendingReboot | Out-Null } } } #Check for Values If ((Test-RegistryValue -Key "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Value "RebootInProgress") -eq $true) { if ($DisplayReason) { Write-Output "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing > RebootInProgress" } $PendingReboot = $true } If ((Test-RegistryValue -Key "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Value "PackagesPending") -eq $true) { if ($DisplayReason) { Write-Output "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing > PackagesPending" } $PendingReboot = $true } # If ((Test-RegistryValue -Key "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Value "PendingFileRenameOperations") -eq $true) { # if ($DisplayReason) { # Write-Output "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager > PendingFileRenameOperations" # } # $PendingReboot = $true # } # If ((Test-RegistryValue -Key "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Value "PendingFileRenameOperations2") -eq $true) { # if ($DisplayReason) { # Write-Output "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager > PendingFileRenameOperations2" # } # $PendingReboot = $true # } If ((Test-RegistryValue -Key "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" -Value "DVDRebootSignal") -eq $true) { if ($DisplayReason) { Write-Output "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce > DVDRebootSignal" } $PendingReboot = $true } If ((Test-RegistryValue -Key "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon" -Value "JoinDomain") -eq $true) { if ($DisplayReason) { Write-Output "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon > JoinDomain" } $PendingReboot = $true } If ((Test-RegistryValue -Key "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon" -Value "AvoidSpnSet") -eq $true) { if ($DisplayReason) { Write-Output "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon > AvoidSpnSet" } $PendingReboot = $true } #region custom # Added "test-path" to each test that did not leverage a custom function from above since # an exception is thrown when Get-ItemProperty or Get-ChildItem are passed a nonexistant key path # Added test to check first if key exists, using "ErrorAction ignore" will incorrectly return $true # 'HKLM:\SOFTWARE\Microsoft\Updates' | Where-Object { Test-Path $PSItem -PathType Container } | ForEach-Object { # try { # $Value = (Get-ItemProperty -Path $PSItem -Name 'UpdateExeVolatile' | Select-Object -ExpandProperty UpdateExeVolatile) -ne 0 # if ($Value) { # if ($DisplayReason) { # Write-Output "UpdateExeVolatile under HKLM:\SOFTWARE\Microsoft\Updates not equals 0" # } # $PendingReboot = $true # } # } # catch { " "} # } # Added test to check first if keys exists, if not each group will return $Null # May need to evaluate what it means if one or both of these keys do not exist $ComputerNameChangeTest = ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName' | Where-Object { test-path $PSItem } | ForEach-Object { (Get-ItemProperty -Path $PSItem ).ComputerName } ) -ne ` ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName' | Where-Object { Test-Path $PSItem } | ForEach-Object { (Get-ItemProperty -Path $PSItem ).ComputerName } ) if ($ComputerNameChangeTest) { if ($DisplayReason) { Write-Output "pending computername change" } $PendingReboot = $true } # Added test to check first if key exists 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending' | Where-Object { (Test-Path $PSItem) -and (Get-ChildItem -Path $PSItem) } | ForEach-Object { if ($DisplayReason) { Write-Output "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending exists and has childitems" } $PendingReboot = $true $PendingReboot | Out-Null # just for the script analysis stuff } #endregion return $PendingReboot } function Start-FileDownload { <# .Description this function can be used to download files, but also checks if the destination has already the file .Parameter DownloadURL url of the source .Parameter FileOutPath path where the file should be saved, with extension .Parameter MaxAgeOfFile maximum file modification date .Example Start-FileDownload -DownloadURL "https://www.microsoft.com/en-us/download/confirmation.aspx?id=104131" -FileOutPath "$($Outpath)\Exchange-$($Version).iso" .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $DownloadURL, [Parameter(Mandatory = $true)] [string] $FileOutPath, [Parameter(Mandatory = $false)] [datetime] $MaxAgeOfFile = (Get-Date).AddHours(-2) ) # verify folder try { $FileName = $FileOutPath.Split("\")[-1] $FolderName = $FileOutPath.replace($FileName, "") New-ItemIfNotExists -Path $FolderName -ItemType Directory } catch { throw "error creating dest folder - $($PSItem.Exception.Message)" } # download try { if ((Test-Path -Path $FileOutPath) -eq $true) { if ((Get-Item $FileOutPath).LastWriteTime -gt $MaxAgeOfFile) { Write-Verbose "found $($FileOutPath), will use it" } else { Write-Verbose "found $($FileOutPath), removing the file because too old" Remove-Item -Path $FileOutPath -Recurse -Force | Out-Null Write-Verbose "downloading from $($DownloadURL) to $($FileOutPath)" $ProgressPreference = "SilentlyContinue" Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile $FileOutPath $ProgressPreference = "Continue" Write-Verbose "download finished" } } else { Write-Verbose "downloading from $($DownloadURL) to $($FileOutPath)" $ProgressPreference = "SilentlyContinue" Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile $FileOutPath $ProgressPreference = "Continue" Write-Verbose "download finished" } } catch { throw "error downloading - $($PSItem.Exception.Message)" } } function Confirm-LatestModuleVersionInstalled { <# .Description this function checks the module version against powershell gallery, if older then it will as to update .Parameter ModuleName name of the module .Parameter AutoRunUpdate automatically start an update for the module, if there is one .Example Confirm-LatestModuleVersionInstalled -Module $ModuleName .NOTES https://blog.it-koehler.com/en/Archive/3359 #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $ModuleName, [Parameter(Mandatory = $false)] [switch] $AutoRunUpdate, [Parameter(Mandatory = $false)] [switch] $SkipConnectionTest ) try { if ($null -eq (Get-Module -Name $ModuleName -ListAvailable)) { throw "module $($ModuleName) not installed" } # test connectivity try { if ($SkipConnectionTest -eq $false) { $ConTestResult = Invoke-WebRequest -Uri "https://www.powershellgallery.com" -UseBasicParsing if ($null -ne $ConTestResult.content) { $RunCheckForNewerVersion = $true } else { throw "could not connect to powershell gallery" } } else { $RunCheckForNewerVersion = $true } } catch { $RunCheckForNewerVersion = $false } if ($RunCheckForNewerVersion -eq $true) { # fetch data from local and psgallery try { # getting version of installed module $Version = (Get-Module -Name $ModuleName -ListAvailable) | Sort-Object Version -Descending | Select-Object Version -First 1 # converting version to string $LocalVersionOfModule = $Version | Select-Object @{n = 'ModuleVersion'; e = { $PSItem.Version -as [string] } } | Select-Object Moduleversion -ExpandProperty Moduleversion # getting latest module version from ps gallery $psgalleryversion = Find-Module -Name $ModuleName -Repository PSGallery | Sort-Object Version -Descending | Select-Object Version -First 1 # converting version to string $onlinever = $psgalleryversion | Select-Object @{n = 'OnlineVersion'; e = { $PSItem.Version -as [string] } } $OnlineVersionOfModule = $onlinever | Select-Object OnlineVersion -ExpandProperty OnlineVersion } catch { throw "error collecting versions - $($PSItem.Exception.Message)" } # update module try { if ([version]"$($LocalVersionOfModule)" -lt [version]"$($OnlineVersionOfModule)") { if ($AutoRunUpdate -eq $false) { Write-Output "the installed version of $($ModuleName) is not the latest" Write-Output "the one installed locally $($LocalVersionOfModule) is lower than the one on the PowerShellGallery $($OnlineVersionOfModule)" # ask for update to proceed do { $askyesno = (Read-Host "do you want to update Module $ModuleName (Y/N)").ToLower() } while ($askyesno -notin @('y', 'n')) if ($askyesno -eq 'y') { $RunUpdate = $true } } if ($RunUpdate -eq $true -or $AutoRunUpdate -eq $true) { Write-Output "updating module $($ModuleName)" Install-Module -Name NTS.Tools -AllowClobber -Force } else { Write-Output "skipping update of module $($ModuleName)" } } # remove modul from current session if ($null -ne (Get-Module -Name $ModuleName)) { Remove-Module -Name $ModuleName -ErrorAction SilentlyContinue } } catch { throw "error updating module - $($PSItem.Exception.Message)" } } # output currently used version of module try { # getting version of installed module $Version = (Get-Module -Name $ModuleName -ListAvailable) | Sort-Object Version -Descending | Select-Object Version -First 1 # converting version to string $LocalVersionOfModule = $Version | Select-Object @{n = 'ModuleVersion'; e = { $PSItem.Version -as [string] } } | Select-Object Moduleversion -ExpandProperty Moduleversion # output Write-Output "using $($ModuleName) with version $($LocalVersionOfModule)" } catch { throw "error collecting local version - $($PSItem.Exception.Message)" } } catch { throw $PSItem.Exception.Message } } function Confirm-RunningAsAdministrator { <# .Description this function checks if the current powershell session is running as administrator .Example Confirm-LatestModuleVersionInstalled -Module $ModuleName .NOTES #> $user = [Security.Principal.WindowsIdentity]::GetCurrent() [bool]$Result = (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) if ($Result -ne $true) { throw "this session is not running as administrator, please restart with administrative privileges" } } function New-ItemIfNotExists { <# .Description this function adds an if statement infront of the new-item function to test if the path exists .Parameter Path FilePath for the item .Parameter ItemType type of the item .Example New-ItemIfNotExists -Path $TempFolderForSQL -ItemType Directory .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Path, [Parameter(Mandatory = $true)] [ValidateSet( "File", "Directory", "SymbolicLink", "Junction", "Hardlink" )] [string] $ItemType ) if (-NOT (Test-Path $Path)) { New-Item -Path $Path -ItemType $ItemType -Force | Out-Null } } function Confirm-DomainConnectivity { <# .Description this function checks if the specified domain can be pinged .Parameter DomainName full qualified domain name of the domain .Example Confirm-DomainConnectivity -DomainName "google.de" .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $DomainName ) $Test = Test-NetConnection -ComputerName $DomainName if ($Test.PingSucceeded -ne $true) { throw "could not ping $($DomainName)" } else { Write-Verbose "ping to $($DomainName) was successfull" } } function Confirm-Question { <# .Description this function can be used to ask yes|no questions, throws an error the the answer is not yes .Parameter Question question as string .Example Confirm-AskYesOrNo -Question "domain intune-center.de available?" .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Question ) $RunCount = 0 do { $askyesno = (Read-Host "$($Question) (y/n)").ToLower() $RunCount++ if ($RunCount -eq "3") { Write-Output "please read the question carefully and then answer with 'y' or 'n'" } elseif ($RunCount -eq "5") { throw "question was answered incorrectly five times, i dont have time for this!" } } while ($askyesno -notin @('y', 'n')) if ($askyesno -ne "y") { throw "the anser was not 'y', stopping execution" } } function Initialize-PowerShellEnviroment { <# .Description installs neccesary components for the powershell gallery .Parameter DomainName installs neccesary components for the powershell gallery .Example Initialize-PowerShellEnviroment .NOTES https://www.recastsoftware.com/resources/enable-psgallery-in-a-configmgr-task-sequence-while-in-winpe/ #> # Source : https://www.recastsoftware.com/resources/enable-psgallery-in-a-configmgr-task-sequence-while-in-winpe/ # Setup LOCALAPPDATA Variable [System.Environment]::SetEnvironmentVariable('LOCALAPPDATA', "$env:SystemDrive\Windows\system32\config\systemprofile\AppData\Local") $WorkingDir = $env:TEMP [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 Set-ExecutionPolicy Unrestricted -Scope Process -Force Import-Module 'PackageManagement' try { Install-PackageProvider -Name Nuget -RequiredVersion 2.8.5.201 -Force | Out-Null } catch { throw "Error installing the powershell provider 'nuget'" } # PackageManagement from PSGallery URL try { $PackageManagementVersion = "1.4.8.1" if (!(Get-Module -Name PackageManagement | Where-Object -Property Version -Like "*$($PackageManagementVersion)*")) { Write-Verbose "installing PackageManagementVersion via file with at least version $($PackageManagementVersion)" $PackageManagementURL = "https://psg-prod-eastus.azureedge.net/packages/packagemanagement.$($PackageManagementVersion).nupkg" Invoke-WebRequest -UseBasicParsing -Uri $PackageManagementURL -OutFile "$WorkingDir\packagemanagement.$($PackageManagementVersion).zip" $Null = New-Item -Path "$WorkingDir\$($PackageManagementVersion)" -ItemType Directory -Force Expand-Archive -Path "$WorkingDir\packagemanagement.$($PackageManagementVersion).zip" -DestinationPath "$WorkingDir\$($PackageManagementVersion)" -Force $Null = New-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\PackageManagement" -ItemType Directory -ErrorAction SilentlyContinue Move-Item -Path "$WorkingDir\$($PackageManagementVersion)" -Destination "$env:ProgramFiles\WindowsPowerShell\Modules\PackageManagement\$($PackageManagementVersion)" -ErrorAction SilentlyContinue -Force } } catch { throw "Error installing the powershell module 'PackageManagement'" } # PowerShellGet from PSGallery URL try { $PowerShellGetVersion = "2.2.5" if (!(Get-Module -Name PowerShellGet | Where-Object -Property Version -Like "*$($PowerShellGetVersion)*")) { Write-Verbose "installing PowerShellGetVersion via file with at least version $($PowerShellGetVersion)" $PowerShellGetURL = "https://psg-prod-eastus.azureedge.net/packages/powershellget.$($PowerShellGetVersion).nupkg" Invoke-WebRequest -UseBasicParsing -Uri $PowerShellGetURL -OutFile "$WorkingDir\powershellget.$($PowerShellGetVersion).zip" $Null = New-Item -Path "$WorkingDir\$($PowerShellGetVersion)" -ItemType Directory -Force Expand-Archive -Path "$WorkingDir\powershellget.$($PowerShellGetVersion).zip" -DestinationPath "$WorkingDir\$($PowerShellGetVersion)" -Force $Null = New-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\PowerShellGet" -ItemType Directory -ErrorAction SilentlyContinue Move-Item -Path "$WorkingDir\$($PowerShellGetVersion)" -Destination "$env:ProgramFiles\WindowsPowerShell\Modules\PowerShellGet\$($PowerShellGetVersion)" -ErrorAction SilentlyContinue -Force } } catch { throw "Error installing the powershell module 'PowerShellGet'" } # Import PowerShellGet & set psgallery Import-Module PowerShellGet # Register-PSRepository -Name "PSGallery" –SourceLocation "https://www.powershellgallery.com/api/v2/" -InstallationPolicy Trusted Set-PSRepository -Name PSGallery -InstallationPolicy Trusted | Out-Null } function Update-WindowsSystem { <# .SYNOPSIS starts windows update process .DESCRIPTION installs module PSWindowsUpdate and starts windows update process .PARAMETER AutoReboot should the vm reboot after applying the updates .EXAMPLE Update-WindowsSystem .NOTES installs ps module PSWindowsUpdate, therefore need internet access #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [bool] $AutoReboot = $false ) # Define Options $ErrorActionPreference = 'Stop' $WUModuleName = "PSWindowsUpdate" # Install module try { Initialize-PowerShellEnviroment Write-Verbose "installing ps module $($WUModuleName)" Set-ExecutionPolicy Unrestricted -Scope Process -Force Install-Module -Name $WUModuleName -Force -WarningAction SilentlyContinue } catch { Write-Output "Error installing the powershell module - $($PSItem.Exception.Message)" break } try { Write-Verbose "importing ps module $($WUModuleName)" if ($null -eq (Get-Module -Name $WUModuleName)) { if ($null -ne (Get-Module -Name $WUModuleName -ListAvailable)) { Import-Module -Name $WUModuleName -Force -WarningAction SilentlyContinue } else { throw "module not installed" } } } catch { throw "error importing the powershell '$($WUModuleName)': $($PSItem.Exception.Message)" } # Get & Install Updates try { Write-Output "installing windows updates" Import-Module -Name $WUModuleName Set-WUSettings -IncludeRecommendedUpdates -Confirm:$false | Out-Null Get-WindowsUpdate -UpdateType Software -AcceptAll -MicrosoftUpdate -IgnoreReboot -Install | Out-Null Write-Output "finished installing windows updates" if ($AutoReboot -eq $true) { Restart-Computer -Force } } catch { Write-Output "error finding or installing updates - $($PSItem.Exception.Message)" break } } function Write-ToLogOrTerminal { <# .SYNOPSIS writes messages to a log file or the terminal .DESCRIPTION writes messages to a log file under "$($env:ProgramData)\NTS\LogFiles\" or to the terminal .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .PARAMETER Terminal writes to Terminal instead of log file .PARAMETER Severity severity of the message .PARAMETER Message message to write in the log file .EXAMPLE Write-ToLogOrTerminal -LogFileName "createvm.log" -Severity Info -Message "vm created" .EXAMPLE Write-ToLogOrTerminal -Terminal -Severity Info -Message "vm created" .NOTES https://adamtheautomator.com/powershell-log-function/ #> [alias ("Write-ToLogOrConsole")] [CmdletBinding()] param( [Parameter(Mandatory = $false, ParameterSetName = "LogFile")] [string] $LogFileName = "", [Parameter(Mandatory = $false, ParameterSetName = "LogFile")] [string] $LogFileFolderPath = "$($env:ProgramData)\NTS\LogFiles", [Parameter(Mandatory = $false, ParameterSetName = "Terminal")] [switch] $Terminal, [Parameter(Mandatory = $false)] [ValidateSet('Verbose', 'Info', 'Warning', 'Error')] [string] $Severity = 'Info', [Parameter(Mandatory = $false)] [string[]] $Message = @("") ) try { if ($LogFileName -ne "") { $LogFilePath = "$($LogFileFolderPath)\$($LogFileName)" New-ItemIfNotExists -Path $LogFileFolderPath -ItemType "Directory" New-ItemIfNotExists -Path $LogFilePath -ItemType "File" } if ($Message -ne "" -and $Message -ne @("") ) { $Message | ForEach-Object { if ($Terminal) { if ($Severity -eq 'Verbose') { Write-Verbose $PSItem } elseif ($Severity -eq 'Info') { Write-Output $PSItem } elseif ($Severity -eq 'Warning') { Write-Warning $PSItem } elseif ($Severity -eq 'Error') { Write-Error $PSItem } } elseif ($LogFileName -ne "") { if ($Severity -eq 'Verbose') { if ($VerbosePreference -eq 'SilentlyContinue') { $WriteToLogFile = $false } else { $WriteToLogFile = $true } } else { $WriteToLogFile = $true } # output message to log if ($WriteToLogFile -eq $true) { Add-Content -Value "[$(Get-Date -format "dd/MM/yyyy HH:mm:ss")] - [$($Severity)] - $($PSItem)" -Path $LogFilePath } } } } } catch { throw $PSItem.Exception.Message } } function Confirm-LogFileParameters { <# .SYNOPSIS a function to verify the parameters for logging .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .PARAMETER Terminal writes to Terminal instead of log file .EXAMPLE $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath -Terminal $Terminal .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $LogFileName, [Parameter(Mandatory = $false)] [string] $LogFileFolderPath, [Parameter(Mandatory = $false)] [bool] $Terminal ) $ErrorActionPreference = 'Stop' try { # define vars $InputParamObject = [PSCustomObject]@{ LogFileName = $LogFileName LogFileFolderPath = $LogFileFolderPath Terminal = $Terminal } if ($LogFileFolderPath -eq "") { $LogFileFolderPath = "$($env:ProgramData)\NTS\LogFiles" } # process input if ($LogFileName -eq "" -and $Terminal -eq $false) { $LogParam = @{ Terminal = $true } } elseif ($LogFileName -ne "" -and $Terminal -eq $true) { throw "you defined a log file name and terminal, got $($InputParamObject)" } elseif ($LogFileName -ne "") { $LogParam = @{ LogFileName = $LogFileName LogFileFolderPath = $LogFileFolderPath } } elseif ($Terminal -eq $true) { $LogParam = @{ Terminal = $true } } elseif ($LogFileFolderPath -ne "" -and $LogFileName -eq "") { throw "you defined the folder but not the log file name, got $($InputParamObject)" } else { throw "Log Parameters were not correctly specified, got $($InputParamObject)" } # output return $LogParam } catch { throw $PSItem.Exception.Message } } function Confirm-ServiceIsRunning { <# .SYNOPSIS a function to verify a windows service is started .PARAMETER ServiceName name of the service .PARAMETER TimeoutInSeconds timeout after the function will fail .EXAMPLE Confirm-ServiceIsRunning -ServiceName MSSQL`$Instance1 .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $ServiceName, [Parameter(Mandatory = $false)] [int] $TimeoutInSeconds = 60 ) $Start = Get-Date $ServiceRunning = $false do { $Service = Get-Service -Name "MSSQL`$INSTANCE1" if ($Service.Status -eq "Running") { Write-Verbose "service is running" $ServiceRunning = $true } else { if (((Get-Date) - $start).TotalSeconds -ge $TimeoutInSeconds) { throw "service is not running after $($TimeoutInSeconds) seconds" } Write-Verbose "service is still not running" Start-Sleep -Seconds 1 } } while ($ServiceRunning -eq $false) } |