Category Archives: Microsoft

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

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!

Powershell Scripting

I know Powershell has been around for quite some time – about the time it was introduced I was actively trying to learn batch files. I ended up using batch files pretty much for everything I could and only relying on powershell for Exchange-related administrative tasks. So this post may be a little ancient for some people.

My imaging process successfully dropped the time required to deploy a newly minted workstation (Windows 10, Office, Updates, Oracle, Java, Firefox/Chrome, Adobe Reader, etc) from 5 hours down to 2 hours. The imaging process used to be 1) Boot workstation from Windows 10 USB thumb drive, 2) Install and configure Windows, 3) Manually install updates, 4) Manually install drivers, 5) Manually install Office, 6) Manually install Oracle, 7) Manually install… the list went on and on.

However, there are several applications that I cannot include in the image as they are either tied to a specific user or tied to a specific workstation name. Antivirus suites generally say not to include in an image process and I’ve adhered to that logic. Some of my other applications below *can* be included in the image, but I wanted to flex my after-image process muscles a bit too. All of these other applications added significant time – just over an hour – and I wanted to be able to script them instead of relying on a semi-manual approach.

  • Image laptop – Installs Windows, Office, Oracle, semi-recent drivers; Join to the Domain
    • 45 minutes
  • Run batch script – Installs Firefox, GoToAssist, Chrome, Acrobat Reader, AnyConnect VPN, Sophos AV; Reboot
    • 30 minutes
  • Download Slack client, Zoom client, and Dell SupportAssist; Install and run, let SupportAssist find missing drivers/update BIOS; Reboot
    • 30 minutes
  • Run Windows Updates; Reboot
    • 15 minutes

So the initial image process (45 minutes), followed by the setup of 3rd party applications (I used a batch script that would install most of what I needed without any interaction) would take upwards of the 2 hours to deploy. Then logging in as the user, setting up their Outlook profile, connecting OneDrive and backups, Configuring VPN, Installing and testing Slack, Zoom, Java and permissions et all. It added up quickly and I was tired of the manual and tedious approach.

My new powershell script will auto download, auto install. This is mostly for my own records, but some of it is commented if you wanted to use it. Total time savings? Another 20 minutes! We’re down to 1 hour and 40 minutes start-to-finish.

My next steps would be to auto install and configure Java (we have a lot of security exceptions), join the domain with a specific OU and prompted name, Remove the Mail App, hide the Cortana button, hide the Search box, hide the Task View button, etc.

# Check for run as administrator

function Test-Admin {
    $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())

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))

# Set Execution Policy (if not already GPO'd)
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted -Force

# Download the newest files from their various sites (requires internet access)
msg * /w First we download Slack, Zoom, and SupportAssist, then this will install Firefox, GoToAssist, Chrome, Adobe Reader, AnyConnect, Sophos, Slack, Zoom, and SupportAssist.
$progresspreference = 'silentlyContinue'
Invoke-WebRequest -OutFile C:\Downloads\slack.exe
Invoke-WebRequest -OutFile C:\Downloads\zoom.exe
Invoke-WebRequest -OutFile C:\Downloads\dell.exe
msg * The Downloads Have Finished. Now Installing software.

# Similar process to the .bat file; installs the MSI and EXE files
Start-Process msiexec.exe -Wait -ArgumentList '/I "\\schfile01\public\helpdesk\_OtherApps\Firefox\Firefox Setup 71.0.msi" /qn'
Start-Process msiexec.exe -Wait -ArgumentList '/I "\\schfile01\public\helpdesk\_OtherApps\GoToAssist_Remote_Support_Unattended_TT_Unattended.msi" /qn'
Start-Process msiexec.exe -Wait -ArgumentList '/I "\\schfile01\public\helpdesk\_OtherApps\Chrome\GoogleChromeStandaloneEnterprise64.msi" /qn'
\\schfile01\public\helpdesk\_OtherApps\AcroRdrDC1902120058\setup.exe | Out-Null
Start-Process msiexec.exe -Wait -ArgumentList '/I "\\schfile01\public\helpdesk\_OtherApps\Cisco\anyconnect-win-4.8.01090-core-vpn-predeploy-k9.msi"'

# Installs the downloaded files from the first steps. Does NOT delete them afterwards
cd C:\Downloads\
./slack.exe | Out-Null
./zoom.exe | Out-Null
./dell.exe | Out-Null
\\schfile01\public\helpdesk\_OtherApps\SophosSetup.exe | Out-Null
msg * Installs complete!

msg * /w Please set the date and time
Start-Process "ms-settings:dateandtime"

msg * /w Run Windows Updates
Start-Process "ms-settings:windowsupdate"

.NET Framework 3.5 Windows 10

One of the major problems I’ve encountered when using an imaging process is that when I need to install additional modules sometimes they don’t install as easily as if I had just manually installed Windows from the start.

