Category Archives: Microsoft

All Microsoft Products (Exchange, SQL, Windows, Server)

Powershell Remote Windows Updates

Current employer has 3 forests, 3 domains, and 2 WSUS servers. During Covid (operationally speaking anyway), we’re in a 95% work-from-home status. One of our WSUS servers at some point in time decided to fill up its disks with updates. At another point in time, no one on the team thought it would be a good idea to setup monitoring or alerting for this system. Yay. Long story short, I have a love/hate relationship with WSUS.

For on-prem systems it works fairly well. GPOs put systems into specific groups (Workstations, Servers, Pilot Groups, etc), and an-eventually-implemented naming convention will allow IT Personnel to easily identify DEV, ADM, and PRD systems at a glance. Approving updates, pushing updates, and reporting updates all works.

What doesn’t work, however, is the automatic installation of updates. This post will turn into 2 posts: 1) Server-related, and 2) Workstation-related. The workstations, especially those that are remote, aren’t patching themselves on the regular. Probably because the users don’t VPN in often (or long enough) AND whomever set WSUS rules up didn’t specify an install-by required date for updates. Users are lazy and don’t like to reboot (myself included), so this just compounds the issue.

However, this post was more for the Server updates path. WSUS was setup to download patches, but the GPO for servers indicates that at no time will the patches be installed on servers. The previous regime had used batchpatch for that purpose. I’ve used PDQdeploy – SSDD. But since I said “previous regime”, and the last member of that batchpatch crowd left over a year ago, we’ve been woefully underpatched since pre-Covid.

Enter PowerShell. Note: I may clean this up a bit, but for now it’s the messy workthrough.

On EVERY managed system you need to have the following pre-requisites:

  • An Administrator account
  • WSMan configured to allow the host(s)
  • WinRM configured
  • PSWindowsUpdate PS Module

  1. Open PowerShell as an Administrator
  2. winrm /quickconfig
  3. Set-Item WSMAN:\localhost\client\trustedhosts -Value *
  4. Install-Module -Name PSWindowsUpdate

If the PSWindowsUpdate fails to install it’s generally due to the fact that you’re running PS 5.2 or below and it’s failing TLS requirements to run NuGet. Enable strongencryption on powershell, restart powershell, and re-run the PSWindowsUpdate to continue.

  • Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
  • Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord

Now we want to create the PS1 file. I located this in my C:\scripts directory on a domain controller because it’s easier that way.

$Server = Read-Host -Prompt 'Enter a Fully Qualified computername.domain.tld - or multiple computers separated by comma and space'
$Credential = Get-Credential
Invoke-WUJob -ComputerName $Server -Credential $Credential -Script {Import-Module PSWindowsUpdate; Install-WindowsUpdate -NotCategory 'Drivers' -MicrosoftUpdate -AcceptAll -IgnoreReboot -SendReport -PSWUSettings @{SmtpServer='YOURSMTPSERVER.DOMAIN.TLD';From='WSUS@YOURDOMAIN.TLD';To='ITUSER@YOURDOMAIN.TLD';Port=25} | Out-File C:\PSWindowsUpdateLog.txt -Append} -Confirm:$false -verbose -RunNow

I’ll eventually remove the credential ask (and hardcode one in) and also have it pull from a comma delaminated file listing all of the required servers. Perhaps I’ll have it ask “which domain” with selections 1 to 3, then “enter credentials for XYZ domain”.

From here we want to save that PS1 file and then run it from the domain controller. Right-click run with powershell. Follow along. I should note that I have the following auto-admin code at the top of the script:

Check for run as administrator
 param([switch]$Elevated)
 function Test-Admin {
     $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
     $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
 }
 if ((Test-Admin) -eq $false)  {
     if ($elevated) {
         # tried to elevate, did not work, aborting
     } else {
         Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
     }
     exit
 }

Look for updates to this post for the workstations or servers. Or not. I get lazy sometimes.

Date Last opened for all applications

Because several applications are paid separately (Visio is the example) from the rest of O365, and we were running low on licenses (and we also didn’t have a Software Audit tool installed), I had to find the quick and dirty (ie cheap and fast) way of finding out the information required.

It was easy to pull who had a license associated with their account. Just log into the Admin Portal admin.microsoft.com, navigate under Billing > Licenses > click on the product.

In my case it’s Visio Plan 2. Licenses 0 available, 32 assigned.

Powershell to the rescue

$name = $env:COMPUTERNAME
$path = "\\fileserver.domain.tld\share\subdir\" + $name + "_lastusedApp.csv"
Get-ChildItem -Path ${env:ProgramFiles(x86)} -Filter "*.exe" -Recurse | Get-ItemProperty | select name,lastaccesstime | sort -Property lastaccesstime | Export-Csv -Path $path -Encoding ascii -NoTypeInformation

