Greyhound.psm1

function Get-GreyhoundInstallLocation {
    $GreyhoundInstallLocation = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Greyhound' -Name InstallLocation).InstallLocation
    if (Test-Path $GreyhoundInstallLocation) {
        $GreyhoundInstallLocation
    } else {
        Write-Host 'Es wurde kein Installationspfad für GREYHOUND gefunden.'
    } 
}



function Get-GreyhoundVersionInfo {
    $GreyhoundServerExe = (Get-GreyhoundInstallLocation) + 'Server\GreyhoundServer.exe'
    if (Test-Path -Path $GreyhoundServerExe) {
        $Version = (Get-Item $GreyhoundServerExe).VersionInfo.ProductVersionRaw
        $Version
    } else {
        Write-Error "GREYHOUND ist nicht installiert." -Category ObjectNotFound
        Break
    }
}



function Invoke-GreyhoundAdmin {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false)]
        [Switch]$Start,
        [Switch]$Stop
    )
   
    try {
        $GreyhoundAdmin = (Get-GreyhoundInstallLocation) + 'Server\GreyhoundAdmin.exe'
        if (!(Test-Path $GreyhoundAdmin)) {
            Throw "Der GREYHOUND Admin wurde nicht gefunden."
        }

        if ($Start) {
            if ((Start-Process -FilePath "$GreyhoundAdmin" -ArgumentList "-Start -NoGui" -Wait -NoNewWindow -PassThru).Exitcode -gt 0) {
                Throw "Der GREYHOUND Admin hat einen ExitCode ausgegeben"
            }
        } elseif ($Stop) {
            if ((Start-Process -FilePath "$GreyhoundAdmin" -ArgumentList "-Stop -NoGui" -Wait -NoNewWindow -PassThru).Exitcode -gt 0) {
                Throw "Der GREYHOUND Admin hat einen ExitCode ausgegeben"
            }
        }

    }
    catch {
        Write-Host $($PSItem.ToString())
    }
}



function Restart-GreyhoundServer {
    Invoke-GreyhoundAdmin -Stop
    Invoke-GreyhoundAdmin -Start
}



function Get-GreyhoundSystemPassword {
    $GreyhoundServerIni = (Get-GreyhoundInstallLocation) + 'Server\GreyhoundServer.ini'
    if (Test-Path $GreyhoundServerIni) {
        $GreyhoundSystemPassword = (Get-Content $GreyhoundServerIni | Select-String -Pattern 'SystemPassword' -SimpleMatch | ConvertFrom-StringData).SystemPassword
        $GreyhoundSystemPassword
    } else {
        Write-Host 'Die GREYHOUND Serverkonfiguration ´"' $GreyhoundServerIni '´" wurde nicht gefunden.'
    }
}



function Get-GreyhoundServerIniValue {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false)]
        [String]$Key
    )

    $GreyhoundServerIni = (Get-GreyhoundInstallLocation) + 'Server\GreyhoundServer.ini'
    if (Test-Path $GreyhoundServerIni) {
        if (!$Key) {
            $Result = Get-Content $GreyhoundServerIni
        } else {
            $Result = (Get-Content $GreyhoundServerIni | Select-String "^$Key=" | ConvertFrom-StringData).$Key
        }
        $Result
    } else {
        Write-Host 'Die GREYHOUND Serverkonfiguration ´"' $GreyhoundServerIni '´" wurde nicht gefunden.'
    }
}



function Get-ApplianceSerialNumbers {
<#
.SYNOPSIS
    Gets the serial numbers of installed harddrives, memory chips and motherboard
.DESCRIPTION
    Long description
.EXAMPLE
    Example of how to use this cmdlet
#>


    # Seriennummern aller Laufwerke ermitteln
    Get-CimInstance CIM_DiskDrive | Select-Object SerialNumber | ForEach-Object -Process {$ApplianceSerialNumbers += @($_.SerialNumber)}
    # Seriennummern aller RAM-Chips ermitteln
    Get-CimInstance CIM_Chip | Select-Object SerialNumber | ForEach-Object -Process {$ApplianceSerialNumbers += @($_.SerialNumber)}
    # Seriennummer des Motherboards ermitteln
    Get-CimInstance CIM_BIOSElement | Select-Object SerialNumber| ForEach-Object -Process {$ApplianceSerialNumbers += @($_.SerialNumber)}

    $ApplianceSerialNumbers
}



