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
        # this will remove the folder $SSRSTempFolder and its items
        Start-CleanUp -FolderToRemove $SSRSTempFolder
 
        .NOTES
 
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $FolderToRemove
    )

    Write-Output "$($env:COMPUTERNAME): removing temp files from $($FolderToRemove)"
    try {
        Remove-Item -Path $FolderToRemove -Recurse -Force
    }
    catch {
        throw "error while cleanup - $($PSItem.Exception.Message)"
    }
    Write-Output "$($env:COMPUTERNAME): cleanup finished"
}

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
        # configures the specified network card
        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)]
        $IPAddress,

        [Parameter(Mandatory = $true)]
        $NetPrefix,

        [Parameter(Mandatory = $false)]
        $DefaultGateway,

        [Parameter(Mandatory = $false)]
        $DNSAddresses,

        [Parameter(Mandatory = $false)]
        $NewName
    )

    try {
        Write-Output "$($env:COMPUTERNAME): 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
        }
        Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\$($InterfaceObject.InterfaceGuid)" -Name EnableDHCP -Value 0
        Start-Sleep -Seconds 2
        
        if ($null -ne $DefaultGateway) {
            $InterfaceObject | New-NetIPAddress -IPAddress $IPAddress -AddressFamily "IPv4" -PrefixLength $NetPrefix -DefaultGateway $DefaultGateway | Out-Null
            Write-Output "$($env:COMPUTERNAME): interface $($InterfaceObject.InterfaceDescription) has the static ip $($IPAddress) now"
        }
        else {
            $InterfaceObject | New-NetIPAddress -IPAddress $IPAddress -AddressFamily "IPv4" -PrefixLength $NetPrefix | Out-Null
            Write-Output "$($env:COMPUTERNAME): interface $($InterfaceObject.InterfaceDescription) has the static ip $($IPAddress) now"
        }        
        if ($null -ne $DNSAddresses) {
            $InterfaceObject | Set-DnsClientServerAddress -ServerAddresses $DNSAddresses
        }
        if ($null -ne $NewName) {
            $InterfaceObject | Rename-NetAdapter -NewName $NewName
            Write-Output "$($env:COMPUTERNAME): interface $($InterfaceObject.InterfaceDescription) renamed to $($NewName)"
        }
    }
    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
        # this checks if the file is in use
        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-RebootPending {
    ###############################################################################
    # Check-PendingReboot.ps1
    # Andres Bohren / www.icewolf.ch / blog.icewolf.ch / a.bohren@icewolf.ch
    # Version 1.0 / 03.06.2020 - Initial Version - Andres Bohren
    # Version 1.1 / 27.04.2022 - Updated Script - Andres Bohren
    ###############################################################################
    <#
    .SYNOPSIS
        This Script checks diffrent Registry Keys and Values do determine if a Reboot is pending.
     
    .DESCRIPTION
    I found this Table on the Internet and decided to Write a Powershell Script to check if a Reboot is pending.
    Not all Keys are checked. But feel free to extend the Script.
     
    https://adamtheautomator.com/pending-reboot-registry-windows/
    KEY VALUE CONDITION
    HKLM:\SOFTWARE\Microsoft\Updates UpdateExeVolatile Value is anything other than 0
    HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager PendingFileRenameOperations value exists
    HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager PendingFileRenameOperations2 value exists
    HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired NA key exists
    HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending NA Any GUID subkeys exist
    HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting NA key exists
    HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce DVDRebootSignal value exists
    HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending NA key exists
    HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress NA key exists
    HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending NA key exists
    HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts NA key exists
    HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon JoinDomain value exists
    HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon AvoidSpnSet value exists
    HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName ComputerName Value ComputerName in HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName is different
     
    .EXAMPLE
    ./Check-PendingReboot.ps1
 
    #>


    function Test-RegistryValue {
        param (
            [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$Path,
            [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()]$Value
        )
        try {
            Get-ItemProperty -Path $Path -Name $Value -EA Stop
            return $true
        }
        catch {
            return $false
        }
    }

    [bool]$PendingReboot = $false

    #Check for Keys
    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") -eq $true) {
        # Write-Host "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired"
        $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting") -eq $true) {
        # Write-Host "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting"
        $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") -eq $true) {
        # Write-Host "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired"
        $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") -eq $true) {
        # Write-Host "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending"
        $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts") -eq $true) {
        # Write-Host "HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts"
        $PendingReboot = $true
    }

    #Check for Values
    If ((Test-RegistryValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Value "RebootInProgress") -eq $true) {
        # Write-Host "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing > RebootInProgress"
        $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Value "PackagesPending") -eq $true) {
        # Write-Host "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing > PackagesPending"
        $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Value "PendingFileRenameOperations") -eq $true) {
        # Write-Host "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager > PendingFileRenameOperations"
        $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Value "PendingFileRenameOperations2") -eq $true) {
        # Write-Host "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager > PendingFileRenameOperations2"
        $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" -Value "DVDRebootSignal") -eq $true) {
        # Write-Host "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce > DVDRebootSignal"
        $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon" -Value "JoinDomain") -eq $true) {
        # Write-Host "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon > JoinDomain"
        $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon" -Value "AvoidSpnSet") -eq $true) {
        # Write-Host "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon > AvoidSpnSet"
        $PendingReboot = $true
    }

    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
        # downloads the file
        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-Output "$($env:COMPUTERNAME): found $($FileOutPath), will use it"
            } 
            else {
                Write-Output "$($env:COMPUTERNAME): found $($SetupName) at $($FileOutPath), removing the files because too old"
                Remove-Item -Path $FileOutPath -Recurse -Force | Out-Null
            }
        }
        Write-Output "$($env:COMPUTERNAME): downloading from $($DownloadURL) to $($FileOutPath)"
        $ProgressPreference = "SilentlyContinue"
        Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile $FileOutPath
        $ProgressPreference = "Continue"
        Write-Output "$($env:COMPUTERNAME): 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
        # verify the latest module version is installed
        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
    )

    try {
        if ($null -eq (Get-Module -Name $ModuleName -ListAvailable)) {
            throw "$($env:COMPUTERNAME): module $($ModuleName) not installed"
        }

        # test connectivity
        try {
            $ConTestResult = Invoke-WebRequest -Uri "https://www.powershellgallery.com" -UseBasicParsing
            if($null -ne $ConTestResult.content) {
                $RunCheckForNewerVersion = $true
            }
            else {
                $RunCheckForNewerVersion = $false
            }
        }
        catch {
            $RunCheckForNewerVersion = $false
        }

        if ($RunCheckForNewerVersion -eq $true) {
            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
                $stringver = $version | Select-Object @{n = 'ModuleVersion'; e = { $PSItem.Version -as [string] } }
                $LocalVersionOfModule = $stringver | 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)"
            }
    
            try {
                if ([version]"$($LocalVersionOfModule)" -ge [version]"$($OnlineVersionOfModule)") {
                    # Write-Output "$($env:COMPUTERNAME): using $($ModuleName) with version $($LocalVersionOfModule)"
                }
                else {
                    if ($AutoRunUpdate -eq $false) {
                        Write-Output "$($env:COMPUTERNAME): the installed version of $($ModuleName) is not the latest"
                        Write-Output "$($env:COMPUTERNAME): the one installed locally ($($LocalVersionOfModule)) is lower than the one on the PowerShellGallery ($($OnlineVersionOfModule))"
                
                        #ask for update to proceed
                        do {
                            $askyesno = (Read-Host "$($env:COMPUTERNAME): 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 "$($env:COMPUTERNAME): updating module $($ModuleName)"
                        Update-Module -Name $ModuleName -Force
                    }
                    else {
                        Write-Output "$($env:COMPUTERNAME): skipping update of module $($ModuleName)"
                    }
                }
                if ($null -ne (Get-Module -Name $ModuleName)) {
                    Remove-Module -Name $ModuleName -ErrorAction SilentlyContinue
                }
            }
            catch {
                throw "error updating module - $($PSItem.Exception.Message)"
            }
        }
        else {
            if ($AutoRunUpdate -eq $false) {
                do {
                    $askyesno = (Read-Host "$($env:COMPUTERNAME): could not connect to powershell gallery, still continue? (Y/N)").ToLower()
                } while ($askyesno -notin @('y', 'n'))
                if ($askyesno -eq 'n') {
                    throw "$($env:COMPUTERNAME): stopping script, because could not connect to powershell gallery to get latest version of the $($ModuleName)"
                }
            }
            else {
                Write-Output "$($env:COMPUTERNAME): could not connect to powershell gallery, skipping the update"
            }            
        }

        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
            $stringver = $version | Select-Object @{n = 'ModuleVersion'; e = { $PSItem.Version -as [string] } }
            $LocalVersionOfModule = $stringver | Select-Object Moduleversion -ExpandProperty Moduleversion

            # output
            Write-Output "$($env:COMPUTERNAME): 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
        # verify the latest module version is installed
        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
        # creates the folder in $TempFolderForSQL if it does not exit
        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
        # verifies google.de can be pinged
        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
        # asks the question and accepts y or n
        Confirm-AskYesOrNo -Question "domain intune-center.de available?"
 
        .NOTES
 
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Question
    )

    $RunCount = 0

    do {
        $askyesno = (Read-Host "$($env:COMPUTERNAME): $($Question) (y/n)").ToLower()
        $RunCount++

        if ($RunCount -eq "3") {
            Write-Output "$($env:COMPUTERNAME): 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"
    }
}