Deployment Instructions:
  1. Create a batch file with this content to call this PowerShell Script
@echo off
set ScriptPath=%~dp0
set ScriptName=deployChocoApps.ps1
PowerShell -ExecutionPolicy Bypass -Command "&'%ScriptPath%%ScriptName%' -Verbose"

Explanation: Powershell will not execute at startup if the computer cannot contact a DC.

2. Ensure that the VPN client perform ‘gpupdate’ upon connection to the domain. Here’s an example for Forticlient:

<on_connect>
<script>
<os>windows</os>
<script>
<![CDATA[gpupdate]]>
</script>
</script>
</on_connect>

Explanation: Fortinet Forticlient recommended xml config addition to enable VPN users to map drives and pull GPOs upon successful VPN connections

3. Create a GPO and apply it at a test OU Container
Create GPO > edit new GPO > User configuration > Preferences > Control Panel Settings > Scheduled Tasks > right-click on scheduled tasks > New > Scheduled Task (At Least Windows 7) > Input these properties:
– Name = Deploy Choco Apps
– Action = replace
– When running the task, use the following user account = NT Authority\System
– Run whether user is logged on or not = True
– Run with highest privileges = Checked
– Hidden = Checked
– Triggers = At Startup, repeat the task every 1 hour indefinitely, enabled
– Actions = Start a program
– Program Script = \\%userdnsdomain%\sysvol\%userdnsdomain%\scripts\deployChocoApps\deployChocoApps.ps1
– start in = \\%userdnsdomain%\sysvol\%userdnsdomain%\scripts\deployChocoApps
– arguments = leave blank or -NonInteractive -WindowStyle Hidden -executionpolicy bypass -ErrorAction SilentlyContinue
– Settings = Allow task to run on demand
– Common = Remove this item when it is no longer applied
– Click OK to apply

# deployChocoApps.ps1
# Purpose: to check whether a computer is domain connected and use choco to maintain certain mandatory applications

# User entered variables
$clientApps=@( # keyword-search, version, installation-appName
    @('Firefox','77.0.1','Firefox'),
    @('Chrome','83.0.4103.97','GoogleChrome')    
    )
$serverApps=@(
    @('Chrome','101.0.4951.54','GoogleChrome')    
    )
$privateRepoName='kimconnectrepo'
$companyDns='10.10.10.10','20.20.20.20'
$privateRepoUrl='https://choco.kimconnect.com/chocolatey'
$priority=1

# Stagger execution timing of script execution
$randomInterval=get-random -Minimum 0 -Maximum 50.00
#start-sleep $randomInterval # Uncomment this line to stagger check-ins

# Autogen variables
$privateRepo="'$privateRepoName' -s '$privateRepoUrl' --priority=$priority"
$computerRole=switch ((Get-CimInstance -ClassName Win32_OperatingSystem).ProductType){
                1 {'client'} # ClientOs
                2 {'domaincontroller'} #ServerOs with DC role
                3 {'memberserver'} #ServerOs machines
                }
$installedApps=Get-CimInstance -ClassName win32_InstalledWin32Program -ErrorAction Stop|select Name,Version
$domainConnected=.{
            try {
                [void]::([System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain())
                return $true
                }
            catch{
                return $false
                }
            }

function higherVersion ($a,$b){
    # This assumes the query as version $a is greater than $b by reconstructing PowerShell version object
    $regexVersion='^(\d+)\.{1}(\d+)\.{0,1}(\d+){0,1}\.{0,1}(\d+){0,1}'
    [void]($a -match $regexVersion)
1..4|%{new-variable "a$_" -value $matches[$_] -force}   
    [void]($b -match $regexVersion)
1..4|%{new-variable "b$_" -value $matches[$_] -force}
    return [Version]::new($a1,$a2,$a3,$a4) -gt [Version]::new($b1,$b2,$b3,$b4)
}

function chocoAppsMaintenance{
    param(
        $apps,
        $installedApps,
        $privateRepo
        )
         
        # Install Chocolatey from the Intranet private repo
        if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
        Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://choco.kimconnect.com/install.ps1'))}
        
        # Set private repo source if it doesn't exist. Courtesy of Tyler Kocheff
        $sources=choco source list
        $privateRepoExists=$sources -match $privateRepoName        
        if (!$privateRepoExists){
            invoke-expression "choco source add -n=$privateRepo"
            }
        
        # Install app if it's not already there, or upgrade if its version is outdated        
        $chocoInstalledApps=choco list -l

        foreach ($app in $apps){
            $appName=$app[0]
            $appVersion=$app[1]
            $appInstall=$app[2]            
            $chocoAppExists=$chocoInstalledApps|?{$_ -like "*$appName*"|select -First 1}
            $chocoVersion=.{[void]($chocoAppExists -match ' ([\d+\.]+)');if($matches){return $matches[1]}else{0}}
            $isChocoLowerVersion=higherVersion $appVersion $chocoVersion            
            
            $appwizAppExists=.{$matchApp=$installedApps|?{$_.Name -like "*$appName*"|select -First 1 }
                            if($matchApp){$matchApp}else{$false}
                            }
            $appwizVersion=if($appwizAppExists){$appwizAppExists.Version}else{$false}
            if($appwizVersion){
                $isAppwizLowerVersion=higherVersion $appVersion $appwizVersion
                }

            write-host "Checking $appName $appVersion`r`nChoco: $chocoVersion`r`nAppwiz: $appwizVersion"
            if (!$chocoAppExists -and !$appwizAppExists){choco install $appInstall -y}
            elseif ($isChocoLowerVersion -and $isAppwizLowerVersion){choco upgrade $appInstall -y}       
            }  
 
        # Upgrade all choco installed apps
        # choco upgrade all -y            
}

if($domainConnected){
    if($computerRole -eq 'client'){
        chocoAppsMaintenance $clientApps $installedApps $privateRepo
    }elseif($computerRole -eq 'memberserver'){
        chocoAppsMaintenance $serverApps $installedApps $privateRepo
    }
}else{
    write-warning "$env:computername is not connected to the domain"
}