Automating WSUS updates with PowerShell/PowerCLI - powershell

I'm looking to automate the process of installing WSUS updates on my VMs. To give a short overview, here are the things I want to accomplish (please let me know if my methods are moronic, I'd love to learn the right way for all of this.):
Check if the particular VM has any WSUS updates to install
If there are updates available, take a snapshot of the VM
Begin the WSUS install
Reboot the system, if necessary
I am currently able to check if the particular VM has updates and take a snapshot. Now I know I could just have this portion of the script run and configure a GPO to accomplish the rest of the tasks, but my thought process is that if I can do it all in the script, I will be able to check that the snapshot of the VM exists prior to installing the update. Below you can see what my script does as of now.
foreach ($vm in $vms) {
if ($vm.PowerState -eq "poweredOn") {
$output = Invoke-VMScript -ScriptText $script -VM $vm -GuestCredential $guestCred
if ($output.ScriptOutput -Notlike '0*') {
New-Snapshot -VM $vm -Name BeforeWSUS
}
}
}
After this I would like to perform a check to see if the snapshot exists for a vm, then install the WSUS update. If a reboot is necessary, then reboot.
Is there a way to do this? A better way to do this? Would really appreciate some insight, I'm new to Powershell.
Edit: I've checked on the PSWindowsUpdate Module, would that need to be on each VM I plan to update?

Yes, you would need PSWindowsUpdate installed on each VM.
You could include something like this in your script to check if PSWindowsUpdate is installed and if not, install it.
Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted
$Modules = "PSWindowsUpdate"
$Modules | ForEach-Object {
If (!(Get-Module -ListAvailable -Name $_)) {
Install-Module $_ -Verbose -Confirm:$false
}
}
I think that Install-Module requires PowerShell version 5.0.
Then you would use Get-WUInstall to install updates from your WSUS server. (It looks like it defaults to WSUS if configured via GPO.)
Probably throw in a -Confirm:$False to avoid it prompting you to allow each update.
More info on PSWindowsUpdate: https://github.com/joeypiccola/PSWindowsUpdate

Related

Powershell Script to reinstall Microsoft teams, Not able to find teams install

Currently I am trying to write a power shell script that will uninstall then install Microsoft Teams.
I have never written a power shell script before and I am having trouble having the script get the initial teams installation so I can uninstall it.
This is what I have written so far, I saw two ways of finding the teams install online and neither is able to find it so I am kinda lost, any help would be much appreciated.
(I know both are commented out I just did it like this for formatting in this question.)
Write-Host "-------------------------------------`n"
# Prompt for credentials
$credential = Get-Credential
$username = $credential.Username
$password = $credential.GetNetworkCredential().Password
Write-Host "Finding teams`n"
# Find teams 1
#$teamsapp = Get-AppxPackage -Name Microsoft.Teams
# Find teams 2
#$teamsapp = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name -eq "Microsoft Teams" }
# Check if installed
if ($teamsapp) {
Write-Host "Microsoft Teams is installed."
} else {
Write-Host "Microsoft Teams is not installed."
}
`
Teams is a bit tricky because it installs per user, not per computer. Assuming you're running the script under the user's account, you can check the following registry location using Get-ChildItem:
Computer\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Teams
This code worked for me:
Get-ChildItem -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' | Where-Object { $_.Name -like '*Teams' }
You should be able to use the "QuietUninstallString" property of the result to get the command needed to uninstall Teams.
As a side note, consider looking into the Teams Machine Wide Installer for deploying teams. It installs to the computer and runs at logon for each user to detect if Teams is installed to their AppData folder. If not, it installs it automatically. This lets you avoid having to run as the user or loop through all the users AppData folder to manipulate user apps.

Powershell - Check if specific modules are installed and take action if not installed

new to stackoverflow and novice at Powershell, so go easy.
I'm looking for a way for a Powershell script to check if specific modules are installed. If not then give a message asking if they would like to install those not installed. If all are installed then proceed with the script.
I've seen the '#requires -Module' option but I want to provide a better prompt rather than the Powershell red text.
For reference, I want to check if AzureAD, ExchangeOnlineManagement, and MSOnline modules are installed
Any help appreciated.
Regards,
Lee
To offer an alternative to Steven's helpful answer that handles the missing modules as a group :
# Define all required modules
$modules = 'AzureAD', 'ExchangeOnlineManagement', 'MSOnline'
# Find those that are already installed.
$installed = #((Get-Module $modules -ListAvailable).Name | Select-Object -Unique)
# Infer which ones *aren't* installed.
$notInstalled = Compare-Object $modules $installed -PassThru
if ($notInstalled) { # At least one module is missing.
# Prompt for installing the missing ones.
$promptText = #"
The following modules aren't currently installed:
$notInstalled
Would you like to install them now?
"#
$choice = $host.UI.PromptForChoice('Missing modules', $promptText, ('&Yes', '&No'), 0)
if ($choice -ne 0) { Write-Warning 'Aborted.'; exit 1 }
# Install the missing modules now.
# Install-Module -Scope CurrentUser $notInstalled
}
As alluded to in your question, there are a few ways to ensure modules are available to a script or module. Modules can define other modules as well as other prerequisites. #Requires can be used, but neither facility will prompt or otherwise install a module. Instead, they will prevent the loading or running of the dependent script etc.
If you want to start building a function you can check if a module is available on the given system with:
Get-Module -Name <ModuleName> -ListAvailable
You can wrap this in a loop with an If block to cover multiple modules:
ForEach( $Module in $RequiredModules )
{
If ( !(Get-Module -ListAvailable -Name $Module) ) {
# find, install and load the module.
}
}
I would draw your attention to the other *Module cmdlets to build out the remaining functionality you described.
Find-Module
Install-Module
Save-Module
Import-Module

Update Windows security remotely using powershell scheduled task

I am trying to Install windows security patches on a remote machine using powershell remoting.
This is the function i am using to Update windows
<#
.SYNOPSIS
This functiion will automatically install all avaialable windows updates on a device and will automatically reboot if needed, after reboot, windows updates will continue to run until no more updates are available.
#>
function Install-WindowsUpdates
{
Install-Module -Name PSWindowsUpdate -RequiredVersion 2.1.0.1 -Force
Import-Module PSWindowsUpdate -Force
Get-WindowsUpdate -install -acceptall
}
When i run this function on a local host, the function is successful in installing windows security patches. I have the below script to do the same remotely:
param(
[Parameter(Mandatory = $true)]
[string] $IPaddress
)
try
{
$secpasswd = ConvertTo-SecureString "Pass#12345678" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("Admin02", $secpasswd)
#Create a Session.
$Session = New-PSSession -ComputerName $IPaddress -Credential $cred
cd C:\Users\Admin01\Documents
. .\Install-WindowsUpdates.ps1
Invoke-Command -Session $Session -ScriptBlock ${function:Install-WindowsUpdates}
return $true
}
catch
{
return $false
}
When i run this script i am getting the below error:
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
+ CategoryInfo : NotSpecified: (:) [Get-WindowsUpdate], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,PSWindowsUpdate.GetWindowsUpdate
+ PSComputerName : 10.0.0.7
I have setup both the loaclhost and remote machine for remoting and able to execute other scripts remotely. Also have enabled WMI on the remote machine.
What other settings i have to do?
Using Scheduled Task:
I am using the following script to start a scheduled task:
param(
[parameter(Mandatory = $true)]
[string]$IPaddress
)
$PSModulePath = $env:PSModulePath
$SplittedModulePath = $PSModulePath.Split(";")
$ModulePath = $SplittedModulePath[0]
$secpasswd = ConvertTo-SecureString "Pass#12345678" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("Admin02", $secpasswd)
#Create a Session. Replace host name with the host name of the remote machine.
$Session = New-PSSession -ComputerName $IPaddress -Credential $cred
$User= "Admin02"
$Action= New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "$env:ALLUSERSPROFILE\Install-WindowsUpdate.ps1"
$Trigger= New-ScheduledTaskTrigger -At 5:05am -Once
Invoke-Command -Session $Session -ScriptBlock { Register-ScheduledTask -TaskName "Install-Updates" -User $Using:User -Action $Using:Action -Trigger $Using:Trigger -RunLevel Highest –Force }
I have copied the below script on the target machine at the path $env:ALLUSERSPROFILE
<#
.SYNOPSIS
This functiion will automatically install all avaialable windows updates on a device and will automatically reboot if needed, after reboot, windows updates will continue to run until no more updates are available.
.PARAMETER computer
Use the Computer parameter to specify the Computer to remotely install windows updates on.
#>
Install-Module -Name PSWindowsUpdate -RequiredVersion 2.1.0.1 -Force
Import-Module PSWindowsUpdate -Force
Get-WindowsUpdate -install -acceptall
After i schedule the task nothing is happening.What i am doing wrong?
Yea, I fought this for weeks and finally have a good solution. The solution is actually built right into the PSWindowsUpdate module. The built in solution does use a windows Task, but it launches right away, and its actually helpful in tracking its completion progress, and it keeps the integration secure. The issue I have found is that PSWindowsUpdate has poor documentation. The following code worked for me:
Invoke-WUJob -ComputerName $svr -Script {ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll -AutoReboot -Install | Out-File C:\PSWindowsUpdate.log } -Confirm:$false -Verbose -RunNow
There is a lot of scattered information on this topic, so please do your reading. PSWindowsUpdate is by far the best library for this job, and although its been a long process for me, I believe the above solution will work for everyone.
Please remember, the computer you are running the above scrip from needs to trust the computer you are trying to update, you can run this script to trust the computer:
Set-Item WSMan:\localhost\Client\TrustedHosts -Value <ComputerName>
NOTE: Wildcards can be used in computer name
I also wanted to give you some information that greatly helped me:
Get-WindowsUpdate: This is the main cmdlet of the module. It lists, downloads, installs or hides a list of updates meeting predefined requisites and sets the rules of the restarts when installing the updates.
Remove-WindowsUpdate: Uninstalls an update
Add-WUServiceManage: Registers a new Windows Update API Service Manager
Get-WUHistory: Shows a list of installed updates
Get-WUSettings: Gets Windows Update client settings
Get-WUInstallerStatus: Gets Windows Update Installer Status, whether it is busy or not
Enable-WURemoting: Enables firewall rules for PSWindowsUpdate remoting
Invoke-WUJob: Invokes PSWindowsUpdate actions remotely
Like for all PowerShell cmdlets, different usage examples can be shown for each command typing Get-Help “command” -examples.
PSWindowsUpdate main parameters
As shown in the previous section, the PSWindowsUpdate module includes different predefined aliases to ease patching processes. However, main parameters for the Get-WindowsUpdate cmdlet will be listed and explained below:
Filtering updates:
AcceptAll: Downloads or installs all available updates
KBArticleID: Finds updates that contain a KBArticleID (or sets of KBArticleIDs)
UpdateID: Specifies updates with a specific UUID (or sets of UUIDs)
Category: Specifies updates that contain a specified category name, such as ‘Updates,’ ‘Security Updates’ or ‘Critical Updates’
Title: Finds updates that match part of title
Severity: Finds updates that match part of severity, such as ‘Important,’ ‘Critical’ or ‘Moderate’
UpdateType: Finds updates with a specific type, such as ‘Driver’ and ‘Software.’ Default value contains all updates
Actions and targets:
Download: downloads approved updates but does not install them
Install: installs approved updates
Hide: hides specified updates to prevent them to being installed
ScheduleJob: specifies date when job will start
SendReport: sends a report from the installation process
ComputerName: specifies target server or computer
Client restart behavior:
AutoReboot: automatically reboots system if required
IgnoreReboot: suppresses automatic restarts
ScheduleReboot: specifies the date when the system will be rebooted.
#How to avoid accidental installs#
Windows updates and patches improve the features and stability of the system. However, some updates can mess up your system and cause instability, especially automatic updates for legacy software such as graphic card drivers. To avoid automatic updates and accidental installs for such applications, you can pause Windows updates.
Alternatively, you can hide the specific updates for those features you don’t want to get updated. When you hide the updates, Windows can no longer download and install such updates. Before you can hide the update, you need to find out its details, including its knowledge base (KB) number and title. Type the cmdlet below to list all the available updates on your system:
Get-WUList
To hide a specific update using the KB number, use your mouse to copy that KB number. Next, type the command below:
Hide-WUUpdate -KBArticleID KB_Number
Highlight the “KB_Number” and click paste to replace that part with the actual KB number.
When prompted to confirm the action, type A, and hit the Enter key. If the command succeeds, the “Get-WUList” lists all the available updates, with hidden updates appearing with the symbol “H” under their status.
The KB number for the update may not be available for some updates. In this case, you can use the title to hide the update. To do this, list all the available updates via the cmdlet below:
Get-WUList
Next, use your mouse to copy the update title. Ensure it is distinct from other update titles. Now, type below command below to hide the update:
Hide-WUUpdate -Title “Update_Title”
Don’t forget to paste the actual update title in the “Update Title” section.
When prompted to confirm the action, type A, and hit the Enter key. If the command succeeds, the “Get-WUList” lists all the available updates. However, the status of hidden updates appears with the symbol “H” underneath them.
How to determine errors
It is of crucial importance to have as much information as possible about Windows Updates installation processes in order to be able to fix erroneous deployments. The Get-WindowsUpdate cmdlet and the rest of cmdlets available in the module, provide a very detailed log level when managing updates, including status, KB ID, Size or Title.
Centralizing all of the computer logs and analyzing them searching for errors, administrators will always be able to know the patch level of their Windows computers and servers.
The above passages came from this site!
This seems to be not possible by design:
Source 1
Source 2
Source 3
It is impossible for remotely connected users to download stuff from the internet it appears.
Speaking about windows update, you have many options like:
Connection using psexec tool then run wuauclt /detectnow /updatenow
If you are using windows 10 /server 2016 , the tools was replaced with USOClient.exe which is more effective.

silent installation using powershell

I am trying to install software using powershell silent scripting. To install this software we need to have JRE installed on machine. For this first we need to check weather JRE installed or not, if not installed then it needs to be installed. What approach needs to be followed?
I have tried with the below of code.
$LASTEXITCODE = 0
$workdir = "C:\Program Files (x86)\Java"
If (!(Test-Path $workdir))
{
$LASTEXITCODE = (Start-Process "D:\jre-6u26-windows-i586.exe" -ArgumentList "/s" -Wait -PassThru).Exitcode
}
If($LASTEXITCODE -eq 0)
{
$DCBdir = "C:\Program Files (x86)\Compart"
If (!(Test-Path $DCBdir))
{
$Installer="D:\sw.exe"
$responsefile="D:\Sresponse.varfile"
$a=#("-q", "-varfile", "$responsefile")
start-process $Installer -ArgumentList $a -wait
}
}
$chkdir = "C:\Program Files (x86)\SWFolder"
if(Test-Path -eq $chkdir)
{
[System.Windows.MessageBox]::Show('Installation completed successfully')
}
When I run script its workingfine as it is checking the previous installation and performing installation if not found the installation. But here I am getting as issue with this code.
If Java installed alredy means it should start the other installation. but here in my case its stopping the complete installation.
after installation completed, I need to display the message like " Installation completed". But here its not working. AnNy wrong in the above code..??
One package manager that I like to use is Chocolatey which has an approved package for JRE, it looks like. A quick check wmi will tell you whether or not java is installed:
$x = Get-WmiObject -Class Win32_Product -Filter "Name like 'Java(TM)%'" | Select -Expand Version
You could also use Test-Path pointed at registry keys you know exist for the package. Once you verify that JRE is not on the machine, then you can call out to Chocolatey to install it.

Active Directory Module

I have written a PowerShell script, an application that allow PC Refresh to set Company, DepartmentNumber, etc. on an AD object. In development everthing works fine. Obviously I have AD installed on my machine. I have compiled my app to a .exe and placed it on a network share where the techs will execute it from there as they start up a new computer or refresh from Windows 7 to Windows 10 mostly.
The problem is the new PC will not have Active Directory installed at this point in time. I need to find a way to have my app intall, import and run Active Directory as on start up of new or refreshed computers. How do I accomplish this?
Below is some relevant code I use to import the module if it exist on the machine.
$RestoreForm_Load = {
# Load the ActiveDirectory module if it's available
# Check if the ActiveDirectory module is installed
if ((Get-Module -ListAvailable | where { $_.Name -eq 'ActiveDirectory' }) -eq $null) {
$labelDialogRedRestore.Text += "You need to install the ActiveDirectory module!`n"
} else {
# Check if the ActiveDirectory module is allready Imported
if ((Get-Module ActiveDirectory) -eq $null) {
Import-Module ActiveDirectory -ErrorAction 'SilentlyContinue'
$labelDialogGreenRestore.Text += "ActiveDirectory module imported`n"
} else {
$labelDialogGreenRestore.Text += "ActiveDirectory allready imported`n"
}
}
Only the Windows Server versions have the AD module, or any other part of the RSAT (Remote Server Administration Tools) for that matter, available for installation out of the box. You can use Add-WindowsFeature (or Install-WindowsFeature, which replaced the former in Windows 2012 and newer) to install the module on a server:
Import-Module ServerManager
$os = (Get-WmiObject Win32_OperatingSystem).Caption
switch -wildcard ($os) {
'Windwos Server 2008*' {
Add-WindowsFeature RSAT-AD-PowerShell -IncludeAllSubFeatures
}
'Windows Server 2012*' {
Install-WindowsFeature RSAT-AD-PowerShell -IncludeAllSubFeatures
}
}
Windows client versions don't come with the RSAT. You need to install the correct RSAT package first before you can install the AD PowerShell cmdlets. The link list in the KB article is a little outdated, though. The link for the Windows 10 Preview RSAT package doesn't work anymore. Here is the download link for the release version.
Once you have installed the update on the clients, you can install the module for instance via dism:
dism /Online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-Powershell
Note that (at least on the client versions) the feature name may differ between versions (the feature is named RemoteServerAdministrationTools-Roles-AD-Powershell in the Windows 7 RSAT, but RSATClient-Roles-AD-Powershell in the Windows 10 RSAT), so you may need to use a switch statement on the clients as well:
$os = (Get-WmiObject Win32_OperatingSystem).Caption
$name = switch -wildcard ($os) {
'Windows 7*' { 'RemoteServerAdministrationTools-Roles-AD-Powershell' }
'Windows 8*' { '???' }
'Windows 10*' { 'RSATClient-Roles-AD-Powershell' }
}
& dism /Online /Enable-Feature /FeatureName:$name
Also, beware that regardless of which system you're installing the module on (server or client) you must have .NET framework 3.5.1 or 4.5 installed, otherwise the module won't work (if you can even install it in the first place).
You could install the module in your script with the following command:
Add-WindowsFeature RSAT-AD-PowerShell
Note that this feature requires the .NET Framework 3.5.1 feature too which can be installed with the following command:
Add-WindowsFeature net-framework-core
While you can install Active Directory on these machines just to run your code, I'd suggest setting up a session to a computer that already has it installed instead. If the Techs are hands on and have credentials to access AD then this will work better.
$Session = New-PSSession -ComputerName DC -Credential (Get-Credential) -Name AD
Enter-PSSession -Session $Session
Import-Module ActiveDirectory
Doing-AD -Stuff
...
Disconnect-PSSession
With this approach, the Tech will be prompted for their credentials, the script will run your AD stuff, and the client machine won't have RSAT tools enabled or installed.