After deploying 20+ of my new GM image laptop, I had a user report that they needed to have MapPoint 2010 installed. Since it’s near the end of 2020, and MapPoint hasn’t received an update since version 2013 came out (8 years ago at the time of this writing) – AND you cannot buy MapPoint from Microsoft any longer – I didn’t quite know what to expect.

Apparently a specialized group of customer care employees utilize MapPoint to “pin” where technicians are supposed to be and keep track of all customer-care-related scheduling. Who knew?

One of the pre-requisites for installation is .NET 3.5 libraries (and their subsequent backwards-compatible 2.0 and 2.5 files). The usual auto install will successfully install this Windows Add-on, but my image doesn’t include all of the necessary source files. Running it through add-remove programs/features also fails.

Easiest way for me was to simply double-click on the Windows 10 ISO to mount it, then run the following command in an Administrative command prompt:

  • Open CMD (as an Administrator)
  • dism /online /enable-feature /featurename:NetFX3 /All /Source:D:\sources\sxs /LimitAccess
  • Obviously change the Source to reflect the drive letter of your mounted ISO

Print Server Printer List

I was adding printers to our monitoring system and needed the IP addresses. Easy peasy, just check the print server, view the properties, and copy paste the IP?

WRONG! The Canon printers were added with port names named after the printer (Canon iR-ADV 4535) and not especially helpful to my needs.

Enter Powershell! Powershell enabled computer and Office (excel) needs to be installed.

  • Open Powershell as Administrator
  • Run the unrestricted execution Policy
  • Create printer_finder.ps1
  • Run the PS1, type in the print server name(s)
  • Open the Excel spreadsheet

set-executionpolicy unrestricted
Script to create a Excel spreadsheet with detailed information about
the printers installed on the server
Script was designed to give you a good description of how your print
server(s) are installed and configured.
* Requires Microsoft Excel be installed on the workstation you are running the script from.
.PARAMETER PrintServers
Name of the server you wish to run the script again. Can also be an
array of servers.
Excel spreadsheet
.\Export-PrinterInfo.ps1 -PrintServers "MyPrintServer"
.\Export-PrinterInfo.ps1 -PrintServers (Get-Content c:\scripts\myprintserverlist.txt)
Author: Martin Pugh
Twitter: @thesurlyadm1n
Spiceworks: Martin9700
Changelog: 1.0 Initial Release
Param (
[string[]]$PrintServers = "MyPrintServer"
Create new Excel workbook
Write-Verbose "$(Get-Date): Script begins!"
Write-Verbose "$(Get-Date): Opening Excel…"
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $True
$Excel = $Excel.Workbooks.Add()
$Sheet = $Excel.Worksheets.Item(1)
$Sheet.Name = "Printer Inventory"
$Sheet.Cells.Item(1,1) = "Print Server"
$Sheet.Cells.Item(1,2) = "Printer Name"
$Sheet.Cells.Item(1,3) = "Location"
$Sheet.Cells.Item(1,4) = "Comment"
$Sheet.Cells.Item(1,5) = "IP Address"
$Sheet.Cells.Item(1,6) = "Driver Name"
$Sheet.Cells.Item(1,7) = "Driver Version"
$Sheet.Cells.Item(1,8) = "Driver"
$Sheet.Cells.Item(1,9) = "Shared"
$Sheet.Cells.Item(1,10) = "Share Name"
$intRow = 2
$WorkBook = $Sheet.UsedRange
$WorkBook.Interior.ColorIndex = 40
$WorkBook.Font.ColorIndex = 11
$WorkBook.Font.Bold = $True
Get printer information
ForEach ($PrintServer in $PrintServers)
{ Write-Verbose "$(Get-Date): Working on $PrintServer…"
$Printers = Get-WmiObject Win32_Printer -ComputerName $PrintServer
ForEach ($Printer in $Printers)
If ($Printer.Name -notlike "Microsoft XPS*")
{ $Sheet.Cells.Item($intRow, 1) = $PrintServer
$Sheet.Cells.Item($intRow, 2) = $Printer.Name
$Sheet.Cells.Item($intRow, 3) = $Printer.Location
$Sheet.Cells.Item($intRow, 4) = $Printer.Comment
If ($Printer.PortName -notlike "*\*") { $Ports = Get-WmiObject Win32_TcpIpPrinterPort -Filter "name = '$($Printer.Portname)'" -ComputerName $Printserver ForEach ($Port in $Ports) { $Sheet.Cells.Item($intRow, 5) = $Port.HostAddress } } #################### $Drivers = Get-WmiObject Win32_PrinterDriver -Filter "__path like '%$($Printer.DriverName)%'" -ComputerName $Printserver ForEach ($Driver in $Drivers) { $Drive = $Driver.DriverPath.Substring(0,1) $Sheet.Cells.Item($intRow, 7) = (Get-ItemProperty ($Driver.DriverPath.Replace("$Drive`:","\\$PrintServer\$Drive`$"))).VersionInfo.ProductVersion $Sheet.Cells.Item($intRow,8) = Split-Path $Driver.DriverPath -Leaf } #################### $Sheet.Cells.Item($intRow, 6) = $Printer.DriverName $Sheet.Cells.Item($intRow, 9) = $Printer.Shared $Sheet.Cells.Item($intRow, 10) = $Printer.ShareName $intRow ++ } } $WorkBook.EntireColumn.AutoFit() | Out-Null
$intRow ++
$Sheet.Cells.Item($intRow,1) = "Printer inventory completed"
$Sheet.Cells.Item($intRow,1).Font.Bold = $True
$Sheet.Cells.Item($intRow,1).Interior.ColorIndex = 40
$Sheet.Cells.Item($intRow,2).Interior.ColorIndex = 40
Write-Verbose "$(Get-Date): Completed!"

Install KACE Agent Remotely

KACE generally works best when installed as part of the initial setup process. If it’s included in the base image and that base image is rolled out to x number of devices, you know that the KACE agent will eventually check-in with your appliance. Then you’ll have a complete inventory of workstations.

Installed after-the-fact is when it gets a bit trickier. The preferred method is to have a GPO install it for you. This works fairly well if your users are 1) always connected to the network and 2) reboot from time to time and 3) the computers are in the correct OU (or the GPO is applied to the correct OU…). But this isn’t always the case – and even still there are instances in which the stars just don’t all align.

