Chocolatey is a great tool for installing and updating software quickly from the Windows CLI. It gets even better when application updates happen automatically. With a little PowerShell we can quickly create everything we need for automatic software updates.

We need a few things to make this happen. First, we need to create a user account that can be used to schedule a task. You could use the local admin account, but then your automatic update breaks when you change the password. It’s better to make a local account for this purpose. We don’t even need to know the password for this account so let’s set a random password.

#Generate Random Password
$minLength = 20
$maxLength = 30
$length = Get-Random -Minimum $minLength -Maximum $maxLength
$letters = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ".ToCharArray()
$uppers = "ABCDEFGHJKLMNPQRSTUVWXYZ".ToCharArray()
$lowers = "abcdefghijkmnopqrstuvwxyz".ToCharArray()
$digits = "23456789".ToCharArray()
$symbols = "_-+=@$%".ToCharArray()
$chars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789_-+=@$%".ToCharArray()

do {
	$pwdChars = "".ToCharArray()
	$goodPassword = $false
	$hasDigit = $false
	$hasSymbol = $false
	$pwdChars += (Get-Random -InputObject $uppers -Count 1)
	for ($i=1; $i -lt $length; $i++) {
		$char = Get-Random -InputObject $chars -Count 1
		if ($digits -contains $char) { $hasDigit = $true }
		if ($symbols -contains $char) { $hasSymbol = $true }
		$pwdChars += $char
	}
	$pwdChars += (Get-Random -InputObject $lowers -Count 1)
	$password = $pwdChars -join ""
	$goodPassword = $hasDigit -and $hasSymbol
} until ($goodPassword)

With the above code we get a random password between 20 and 30 digits set to the $password variable. Now we can make the user account. PowerShell won’t let use us a variable containing a plain text password to create an account, but “$SecurePassword=ConvertTo-SecureString” is an easy fix.

#Create Account
$SecurePassword=ConvertTo-SecureString  $password –asplaintext –force
$user = "chocoUpdater"

Remove-LocalUser -InputObject $user -Confirm:$false
New-LocalUser -Name $user -Password $SecurePassword -Confirm:$false
Set-LocalUser -Name $user -PasswordNeverExpires:$true
Add-LocalGroupMember -Group Administrators -Member $user -Confirm:$false

Now we have an account with a random password in the local administrators group. So far so good. Let’s make a scheduled task to do “choco upgrade all -a.” Scheduled tasks require alot of clicks in the GUI so this is a good use of the CLI.

#Create scheduled task
Unregister-ScheduledTask -TaskName "choco_software_update" -Confirm:$false

$action = New-ScheduledTaskAction -Execute 'choco' -Argument 'upgrade all -a'

$days = @("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
$rand_day = (Get-Random -InputObject $days -Count 1)

$hours = @("1am", "2am", "3am", "4am", "5am", "6am", "7am", "8am", "9am", "10am", "11am", "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", "7pm", "8pm","9pm", "10pm", "11pm")
$rand_hour = (Get-Random -InputObject $hours -Count 1)

$trigger = @(
	$(New-ScheduledTaskTrigger  -Weekly -WeeksInterval 1 -DaysOfWeek $rand_day -At $rand_hour)
)

New-ScheduledTaskPrincipal -UserId chocoUpdater -RunLevel Highest
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "chocoUpdate" -Description "Daily checks for software updates." -user $user -password $password -RunLevel Highest

Remove-Variable password

This sets a trigger to update software weekly at a random hour on a random day. It’s important to have some variation in the schedule to prevent all your PCs from calling out to Chocolatey at the same time. You will get blocked (temporarily) from the community repository if you initiate too many connections at the same time! Setting up an internal Chocolatey repository is another option. https://chocolatey.org/docs/how-to-set-up-chocolatey-server

We can remove the $password variable now that we are done with it. If we ever need the password to this account we can change it with an admin account, or re-run the script to make a new password.

Now we can put it all together.

#
#create_choco_update_task.ps1
#Brian Hull 2020
#
#This script will create chocoUpdater Account with a random password 
#and use this account to schedule software upadates with choco.
#

#Generate Random Password
$minLength = 20
$maxLength = 30
$length = Get-Random -Minimum $minLength -Maximum $maxLength
$letters = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ".ToCharArray()
$uppers = "ABCDEFGHJKLMNPQRSTUVWXYZ".ToCharArray()
$lowers = "abcdefghijkmnopqrstuvwxyz".ToCharArray()
$digits = "23456789".ToCharArray()
$symbols = "_-+=@$%".ToCharArray()
$chars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789_-+=@$%".ToCharArray()

do {
	$pwdChars = "".ToCharArray()
	$goodPassword = $false
	$hasDigit = $false
	$hasSymbol = $false
	$pwdChars += (Get-Random -InputObject $uppers -Count 1)
	for ($i=1; $i -lt $length; $i++) {
		$char = Get-Random -InputObject $chars -Count 1
		if ($digits -contains $char) { $hasDigit = $true }
		if ($symbols -contains $char) { $hasSymbol = $true }
		$pwdChars += $char
	}
	$pwdChars += (Get-Random -InputObject $lowers -Count 1)
	$password = $pwdChars -join ""
	$goodPassword = $hasDigit -and $hasSymbol
} until ($goodPassword)

#Create Account
$SecurePassword=ConvertTo-SecureString  $password –asplaintext –force
$user = "chocoUpdater"

Remove-LocalUser -InputObject $user -Confirm:$false
New-LocalUser -Name $user -Password $SecurePassword -Confirm:$false
Set-LocalUser -Name $user -PasswordNeverExpires:$true
Add-LocalGroupMember -Group Administrators -Member $user -Confirm:$false

#Create scheduled task
Unregister-ScheduledTask -TaskName "choco_software_update" -Confirm:$false

$action = New-ScheduledTaskAction -Execute 'choco' -Argument 'upgrade all -a'

$days = @("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
$rand_day = (Get-Random -InputObject $days -Count 1)

$hours = @("1am", "2am", "3am", "4am", "5am", "6am", "7am", "8am", "9am", "10am", "11am", "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", "7pm", "8pm","9pm", "10pm", "11pm")
$rand_hour = (Get-Random -InputObject $hours -Count 1)

$trigger = @(
	$(New-ScheduledTaskTrigger  -Weekly -WeeksInterval 1 -DaysOfWeek $rand_day -At $rand_hour)
)

New-ScheduledTaskPrincipal -UserId chocoUpdater -RunLevel Highest
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "chocoUpdate" -Description "Daily checks for software updates." -user $user -password $password -RunLevel Highest

Remove-Variable password

create_choco_update_task.ps1

Further Reading:
https://adamtheautomator.com/powershell-random-password/

https://www.reddit.com/r/PowerShell/comments/al2v1z/generate_good_password/

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.localaccounts/add-localgroupmember?view=powershell-5.1

https://github.com/MicrosoftDocs/azure-docs/issues/10061

https://chocolatey.org/

https://chocolatey.org/docs/how-to-set-up-chocolatey-server

https://hull1.com/choco