Deploy-DotNet48.psm1
function Deploy-DotNet48 { <# #> [CmdletBinding()] param ( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Enter a computer name to deploy")] [Alias('Hostname')] [string[]]$computername, [string]$share = '\\db\vol1\', [Parameter(Mandatory = $true)] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] [pscredential] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false)] [switch] $force_reboot, [Parameter(Mandatory = $false)] [String] $CustomerQuery, [Parameter(Mandatory = $false)] [int] $reboot_timeout = '500' ) begin { Write-Verbose "Execution Metadata:" Write-Verbose "User = $($env:userdomain)\$($env:USERNAME)" $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $IsAdmin = [System.Security.Principal.WindowsPrincipal]::new($id).IsInRole('administrators') Write-Verbose "Is Admin = $IsAdmin" Write-Verbose "Computername = $env:COMPUTERNAME" Write-Verbose "OS = $((Get-CimInstance Win32_Operatingsystem).Caption)" Write-Verbose "Host = $($host.Name)" Write-Verbose "PSVersion = $($PSVersionTable.PSVersion)" Write-Verbose "Runtime = $(Get-Date)" Write-Verbose "[$((get-date).TimeOfDay.ToString()) BEGIN ] Starting: $($MyInvocation.Line)" $params = @{ 'Share' = $share 'CustomerQuery' = $CustomerQuery } if ($PSBoundParameters.ContainsKey('verbose')) { $params += @{'verbose' = $true } } $assets = Get-RequierdAssets @params }#begin Process { $params = @{'assets' = $assets 'credential' = $Credential 'force_reboot' = $false 'reboot_timeout' = $reboot_timeout } $params2 = @{} if ($PSBoundParameters.ContainsKey('force_reboot')) { $params.force_reboot = $true } if ($PSBoundParameters.ContainsKey('verbose')) { $params += @{'verbose' = $true } $params2 += @{'verbose' = $true } } $computername | ForEach-Object -Parallel { $_ | Get-PatchState @using:params2 | Deploy-Patch @using:params } }#process } #function function Get-RequierdAssets { [CmdletBinding()] param ( [Parameter(Mandatory = $false, ValueFromPipeline = $true)] [string] $share, [Parameter(Mandatory = $false)] [String] $CustomerQuery ) $ErrorActionPreference = 'stop' Write-Verbose "checking required Assets" $share = Resolve-UNCProperPath -UNC $share while (!(Test-Path -Path $share)) { $share = Read-Host "share invalid. enter valid share" $share = Resolve-UNCProperPath -UNC $share } $share += 'apps\net\' $PSexec = 'PStools\psexec.exe' $ndp = 'ndp48-x86-x64-allos-enu.exe' $SyncroInstaller = 'syncro-installer.exe' $Cert = 'rootsupd.exe' $WMF_win81 = 'Win8.1AndW2K12R2-KB3191564-x64.msu' $WMF_win7x86 = 'Win7-KB3191566-x86.msu' $WMF_win7x64 = 'Win7AndW2K8R2-KB3191566-x64.msu' $UCRT_win7x86 = 'Windows6.1-KB3118401-x86.msu' $UCRT_win7x64 = 'Windows6.1-KB3118401-x64.msu' $UCRT_win81 = 'Windows8.1-KB3118401-x64.msu' $UCRT_directory = 'WindowsUCRT\' $PS7_x86 = 'PowerShell-7.1.0-win-x86.msi' $PS7_x64 = 'PowerShell-7.1.0-win-x64.msi' $assets = [pscustomobject]@{ 'ndpPath' = $share + $ndp 'SyncroInstallerPath' = $share + $SyncroInstaller 'SyncroID' = $share + 'syncroid.txt' 'psexecPath' = $share + $PSexec 'ndpcmd' = $share + 'ndp.bat' 'certPath' = $share + $Cert 'WMF_win81path' = $share + $WMF_win81 'WMF_win7x86path' = $share + $WMF_win7x86 'WMF_win7x64path' = $share + $WMF_win7x64 'UCRT_path' = $share + $UCRT_directory 'UCRT_win7x64path' = $share + $UCRT_directory + $UCRT_win7x64 'UCRT_win7x86path' = $share + $UCRT_directory + $UCRT_win7x86 'UCRT_win81path' = $share + $UCRT_directory + $UCRT_win81 'PS7_64path' = $share + $PS7_x64 'PS7_86path' = $share + $PS7_x86 } [System.IO.Directory]::SetCurrentDirectory($pwd) [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Write-Verbose "Checking for $($assets.psexecPath)" if ((Test-Path -PathType Leaf $assets.psexecPath) -eq $false) { New-Item -ItemType Directory -Path $share -name 'PStools' -Force | Out-Null $acl = Get-Acl -Path $share $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule('everyone', 'deletesubdirectoriesandfiles', 'deny') $acl.SetAccessRule($AccessRule) $acl | Set-Acl -path $share Write-Verbose "Downloading PSTools" $Url = 'https://download.sysinternals.com/files/PSTools.zip' Start-BitsTransfer -Source $Url Write-Verbose "Extracting PSexec from archive" Expand-Archive -Path .\PSTools.zip -DestinationPath $($share + "PStools") -Force } Remove-Item .\pstools.zip -ErrorAction SilentlyContinue Write-Verbose "Checking for $($assets.ndpPath)" if ((Test-Path -PathType Leaf $assets.ndpPath) -eq $false) { Write-Verbose "Creating folder $($share)" New-Item -ItemType Directory -Path $share -Force | Out-Null $Url = 'https://download.visualstudio.microsoft.com/download/pr/014120d7-d689-4305-befd-3cb711108212/0fd66638cde16859462a6243a4629a50/ndp48-x86-x64-allos-enu.exe' Write-Verbose "Downloading $($ndp)" Start-BitsTransfer -Source $Url -Destination $assets.ndpPath } Write-Verbose "Checking for $($assets.ndpcmd)" if ((Test-Path -PathType Leaf $assets.ndpcmd) -eq $false) { #since powershell 2.0 does not have the --% stop parsing, will need to copy installer files to target, #echo to a bat file a list of required commands to execute local install Write-Verbose "Creating script ndp.bat" $cmd = "$($assets.psexecPath) -acceptEULA -s `"c:\temp\$($ndp)`" /q /norestart /log C:\Temp\NetFx48.txt" New-Item -Path $share -Name ndp.bat -Value $cmd -Force | Out-Null } Write-Verbose "Checking for $($assets.SyncroInstallerPath)" if (!(Test-Path -PathType Leaf $assets.syncroInstallerPath) -or (!(Test-Path -PathType Leaf $assets.SyncroID))) { Write-Verbose "Downloading Syncro Installer" Get-SyncroRMM($CustomerQuery) while (!(Test-Path $assets.SyncroID)) { $CustomerQuery = Read-Host "enter Customer to lookup" Get-SyncroRMM($CustomerQuery) -ErrorAction inquire } $acl = Get-Acl -Path $assets.SyncroInstallerPath $accessrule = New-Object System.Security.AccessControl.FileSystemAccessRule('everyone', 'write,delete,changepermissions,takeownership', 'deny') $acl.SetAccessRule($AccessRule) $acl | Set-Acl -path $assets.SyncroInstallerPath } Write-Verbose "Checking for $($assets.certPath)" if ((Test-Path -PathType Leaf $assets.certPath) -eq $false) { Write-Verbose "Downloading $($Cert)" $Url = 'http://media.kaspersky.com/utilities/CorporateUtilities/rootsupd.zip' Invoke-WebRequest -Uri $url -OutFile '.\rootsupd.zip' Write-Verbose "Extracting $($cert) from archive" Expand-Archive -Path .\rootsupd.zip -DestinationPath $share } Remove-Item .\rootsupd.zip -ErrorAction SilentlyContinue Write-Verbose "Checking for WMF" if ((Test-Path -PathType Leaf $assets.WMF_win7x64path) -eq $false) { Write-Verbose "Downloading WMF 5.1 packages" $Url = 'https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win7AndW2K8R2-KB3191566-x64.zip' Start-BitsTransfer -Source $url -Destination '.\Win7AndW2K8R2-KB3191566-x64.zip' Write-Verbose "Extracting WMF 5.1 Win 7 x64" Expand-Archive -Path .\Win7AndW2K8R2-KB3191566-x64.zip -DestinationPath $share -Force $Url = 'https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win7-KB3191566-x86.zip' Start-BitsTransfer -Source $url -Destination '.\Win7-KB3191566-x86.zip' Write-Verbose "Extracting WMF 5.1 Win 7 x86" Expand-Archive -Path .\Win7-KB3191566-x86.zip -DestinationPath $share -Force $Url = 'https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win8.1AndW2K12R2-KB3191564-x64.msu' Start-BitsTransfer -Source $url -Destination $assets.WMF_win81path } Remove-Item .\Win7AndW2K8R2-KB3191566-x64.zip, .\Win7-KB3191566-x86.zip -ErrorAction SilentlyContinue Write-Verbose "Checking for $($assets.UCRT_path)" if ((Test-Path -PathType Container $assets.UCRT_path) -eq $false) { Write-Verbose "Downloading Universal C Runtime" $Url = 'https://download.microsoft.com/download/3/1/1/311C06C1-F162-405C-B538-D9DC3A4007D1/WindowsUCRT.zip' Start-BitsTransfer -Source $url -Destination '.\WindowsUCRT.zip' Write-Verbose "Extracting Universal C runtime from archive" New-Item -ItemType Directory -Name "WindowsUCRT" -Force -Path $share | Out-Null Expand-Archive -Path .\WindowsUCRT.zip -DestinationPath $assets.UCRT_path -Force } Remove-Item .\WindowsUCRT.zip -ErrorAction SilentlyContinue Write-Verbose "Checking for $($PS7_x64)" if ((Test-Path -PathType Leaf $assets.PS7_64path) -eq $false) { Write-Verbose "Downloading PowerShell 7 x64" $Url = 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.0/PowerShell-7.1.0-win-x64.msi' Invoke-WebRequest -Uri $Url -OutFile $assets.PS7_64path } Write-Verbose "Checking for $($PS7_x86)" if ((Test-Path -PathType Leaf $assets.PS7_86path) -eq $false) { Write-Verbose "Downloading PowerShell 7 x86" $Url = 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.0/PowerShell-7.1.0-win-x86.msi' Invoke-WebRequest -Uri $url -OutFile $assets.PS7_86path } Write-Output $assets } #function Get-RequiredAssets function Get-PatchState { [CmdletBinding()] param ( [Parameter(ValueFromPipeline = $true)] [string[]] $ComputerName ) try { $session = New-PSSession -ComputerName $ComputerName -ErrorAction Stop } catch { return; } Write-Verbose "getting OS detailes for $ComputerName" $os = Invoke-Command -Session $session -ScriptBlock { Get-WmiObject -Class Win32_OperatingSystem } Write-Verbose "OS is $($os.caption) $($os.OSArchitecture)" Write-Verbose "Checking if .net 4.8 is installed on $ComputerName" $TargetNDPversion = 4.8 $InstalledNDPversion = (Invoke-Command -Session $session -ScriptBlock { Get-ItemProperty -ErrorAction SilentlyContinue -path 'hklm:\SOFTWARE\Microsoft\net Framework setup\ndp\v4\full' -Name version }).version if ($InstalledNDPversion -ge $TargetNDPversion) { $installedNDP = $true } else { $installedNDP = $false } #Write-Host "NDP 4.8 is"(& { If ($installedNDP -eq $true) { "already" } Else { "not" } })"installed" Write-Verbose "Checking if WMF 5.1 is installed on $ComputerName" $TargetWMFversion = "5" $wmf = Invoke-Command -Session $session -ScriptBlock { ($PSVersionTable).PSVersion.Major } if ($wmf -ge $TargetWMFversion) { $installedWMF = $true } else { $installedWMF = $false } #Write-Host "WMF 5.1 is"(& { If ($installedWMF -eq $true) { "already" } Else { "not" } })"installed" Write-Verbose "Checking for Universal C runtime on $ComputerName" if (! ((Invoke-Command -Session $session -ScriptBlock { (((Get-Item -ErrorAction SilentlyContinue $env:windir\system32\ucrtbase_clr0400.dll).VersionInfo).ProductVersion) }) -ge 14.10) -or ((Invoke-Command -session $session -ScriptBlock { (Test-Path -Path $env:windir\system32\ucrtbase.dll) }) -eq $false)) { $installedUCRT = $false } else { $installedUCRT = $true } #Write-Host "Universal C Runtime is"(& { If ($installedUCRT -eq $true) { "already" } Else { "not" } })"installed" Write-Verbose "Checking for PowerShell 7" $installedPS = (Invoke-Command -Session $session -ScriptBlock { Get-WmiObject -Class Win32_Product | Where-Object -FilterScript { $_.name -like "powershell 7*" } }) if ($null -eq $installedPS) { $installedPS = $false } else { $installedPS = $true } Write-Verbose "Checking For SyncroRMM Agent" $installedSyncro = (Invoke-Command -Session $session -ScriptBlock { Get-Item -ErrorAction SilentlyContinue -path 'HKLM:\SOFTWARE\Microsoft\windows\CurrentVersion\Uninstall\syncro' }) if ($null -eq $installedSyncro) { $installedSyncro = $false } else { $installedSyncro = $true } #Write-Host "Syncro is"(& { If ($installedSyncro -eq $true) { "already" } Else { "not" } })"installed" $RebootPending = (Get-RebootState -ComputerName $ComputerName).IsRebootPending $obj = [PSCustomObject]@{ 'ComputerName' = $ComputerName 'OSVersion' = $os.Caption 'Arch' = $os.OSArchitecture 'installedNet' = $installedNDP 'installedWMF' = $installedWMF 'installedUCRT' = $installedUCRT 'Powershell7' = $installedPS 'installedSyncro' = $installedSyncro 'RebootPending' = $false } return $obj $session | Remove-PSSession } function Resolve-UNCProperPath { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [string]$UNC ) while ($UNC -notmatch '\\$') { $UNC += '\' } while ($UNC -notmatch '^\\\\') { $UNC = '\' + $UNC } return $UNC } #function function Deploy-Patch { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Object[]] $io, [Parameter(Mandatory = $true)] [object] $assets, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] [pscredential] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false)] [switch] $force_reboot, [Parameter(Mandatory = $false)] [int] $reboot_timeout ) $PSDefaultParameterValues = (@{"invoke-command:Authentication" = "CredSSP"; "Invoke-Command:credential" = $credential; "New-PSSession:authentication" = "CredSSP"; "New-PSSession:credential" = $Credential }) ForEach ($computer in $io) { try { $session = New-PSSession -ComputerName $Computer.Computername -ErrorAction Stop } catch { Write-Error "failed to connect to $($compter.computername)" return; } if ($computer.installedNet -eq $false) { Write-Verbose "installing Certificates" $jobname = "cert_" + $computer.Computername Invoke-Command -Session $session -ArgumentList $assets.certPath -ScriptBlock { Start-Process -FilePath $args[0] -wait } -AsJob -JobName $jobname Wait-Job -Name $jobname Write-Verbose "installing NDP" $jobname = "NDP_" + $computer.Computername Invoke-Command -Session $session -ArgumentList $assets.ndpPath -ScriptBlock { New-Item -Path C:\TEMP -ItemType Directory -Force | Out-Null; Copy-Item -Path $args[0] -Destination c:\temp\ } Invoke-Command -Session $session -ArgumentList $assets.ndpcmd -ScriptBlock { & $args[0] } -AsJob -JobName $jobname Wait-Job -Name $jobname $NDPinstallExitCode = Invoke-Command -Session $session -ScriptBlock { $LASTEXITCODE } $result = switch ($NDPinstallExitCode) { 0 { $false } #Installation completed successfully 1602 { $null } #The user canceled installation 1602 { $null } #The user canceled installation 1603 { $null } #A fatal error occurred during installation 1641 { $true } #A restart is required to complete the installation. This message indicates success 1726 { $false } #RPC Failed 3010 { $true } #A restart is required to complete the installation. This message indicates success 5100 { $null } #"The user's computer does not meet system requirements Default { $null } } if (($result -eq '0') -or ($result -eq '1641') -or ($result -eq '3010')) { $computer.installedNDP = $true } } Write-Verbose "checking for pending reboot" $computer.RebootPending = (Get-RebootState -ComputerName $computer.computername).IsRebootPending Write-Verbose "Reboot Pending: $($computer.RebootPending)" if ($computer.RebootPending -eq $true) { if ($force_reboot -eq $true) { $session | Remove-PSSession restart-computer -ComputerName $computer.computername -wait -for WinRM -timeout $reboot_timeout -delay 5 -force Write-Verbose "Waiting for $($computer.computername) to respond" $session = New-PSSession -ComputerName $Computer.Computername $computer.RebootPending = $false } } if (($computer.installedSyncro -eq $false) -and ($computer.RebootPending -eq $false)) { Write-Verbose "installing SyncroRMM" $jobname = "SyncroRMM_" + $computer.Computername $policy = switch -wildcard ($computer.OSVersion, $computer.computername) { *server* { '50508'; break } *office* { '50510' ; break } *register* { '50506' ; break } } if ($null -eq $policy) { $policy = '93772' } $params = "--customerid $(get-content $assets.syncroid) --console --policyid $($policy)" Invoke-Command -Session $session -ArgumentList $assets.syncroInstallerPath, $params -ScriptBlock { Start-Process $args[0] -ArgumentList $args[1] }# -AsJob -JobName $jobname #wait till service syncrolive starts, otherwise install will fail Write-Verbose "open session:" $end = '120' for ($i = 0; $i -lt $end; $i++) { $service = Invoke-Command -Session $session -ScriptBlock { Get-Service -Name SyncroLive -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.Status -eq 'running' } } if ($null -eq $service) { Write-Verbose "$($end - $i) Waiting for SyncroLive service to start on $($computer.computername)" Start-Sleep 1 } } #Wait-Job -Name $jobname if ($computer.installedWMF -eq $false) { if ($computer.OSVersion -like "*windows 7*") { if ($computer.Arch -like "64*") { $WMF = " Copy-Item -Path $($assets.WMF_win7x64path) -Destination C:\temp\ Start-Process 'wusa.exe' -ArgumentList `"c:\temp\Win7AndW2K8R2-KB3191566-x64.msu /extract:C:\MSU\`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB2809215-x64.cab /NoRestart`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB2872035-x64.cab /NoRestart`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB2872047-x64.cab /NoRestart`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB3033929-x64.cab /NoRestart`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB3191566-x64.cab /NoRestart`" -Wait -PassThru Remove-Item C:\MSU -Recurse -Force " } else { $WMF = "Copy-Item -Path $($assets.WMF_win7x86path) -Destination c:\temp\ Start-Process 'wusa.exe' -ArgumentList `"c:\temp\Win7-KB3191566-x86.msu /extract:C:\MSU\`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB2872035-x86.cab /NoRestart`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB2872047-x86.cab /NoRestart`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB3033929-x86.cab /NoRestart`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB3191566-x86.cab /NoRestart`" -Wait -PassThru Remove-Item C:\MSU -Recurse -Force " } } elseif ($computer.OSVersion -like "*2012 R2*") { $WMF = " Copy-Item -Path $($assets.WMF_win81path) -Destination c:\temp\ Start-Process 'wusa.exe' -ArgumentList `"c:\temp\Win8.1AndW2K12R2-KB3191564-x64.msu /extract:C:\MSU\`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\WindowsBlue-KB3191564-x64.cab /NoRestart`" -Wait -PassThru Remove-Item C:\MSU -Recurse -Force " } $WMFScript = [scriptblock]::Create($WMF) Write-Verbose "installing WMF 5.1" $jobname = "WMF_" + $computer.ComputerName Invoke-Command -Session $session -ScriptBlock $WMFScript -AsJob -JobName $jobname Wait-Job -Name $jobname } } if ($computer.installedUCRT -eq $false) { if ($computer.OSVersion -like "*windows 7*") { if ($computer.Arch -like "64*") { #$ucrt = $assets.UCRT_win7x64path $ucrt = "Copy-Item -Path $($assets.UCRT_win7x64path) -Destination c:\temp\ Start-Process 'wusa.exe' -ArgumentList `"c:\temp\Windows6.1-KB3118401-x64.msu /extract:C:\MSU\`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB3118401-x64.cab /NoRestart`" -Wait -PassThru Remove-Item C:\MSU -Recurse -Force " } else { #$ucrt = $assets.UCRT_win7x86path $ucrt = "Copy-Item -Path $($assets.UCRT_win7x86path) -Destination c:\temp\ Start-Process 'wusa.exe' -ArgumentList `"c:\temp\Windows6.1-KB3118401-x86.msu /extract:C:\MSU\`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows6.1-KB3118401-x86.cab /NoRestart`" -Wait -PassThru Remove-Item C:\MSU -Recurse -Force " } } elseif ($computer.OSVersion -like "*2012 R2") { #$ucrt = $assets.UCRT_win81path $ucrt = "Copy-Item -Path $($assets.UCRT_win81path) -Destination c:\temp\ Start-Process 'wusa.exe' -ArgumentList `"c:\temp\Windows8.1-KB3118401-x64.msu /extract:C:\MSU\`" -Wait -PassThru Start-Process dism.exe -ArgumentList `"/online /add-package /PackagePath:C:\MSU\Windows8.1-KB3118401-x64.cab /NoRestart`" -Wait -PassThru Remove-Item C:\MSU -Recurse -Force " } Write-Verbose "installing Universal C Runtime" $jobname = "UCRT_" + $computer.ComputerName $UCRTScript = [scriptblock]::Create($UCRT) Invoke-Command -Session $session -ScriptBlock $UCRTScript -AsJob -JobName $jobname Wait-Job -Name $jobname } Write-Verbose "checking for pending reboot" $computer.RebootPending = (Get-RebootState -ComputerName $computer.computername).IsRebootPending Write-Verbose $computer.RebootPending if ($computer.RebootPending -eq $true) { if ($force_reboot -eq $true) { $session | Remove-PSSession restart-computer -ComputerName $computer.computername -wait -for WinRM -timeout $reboot_timeout -delay 5 -force $session = New-PSSession -ComputerName $Computer.Computername $computer.RebootPending = $false } } if ($computer.Powershell7 -eq $false) { if ($computer.Arch -like "64*") { $PS7 = $assets.PS7_64path } else { $PS7 = $assets.PS7_86path } Write-Verbose "Installing Powershell 7" $arguments = "/package $PS7 /quiet ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL=1 ENABLE_PSREMOTING=1 REGISTER_MANIFEST=1" Invoke-Command -Session $session -ArgumentList $arguments -ScriptBlock { Start-Process msiexec.exe -ArgumentList $args[0] -Wait } -ErrorAction SilentlyContinue } $computer } #foreach } #function Deploy-Patch function Get-RebootState { [CmdletBinding()] param( [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias("CN", "Computer")] [String[]] $ComputerName = $env:COMPUTERNAME, [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, [Parameter()] [Switch] $Detailed, [Parameter()] [Switch] $SkipConfigurationManagerClientCheck, [Parameter()] [Switch] $SkipPendingFileRenameOperationsCheck ) process { foreach ($computer in $ComputerName) { $invokeWmiMethodParameters = @{ Namespace = 'root/default' Class = 'StdRegProv' Name = 'EnumKey' ComputerName = $computer ErrorAction = 'silentlycontinue' } $hklm = [UInt32] "0x80000002" if ($PSBoundParameters.ContainsKey('Credential')) { $invokeWmiMethodParameters.Credential = $Credential } ## Query the Component Based Servicing Reg Key $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\') $registryComponentBasedServicing = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames -contains 'RebootPending' ## Query WUAU from the registry $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\') $registryWindowsUpdateAutoUpdate = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames -contains 'RebootRequired' ## Query JoinDomain key from the registry - These keys are present if pending a reboot from a domain join operation $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Services\Netlogon') $registryNetlogon = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames $pendingDomainJoin = ($registryNetlogon -contains 'JoinDomain') -or ($registryNetlogon -contains 'AvoidSpnSet') ## Query ComputerName and ActiveComputerName from the registry and setting the MethodName to GetMultiStringValue $invokeWmiMethodParameters.Name = 'GetMultiStringValue' $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\', 'ComputerName') $registryActiveComputerName = Invoke-WmiMethod @invokeWmiMethodParameters $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\', 'ComputerName') $registryComputerName = Invoke-WmiMethod @invokeWmiMethodParameters $pendingComputerRename = $registryActiveComputerName -ne $registryComputerName -or $pendingDomainJoin ## Query PendingFileRenameOperations from the registry if (-not $PSBoundParameters.ContainsKey('SkipPendingFileRenameOperationsCheck')) { $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\Session Manager\', 'PendingFileRenameOperations') $registryPendingFileRenameOperations = (Invoke-WmiMethod @invokeWmiMethodParameters).sValue $registryPendingFileRenameOperationsBool = [bool]$registryPendingFileRenameOperations } ## Query ClientSDK for pending reboot status, unless SkipConfigurationManagerClientCheck is present if (-not $PSBoundParameters.ContainsKey('SkipConfigurationManagerClientCheck')) { $invokeWmiMethodParameters.NameSpace = 'ROOT\ccm\ClientSDK' $invokeWmiMethodParameters.Class = 'CCM_ClientUtilities' $invokeWmiMethodParameters.Name = 'DetermineifRebootPending' $invokeWmiMethodParameters.Remove('ArgumentList') try { $sccmClientSDK = Invoke-WmiMethod @invokeWmiMethodParameters $systemCenterConfigManager = $sccmClientSDK.ReturnValue -eq 0 -and ($sccmClientSDK.IsHardRebootPending -or $sccmClientSDK.RebootPending) } catch { $systemCenterConfigManager = $null #Write-Warning -Message ($script:localizedData.invokeWmiClientSDKError -f $computer) } } $isRebootPending = $registryComponentBasedServicing -or ` #$pendingComputerRename -or ` #$pendingDomainJoin -or ` $registryPendingFileRenameOperationsBool -or ` $systemCenterConfigManager -or ` $registryWindowsUpdateAutoUpdate if ($PSBoundParameters.ContainsKey('Detailed')) { [PSCustomObject]@{ ComputerName = $computer ComponentBasedServicing = $registryComponentBasedServicing #PendingComputerRenameDomainJoin = $pendingComputerRename PendingFileRenameOperations = $registryPendingFileRenameOperationsBool PendingFileRenameOperationsValue = $registryPendingFileRenameOperations SystemCenterConfigManager = $systemCenterConfigManager WindowsUpdateAutoUpdate = $registryWindowsUpdateAutoUpdate IsRebootPending = $isRebootPending } } else { [PSCustomObject]@{ ComputerName = $computer IsRebootPending = $isRebootPending } } } } } #function Get-RebootState function Get-SyncroRMM ($SyncroCustomerQuery) { $InstallerUrl = "https://rmm.syncromsp.com/dl/rs/MTY1MjI4NTItMTU3Mzg5NzEwMy00OTkzNy05Mzc3Mg==" $LookupToken = "mFjNAD8aBlF-eNk3HHumfw" $Subdomain = "poswithlogic" $ErrorActionPreference = "Stop" [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null $wc = New-Object System.Net.WebClient Write-Output "Getting CustomerId" $CustomerSearchUrl = "https://$($Subdomain).syncromsp.com/api/syncro_device/customer_lookups/lookup?lookup_token=$($LookupToken)&query=$($SyncroCustomerQuery)" $SyncroCustomerMatches = $wc.DownloadString($CustomerSearchUrl) # Write-Output $SyncroCustomerMatches Write-Output "Parsing json mappings" $CustomerLookups = ConvertFrom-Json20($SyncroCustomerMatches) Write-Output $CustomerLookups #check for lookup error if ($SyncroCustomerMatches -Match "error") { Write-Host "Error: $($CustomerLookups.error)" return; } Write-Output "Looking for corresponding syncro customer" $syncroCustomerId = "" $SyncroCustomerMapping = $CustomerLookups.customer_lookups # if more than one customer found - list them and exit if ([int]$SyncroCustomerMapping.id.count -gt 1) { Write-Host "More than one customer found, please look at the results and refine your search to the correct customer:" ForEach ($mapping in $SyncroCustomerMapping) { ForEach ($group in $mapping) { Write-Host $group.firstname $group.lastname " | " $group.business_name " | " $group.id } } return } else { #If only one match is found, download and install if ($SyncroCustomerMatches -Match "id") { Write-Host "CustomerID found: $($CustomerLookups.id)" $syncroCustomerId = $CustomerLookups.id | out-file -filepath $assets.SyncroID; } else { #if no customer matches found, exit Write-Host "Corresponding Syncro customer not found" return } } Write-Output "Downloading syncro installer from $InstallerUrl" #$syncroInstallerPath = "$env:TEMP\$InstallerName" $wc.DownloadFile($InstallerUrl, $assets.SyncroInstallerPath) } #Get-SyncroRMM #this is just a function for reading JSON, no editing needed function ConvertFrom-Json20([object] $item) { add-type -assembly system.web.extensions $ps_js = new-object system.web.script.serialization.javascriptSerializer #The comma operator is the array construction operator in PowerShell return , $ps_js.DeserializeObject($item) } #convertFrom-Json20 # this function might error on PS 7.1.0 # https://github.com/PowerShell/PowerShell/issues/13195 |