function New-GreyhoundVM {
    [CmdletBinding()]
    Param (
        # Just some help
        [Parameter(Mandatory=$true)]
        [String]$VmName,

        [Parameter(Mandatory=$false)]
        [String]$VmNotes,
        [String]$VmSwitchName = (Get-VMSwitch | Where-Object SwitchType -eq External)[0].Name,
        [Int64]$VmVHDSize = 64GB,
        [String]$VHDPath,
        [String]$VmBasePath = (Get-VMHost).VirtualMachinePath + '\' + $VmName,
        [Int]$VmGeneration = 2,
        [Int]$VMProcessorCount = 4,
        [Int64]$VmMemoryStartupBytes = 4GB,
        [Int64]$VmMemoryMinimumBytes = 4GB,
        [Int64]$VMMemoryMaximumBytes = 8GB
    )

    if (!$VHDPath) {
        # Vm mit neuer VHD erstellen falls keine VHD angegeben wurde
        $VHDPath + '.vhdx'
        New-VM -Name $VmName -Generation $VmGeneration -SwitchName $VmSwitchName -NewVHDPath $VHDPath -NewVHDSizeBytes $VmVHDSize |
            Set-VM -ProcessorCount $VMProcessorCount -DynamicMemory -MemoryStartupBytes $VmMemoryStartupBytes -MemoryMinimumBytes $VmMemoryMinimumBytes -MemoryMaximumBytes $VMMemoryMaximumBytes -Notes $VmNotes -Passthru |
            Move-VMStorage -DestinationStoragePath $VmBasePath

    } else {
        # Vm mit angegebener VHD erstellen
        if (Test-Path $VHDPath) {
            New-VM -Name $VmName -Generation $VmGeneration -SwitchName $VmSwitchName -VHDPath $VHDPath |
                Set-VM -ProcessorCount $VMProcessorCount -DynamicMemory -MemoryStartupBytes $VmMemoryStartupBytes -MemoryMinimumBytes $VmMemoryMinimumBytes -MemoryMaximumBytes $VMMemoryMaximumBytes -Notes $VmNotes -Passthru |
                Move-VMStorage -DestinationStoragePath $VmBasePath
        } else {
            Write-Error 'Der Pfad zur VHD-Datei ist ungültig.'
        }
    }

    # Add Windows Iso
    #Add-VMDvdDrive -VMName $VmName -Path C:\Hyper-V\_ISOs\14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_DE-DE.ISO-updated.iso

    # Change boot order to boot from windows iso
    #Set-VMFirmware -VMName $VmName -FirstBootDevice (Get-VMDvdDrive -VMName $VmName)

}