Just need to run that remotely on the system. You can psexec or I was trying to use the invoke-command but was coming up with lack of rights to run remote scripts and didn’t need to look any further.

The .csv then shows a list of all installed applications and the last time they were opened. Unfortunately if someone never reboots their system (uptime over a year or so) and keeps the application(s) open that entire time, it’ll appear as though they haven’t “used” it in the last year. Just something to keep in mind.

Corrupt User Profile

Eventviewer was showing Event ID 1515 and I was logged in as with a temporary profile. No other user was experiencing this, so I went about fixing with REGEDIT.

  • Open Regedit
  • Start > Run > regedit
  • Navigate to HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\
  • Find any profiles with “.bak” in them
  • Delete the .bak entire subkey
  • Log off, log on

Windows Apps

So this all started because I was unable to open the Windows Security Center. It just wouldn’t open.

I tried to open SecHealthUI.exe directly (C:\Windows\SystemApps\Microsoft.Windows.SecHealthUI*\) and that failed out. Eventviewer showed Faulting application name: sechealthui.exe, faulting module name: KERNELBASE.dll. Someone wanted me to re-run the kernelbase.dll registry (regsvr32 kernelbase.dll) which doesn’t do anything unless you give yourself administrative privs to the kernelbase.dll file. And didn’t fix my issue anyway.

What did help me was re-installing all of the Windows Apps with the following powershell command:

  • windows key + x
  • Select Windows Powershell (Admin)
  • Get-AppXPackage -AllUsers | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml"}
  • Profit

And, because this is entitled “Widnows Apps”..

  • Uninstall XBox Windows-related items
  • Select Windows Powershell (Admin)
  • Get-AppXPackage *xboxapp* -AllUsers | Remove-AppXPackage

Battery Health Windows

Had a remote user complain that his laptop’s battery was only lasting “about 5 minutes” before it would either shutdown or he’d have to plug it in. The laptop was 3 years old, but the battery had already been replaced ~6 months ago.

It’s at this point that I’d like to push my own thinking. This user had received a new-to-him laptop as the first member of his team to be upgraded from Windows 7 to Windows 10. If you’re familiar with Dell products, he upgraded from a 7480 to a 7490 laptop. If you’re also familiar with how office politics goes, as soon as one person on a team has something new/shiny, the others want it too. Well this PoC upgrade process (he was part of a pilot group) went well. Fast forward 6 months and the rest of his team is receiving 7400 laptops and he feels like he’s missing out (total conjecture) as part of their windows 10 upgrade process.

Anyway, you can run the battery report remotely assuming they’re somehow connected to the network – usually via a VPN or directly on the network.

  • Run Powershell as an Administrator
    • windows key + x, Windows Powershell (Admin)
  • powercfg /batteryreport /output "C:\temp\battery-report.html"
  • Open the battery-report.html in any browser

Windows Change Network Type

I completely understand the segmentation of networks based on labels – Private for trusted internal communications, Public for non-trusted or external communications, and DomainAuthenticated for domain-joined systems. This allows for a more fine-tuned windows firewall experience and can create a more secure environment.

Unless the automatic labels get it wrong. OK, I should preface that I’ve never seen the automatic network labels go the wrong way – that is to say a “public” connection that is actually labeled as a private/trusted one. However, it’s still a PITA when Windows thinks the connection is untrusted (public) when it’s really something on the internal network.

It generally does this with my VPN connections. TUN and TAP both have had this issue, so I think it’s more a Windows-feature than anything else.

Enter PowerShell. You’ll see a theme here, I’ve been PSing a lot more lately.

  • Run an Elevated Powershell (run as administrator)
  • List out all network profiles
    • Get-NetConnectionProfile
  • Find the Interface Index of your connection, then set the category
    • Set-NetConnectionProfile -InterfaceIndex 22 -NetworkCategory Private

There are ways to do this from a Registry Editor perspective as well, but this seems to be the easiest way (and doesn’t require a reboot). More info here http://woshub.com/how-to-change-a-network-type-from-public-to-private-in-windows/

Exchange Truncate Logs

In a hybrid environment had an Exchange server on-prem (2016) that was not being backed up by normal means. In fact, now that I’m writing this, I’m pretty sure it’s not being backed up at all; something I’ll look into eventually.

Anyway, this Exchange server was filling up its drive space for logs. So I “faked” a backup and truncated the logs without any dismounting of the storage or taking the system offline.

  • Run CMD as an administrator
  • diskshadow
  • add volume e: (this is assuming your Exchange DB and Logs directories are on the E: drive)
  • begin backup
  • create
  • end backup
  • Profit!