Deploy-net48.ps1

<#
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
#>

[CmdletBinding(DefaultParameterSetName = 'Deploy')]
param (
    [Parameter(Mandatory = $true, HelpMessage = "Enter a target computer name to deploy")]
    [Alias('Hostname', 'CN')]
    [string[]]$computername,
    [parameter(ParameterSetName = 'Deploy', Mandatory = $true)]
    [string]$share,
    [Parameter(Mandatory = $true)]
    [ValidateNotNull()]
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    [pscredential]
    $Credential = [System.Management.Automation.PSCredential]::Empty,
    [Parameter(ParameterSetName = 'Deploy', Mandatory = $false)]
    [int]
    [ValidateRange(30, 1000)]
    $reboot_timeout,
    [parameter(ParameterSetName = 'check')]
    [switch]$check
)
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)"

#initialize an array of assets and their properties
$assets = [System.Collections.ArrayList]@()
$assets += ([PSCustomObject]@{
        'name'         = 'Certificate'
        'OS'           = '*'
        'arch'         = '*'
        'URL'          = 'http://media.kaspersky.com/utilities/CorporateUtilities/rootsupd.zip'
        'method'       = 'webclient'
        'pkg'          = 'rootsupd.exe'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = $null
    })
$assets += ([PSCustomObject]@{
        'name'         = 'PSTools'
        'OS'           = '*'
        'arch'         = '*'
        'URL'          = 'https://download.sysinternals.com/files/PSTools.zip'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'psexec.exe'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = $null
    })
$assets += ([PSCustomObject]@{
        'Name'         = 'dotnet 4.8'
        'OS'           = '*'
        'arch'         = '*'
        'URL'          = 'https://download.visualstudio.microsoft.com/download/pr/014120d7-d689-4305-befd-3cb711108212/0fd66638cde16859462a6243a4629a50/ndp48-x86-x64-allos-enu.exe'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'ndp48-x86-x64-allos-enu.exe'
        'pre_process'  = "New-Item -Path C:\TEMP -ItemType Directory -Force | Out-Null ; copy-item -path $("$share" + "ndp48-x86-x64-allos-enu.exe") -destination c:\temp\"
        'post_precess' = $null
        'cmd'          = "$share" + "psexec.exe -acceptEULA -s " + "`"c:\temp\ndp48-x86-x64-allos-enu.exe`" /q /norestart /log C:\Temp\NetFx48.txt"
        'script'       = $null
        'pass'         = @"
(Invoke-Command -Session `$session -ScriptBlock { Get-ItemProperty -ErrorAction SilentlyContinue -path 'hklm:\SOFTWARE\Microsoft\net Framework setup\ndp\v4\full' -Name version }).version
"@

    })
$assets += ([PSCustomObject]@{
        'Name'         = 'WMF 5.1 Win7 x86'
        'OS'           = '6.1'
        'arch'         = '32'
        'URL'          = 'https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win7-KB3191566-x86.zip'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'Win7-KB3191566-x86.msu'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = @"
Copy-Item -Path $("$share\Win7-KB3191566-x86.msu") -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
"@

        'pass'         = @"
Invoke-Command -Session `$session -ScriptBlock { (`$PSVersionTable).PSVersion.Major }
"@

    })
$assets += ([PSCustomObject]@{
        'Name'         = 'WMF 5.1 Win7 x64'
        'OS'           = '6.1'
        'arch'         = '64'
        'URL'          = 'https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win7AndW2K8R2-KB3191566-x64.zip'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'Win7AndW2K8R2-KB3191566-x64.msu'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = @"
Copy-Item -Path $("$share\Win7AndW2K8R2-KB3191566-x64.msu") -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
"@

        'pass'         = @"
Invoke-Command -Session `$session -ScriptBlock { (`$PSVersionTable).PSVersion.Major }
"@

    })
$assets += ([PSCustomObject]@{
        'Name'         = 'WMF 5.1 Win 8.1 x64'
        'OS'           = '6.3'
        'arch'         = '64'
        'URL'          = 'https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/Win8.1AndW2K12R2-KB3191564-x64.msu'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'Win8.1AndW2K12R2-KB3191564-x64.msu'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = @"
Copy-Item -Path $("$share\Win8.1AndW2K12R2-KB3191564-x64.msu") -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
"@

        'pass'         = @"
Invoke-Command -Session `$session -ScriptBlock { (`$PSVersionTable).PSVersion.Major }
"@
    
    })
