# installProgramFromExeZipArchive.ps1

# Automated installation of the racadm program
$computerlist=@'
HyperV01
HyperV02
'@
$fileURL="https://dl.dell.com/FOLDER08543783M/1/DellEMC-iDRACTools-Web-WINX64-10.3.0.0-4945.exe"
$expectedExecutable='racadm.exe'
$expectedInstallPath='C:\Program Files\Dell\SysMgt\iDRACTools\racadm'
$stageFolder='C:\Temp\'
$computernames=@($computerlist -split "`n")|%{$_.Trim()}

function installProgramFromExeZipArchive{
    param(
        $computernames=$env:computername,
        $fileURL="https://dl.dell.com/FOLDER07549599M/1/DellEMC-iDRACTools-Web-WINX64-10.2.0.0-4583_A00.exe",
        $expectedExecutable='racadm.exe',
        $expectedInstallPath='C:\program files\Dell\SysMgt\iDRACTools\racadm',
        $stageFolder='C:\Temp\'
    )
    $results=[hashtable]@{}
    function addEnvironmentalPath($pathToAdd){
        $registryEnvironment='Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment'
        $pathToAdd=if($pathToAdd -match '\\$'){$pathToAdd -replace '.$'}else{$pathToAdd}
        try{
            $originalPaths=(Get-ItemProperty -Path $registryEnvironment -Name PATH).path
            $pathsArray=$originalPaths -split ';'|?{$_.trim() -ne ''}|%{if($_ -match '\\$'){$_ -replace '.$'}else{$_}}|Sort-Object -Unique
            if($pathToAdd -in $pathsArray){
                write-host "$pathToAdd is already included in the environmental paths: '$originalPaths'"
                return $true
            }else{
                $newPathsArray=$pathsArray+$pathToAdd
                $newPaths=$newPathsArray -join ';'
                Set-ItemProperty -Path $registryEnvironment -Name PATH -Value $newPaths
                $newRegistryEnvironment=(Get-ItemProperty -Path $registryEnvironment -Name PATH).Path
                write-host "Environmental paths have been changed:`r`nFrom: $originalPaths`r`nTo: $newPaths"
                $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
                return $true
            }
        }catch{
            write-warning $_
            return $false
        }
    }
    foreach($computerName in $computernames){
        $alreadyInstalled=invoke-command -computername $computername -scriptblock{param($expectedExecutable);try{get-command $expectedExecutable -EA Ignore}catch{$false}} -Args $expectedExecutable
        if($alreadyInstalled){
            write-host "$computername has already installed $expectedExecutable"
            $results+=[hashtable]@{$computername=$true}
        }else{
            # Autogen variables
            $fileName=[regex]::match($fileURL,'[^/\\&\?]+\.\w{3,4}(?=([\?&].*$|$))').value
            $translatedVolume=[regex]::match($stageFolder,'^(\w)\:').captures.groups[1].value+'$'
            $translatedFoldername=[regex]::match($stageFolder,'\:(.*)$').captures.groups[1].value
            $remoteSmbPath=join-path $('\\'+$computerName+"\$translatedVolume") $translatedFoldername
            try{
                # Download the file directly onto target server's staging folder
                Import-Module BitsTransfer
                if(!(test-path $remoteSmbPath)){$null=mkdir $remoteSmbPath}
                # Start-BitsTransfer -Source $fileURL -Destination $output
                [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
                Start-BitsTransfer -Source $fileURL -Destination $remoteSmbPath
                $psSession=new-pssession $computerName
                if($psSession.State -eq 'Opened'){
                    $result=invoke-command -Session $psSession -ScriptBlock{
                        param($stageFolder,$filename,$expectedExecutable,$expectedInstallPath,$addEnvironmentalPath)
                        # write-host "$env:computername : $stageFolder,$filename,$expectedExecutable,$expectedInstallPath"
                        # pause                 
                        $commandAvailable=try{get-command $expectedExecutable -EA Ignore}catch{$false}
                        $toInstall=if(!$commandAvailable){
                            if(!($env:path -like "*$expectedInstallPath*")){$env:path+=";$expectedInstallPath"}                     
                            $null=RefreshEnv
                            $commandNowAvailable=try{get-command $expectedExecutable -EA Ignore}catch{$false}
                            if($commandNowAvailable){$false}else{$true}
                        }else{
                            write-host "Command $expectedExecutable is available on $env:computername"
                            $true
                        }
                        if ($toInstall){
                            $exeExists=try{ls $expectedInstallPath -EA Ignore|?{$_.Name -like $expectedExecutable}}catch{$false}
                            if($exeExists){
                                [scriptblock]::create($addEnvironmentalPath).invoke($expectedInstallPath)                               
                                $null=RefreshEnv
                                $commandNowAvailable=try{get-command $expectedExecutable -EA Ignore}catch{$false}
                                if($commandNowAvailable){return $true}else{return $false}
                            }else{
                                $expectedFileLocation=join-path $stageFolder $filename
                                $zipFilename=$([System.IO.Path]::GetFileNameWithoutExtension($expectedFileLocation))+'.zip'
                                $newZipFile=join-path $stageFolder $zipFilename
                                #[System.IO.Path]::GetExtension($expectedFileLocation)
                                Rename-Item -Path $expectedFileLocation -NewName $newZipFile -EA Ignore
                                # $null=Expand-Archive -Path $newZipFile -DestinationPath $stageFolder
                                # alternative method: backward compatible to older systems
                                Add-Type -AssemblyName System.IO.Compression.FileSystem
                                $null=[System.IO.Compression.ZipFile]::ExtractToDirectory($newZipFile, $stageFolder)
                                $msiFile=(ls $stageFolder|?{$_.FullName -match '\.msi$'}|sort -property LastWriteTime|select -first 1).FullName
                                Start-Process $msiFile -ArgumentList "/quiet" -wait
                                # this command doesn't work: msiexec /i $msiFile /quiet /qn /norestart
                                $environmentalPathExists=$env:path -like "*$expectedInstallPath*"
                                if(!$environmentalPathExists){
                                    $env:path+=";C:\Program Files\Dell\SysMgt\iDRACTools\racadm"
                                    $null=RefreshEnv
                                }
                                $commandNowAvailable=try{get-command $expectedExecutable -EA Ignore}catch{$false}
                                if($commandNowAvailable){return $true}else{return $false}
                            }
                        }                   
                    } -Args $stageFolder,$fileName,$expectedExecutable,$expectedInstallPath,${function:addEnvironmentalPath}
                    Remove-PSSession $psSession
                    write-host "$computername result: $result"
                    $results+=[hashtable]@{$computername=$result}
                }else{
                    write-warning "Unable to connect to $computername via WinRM"
                    $results+=[hashtable]@{$computername=$null}
                }
            }catch{
                write-warning $_
                $results+=[hashtable]@{$computername=$null}
            }
        }
    }
    return $results
}
installProgramFromExeZipArchive $computernames