AppManiProgramManager.psm1
# Version: 1.4.1 Date: 220817 Last Updated by: rod@appmani.com # / Fixed Get-ProgramArchitecture's output from x32 to x86 # Version: 1.4.0 Date: 220817 Last Updated by: rod@appmani.com # / Changed Get-InstalledProgram/Service parameter 'Program' to 'ProgramName' # / Get-InstalledProgram's use of wildcards will only be used depending on function call's parameters # + Added new functions Set-RegistryItem, Get-ProgramArchitecture # * Improved error handling responses # 1.3.2 ! Fixed a syntax error # 1.3.1 - Removed some lines for debugging # 1.3.0 * Set ProgressPreference to SilentlyContinue to improve download time # + Now displays current and latest available version of program # 1.2.2 + Added functions Add-InstallerFolder and Remove-InstallerFolder in functions to export in module manifest # 1.2.1 - Removed uneeded files in package # 1.2.0 + Added Add-InstallerFolder function so all installer related files will go to a single folder # / Changed Remove-Installer function to Remove-InstallerFolder # 1.1.2 / Changed version just to test updates # 1.1.1 / Changed author to Appmani # 1.1.0 + Added Confirm-ServiceInstallation function # 1.0.0 + First upload # This function is to mitigate the Invoke-WebRequest error where it won't run because IE First Run Customization hasn't been done yet. Using the switch parameter UseBasicParsing would work for regular web requests, but not for Downloads Function Test-WebRequest { Param ( $URI ) # Loop until system is able to successfully invoke a web request while ($null -eq $webRequest) { try { $webRequest = Invoke-WebRequest -Uri $URI } # Catches the exception where IE first run customization has not been done yet catch [System.NotSupportedException] { Write-Host "Disabling IE First RunCustomization..." -NoNewline try { Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Internet Explorer\Main" -Name "DisableFirstRunCustomize" -Value 2 } catch { Write-Warning "Failed to disable IE First RunCustomization: $($Error[0])" return $null } } # catches other exceptions catch { Write-Warning "Failed to execute test webrequest: $($Error[0])" return $null } } return $webrequest } # Downloads installer Function Get-Installer { Param ( $DownloadLink, $SavePath, $FileName ) $ProgressPreference = 'SilentlyContinue' # Tests if save path is existing if (Test-Path $savePath) { # If a preferred filename is not provided, the test after the last '/' of the download link will serve as the filename of the downloaded file if ($null -eq $FileName) { $filename = $DownloadLink.Substring($DownloadLink.LastIndexOf("/") + 1) } $SavePath = $SavePath + $fileName # Downloads the file try { Invoke-WebRequest -Uri $downloadLink -OutFile $savePath } catch { Write-Warning "Unable to download installer: $($Error[0])" return $null } return $SavePath } else { Write-Warning "Download path $SavePath not existing. Please specify a valid path." } return $null } # Creates folder for storing installation files e.g. msi, exe, config files, etc Function Add-InstallerFolder { Param ( $Path ) If (Test-Path -Path $Path) { try { Remove-Item -Path $Path -Force -Recurse #-ErrorAction Stop } catch { Write-Warning "Failed to delete installer folders and its contents: $($Error[0])" return $null } } try { $installFilesFolder = New-Item -Path $Path -ItemType Directory return $installFilesFolder } catch { Write-Warning "Failed to create installer folder: $($Error[0])" return $null } } #Deletes installer Function Remove-InstallerFolder { Param ( $Path ) # Removes a file try { Remove-Item -Path $Path -Recurse -Force } catch { Write-Warning "Failed to delete installer folder: $($Error[0])" return $null } } # Checks the registry for entries of the installed program and returns information about it Function Get-InstalledProgram { Param ( $ProgramName ) $Apps = @() $Apps += Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" # 32 Bit $Apps += Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" # 64 Bit $installedProgram = $Apps | Where-Object DisplayName -like "$ProgramName" return $installedProgram } # Checks the registry for entries of the isntalled service and returns information about it Function Get-InstalledService { Param ( $ProgramName ) $RegistryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$ProgramName" $installedService = Get-ItemProperty -Path $RegistryPath -ErrorAction SilentlyContinue return $installedService } #Installs program using a one-liner msiexec or calls the installer executable with additional arguments Function Install-Program { Param ( $InstallCommand, $Program ) try { cmd /c $InstallCommand } catch { Write-Warning "Unable to install $($Program): $($Error[0])" return 1 } #Write-Host "Exit code: $LASTEXITCODE" return $LASTEXITCODE } Function Confirm-ProgramInstallation { Param ( $ProgramName ) # Loops X number of times to check registry keys for the program $tries = 0 while ($tries -le 30) { $tries++ Write-Host "Verifying installation. Tries: $tries" $installedProgram = Get-InstalledProgram -ProgramName $ProgramName if ($null -ne $installedProgram) { return $installedProgram } Start-Sleep -s 15 } Write-Warning "Script has reached the maximum number of retries on installation verification. Please investigate for issues." return $null } Function Confirm-ServiceInstallation { Param ( $ProgramName ) # Loops X number of times to check registry keys for the service $tries = 0 while ($tries -le 30) { $tries++ Write-Host "Verifying installation. Tries: $tries" $installedService = Get-InstalledService -Program $ProgramName if ($null -ne $installedService) { return $installedService } Start-Sleep -s 15 } Write-Warning "Script has reached the maximum number of retries on installation verification. Please investigate for issues." return $null } # Compares current version of a program from the registry and what's on the download link. There are programs that won't have registry entries and programs that won't have their versions on the download link, so please check first before using Function Compare-Versions { Param ( $InstalledProgram, $DownloadLink, $downloadLinkRegex, $regexMatchIndex = 1 ) $DownloadLink -match $downloadLinkRegex | Out-Null $latestVersion = $matches[$regexMatchIndex] -replace '[.]', '' $currentVersion = $($InstalledProgram.DisplayVersion) -replace '[.]','' Write-Host "Version installed: $currentVersion" Write-Host "Latest version: $latestVersion" if ($latestVersion.equals($currentVersion)) { return $false } else { return $true } } Function Set-RegistryItem { Param ( $RegistryPath, $Name, $Value, $PropertyType ) # Create the key if it does not exist If (-NOT (Test-Path $RegistryPath)) { try { New-Item -Path $RegistryPath -Force -ErrorAction Stop #| Out-Null Write-Host "Successfully created new registry path $RegistryPath." } catch { return $null } } # Now set the value try { New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force -ErrorAction Stop Write-Host "Successfully set registry item $RegistryPath\$Name to $Value." } catch { return $null } } Function Get-ProgramArchitecture { Param ( $Program ) if ($Program.PSParentPath -eq 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall') { return "x86" } elseif ($Program.PSParentPath -eq 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall') { return "x64" } else { return $null } } Function Send-Keys { Param ( $ApplicationWindowTitle, $Keys ) $wshell = New-Object -ComObject wscript.shell; $wshell.AppActivate($ApplicationWindowTitle) $wshell.SendKeys($Keys) } Export-ModuleMember -Function 'Test-WebRequest' Export-ModuleMember -Function 'Get-Installer' Export-ModuleMember -Function 'Add-InstallerFolder' Export-ModuleMember -Function 'Remove-InstallerFolder' Export-ModuleMember -Function 'Get-InstalledProgram' Export-ModuleMember -Function 'Get-InstalledService' Export-ModuleMember -Function 'Install-Program' Export-ModuleMember -Function 'Confirm-ProgramInstallation' Export-ModuleMember -Function 'Confirm-ServiceInstallation' Export-ModuleMember -Function 'Compare-Versions' Export-ModuleMember -Function 'Set-RegistryItem' Export-ModuleMember -Function 'Get-ProgramArchitecture' Export-ModuleMember -Function 'Send-Keys' |