$assets += ([PSCustomObject]@{
        'Name'         = 'UCRT Win 7 x86'
        'OS'           = '6.1'
        'arch'         = '32'
        'URL'          = 'https://download.microsoft.com/download/3/1/1/311C06C1-F162-405C-B538-D9DC3A4007D1/WindowsUCRT.zip'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'Windows6.1-KB3118401-x86.msu'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = @"
Copy-Item -Path $("$share\Windows6.1-KB3118401-x86.msu") -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
"@

        'pass'         = @"
((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)
"@

    })
$assets += ([PSCustomObject]@{
        'Name'         = 'UCRT Win 7 x64'
        'OS'           = '6.1'
        'arch'         = '64'
        'URL'          = 'https://download.microsoft.com/download/3/1/1/311C06C1-F162-405C-B538-D9DC3A4007D1/WindowsUCRT.zip'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'Windows6.1-KB3118401-x64.msu'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = @"
Copy-Item -Path $("$share\Windows6.1-KB3118401-x86.msu") -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
"@

        'pass'         = @"
((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)
"@

    })
$assets += ([PSCustomObject]@{
        'Name'         = 'UCRT Win 8.1 x64'
        'OS'           = '6.3'
        'arch'         = '64'
        'URL'          = 'https://download.microsoft.com/download/3/1/1/311C06C1-F162-405C-B538-D9DC3A4007D1/WindowsUCRT.zip'
        'method'       = 'Start-BitsTransfer'
        'pkg'          = 'Windows8.1-KB3118401-x64.msu'
        'pre_process'  = $null
        'post_precess' = $null
        'cmd'          = $null
        'script'       = @"
Copy-Item -Path $("$share\Windows6.1-KB3118401-x86.msu") -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
"@

        'pass'         = @"
((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)
"@

    })

Get-Module deploy-dotnet48 | Remove-Module -Force
$scriptpath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
Import-Module $scriptpath\Deploy-DotNet48.psm1 -Force

if (! ($PSBoundParameters.ContainsKey('check'))) {
    #verify that share exists
    $share = $share | Resolve-UNCProperPath -verbose:$VerbosePreference


    foreach ($asset in $assets) {
        $asset | Add-Member -Name 'fullpath' -Value (Join-Path -Path $share -ChildPath $asset.pkg) -MemberType NoteProperty
        if ($null -ne $asset.cmd) {
            $asset | Add-Member -MemberType NoteProperty -Value (join-path -path $share -childpath ($($asset.pkg) + (".bat"))) -Name 'ScriptFullPath'
        }
    }

    #download and process the required assets to deploy
    Get-RequierdAssets -assets $assets -share $share -verbose:$VerbosePreference
}
#check each computer for patch state
foreach ($computer in $computername) {
    $params = @{
        'ComputerName' = $computer
        'Assets'       = $assets
        'Credential'   = $Credential
        'Verbose'      = $VerbosePreference
    }
    $result = Get-PatchState @params
    $obj = [PSCustomObject]@{
        'ComputerName' = $computer
        'RebootState'  = $null
    }

    #update the computer object with the assets to be installed
    Copy-Property -From $result -To $obj
    foreach ($p in Get-Member -In $obj -MemberType NoteProperty) {
        $ret = switch ($p.name) {
            #do not evaluate computername
            'computername' { $obj.($p.Name) }
            'ndp48-x86-x64-allos-enu.exe' {
                if (($obj.($p.Name)) -lt '4.8') {
                    'missing'
                }
                else { 'present' } 
            }
            'Win7-KB3191566-x86.msu' {
                if (($obj.($p.Name)) -lt '5') {
                    'missing'
                }
                else { 'NA' } 
            }
            'Win7AndW2K8R2-KB3191566-x64.msu' {
                if (($obj.($p.Name)) -lt '5') {
                    'missing'
                }
                else { 'NA' } 
            }
            'Win8.1AndW2K12R2-KB3191564-x64.msu' {
                if (($obj.($p.Name)) -lt '5') {
                    'missing'
                }
                else { 'NA' } 
            }
            Default {
                if (($obj.($p.Name)) -eq 'True') {
                    'present'
                }
                elseif (($obj.($p.Name)) -eq 'False') {
                    'missing'
                }
                else {
                    'NA'
                }
            }
        }
        $obj.($p.Name) = $ret
    }
    Write-Verbose $obj
    if (! ($PSBoundParameters.ContainsKey('check'))) {
        #deploy missing assets
        $missingassets = $assets | Where-Object -FilterScript { $obj.($_.pkg) -eq 'missing' }
        foreach ($asset in $missingassets) {
            $params = @{
                'ComputerName' = $computer
                'Asset'        = $asset
                'Credential'   = $Credential
                'Share'        = $share
                'Verbose'      = $VerbosePreference
            }
            if ($PSBoundParameters.ContainsKey('reboot_timeout')) {
                $params += @{'reboot_timeout' = $reboot_timeout }
            }
            $result = deploy-patch @params
            $obj.$($asset.pkg) = $result.Status
            $obj.RebootState = $($result.RebootState)
        }
    }
    Write-Output $obj
}