function Get-MariaDBSetup {
    [CmdletBinding()]
    param (
        [string]$DownloadDir=$PWD.Path,
        [string]$BaseUrl = 'https://greyhound-software.com/files/greyhound',
        [string]$SetupName = 'MariaDBSetup.msi'
    )

    try {
        $RemoteFile = $RemoteFile = "$BaseUrl/$SetupName"
        $LocalFile = $DownloadDir.TrimEnd('\') + '\' + $SetupName

        if (Test-Path $LocalFile) {
            [Int64]$RemoteFileSize = (Invoke-WebRequest -Uri $RemoteFile -Method Head -UseBasicParsing).Headers.'Content-Length'
            [Int64]$LocalFileSize = (Get-Item $LocalFile).Length

            Write-Verbose "LocalFileSize: $LocalFileSize RemoteFileSize: $RemoteFileSize"

            if ($RemoteFileSize -gt 0 -and $LocalFileSize -gt 0) {
                if ($RemoteFileSize -ne $LocalFileSize) {
                    Start-BitsTransfer -Source $RemoteFile -Destination $LocalFile -Description "Downloading $RemoteFile"
                } else {
                    Write-Host "Die Datei `"$SetupName`" mit einer Dateigroesse von $RemoteFileSize Bytes existiert bereits."
                }
            } else {
                Throw "Es ist ein Fehler beim Dateigroessenvergleich aufgetreten."
            }
        } else {
            Start-BitsTransfer -Source $RemoteFile -Destination $LocalFile -Description "Downloading $RemoteFile"
        }

        if (Test-Path $LocalFile) {
            $LocalFile
        } else {
            Throw "Es ist ein Fehler beim Herunterladen der Datei '$RemoteFile' aufgetreten."
        }
    }
    catch {
        Write-Error $($PSItem.ToString()) -Category ObjectNotFound
    } 
}



function Get-GreyhoundServerSetup {
    [CmdletBinding()]
    param (
        [string]$DownloadDir=$PWD.Path,
        [string]$BaseUrl = 'https://greyhound-software.com/files/greyhound',
        [string]$SetupBaseName = 'GreyhoundSetup',
        [switch]$Beta,
        [switch]$Test
    )

    try {
        if ($Beta) {
            $SetupName = "${SetupBaseName}Beta.exe"
        } elseif ($Test) {
            $SetupName = "${SetupBaseName}Test.exe"
        } else {
            $SetupName = "${SetupBaseName}.exe"
        }
    
        $RemoteFile = "$BaseUrl/$SetupName"
        $LocalFile = $DownloadDir.TrimEnd('\') + '\' + $SetupName
    
        if (Test-Path $LocalFile) {
            [Int64]$RemoteFileSize = (Invoke-WebRequest -Uri $RemoteFile -Method Head -UseBasicParsing).Headers.'Content-Length'
            [Int64]$LocalFileSize = (Get-Item $LocalFile).Length

            Write-Verbose "LocalFileSize: $LocalFileSize RemoteFileSize: $RemoteFileSize"
   
            if ($RemoteFileSize -gt 0 -and $LocalFileSize -gt 0) {
                if ($RemoteFileSize -ne $LocalFileSize) {
                    Start-BitsTransfer -Source $RemoteFile -Destination $LocalFile -Description "Downloading $RemoteFile"
                } else {
                    Write-Host "Die Datei `"$SetupName`" mit einer Dateigroesse von $RemoteFileSize Bytes existiert bereits."
                }
            } else {
                Throw "Es ist ein Fehler beim Dateigroessenvergleich aufgetreten."
            }
        } else {
            Start-BitsTransfer -Source $RemoteFile -Destination $LocalFile -Description "Downloading $RemoteFile"
        }
    
        if (Test-Path $LocalFile) {
            $LocalFile
        } else {
            Throw "Es ist ein Fehler beim Herunterladen der Datei '$RemoteFile' aufgetreten."
        }
           
    }
    catch {
        Write-Error $($PSItem.ToString()) -Category ObjectNotFound
    }
    
}



function Install-MariaDB {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true,
        ValueFromPipeline)]
        [String]$SetupFile,
        [Parameter(Mandatory=$false)]
        [String]$ServiceName='MySql',
        [Boolean]$AllowRemoteRootAccess,
        [Boolean]$SkipNetworking,
        [String]$Password
    )

    $SetupFile = Resolve-Path($SetupFile)
    if (!(Test-Path -Path $SetupFile)) {
        Write-Error "Die Datei '$SetupFile' wurde nicht gefunden." -Category ObjectNotFound
        Break
    }

    $Command = (Get-Command 'msiexec').Path
    $Arguments = @(
        "/package",
        "`"$SetupFile`"",
        "SERVICENAME=$ServiceName",
        "/qn"
    )

    if ($Password) {
        $Arguments += @("PASSWORD=$Password")
    }

    if ($AllowRemoteRootAccess) {
        $Arguments += @("ALLOWREMOTEROOTACCESS=True")
    } else {
        $Arguments += @("ALLOWREMOTEROOTACCESS=False")
    }

    Write-Host 'Die Datei' $SetupFile 'wird installiert...'

    try {
        $CommandBasename = $Command.Substring($Command.LastIndexOf('\') + 1, $Command.LastIndexOf('.') - $Command.LastIndexOf('\') - 1)
        $StdOutFile = "$env:TEMP\$CommandBasename.stdout"
        $StdErrFile = "$env:TEMP\$CommandBasename.stderr"

        if ((Start-Process -FilePath "$Command" -ArgumentList "$Arguments" -RedirectStandardOutput $StdOutFile -RedirectStandardError $StdErrFile -Wait -PassThru).ExitCode -eq 0) {
            Get-Content $StdOutFile
        } else {
            (Get-Content $StdErrFile)
            Throw "Es ist ein Fehler bei der Installation aufgetreten. StdOut: " + (Get-Content $StdErrFile)
        }
    } catch {
        Write-Error "Es ist ein Fehler bei der Ausführung des Befehls '$Command $Arguments' aufgetreten: $($PSItem.ToString())"
    }
    finally {
        Remove-Item $StdOutFile -Force -ErrorAction SilentlyContinue
        Remove-Item $StdErrFile -Force -ErrorAction SilentlyContinue 
    }
}



function Install-GreyhoundServer {
    [CmdletBinding()]
    [Alias("Update-GreyhoundServer")]
    param (
        [Parameter(Mandatory=$true,
        ValueFromPipeline)]
        [String]$SetupFile,
        [Parameter(Mandatory=$false)]
        [String]$ContractNo,
        [String]$SerialNo,
        [ValidateSet('Complete', 'Server', 'Client')][String]$Kind='Server',
        [String]$TargetDir="$ENV:ProgramData\GREYHOUND\",
        [Switch]$DesktopShortcut=$false,
        [Switch]$StartMenuShortcut=$false,
        [Switch]$QuicklaunchShortcut=$false,
        [Switch]$DefaultMailClient=$false,
        [Switch]$PrinterDriver=$false,
        [Switch]$AutoStart=$false,
        [String]$DatabaseUser='root',
        [String]$DatabasePass='root',
        [int16]$DatabasePort=3306,
        [ValidateSet('Small', 'Medium', 'Large')][String]$DatabaseTemlate='Large',
        [String]$AdminPassword='admin',
        [Switch]$Force=$false
    )

    try {
        $SetupMode = ''

        $SetupFile = Resolve-Path($SetupFile)
        if (Test-Path -Path $SetupFile) {
            $Command = $SetupFile
        } else {
            Write-Error "Die Datei '$SetupFile' wurde nicht gefunden." -Category ObjectNotFound
            Break
        }

        if (Get-GreyhoundInstallLocation) {
            $SetupMode = 'Update'
            try {
                $InstalledVersion = Get-GreyhoundVersionInfo
                $SetupVersion = (Get-Item $SetupFile).VersionInfo.ProductVersionRaw

                if ($InstalledVersion.Revision -gt $SetupVersion.Revision) {
                    Write-Host "Die installierte Version $InstalledVersion ist neuer als die Installationsdatei $SetupVersion" 
                    if (!($Force)) {
                        Break
                    }
                } elseif ($InstalledVersion.Revision -eq $SetupVersion.Revision) {
                    Write-Host "Es ist bereits die Version $InstalledVersion installiert." 
                    if (!($Force)) {
                        Break
                    }
                }
    
                [String]$ContractNo = Get-GreyhoundServerIniValue 'ContractNumber'
                [String]$SerialNo = Get-GreyhoundServerIniValue 'Serial'  
    
                $Arguments = @(
                    "-silent",
                    "-contract", "$ContractNo",
                    "-serial", "$SerialNo",
                    "-kind", "$Kind"
                )                  
            }
            catch {
                Write-Error "Es ist ein Fehler bei der Versionsermittlung aufgetreten."
            }
        } else {
            $SetupMode = 'Install'
            $Arguments = @(
                "-silent",
                "-contract", "$ContractNo",
                "-serial", "$SerialNo",
                "-kind", "$Kind",
                "-targetdir", "$TargetDir",
                "-databaseuser", "$DatabaseUser",
                "-databasepass", "$DatabasePass",
                "-databaseport", "$DatabasePort"
                "-databasetemplate", "$DatabaseTemlate",
                "-adminpassword", "$AdminPassword"
            )

            if (!$AutoStart) {
                $Arguments += @("-nostart")
            }

            if (!$DesktopShortcut) {
                $Arguments += @("-nodesktop")
            }

            if (!$StartMenuShortcut) {
                $Arguments += @("-nostartmenu")
            }

            if (!$QuicklaunchShortcut) {
                $Arguments += @("-noquicklaunch")
            }

            if (!$DefaultMailClient) {
                $Arguments += @("-nodefaultmail")
            }

            if (!$PrinterDriver) {
                $Arguments += @("-noprinterdriver")
            }
        }

        if (!($ContractNo -and $SerialNumber)) {
            if ($SetupMode -eq 'Install') {
                Write-Host "GREYHOUND wird installiert..."
            } else {
                $VersionInstalled = (Get-GreyhoundVersionInfo).ToString()
                Write-Host "Die GREYHOUND-Installation" $VersionInstalled "wird aktualisiert..."
            }
 
            if ((Start-Process -FilePath "$Command" -ArgumentList "$Arguments" -Wait -PassThru).ExitCode -eq 0) {
                Write-Host "Die GREYHOUND-Installation war erfolgreich."
            } else {
                Throw "Das GREYHOUND-Setup hat einen unbekannten Fehler gemeldet."
            }
        } else {
            Write-Error "Fuer eine Installation oder ein Update sind eine Vertragsnummer und eine Seriennummer notwendig." -Category NotSpecified
            Break
        }
    }
    catch {
        Write-Error "Es ist ein Fehler bei der GREYHOUND-Installation aufgetreten: $($PSItem.ToString())"
    }
}



function New-GreyhoundDatabaseDump {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false,
        ValueFromPipeline)]
        [string]$MySqlUser='root',
        [string]$MySqlPassword='',
        [string]$MySqlParameters='--default-character-set=latin1',
        [string]$MySqlDatabase='greyhound',
        [switch]$Compress,
        [string]$DestinationPath=$PWD.Path
    )

    $MySqlInstallPath = (Get-ChildItem 'C:\Program Files\Maria*').FullName
    $MySqlDump= $MySqlInstallPath + '\bin\mysqldump.exe'
    $StdErr = $env:TEMP + '\mysql.stderr'
        
    if (!(Test-Path -Path $MySqlDump)) {
        Write-Warning 'Die Exe-Datei' $MySqlDump 'wurde nicht gefunden.'
        Break
    }

    $MySqlDumpArgs = @(
        "--user=$MySqlUser"
    )

    if ($MySqlPassword) {
        $MySqlDumpArgs += @(
            "--password=$MySqlPassword"
        )
    }
    
    $MySqlDumpArgs += @(
        "--default-character-set=latin1",
        "$MySqlDatabase"
    )

    try {
        [string]$SqlFile = $DestinationPath + '\' + $MySqlDatabase + '.sql'
        if ((Start-Process -FilePath $MySqlDump -ArgumentList $MySqlDumpArgs -RedirectStandardOutput $SqlFile -RedirectStandardError $StdErr -Wait -NoNewWindow -PassThru).Exitcode -gt 0) {
            if (Test-Path -Path $StdErr) {
                $ExceptionText = Get-Content $StdErr
                Remove-Item $StdErr
            }
            Throw $ExceptionText
        } else {
            Write-Host $SqlFile
        }
    }
    catch {
        Write-Host $($PSItem.ToString())
        Remove-Item $SqlFile
        Break 
    }

    if ($Compress) {
        if (Test-Path -Path $SqlFile) {
            $Zip = (Get-Item  $SqlFile).DirectoryName + '\' + (Get-Item  $SqlFile).BaseName + '.zip'
            if (Test-Path -Path $Zip) {
                Remove-Item $Zip
            }
            
            try {
                Write-Verbose "Der Datenbank-Dump wird komprimiert. Ziel: $Zip"
                Compress-Archive -Path $SqlFile -DestinationPath $Zip -CompressionLevel Optimal
            }
            catch {
                Write-Error "Es ist ein Fehler beim Komprimieren des Datenbank-Dumps aufgetreten."
            }
            finally {
                Write-Verbose "Die Datei $SqlFile wird gelöscht."
                Remove-Item $SqlFile 
            }

        } else {
            Write-Warning "Die Datenbank-Datei $SqlFile konnte nicht komprimiert werden, weil sie nicht vorhanden ist."
        }
    }
}



function Invoke-MySqlQuery {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$MySqlQuery,
        [Parameter(Mandatory=$false)]
        [string]$MySqlHostname='localhost',
        [string]$MySqlUser='root',
        [string]$MySqlPass=''
    )

    $MySqlInstallPath = (Get-ChildItem 'C:\Program Files\Maria*').FullName
    $Exe = $MySqlInstallPath + '\bin\mysql.exe'
    
    if (!(Test-Path -Path $Exe)) {
        Write-Warning 'Die Exe-Datei' $Exe 'wurde nicht gefunden.'
        Break
    }

    $Arguments = @(
        "--host=$MySqlHostname"
        "--user=$MySqlUser"
        '--execute="' + $MySqlQuery + '"'
    )

    if ($MySqlPass) {
        $Arguments += @(
            "--password=$MySqlPass"
        )
    }
    
    try {
        $ExeBasename = $Exe.Substring($Exe.LastIndexOf('\') + 1, $Exe.LastIndexOf('.') - $Exe.LastIndexOf('\') - 1)
        $StdOut = $env:TEMP + $ExeBasename + '.stdout'
        $StdErr = $env:TEMP + $ExeBasename + '.stderr'
    
        Start-Process -FilePath "$Exe" -ArgumentList "$Arguments" -Wait -RedirectStandardOutput $StdOut -RedirectStandardError $StdErr -NoNewWindow
        if (Test-Path -Path $StdOut) {
            Get-Content $StdOut
        }
        if (Test-Path -Path $StdErr) {
            Get-Content $StdErr
        }           
    }
    finally {
        Remove-Item $StdOut -Force
        Remove-Item $StdErr -Force      
    }
}



function Remove-GreyhoundDatabase {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false,
        ValueFromPipeline)]
        [string]$MySqlHostname='localhost',
        [string]$MySqlUser='root',
        [string]$MySqlPass=''
    )

    $MySqlQuery = 'DROP DATABASE greyhound;'

    Invoke-MySqlQuery -MySqlQuery $MySqlQuery -MySqlHostname $MySqlHostname -MySqlUser $MySqlUser -MySqlPass $MySqlPass
}