Advertisement
SmallTarzan

Untitled

Dec 2nd, 2024 (edited)
105
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # Script configuration
  2. [CmdletBinding()]
  3. param (
  4.     [string]$LogPath = 'C:\install\logs',
  5.     [switch]$Force,
  6.     [switch]$SkipTeamViewer,
  7.     [switch]$Test,
  8.     [switch]$PC,
  9.     [switch]$Laptop
  10. )
  11.  
  12. # Enable strict mode for better error catching
  13. Set-StrictMode -Version Latest
  14. $ErrorActionPreference = 'Stop'
  15.  
  16. # Load required assemblies
  17. Add-Type -AssemblyName System.Windows.Forms
  18.  
  19. # The InstallationResult class definition
  20. class InstallationResult {
  21.     [string]$Name
  22.     [bool]$Success
  23.     [string]$Message
  24.     [datetime]$Timestamp
  25.    
  26.     InstallationResult([string]$name, [bool]$success, [string]$message) {
  27.         $this.Name = $name
  28.         $this.Success = $success
  29.         $this.Message = $message
  30.         $this.Timestamp = Get-Date
  31.     }
  32. }
  33.  
  34. Clear-Host
  35.  
  36. # Initialize logging
  37. function Initialize-InstallationEnvironment {
  38.     param (
  39.         [string]$LogDirectory
  40.     )
  41.    
  42.     $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
  43.     $logFile = Join-Path $LogDirectory "installation_$timestamp.log"
  44.    
  45.     if (-not (Test-Path $LogDirectory)) {
  46.         New-Item -Path $LogDirectory -ItemType Directory -Force | Out-Null
  47.     }
  48.    
  49.     return $logFile
  50. }
  51.  
  52. # Enhanced logging function
  53. function Write-LogMessage {
  54.     param (
  55.         [Parameter(Mandatory=$true)]
  56.         [string]$Message,
  57.        
  58.         [Parameter(Mandatory=$false)]
  59.         [ValidateSet('Info', 'Warning', 'Error', 'Success')]
  60.         [string]$Level = 'Info',
  61.        
  62.         [Parameter(Mandatory=$false)]
  63.         [string]$LogFile
  64.     )
  65.    
  66.     $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
  67.     $logMessage = "[$timestamp] [$Level] $Message"
  68.    
  69.     $colorMapping = @{
  70.         'Info' = 'Cyan'
  71.         'Warning' = 'Yellow'
  72.         'Error' = 'Red'
  73.         'Success' = 'Green'
  74.     }
  75.    
  76.     Write-Host $logMessage -ForegroundColor $colorMapping[$Level]
  77.    
  78.     if ($LogFile) {
  79.         Add-Content -Path $LogFile -Value $logMessage
  80.     }
  81. }
  82. # Function to check network connectivity
  83. function Test-NetworkConnectivity {
  84.     try {
  85.         $null = Test-Connection -ComputerName 'google.com' -Count 2 -ErrorAction Stop
  86.         return $true
  87.     }
  88.     catch {
  89.         return $false
  90.     }
  91. }
  92.  
  93. # Function to check available disk space
  94. function Test-DiskSpace {
  95.     param (
  96.         [int]$RequiredSpaceGB = 5,
  97.         [string]$LogFile
  98.     )
  99.    
  100.     try {
  101.         $drive = Get-PSDrive -Name 'C' -ErrorAction Stop
  102.         $driveFreeSpace = $drive.Free
  103.        
  104.         if ($driveFreeSpace -lt ($RequiredSpaceGB * 1GB)) {
  105.             throw "Not enough disk space. Required: ${RequiredSpaceGB}GB, Available: $([math]::Round($driveFreeSpace / 1GB, 2))GB"
  106.         }
  107.         else {
  108.             Write-LogMessage -Message "Sufficient disk space available: $([math]::Round($driveFreeSpace / 1GB, 2))GB" -Level Info -LogFile $LogFile
  109.         }
  110.     }
  111.     catch {
  112.         Write-LogMessage -Message "Error checking disk space: $_" -Level Error -LogFile $LogFile
  113.         throw
  114.     }
  115. }
  116.  
  117. # Configuration validation
  118. function Test-InstallationPrerequisites {
  119.     param (
  120.         [string]$LogFile
  121.     )
  122.    
  123.     Write-LogMessage -Message "Checking installation prerequisites..." -Level Info -LogFile $LogFile
  124.    
  125.     $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
  126.     if (-not $isAdmin) {
  127.         Write-LogMessage -Message "Script must be run as administrator." -Level Error -LogFile $LogFile
  128.         throw "Administrator privileges required"
  129.     }
  130.  
  131.     if (-not (Test-NetworkConnectivity)) {
  132.         Write-LogMessage -Message "No network connectivity. Please ensure you are connected to the internet. Some installations might fail." -Level Warning -LogFile $LogFile
  133.     }
  134.     else {
  135.         Write-LogMessage -Message "Internet connection available." -Level Info -LogFile $LogFile
  136.     }
  137.  
  138.     try {
  139.         Test-DiskSpace -RequiredSpaceGB 10 -LogFile $LogFile
  140.     }
  141.     catch {
  142.         Write-LogMessage -Message "Insufficient disk space: $_" -Level Error -LogFile $LogFile
  143.         throw $_
  144.     }
  145. }
  146.  
  147. function Test-RecursivePath {
  148.     param (
  149.         [string]$SoftwareName,
  150.         [string]$LogFile
  151.     )
  152.  
  153.     # Ensure the software list is populated
  154.     if (-not $softwareList -or $softwareList.Count -eq 0) {
  155.         Write-LogMessage -Message "Software list is empty. Please populate the list before running the script." -Level Error -LogFile $LogFile
  156.         return $false
  157.     }
  158.  
  159.     # Retrieve the matching software entry
  160.     $softwareEntry = $softwareList | Where-Object { $_.Name -eq $SoftwareName }
  161.  
  162.     if (-not $softwareEntry) {
  163.         Write-LogMessage -Message "Software $SoftwareName not found in the list." -Level Error -LogFile $LogFile
  164.         return $false
  165.     }
  166.  
  167.     # Use CheckName for search patterns and VerifyFile for validation
  168.     $searchPattern = $softwareEntry.CheckName
  169.     $verifyFile = $softwareEntry.VerifyFile
  170.  
  171.     # Common installation directories
  172.     $commonPaths = @(
  173.         "C:\Program Files",
  174.         "C:\Program Files (x86)",
  175.         "${env:ProgramFiles}",
  176.         "${env:ProgramFiles(x86)}",
  177.         "${env:LocalAppData}\Programs",
  178.         "${env:ProgramData}",
  179.         "C:\Program Files\WindowsApps"
  180.     )
  181.  
  182.     Write-LogMessage -Message "Searching for $SoftwareName installation..." -Level Info -LogFile $LogFile
  183.     $directoryFound = $false
  184.  
  185.     foreach ($basePath in $commonPaths) {
  186.         if (Test-Path $basePath) {
  187.             try {
  188.                 # Search for directories matching the CheckName pattern
  189.                 $matchingDirs = Get-ChildItem -Path $basePath -Directory -Recurse -ErrorAction SilentlyContinue |
  190.                     Where-Object { $_.FullName -like "*$searchPattern*" }
  191.  
  192.                 foreach ($dir in $matchingDirs) {
  193.                     # Validate using VerifyFile, if provided
  194.                     if ($verifyFile) {
  195.                         $fullVerifyFilePath = Join-Path $dir.FullName $verifyFile
  196.                         if (Test-Path $fullVerifyFilePath) {
  197.                             Write-LogMessage -Message "Found valid installation directory for $SoftwareName" -Level Success -LogFile $LogFile
  198.                             Write-LogMessage -Message "- $($dir.FullName)" -Level Info -LogFile $LogFile
  199.                             return $true
  200.                         }
  201.                     } else {
  202.                         Write-LogMessage -Message "Found potential installation directory for $SoftwareName (no VerifyFile):" -Level Warning -LogFile $LogFile
  203.                         Write-LogMessage -Message "- $($dir.FullName)" -Level Info -LogFile $LogFile
  204.                         return $true
  205.                     }
  206.                 }
  207.             } catch {
  208.                 Write-LogMessage -Message "Error searching in {$basePath}: $_" -Level Warning -LogFile $LogFile
  209.                 continue
  210.             }
  211.         }
  212.     }
  213.  
  214.     Write-LogMessage -Message "No installation directories found for $SoftwareName." -Level Warning -LogFile $LogFile
  215.     return $false
  216. }
  217.  
  218.  
  219.  
  220.  
  221. function Test-SoftwareInstallation {
  222.     param (
  223.         [Parameter(Mandatory=$true)]
  224.         [hashtable]$Software,
  225.         [string]$LogFile
  226.     )
  227.  
  228.     # Define registry paths
  229.     $registryPaths = @(
  230.         "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
  231.         "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
  232.         "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
  233.         "HKCU:\Software\Microsoft\$($Software.Name)"
  234.     )
  235.  
  236.     # Initialize verification results
  237.     $verificationResults = @{
  238.         RegistryFound  = $false
  239.         PathFound      = $false
  240.         ProcessFound   = $false
  241.         RegistryDetails = $null
  242.     }
  243.  
  244.     try {
  245.         # Check registry for software
  246.         foreach ($path in $registryPaths) {
  247.             try {
  248.                 $registryEntries = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
  249.                     Where-Object { $_.DisplayName -like $Software.CheckName }
  250.                 if ($registryEntries) {
  251.                     $verificationResults.RegistryFound = $true
  252.                     $verificationResults.RegistryDetails = @{
  253.                         DisplayName     = $registryEntries.DisplayName
  254.                         DisplayVersion  = $registryEntries.DisplayVersion
  255.                         InstallLocation = if ($registryEntries.PSObject.Properties.Name -contains 'InstallLocation') {
  256.                             $registryEntries.InstallLocation
  257.                         } else {
  258.                             $null
  259.                         }
  260.                     }
  261.                 }
  262.             } catch { continue }
  263.         }
  264.  
  265.         # Check critical file if InstallLocation exists
  266.         if ($Software.VerifyFile -and $verificationResults.RegistryDetails.InstallLocation) {
  267.             $verifyPath = Join-Path $verificationResults.RegistryDetails.InstallLocation $Software.VerifyFile
  268.             if ($verifyPath -and (Test-Path $verifyPath)) {
  269.                 $verificationResults.PathFound = $true
  270.             }
  271.         }
  272.  
  273.         # Check for running process
  274.         if (Get-Process -Name $Software.Name -ErrorAction SilentlyContinue) {
  275.             $verificationResults.ProcessFound = $true
  276.         }
  277.  
  278.         # Override if no directory or critical file is found
  279.         if (-not $verificationResults.PathFound) {
  280.             Write-LogMessage -Message "No valid installation directory or critical files found for $($Software.Name)." -Level Warning -LogFile $LogFile
  281.             return @{ IsInstalled = $false; Details = $verificationResults }
  282.         }
  283.  
  284.         # Final decision
  285.         $isInstalled = $verificationResults.RegistryFound -or $verificationResults.PathFound -or $verificationResults.ProcessFound
  286.         return @{ IsInstalled = $isInstalled; Details = $verificationResults }
  287.     } catch {
  288.         throw $_
  289.     }
  290. }
  291.  
  292.  
  293.  
  294.  
  295. function Set-UninstallInformation {
  296.     param (
  297.         [Parameter(Mandatory=$true)]
  298.         [string]$DisplayName,
  299.         [Parameter(Mandatory=$true)]
  300.         [string]$UninstallString,
  301.         [Parameter(Mandatory=$false)]
  302.         [string]$Publisher = "Medirex Group",
  303.         [Parameter(Mandatory=$false)]
  304.         [string]$DisplayVersion = "",
  305.         [Parameter(Mandatory=$false)]
  306.         [string]$EstimatedSize = "",
  307.         [Parameter(Mandatory=$false)]
  308.         [string]$InstallLocation = "",
  309.         [string]$LogFile
  310.     )
  311.  
  312.     try {
  313.         $uninstallPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$DisplayName"
  314.        
  315.         if (-not (Test-Path $uninstallPath)) {
  316.             New-Item -Path $uninstallPath -Force | Out-Null
  317.         }
  318.  
  319.         $properties = @{
  320.             "DisplayName" = $DisplayName
  321.             "UninstallString" = $UninstallString
  322.             "Publisher" = $Publisher
  323.             "NoModify" = 1
  324.             "NoRepair" = 1
  325.         }
  326.  
  327.         if ($DisplayVersion) { $properties["DisplayVersion"] = $DisplayVersion }
  328.         if ($EstimatedSize) { $properties["EstimatedSize"] = $EstimatedSize }
  329.         if ($InstallLocation) { $properties["InstallLocation"] = $InstallLocation }
  330.  
  331.         foreach ($prop in $properties.GetEnumerator()) {
  332.             Set-ItemProperty -Path $uninstallPath -Name $prop.Key -Value $prop.Value
  333.         }
  334.  
  335.         Write-LogMessage -Message "Successfully set uninstall information for ${DisplayName}" -Level Info -LogFile $LogFile
  336.     }
  337.     catch {
  338.         Write-LogMessage -Message "Error setting uninstall information for ${DisplayName}: $_" -Level Error -LogFile $LogFile
  339.         throw
  340.     }
  341. }
  342.  
  343. function Get-SoftwareList {
  344.     param (
  345.         [switch]$Test,
  346.         [switch]$PC,
  347.         [switch]$Laptop
  348.     )
  349.  
  350.     $softwareList = @()
  351.  
  352.     # Common software
  353.     $commonSoftware = @(
  354.         @{
  355.             Name = "7-Zip"
  356.             InstallerPath = Join-Path $PSScriptRoot "7-Zip\7z2408-x64.exe"
  357.             CheckName = "*7-Zip*"
  358.             Arguments = "/S"
  359.             UninstallArgs = "/S"
  360.             MaxRetries = 3
  361.             UseMsiexec = $false
  362.             UninstallKey = "7-Zip"
  363.             Publisher = "Igor Pavlov"
  364.             VerifyFile = "7zFM.exe"
  365.         },
  366.         @{
  367.             Name = "Adobe Reader (Slovak)"
  368.             InstallerPath = Join-Path $PSScriptRoot "AdobeReader\Reader_sk_install.exe"
  369.             CheckName = "*Acrobat*"
  370.             Arguments = "/sAll /msi EULA_ACCEPT=YES"
  371.             UninstallArgs = "/sAll"
  372.             MaxRetries = 3
  373.             UseMsiexec = $false
  374.             VerifyFile = "Acrobat.exe"
  375.             NeedsCopy = $true
  376.             Publisher = "Adobe"
  377.         },
  378.         @{
  379.             Name = "TeamViewer Host"
  380.             InstallerPath = Join-Path $PSScriptRoot "Teamviewer.host\TeamViewer_Host_Setup_x64.exe"
  381.             CheckName = "*TeamViewer*"
  382.             Arguments = "/S"
  383.             UninstallArgs = "/S"
  384.             MaxRetries = 3
  385.             UseMsiexec = $false
  386.             Publisher = "TeamViewer"
  387.             VerifyFile = "TeamViewer.exe"
  388.         },
  389.         @{
  390.             Name = "FortiClient"
  391.             InstallerPath = Join-Path $PSScriptRoot "FortiClient\FortiClient.msi"
  392.             CheckName = "*FortiClient*"
  393.             Arguments = { param($path) "/i `"$path`" /qn" }
  394.             UninstallArgs = "/qn"
  395.             UseMsiexec = $true
  396.             MaxRetries = 3
  397.             Publisher = "Fortinet Technologies Inc"
  398.             VerifyFile = "FortiClient.exe"
  399.         },
  400.         @{
  401.             Name = "Microsoft Teams"
  402.             InstallerPath = Join-Path $PSScriptRoot "Microsoft Teams\MSTeams-x64.msix"
  403.             CheckName = "*Teams*"
  404.             Arguments = ""
  405.             UninstallArgs = ""
  406.             MaxRetries = 3
  407.             UseMsiexec = $false
  408.             Publisher = "Microsoft"
  409.             VerifyFile = "ms-teams.exe"
  410.         }
  411.     )
  412.  
  413.     $softwareList += $commonSoftware
  414.  
  415.     # PC-specific software
  416.     if ($PC) {
  417.         $softwareList += @(
  418.             @{
  419.                 Name = "LibreOffice"
  420.                 InstallerPath = Join-Path $PSScriptRoot "LibreOffice\LibreOffice_24.8.3_Win_x86-64.msi"
  421.                 CheckName = "*LibreOffice*"
  422.                 Arguments = { param($path) "/i `"$path`" /qn ADDLOCAL=ALL REMOVE=gm_o_Onlineupdate" }
  423.                 UninstallArgs = "/qn"
  424.                 UseMsiexec = $true
  425.                 MaxRetries = 3
  426.                 VerifyFile = "soffice.exe"
  427.                 Publisher = "The Document Foundation"
  428.             }
  429.         )
  430.     }
  431.  
  432.     # Laptop-specific software
  433.     if ($Laptop) {
  434.         $chassisType = (Get-CimInstance -ClassName Win32_SystemEnclosure).ChassisTypes
  435.         if (-not ($chassisType -contains 9 -or $chassisType -contains 10 -or $chassisType -contains 14)) {
  436.             $response = Read-Host "Device does not appear to be a laptop. Continue anyway? (Y/N)"
  437.             if ($response.ToUpper() -ne "Y") {
  438.                 throw "Installation aborted by user"
  439.             }
  440.         }
  441.  
  442.         $softwareList += @(
  443.             @{
  444.                 Name = "Office 2021"
  445.                 InstallerPath = Join-Path $PSScriptRoot "Office\OfficeSetup.exe"
  446.                 CheckName = "*Microsoft Office*"
  447.                 Arguments = "/quiet"
  448.                 UninstallArgs = "/quiet"
  449.                 MaxRetries = 3
  450.                 UseMsiexec = $false
  451.                 Publisher = "Microsoft"
  452.                 VerifyFile = "WINWORD.EXE"
  453.             }
  454.         )
  455.     }
  456.  
  457.     # Add extra software for full installations
  458.     if (-not $Test) {
  459.         $softwareList += @(
  460.             @{
  461.                 Name = "ESET Endpoint Antivirus"
  462.                 InstallerPath = Join-Path $PSScriptRoot "ESET\CLIENT ESET Endpoint Antivirus (sk_SK) 10x.exe"
  463.                 CheckName = "*ESET*"
  464.                 Arguments = "/quiet"
  465.                 UninstallArgs = "/quiet"
  466.                 MaxRetries = 3
  467.                 UseMsiexec = $false
  468.                 Publisher = "ESET, spol. s r.o."
  469.                 VerifyFile = "ecmds.exe"
  470.             },
  471.             @{
  472.                 Name = "CrowdStrike EDR"
  473.                 InstallerPath = Join-Path $PSScriptRoot "EDR\!New_Install_CrowdStrike.bat"
  474.                 CheckName = "*CrowdStrike*"
  475.                 Arguments = ""
  476.                 UninstallArgs = ""
  477.                 MaxRetries = 3
  478.                 UseMsiexec = $false
  479.                 Publisher = "CrowdStrike, Inc."
  480.                 VerifyFile = "CSFalconService.exe"
  481.             }
  482.         )
  483.     }
  484.  
  485.     return $softwareList
  486. }
  487.  
  488.  
  489. function Install-Software {
  490.     param (
  491.         [Parameter(Mandatory=$true)]
  492.         [hashtable]$Software,
  493.         [string]$LogFile,
  494.         [int]$Timeout = 300
  495.     )
  496.    
  497.     # Initialize the InstallationResult object
  498.     $result = [InstallationResult]::new($Software.Name, $false, "")
  499.    
  500.     try {
  501.         Write-LogMessage -Message "Processing $($Software.Name)..." -Level Info -LogFile $LogFile
  502.        
  503.         # Use new verification function
  504.         $verificationResult = Test-SoftwareInstallation -Software $Software -LogFile $LogFile
  505.        
  506.         if (-not $verificationResult.IsInstalled) {
  507.             # Handle copy if needed
  508.             if ($Software.ContainsKey('NeedsCopy') -and $Software.NeedsCopy) {
  509.                 $originalInstallerPath = $Software.InstallerPath
  510.                 $Software.InstallerPath = "$PSScriptRoot\AdobeReader\InstallerBackup_$(Get-Random).exe"
  511.                 Copy-Item -Path $originalInstallerPath -Destination $Software.InstallerPath -Force
  512.             }
  513.  
  514.             # Special handling for Microsoft Teams
  515.             if ($Software.Name -eq "Microsoft Teams") {
  516.                 Write-LogMessage -Message "Installing $($Software.Name) using Add-AppProvisionedPackage..." -Level Info -LogFile $LogFile
  517.                
  518.                 try {
  519.                     Add-AppProvisionedPackage -Online -PackagePath "$($Software.InstallerPath)" -SkipLicense
  520.                     $result.Success = $true
  521.                     $result.Message = "Installation successful"
  522.                 } catch {
  523.                     Write-LogMessage -Message "Failed to provision package: $_" -Level Error -LogFile $LogFile
  524.                     throw
  525.                 }
  526.             } else {
  527.                 if (-not (Test-Path $Software.InstallerPath)) {
  528.                     throw "Installer not found at path: $($Software.InstallerPath)"
  529.                 }
  530.                
  531.                 Write-LogMessage -Message "Installing $($Software.Name)..." -Level Info -LogFile $LogFile
  532.                
  533.                 $process = if ($Software.UseMsiexec) {
  534.                     $arguments = if ($Software.Arguments -is [scriptblock]) {
  535.                         & $Software.Arguments $Software.InstallerPath
  536.                     } else {
  537.                         $Software.Arguments
  538.                     }
  539.                     Start-Process -FilePath msiexec -ArgumentList $arguments -PassThru -NoNewWindow
  540.                 } else {
  541.                     Start-Process -FilePath $Software.InstallerPath -ArgumentList $Software.Arguments -PassThru -NoNewWindow
  542.                 }
  543.                
  544.                 if ($process.WaitForExit($Timeout * 1000)) {
  545.                     # Allow some time for installation to complete
  546.                     Start-Sleep -Seconds 10
  547.                    
  548.                     # Verify installation using new function
  549.                     $postInstallVerification = Test-SoftwareInstallation -Software $Software -LogFile $LogFile
  550.  
  551.                     # Debug: Log the post-install verification result
  552.                     Write-LogMessage -Message "Post-install verification: $($postInstallVerification | ConvertTo-Json -Depth 2)" -Level Info -LogFile $LogFile
  553.                    
  554.                     if ($postInstallVerification.IsInstalled) {
  555.                         if ($result -is [InstallationResult]) {
  556.                             $result.Success = $true
  557.                             $result.Message = "Installation successful"
  558.                             Write-LogMessage -Message "$($Software.Name) installed successfully" -Level Success -LogFile $LogFile
  559.                         } else {
  560.                             Write-LogMessage -Message "Result object is not of type InstallationResult. Actual type: $($result.GetType().Name)" -Level Error -LogFile $LogFile
  561.                             throw "Unexpected object type for result"
  562.                         }
  563.  
  564.                         # Set uninstall information
  565.                         $uninstallString = if ($Software.UseMsiexec) {
  566.                             "MsiExec.exe /X`"$($Software.InstallerPath)`" $($Software.UninstallArgs)"
  567.                         } else {
  568.                             "$($Software.InstallerPath) $($Software.UninstallArgs)"
  569.                         }
  570.  
  571.                         $displayVersion = ""
  572.                         if ($postInstallVerification.Details.RegistryDetails -and
  573.                             $postInstallVerification.Details.RegistryDetails.PSObject.Properties['DisplayVersion']) {
  574.                             $displayVersion = $postInstallVerification.Details.RegistryDetails.DisplayVersion
  575.                         }
  576.  
  577.                         $installLocation = ""
  578.                         if ($postInstallVerification.Details.RegistryDetails -and
  579.                             $postInstallVerification.Details.RegistryDetails.PSObject.Properties['InstallLocation']) {
  580.                             $installLocation = $postInstallVerification.Details.InstallLocation
  581.                         } elseif ($Software.ContainsKey('VerifyFile')) {
  582.                             $installLocation = Split-Path $Software.VerifyFile -Parent
  583.                         }
  584.  
  585.                         Set-UninstallInformation `
  586.                             -DisplayName $Software.Name `
  587.                             -UninstallString $uninstallString `
  588.                             -Publisher $Software.Publisher `
  589.                             -DisplayVersion $displayVersion `
  590.                             -InstallLocation $installLocation `
  591.                             -LogFile $LogFile
  592.                     } else {
  593.                         throw "Installation verification failed"
  594.                     }
  595.                 } else {
  596.                     $process.Kill()
  597.                     throw "Installation timeout exceeded"
  598.                 }
  599.             }
  600.         } else {
  601.             $result.Success = $true
  602.             $result.Message = "Already installed"
  603.             Write-LogMessage -Message "$($Software.Name) is already installed" -Level Success -LogFile $LogFile
  604.         }
  605.     }
  606.     catch {
  607.         $result.Success = $false
  608.         $result.Message = "Installation failed: $_"
  609.         Write-LogMessage -Message "Failed to install $($Software.Name): $_" -Level Error -LogFile $LogFile
  610.        
  611.         if (-not $Force) {
  612.             $response = Read-Host "Installation failed. Continue with remaining installations? (Y/N)"
  613.             if ($response.ToUpper() -ne "Y") {
  614.                 throw "Installation aborted by user"
  615.             }
  616.         }
  617.     }
  618.     return $result
  619. }
  620.  
  621. # Main execution block
  622. try {
  623.     if (-not $Test) {
  624.         $response = Read-Host "The -Test switch is not set. Are you sure you want to proceed with a full installation? (Y/N)"
  625.         if ($response.ToUpper() -ne "Y") {
  626.             throw "Full installation aborted by user"
  627.         }
  628.     }
  629.    
  630.     $logFile = Initialize-InstallationEnvironment -LogDirectory $LogPath
  631.     Write-LogMessage -Message "Starting software installation process" -Level Info -LogFile $logFile
  632.     Test-InstallationPrerequisites -LogFile $logFile
  633.  
  634.     $results = @()
  635.     $SoftwareList = Get-SoftwareList -Test:$Test -PC:$PC -Laptop:$Laptop
  636.  
  637.     foreach ($software in $SoftwareList) {
  638.         Write-LogMessage -Message "Checking if $($software.Name) is already installed..." -Level Info -LogFile $logFile
  639.    
  640.         # Check if installation directories exist
  641.         $directoryFound = Test-RecursivePath -SoftwareName $software.Name -LogFile $logFile
  642.    
  643.         # Check installation status
  644.         $verification = Test-SoftwareInstallation -Software $software -LogFile $logFile
  645.    
  646.         if (-not $directoryFound -and -not $verification.IsInstalled) {
  647.             Write-LogMessage -Message "No installation markers found for $($software.Name). Proceeding with installation..." -Level Info -LogFile $logFile
  648.         } elseif ($verification.IsInstalled) {
  649.             Write-LogMessage -Message "$($software.Name) is already installed. Skipping installation." -Level Success -LogFile $logFile
  650.             continue
  651.         }
  652.    
  653.         $retryCount = 0
  654.         $installed = $false
  655.         $originalInstallerPath = $software.InstallerPath
  656.    
  657.         while (-not $installed -and $retryCount -lt $software.MaxRetries) {
  658.             try {
  659.                 $result = Install-Software -Software $software -LogFile $logFile
  660.                 $results += $result
  661.                 $installed = $result.Success
  662.    
  663.                 if ($installed) {
  664.                     Write-LogMessage -Message "Successfully installed $($software.Name)" -Level Success -LogFile $logFile
  665.                 } else {
  666.                     $retryCount++
  667.                     if ($retryCount -lt $software.MaxRetries) {
  668.                         Write-LogMessage -Message "Retry $retryCount of $($software.MaxRetries) for $($software.Name)" -Level Warning -LogFile $logFile
  669.                         Start-Sleep -Seconds (2 * $retryCount)
  670.    
  671.                         if ($software.ContainsKey('NeedsCopy') -and $software.NeedsCopy) {
  672.                             $software.InstallerPath = "$PSScriptRoot\TempInstaller_$(Get-Random).exe"
  673.                             Copy-Item -Path $originalInstallerPath -Destination $software.InstallerPath -Force
  674.                         }
  675.                     }
  676.                 }
  677.             } catch {
  678.                 Write-LogMessage -Message "Error during installation attempt $retryCount for $($software.Name): $_" -Level Error -LogFile $logFile
  679.                 $retryCount++
  680.                 if ($retryCount -ge $software.MaxRetries) {
  681.                     Write-LogMessage -Message "Maximum retry attempts reached for $($software.Name)" -Level Error -LogFile $logFile
  682.                     if (-not $Force) {
  683.                         $response = Read-Host "Continue with remaining installations? (Y/N)"
  684.                         if ($response.ToUpper() -ne "Y") {
  685.                             throw "Installation process aborted by user"
  686.                         }
  687.                     }
  688.                 }
  689.             }
  690.         }
  691.     }
  692.  
  693.     # Installation Summary
  694.     Write-LogMessage -Message "`nInstallation Summary:" -Level Info -LogFile $logFile
  695.  
  696.     $successfulInstalls = $results | Where-Object { $_.Success }
  697.     $failedInstalls = $results | Where-Object { -not $_.Success }
  698.  
  699.     if ($successfulInstalls) {
  700.         Write-LogMessage -Message "Successfully installed:" -Level Success -LogFile $logFile
  701.         foreach ($result in $successfulInstalls) {
  702.             Write-LogMessage -Message "- $($result.Name): $($result.Message)" -Level Success -LogFile $logFile
  703.         }
  704.     }
  705.  
  706.     if ($failedInstalls) {
  707.         Write-LogMessage -Message "`nFailed installations:" -Level Error -LogFile $logFile
  708.         foreach ($result in $failedInstalls) {
  709.             Write-LogMessage -Message "- $($result.Name): $($result.Message)" -Level Error -LogFile $logFile
  710.         }
  711.     }
  712.  
  713.     # Final Status
  714.     $totalCount = $results.Count
  715.     $successCount = ($successfulInstalls | Measure-Object).Count
  716.     $failCount = ($failedInstalls | Measure-Object).Count
  717.  
  718.     Write-LogMessage -Message "`nFinal Status:" -Level Info -LogFile $logFile
  719.     Write-LogMessage -Message "Total applications processed: $totalCount" -Level Info -LogFile $logFile
  720.     Write-LogMessage -Message "Successfully installed: $successCount" -Level Success -LogFile $logFile
  721.  
  722.     $levelForFail = if ($failCount -eq 0) { 'Success' } else { 'Error' }
  723.     Write-LogMessage -Message "Failed installations: $failCount" -Level $levelForFail -LogFile $logFile
  724. }
  725. catch {
  726.     Write-LogMessage -Message "Critical error occurred: $_" -Level Error -LogFile $logFile
  727.     throw
  728. }
  729. finally {
  730.     Write-LogMessage -Message "Installation script completed. Log file: $logFile" -Level Info -LogFile $logFile
  731.  
  732.     if (-not $SkipTeamViewer) {
  733.         $teamViewerPath = "C:\Program Files\TeamViewer\TeamViewer.exe"
  734.         if (Test-Path $teamViewerPath) {
  735.             Start-Process -FilePath $teamViewerPath
  736.             Write-LogMessage -Message "Launched TeamViewer" -Level Info -LogFile $logFile
  737.         } else {
  738.             Write-LogMessage -Message "TeamViewer executable not found at expected location" -Level Warning -LogFile $logFile
  739.         }
  740.     }
  741.  
  742.     $teamViewerConfigPath = "C:\install\Teamviewer.host"
  743.     if (Test-Path $teamViewerConfigPath) {
  744.         Start-Process -FilePath "explorer.exe" -ArgumentList $teamViewerConfigPath
  745.         Write-LogMessage -Message "Opened TeamViewer configuration folder" -Level Info -LogFile $logFile
  746.     } else {
  747.         Write-LogMessage -Message "TeamViewer configuration folder not found" -Level Warning -LogFile $logFile
  748.     }
  749.    
  750.     Write-LogMessage -Message "Please import the TeamViewer configuration." -Level Info -LogFile $logFile
  751.     Pause
  752. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement