Use PowerShell to install Windows Updates in vSphere - powershell

I have a cluster of vSphere windows clients approximately 100 that I want to remotely automate windows updates on weekly. I have listed all the windows machine out in text file on my desktop. I have run the PSWindowsUpdate module on my local windows10 machine with command:
Install-Module -Name PSWindowsUpdate and then executed the below script successful for my local machine to run windows updates.
#Import-Module PSWindowsUpdate
#Remove-Item -Path C:\Scripts\status.tx
#Start-Transcript -Path C:\Scripts\status.txt
#$Updates = "Critical Updates", "Security Updates"
#Get-WUInstall -AcceptALL- Verbose -IgnoreReboot -Category $Updates
#Write-Host "Done"
#Stop-Transcript
#Start-Sleep -s 120
#Restart-Computer -Force -Confirm:$false
-- after pc restarts run as PS As Administrator
#Get-WindowsUpdate
However, I am not a expert at PowerShell so, I do not know what to additionally script to accomplish the task of remotely updating 100 or so windows clients in vSphere.
Any suggestion would be appreciated.

You can try with the invoke-command. You can create a server list from a DC:
$Servers = Get-ADObject -Filter * -Properties Name,OperatingSystem | Where-Object OperatingSystem -like '*Server*'
And use this list with a loop like this
ForEach($_ in $Servers)
{
Invoke-Command -ScriptBlock {Get-WUInstall -AcceptALL- Verbose -IgnoreReboot -Category $Updates } -ComputerName $_.Name -ErrorAction SilentlyContinue
}

Related

Is there a way to query Server Manager using PowerShell

Part of our daily process is to RDP to a remote machine and check the services are running. We have to check “File and Storage Services”, “IIS”, “Local Server”, and “All Servers” (see image).
Can I do this remotely through PowerShell? I have a script (Get-Service -ComputerName [remote computer name]), but which services do I list to check these 4 main areas are running?
Server Manager
Tried:
get-service -Name "LanmanServer", "LanmanWorkstation" -ComputerName [computername]
get-service -ComputerName [computername] | Where-Object {$.Status -ne "Running"}
get-service -ComputerName [computername] | Where-Object {$.Status -eq "Running"}
So I can list the services, and if they're running or not, but this doesn't tell me what I need

Invoke-Command doesn't return to local machine but software is installed

The tl;dr: I'm puzzled as to why my script isn't returning back to the deployment machine. Does anyone have an idea why?
I was provided an EXE and a couple arguments to install some software. I'm using PowerShell's Start-Process to run it. The deployment machine is Win Server 2012 domain controller, logged in and being run as domain admin. The test machines are two Windows 10 Pro domained machines. All three have PS 5.1.
Being a silent install, there is no user interaction required. If I run the exact command locally, via RDP, or in a pssession, with just c:\Software.exe /silent /arg2removed, it installs and returns as expected.
The script runs fine up to a point. Nothing happens after Start-Process inside Invoke-Command 's -ScriptBlock. In a separate PowerShell window, I can use Enter-PSSession for each of the two client machines, and Get-Service and Get-Process both show the software's service and background processes, respectively. I can Ctrl+c on the deployment machine and get back to a prompt. No errors are reported at any time.
Here's the Start-Process chunk. I've read the help and it doesn't sound like I'm missing anything that would allow the ScriptBlock to finish. If I prepend Start-Process with Write-Host (like we all do), it echoes the command that would run and I get back to a command prompt on the deployment machine.
# Start the installer.
Start-Process `
-FilePath "C:\${using:SrcExe}" `
-ArgumentList "/SILENT", "/arg2removed" `
-WorkingDirectory C:\ `
-Wait `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InstallErrors
Here's most of the script. The only items before Invoke-Command are where I set up $ComputersToInstallOn, enter the credentials (yes I'm sure they're correct), and supply the path to the EXE.
Invoke-Command `
-ComputerName $ComputersToInstallOn `
-Credential $Creds `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InvokeCommErrors `
-ScriptBlock {
# Get and print the destination machine's hostname
$ThisMachine = Get-Content Env:\COMPUTERNAME ; $ThisMachine
# Print the current date and time
Get-Date
# Check if Sentinel processes are running. If not, assume it's not installed.
$S1Procs = get-process sentinel*
if([string]::IsNullOrEmpty($S1Procs)) {
# Sentinel isn't installed. Continue.
# Map a drive letter to $SrcFolder. Not theoretically necessary but Start-Process complains when copying with the UNC path directly.
New-PSDrive `
-Name S `
-PSProvider FileSystem `
-Credential ${using:Creds} `
-Root ${using:SrcFolder} `
-verbose
# List remote folder
Get-ChildItem S:\
# Copy the $SrcExe to C:\
Copy-Item `
-Path "S:\${using:SrcExe}" `
-Destination C:\ `
-Verbose `
-ErrorAction Stop `
-ErrorVariable CopyErrors
# Unmount drive
Remove-PSDrive S -verbose
# Verify EXE exists locally
Get-ChildItem -Path C:\${using:SrcExe}
# If there were copy errors, abort.
if ($CopyErrors) {
Write-Host "There was an error copying '${using:SrcExe}' to $ThisMachine. Aborting."
exit 1 } else {
# All good so far. Continue to install.
Write-Host "$(Get-Date -UFormat '%Y%m%d %H:%M:%S') : Starting install on ${ThisMachine}. You may need to Ctrl+C to return to the local machine. Check processes on each machine though."
# Start the installer.
Start-Process `
-FilePath "C:\${using:SrcExe}" `
-ArgumentList "/SILENT", "/arg2removed" `
-WorkingDirectory C:\ `
-Wait `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InstallErrors
# ScriptBlock doesn't seem to make it to anything after Start-Process.
# Remove the EXE.
Remove-Item "C:\${using:SrcExe}" -Verbose -ErrorAction SilentlyContinue
exit 0
# Get-Process -Name Sentinel*
# echo "Sleeping. Now would be the time to abort."
# Start-Sleep 15
}
} else {
Write-Host "Sentinel appears to be installed and running."
$S1Procs
Get-Service -Name Sentinel* | Where-Object { $_.Status -match "Running" }
exit 0
}
}
if($InvokeCommErrors){
Write-Host "There were some errors."
}
EDIT: Added some requested info.

How to run commands on a remote computer using PSExec and PowerShell?

I am trying to install an .exe program remotely to many workstations in the domain.
The command I am trying to run in the remote computer's PowerShell instance:
.\PsExec.exe \\$Computer /s cmd /c %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Unrestricted -File \\FileShare\Software\DellSupportAssist.ps1
And the script I am calling in the command above:
#Retrieves a list of computers in AD.
$Computers = Get-ADComputer -Filter 'Name -like "Workstation*"' | Select-Object -ExpandProperty Name
ForEach ($Computer in $Computers) {
#Opens a remote session into the computer.
.\PsExec.exe C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe \\$Computer
#Installs Dell SupportAssist for each computer
Invoke-WebRequest -Uri "https://downloads.dell.com/serviceability/catalog/SupportAssistInstaller.exe" -OutFile "C:\Users\$env:Username\Downloads\SupportAssistInstaller.exe"
Start-Process "C:\Users\$env:Username\Downloads\SupportAssistInstaller.exe" -ArgumentList "/q" -Wait -NoNewWindow
Write-Host "Installing Dell SupportAssist on $Computer" -ForegroundColor Green
#Checks whether Dell SupportAssist was successfully installed or not
if (Get-WmiObject -Class win32_product -ComputerName $Computer | Where {$_.Name -match "Dell Support*"} | Select-Object -ExpandProperty Name) {
Write-Host "Dell SupportAssist was successfully installed on $Computer" -ForegroundColor Yellow
} else {
Write-Host "Dell SupportAssist was not successfully installed on $Computer" -ForegroundColor Red
}
exit
}
I am having the following issues:
'Target name incorrect' (for half of the computers in the domain.)
.\PsExec.exe : The term '\FileShare\Software\DellSupportAssist.ps1' is not recognized as the name of a cmdlet, function, script file, or operable (for the computers I can connect to in the domain)

Saving Active Directory Configuration settings to a file

I'm using my machine to run a script on the Domain Controller server using Enter-PSSession. It all works except, I can't save the outputs from the script on my local machine.
I want to save the outputs from the script as objects in my local machine in a csv format (Not on the Domain Controller server).
What I'm trying to do is save results from running commands like Get-ADDomainController etc..
Can someone please help me with this?
As for this …
I can't save the outputs from the script on my local machine.
… sure you can. Just create a log file as part of your session and copy that file back to your workstation for review, or just use the *-Transcript cmdlets to that creates a file automatically that you can copy over. The transcript will record everything that happens in the sessions.
Get-Command -Name '*transcript*' | ft -a
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Start-Transcript 3.0.0.0 Microsoft.PowerShell.Host
Cmdlet Stop-Transcript 3.0.0.0 Microsoft.PowerShell.Host
# get function / cmdlet details
(Get-Command -Name Start-Transcript).Parameters
Get-help -Name Start-Transcript -Full
Get-help -Name Start-Transcript -Online
Get-help -Name Start-Transcript -Examples
Or, don't use the interactive Enter-PSSession (explicit PowerShell Remoting) that puts you directly on the DC. Use a Implicit PSRemoting session, using New-PSSession and proxy the AD cmdlets to your machine for use.
$SessionAD = New-PSSession -ComputerName ''dc01.contoso.com
Invoke-Command $SessionAD -Command {Import-Module ActiveDirectory}
Import-PSSession $SessionAD -Module ActiveDirectory | Out-Null
$ADUSers = Get-ADuser -Filter *
$var = $ADUSers | Select-Object -Property Name, SamaccountName | Out-GridView -OutputMode Single
$GroupsMember = Get-ADUser -Filter ('Name -eq "' + $var.Name + '"') -Property MemberOf |
Select -ExpandProperty MemberOf |
Get-ADGroup -Property MemberOf |
Select Name
$GroupsMember
Get-PSSession | Remove-PSSession
Then you can run ADDS cmdlets as if they are actually on your machine and results are on your machine, or if you are on Window 8 or higher, just download and install (Win 7 - 8) / enable the RSAT tools (Win 10) directly and use them.
Remoting the Implicit Way
PowerShell Implicit Remoting: Never Install a Module Again
Also, take a look and Invoke-Command for running command locally or remotely.

Powershell script Memory usage

I'm running a simple script to get all inactive users (enabled state) not logged in for 60 days as below. The script seems to work fine on my workstation with ARS 6.7 & WinXP . But if i run the same script on another workstation with ARS 6.9 & Win7 the RAM usage of the script goes on increasing over time & it finally throws OutofMemory exception (after reaching 1.5+ GB) & aborts. The same script when run on WinXP workstation (ARS v6.7) does not consume more than 50 MB of RAM throughout. The domain i'm scanning is pretty big with over 550000 accounts. I'm totally confused about the issue here...Pls help!
[datetime]$TodayDate = Get-Date
[datetime]$InActivityDate = $TodayDate.AddDays(-62).Date
try
{
Get-QADUser -SearchRoot $SearchOU -Service $Service -SizeLimit $SizeLimit -PageSize 1000 -Enabled -DontUseDefaultIncludedProperties -IncludedProperties SamAccountName,`
Name,ParentContainer,DN,LastLogon,WhenCreated,PasswordLastSet,employeeID,`
employeeNumber,Manager,AccountIsDisabled,co,scriptPath |`
#Filter out inactive accounts
Where-Object {$_.LastLogonTimeStamp -lt $InActivityDate} |`
Select-Object SamAccountName,Name,ParentContainer,DN,LastLogon,WhenCreated,`
PasswordLastSet,employeeID,employeeNumber,Manager,`
AccountIsDisabled,co,scriptPath | Export-Csv $OutputFile -NoTypeInformation
}
catch
{
$ErrorMessage = $_.Exception.Message
$ErrTime = Get-Date
Write-Host "Error occured:`n$ErrorMessage" -ForegroundColor Red
Write-Output "[$ErrTime] Error occured:`n$ErrorMessage" | Out-File $OutputFile -Append
}
You should delegate filtering to AD side, quoting from Get-ADUser help: Get-ADUser -filter { lastLogon -le $logonDate } In your case, Get-QADUser cannot delegate filtering, so swap to Microsoft's AD module: Get-ADUser -filter { lastLogon -le $InactivityDate }. To get the module, install RSAT for oyur Windows version, then (if not enabled by default) go "Add/remove Windows components - RSAT - Role administration services - AD/LDAP - Windows Powershell module" and enable it.
Launching the Quest Shell in MTA mode has solved this problem. This can simply be done by adding "-mta" parameter to existing shortcut.
eg.
C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe
-psconsolefile "C:\Program Files\Quest Software\Management Shell for AD\ConsoleSettings.psc1" -noexit -Mta -command ". 'C:\Program
Files\Quest Software\Management Shell for AD\qsft.ps1'"