Then there is Covid/Remote work. Computer GPO and startup-based-User-GPOs just don’t work well with the current on-prem Domain Controllers and remote workforce. Ok, enough about this, let’s get onto how!

Enter PSExec, the tried and true remote management tool. This assumes you have administrative permissions to access the remote system AND that the remote system is somehow connected to your network (via VPN).

Remotely Connect to workstation
psexec \\computername powershell.exe
mkdir c:\kace
Copy KACE Agent to the remote workstation
cp \\yourkaceservername\client\agent_provisioning\windows_platform\ampagent-9.1.204-x86.msi c:\kace\
Run the MSI quietly
cd c:\kace
msiexec.exe /i ampagent-9.1.204-x86.msi host=fqdn.of.kace.server.tld nohooks=1 /qn

If the computer is NOT running a recent version of powershell – looking at you Windows 7 – you’ll have to replace powershell.exe with cmd.exe. And since cmd.exe doesn’t support UNC paths you’ll have to use net use to mount the drives as a letter and then copy that way. Or just start > run > \\computername\c$, and manually copy to the c:\kace directory.

Another way is via the Windows Admin Center (, Add the computer with required credentials, then launch PowerShell on the left-side panel.

I also found a few workstations that had a fully configured KACE agent installed but just refused to collect and send inventory information. In that case I ran a manual check:

c:\program files (x86)\quest\kace\runkbot 1 0
c:\program files (x86)\quest\kace\runkbot 4 0

Remove Hidden KACE Agent

We utilize KACE (SMA K1000) for our helpdesk/ticketing, and inventory management. Part of our deployment packages include the KACE agent – this agent is required to send back data about the system it is installed upon (username, OS specs, hardware specs, etc).

Unfortunately, there are some times that the KACE agent doesn’t play nicely and it needs to be reinstalled. Or, in my case (pun intended), I needed to make a new GM image for deployment and it is recommended to NOT have the KACE agent installed prior to sysprep.

Open an administrator command prompt:

wmic product where "name like '%kace%'" call uninstall /nointeractive

This will find and remove any “KACE” related software currently residing on your system. You should see the messages “Method execution successful” and “ReturnValue = 0” if this runs successfully.

Note: This does not remove any existing firewall rules or files created outside of the standard install/uninstall configuration.

List Members of Dynamic Distribution Group

Like all good companies, we have dynamic distribution groups / lists that are based on the location of the user. Example, a user has the Office location of “Australia”, the dynamic list has the filter of:

((((((Office -eq 'Australia') -and (RecipientType -eq 'UserMailbox'))) -or (((CustomAttribute2 -like 'IncludeAllAUSLists') -and (-not(CustomAttribute1 -like 'excludefromdynamic')))))) -and (-not(Name -like 'SystemMailbox{*')) -and (-not(Name -like 'CAS_{*')) -and (-not(RecipientTypeDetailsValue -eq 'MailboxPlan')) -and (-not(RecipientTypeDetailsValue -eq 'DiscoveryMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'PublicFolderMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'ArbitrationMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuxAuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'SupervisoryReviewPolicyMailbox')))

The user was added and then the office location was of Australia was added (after troubleshooting below). He complained he did not receive an email that was sent to this distribution list, so I had to verify he was a member.

Connect to your Office 365 online powershell (I have it previously documented if you don’t remember).

$GROUP = Get-DynamicDistributionGroup "All-Australia-Employees"
Get-Recipient -RecipientPreviewFilter $FTE.RecipientFilter -OrganizationalUnit $FTE.RecipientContainer