Bulk Update AD UPNs

You may have a need to update UPNs (user principal names) for your AD users so they can be more easily used with Microsoft Office 365.

The below script can be used to generate a list of accounts that need to be update and the commands to do them. The script also generates an ‘undo’ command so you can easily go back. This is handy for testing on a few users at a time versus doing all at once.

You enter the new UPN domain, adjust filters if needed, and run it. It will export a CSV file to your desktop with the commands and other details.

$NewUPNDomain = "mydomain.com"

$outFilePath = $ENV:UserProfile + "\" + "Desktop" + "\" + "AD_UPN_Update" + "_$(get-random)" + ".txt"

Function Generate-UPNUPdates($NewUPNDomain, $outFilePath) {

	"ADName;OldUPN;NewUPN;Command;Command2" | out-File $outFilePath

	#initial set of users
	$aDUsers = Get-ADUser -ldapfilter  "(&(samaccountname=*))" -properties "UserPrincipalName","mail"
	$aDUsers = $aDUsers | where { (-not ($_.UserPrincipalName -match $NewUPNDomain) )}
	$aDUsers = $aDUsers | where { ($_.Enabled -eq $true) }
	$aDUsers = $aDUsers | where { ($_.mail -ne $null) }
	$tempList = @()
	#loop through count getting random users
	$aDUsers | foreach {
		if (-not ($tempList -contains $_.UserPrincipalName) ) {
			$usersName = $_.UserPrincipalName.Split('@')[0]
			$New_UPN = $usersName + "@" + $NewUPNDomain
			"UPN update for AD User $($_.SamAccountName) from old UPN of $($_.UserPrincipalName) to new UPN of $($New_UPN)"
			( $($_.SamAccountName) + ";" + $($_.UserPrincipalName) + ";" + $New_UPN + ";" + "Get-ADUser '$($_.SamAccountName)' | Set-ADUser -UserPrincipalName '$New_UPN'" + ";" + "Get-ADUser '$($_.SamAccountName)' | Set-ADUser -UserPrincipalName '$($_.UserPrincipalName)'" ) | out-file -append $outFilePath
			$tempList += $($_.UserPrincipalName)

Generate-UPNUPdates $NewUPNDomain $outFilePath

Posted in Documentation, IT | Tagged | Leave a comment

PowerShell Tips and Tricks

Variable Types

Pretty simple, just add brackets and the type.



[string]$MyVar = “blah”

[string]$MyVar = 1

[int]$MyVar = $MyVar

Parenthesis ( )

These sometimes must be used and other times it is better to use them for your own sanity. Just like in Math you can use these to control the order of operations.

Example: ((5 – 4) + 1) / 2 = 1

With variables they work well too and sometimes are needed when variables have multiple sub properties.

Example: (($Computer).System).Ram

Brackets []

Brackets are used with arrays. Simply use the below to determine what type of variable you are working with and add brackets with a number.

Example (find how many items are in an array):


Sometimes count won’t return anything. This always bugs me but is due to your variable not being an array (if it has a single item). So you’ll see 2 or more when you have an actual array. You can force the output to be an array too.

Example (first item in array):


Example (first 10 items in array):


You can use “..” to have powershell count. For example 1..100 gives you 1 to 100. It can also be used in brackets for arrays.

Example (get random item):

($MyVar | get-random)

Example (get the first item from the output of your commands):

(My-Command | My-OtherCommand)[0]

Squiggly Brackets {}

Use these for when you want to put code inside something such as a function or command in a variable.

Function DoStuff() {



$myCommand = { “stuff”; get-childitem }

Comparison & Logic & Math

[math]::round(1.0001, 1)

(-not ($true))

(($true -and $false -and $true) -or $false)

Getting output into variable (write-host vs “”)

PowerShell has multiple ways of outputting to screen. Similar to batch files there is standard output and standard error. I prefer to use just double quotes to output to the screen now as it is easier to capture this in a variable when using functions.

Example (will not capture output into variable):

Function OutputText() {

Write-host “MyText”


$MyVar = OutputText

Example (will actually capture output into variable):

Function OutputText() {



$MyVar = OutputText

Another good option is to make your own output function to handle output in multiple ways.

Function OutputText($text) {

Write-host $text



OutputText “Blah”

$MyVar = OutputText “Blah2”

Adding a column with select-object

This is super handy when you are working with nested loops. Say you are looping through a list of computers and then checking to see if a file exists on them and what the file’s timestamp of last write is. You can then easily use this array of data in a variable or export to csv.

Example (This will get you the computer name and last write time of all 3 computers in a table):

$output = “computer1″,”computer2″,”computer3” | foreach {

$cItem = $_

Gci “\\$($_)\c`$\windows\system32\calc.exe“) | Select @{l=”ComputerName”;e={$cItem}}, LastWriteTIme


Getting variables to work inside of quotes

Say you want to use write-host or simply “” to output a string with a variable.

Example (both ways work):

$HourAgo = (get-date).AddHours(-1)

“The time is: $($HourAgo)”

“The time is: $HourAgo”

Example (no el-work-o):

“The time is: $HourAgo.Year”

Working example:

“The time is: $($HourAgo.Year)”

It is simply best to add “$(  $myvarhere  )” to every string you have a variable in.

How to interact with commands and variables (see methods and properties)

Use get-member:

$a = (get-date)

$a | GM

Use GetType to see what type of variable it is:


You also don’t need to put output into a variable every time, you can simply:


Getting special symbols inside of strings with backtick

Simply use the backtick to do this or single quotes.

Example (gets a $ symbol into a string):

‘This is a cash statement’

But say you want to have a variable in the string as well as a non-variable:

“This is a `$cash statement. So is $($this).”

Foreach Loops and Current Item Variable

Some people like to use for loops. I like to use foreach loops.

In the below example we loop through a list of files and then get the contents and then loop through the contents looking for a specific line. You can see that I’m using the “$_” variable to get the current item in the array. This is a nested loop though. So the “$_” variable gets overwritten each time you go down a level (add a foreach). So you will want to set the current item to a variable first before moving down a level. It can be easy to forget to do this and hard to troubleshoot later on when you don’t do this.

GCI | foreach {

$cItem = $_

Get-content $cItem | foreach { if ($_ -match “mystringmatch”) { $_ }  }


Functions and parameters

You can use functions and pass variables to them many ways. Here are a few examples.

A simple function that takes two variables in and tries to subtract them. Can’t guarantee that it’ll work because we are not validating that the input is a integer.

Function Calc($var1, $var2) {

Return ($var1 – $var2)


Calc 2 1 #works

Calc “me” “my” #gives an odd error

Another example of the above but with some validation:

Function Calc([int]$var1, [int]$var2) {

Return [int]($var1 – $var2)


Calc 2 1 #works

Calc “me” “my” #gives convert error

A better example of using parameter sets:

Function Calc() {


param (

[int]$var1 = 0,

[int]$var2 = 0




($var1 – $var2)


calc -var1 2 -var2 1 #works

calc 2 1 #works

calc “blah” “blah” #gives convert error

Calc #works, as both variables are by default set to zero

To sum it up. Using parameter sets is highly recommended when writing code that people will be interacting with. It makes it much easier to visually see what parameters are getting what variables passed to them. But when writing scripts it may not be as necess

Posted in Documentation, IT | Tagged | Leave a comment

ADFS SQL Migration

You may want to move your back end ADFS dababases to a new SQL server. This guide shows you how.

This guide is intended for ADFS clusters where you have more than one ADFS server using MS SQL. There are other ways of setting up ADFS with WID (SQL Express). This guide is only for a shared MS SQL server.

Prep work:

  • Ensure you have your new ADFS SQL databases in place on the new SQL server. Just make a copy only backup and restore over the top of new blank databases on the new SQL server.
  • Ensure you have permissions setup in SQL for the new databases. Use the same permissions that were setup before.
  • IMPORTANT: Use the below commands to see what settings are currently in the ADFS servers.

$adfsSecurityTokenService = Get-WmiObject -namespace root/ADFS -class SecurityTokenService

Example Output: Data Source=<sqlservername>;Initial Catalog=AdfsConfigurationV3;Integrated Security=True;Min Pool Size=20

get-AdfsProperties | select artifactdbconnection

Example Output: Data Source=<sqlservername>;Initial Catalog=AdfsArtifactStore;Integrated Security=True;Min Pool Size=20


The trick here is that you need to run both command but the get/set-adfsproperties command only works when ADFS is running. The first WMI command will only work when ADFS is stopped. If you stop ADFS, run the first WMI command, and try to start ADFS it may fail to start. Then you can’t run the second command. So the order of these commands is very important.

How to switch it:

  1. Run the below commands while everything is running.
  2. The temp put one will fail. Ignore this.
  3. The set-adfsprops command should work (it won’t if adfs is off)
  4. Stop adfs services
  5. Run temp put again (should work now)
  6. Rename DBs on old server (to ensure you are no longer using them)
  7. Start services.
  8. Do this on both ADFS servers.

$adfsSecurityTokenService = Get-WmiObject -namespace root/ADFS -class SecurityTokenService
$adfsSecurityTokenService.ConfigurationdatabaseConnectionstring=”Data Source=<sqlservername>;Initial Catalog=AdfsConfigurationV3;Integrated Security=True;Min Pool Size=20″

Set-AdfsProperties –artifactdbconnection “Data Source=<sqlservername>;Initial Catalog=AdfsArtifactStore;Integrated Security=True;Min Pool Size=20”




Posted in Uncategorized | Leave a comment

PowerShell Certificate Request from Enterprise PKI CA Server

This command will allow you to quickly get a certificate automatically. This is very useful for automating deployments of IIS or other web services that require a certificate to function. It uses your windows EPKI servers to get the certificates. There are a few requirements though.


  1. You have access to the certificate templates
  2. You have your template setup to auto approve
  3. You have your template setup based on the web server template

Here is the set of commands. You move powershell to the local machine cert store (where IIS can get them and the type of template you are using would be stored). You then request the cert by template name. You can specify the subject name and other DNS names (note you can do a SANs cert here too). Once you have the cert the next command will set a friendly name for the cert (on this computer). The friendly name can be anything and will not transfer from computer to computer. It then outputs the thumbprint too.

Set-Location 'Cert:\LocalMachine\My'
$cert = Get-Certificate -Template "[cert_template_name_here]" -Url ldap: -SubjectName ("CN=" + "blah.blah.com") -DnsName "blah.blah.com", "blah", "tom.tom.com", "tom", "" -CertStoreLocation Cert:\LocalMachine\My
gci | where {$_.Thumbprint -eq $cert.Certificate.Thumbprint} | foreach { $_.FriendlyName = "my blah and tom cert" }
Posted in IT | Tagged , | Leave a comment

ADFS Important URLs

After setting up ADFS I frequently forget the URLs used by ADFS. It’s funny that they don’t really show you these after an install. But here they are and what they are used for.


This is the IDP initiatedsign on page. This is where you can access your relying party trusts. This is used when the relying part does not support SP. (You need to use your ADFS server website to login to their services)



This is your ADFs federated metadata. This data can be published to the internet so that your relying party trusts can read what properties you send over in claims and what you accept. It also publishes your public certificate data so when your ADFS server auto renews certificates other servers can read this data automatically and update their end too. This avoids issues with the cert renewal process (if they support it).



This URL can be used by internal users via the IDP page to log directly into another relying party trust. This way the don’t have to actually use the IDP page manually. You just enter the partner URL found in the ADFS console for your relying party trust.


Posted in IT, Uncategorized | Tagged | Leave a comment

Server 2016 and Windows Updates

You may need to force Windows Server 2016 to rescan for windows updates. It has changed slightly in Server 2016. So here is how:

$list1 = (Get-ADComputer -Filter {OperatingSystem -Like "*server 2016*"} -properties operatingsystem).Name | where {$_ -match "PROD"} 
$list1 | foreach {
PsExec.exe -s -d "\\$($_)" c:\windows\system32\UsoClient.exe startscan
PsExec.exe -s -d "\\$($_)" wuauclt /reportnow
Posted in IT, Uncategorized | Tagged | Leave a comment

New new disk drives to Windows VMs in VMWare

You may want to add a bunch of new drives to your servers all at once. In my case this was useful for installing a new agent on a large amount of servers. These commands can be used with PowerCLI and WinRM to add new disk drives to the VMs, format them, and mount them as a drive letter in Windows. Then you are all set to use them right away.

Power CLI Commands:

#gather list

#static way
$VMsList = "VM1","VM2"

#dynamic way
$VMsList = (Get-VM | where {$_.Name -match "WIN"}).Name

#add the new disk as 10GB, persistent, and EagerZeroedThick
$VMsList | foreach {
	Get-VM "$($_)" | New-HardDisk -CapacityGB 10 -Persistence persistent -StorageFormat EagerZeroedThick


Next up you must connect to each Windows VM via PowerShell WinRM and format and mount the drives. This will move the CD ROM drive to F: from D:, initialize all disks as GPT, and then partition them as the D: drive letter.

$VMsList | foreach {
	Invoke-Command -ComputerName "$($_)" {
		#move cdrom drive to F: drive from D: drive
		Get-WmiObject -Class Win32_volume -Filter "DriveLetter = 'd:'" |Set-WmiInstance -Arguments @{DriveLetter='F:'}

		#format all new disks
		Get-Disk | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "" -Confirm:$false

		#setup new Disks, labels, and drive letters
		Get-Disk | foreach {
			if (($_.Size -eq 10GB)) {
				Set-Partition -DriveLetter (($_ | Get-Partition).DriveLetter)[1] -NewDriveLetter "D"


Posted in IT, Uncategorized | Tagged , , | Leave a comment

Exchange CAL Licensing – Mobile Device Policy

You may be forced to use an Exchange Enterprise CAL if you customize an Active Sync / Mobile Device policy. These commands help you find how Exchange thinks you are licensed and how you can fix it.


# Collect license info

$a1 = Get-ExchangeServerAccessLicenseUser -LicenseName "Exchange Server 2013 Standard CAL"

$a2 = Get-ExchangeServerAccessLicenseUser -LicenseName "Exchange Server 2013 Enterprise CAL"

#total standard licenses (even enterprise counts here)

#total enterprise licenses 

#total standard only licenses
$a1.count - $a2.count


Now take a look at what your mobile device polcy(ies) look like settings wise.

Get-MobileDeviceMailboxPolicy | select AllowBluetooth,AllowBrowser,AllowCamera,AllowConsumerEmail,AllowDesktopSync,AllowInternetSharing,AllowIrDA,AllowRemoteDesktop,AllowStorageCard,AllowTextMessaging,AllowUnsignedApplications,AllowUnsignedInstallationPackages,AllowWiFi,ApprovedApplicationList,UnapprovedInROMApplicationList


Set those settings back to the defaults (allowed) and it’ll take care of your licensing issues. But be warned that it could cause warnings or issues on any Active Sync users. You should test this with a new policy first before just changing the policy everyone is using.


Posted in IT, Uncategorized | Tagged , | Leave a comment

Checklist to review when inheriting an Exchange Server

These are all the settings that you should look into when working with a new Exchange server that you are not familiar with. There seem to be a ton of settings that are left forgotten over time and may need to be updated as the business changes over time.

  1. Organization configuration
    1. Get-OrganizationConfig
  2. Organization Transport (sending/receiving message limits)
    1. Get-TransportConfig | Format-List MaxReceiveSize,MaxSendSize,MaxRecipientEnvelopeLimit
    2. https://docs.microsoft.com/en-us/exchange/mail-flow/message-size-limits?view=exchserver-2019
  3. Active Sync Organization Configruation
    1. Get-ActiveSyncOrganizationSettings | FL DefaultAccessLevel,AdminMailRecipients
  4. Exchange Servers
    1. Get-ExchangeServer
  5. Mailbox Databases
    1. If in DAG or not
      1. Get-MailboxDatabaseCopyStatus
    2. Size restrictions
      1. Get-mailboxDatabase | select Identity,ProhibitSendReceiveQuota,ProhibitSendQuota,IssueWarningQuota,RecoverableItemsQuota,DeletedItemRetention
  6. Mailboxes
    1. Individual size restrictions
      1. get-mailbox | where { ($_.ProhibitSendQuota -ne “Unlimited”) -or ($_.ProhibitSendReceiveQuota -ne “Unlimited”) -or ($_.RecipientLimits -ne “Unlimited”) -or ($_.MaxSendSize -ne “Unlimited”) -or ($_.MaxReceiveSize -ne “Unlimited”) } | select ProhibitSendQuota, ProhibitSendReceiveQuota, RecipientLimits, MaxSendSize, MaxReceiveSize
    2. Full access
      1. get-mailbox | Get-MailboxPermission | where { ($_.AccessRights -eq “FullAccess”) -and ($_.IsInherited -eq $false)}
  7. Exchange Apps
    1. Get-app
  8. Malware Filter
    1. get-malwarefilterpolicy | select Name, Action, IsDefault, *Notifications, *SenderAdminAddress
  9. Outlook Anywhere
    1. Get-OutlookAnywhere | select ExternalHostname, InternalHostname, ExternalClientAuthenticationMethod, InternalClientAuthenticationMethod, IISAuthenticationMethods
  10. Malware Scans (Exchange A/V)
    1. Get-MalwareFilterPolicy
    2. Get-MalwareFilteringServer | fl *
  11. Transport Rules
    1. Get-TransportRule
  12. Receive Connectors
    1. Get-ReceiveConnector | select Name, Enabled, Bindings, AuthMechanism, RequireTLS, MaxMessageSize, MessageRateLimit, MaxRecipientsPerMessage, RemoteIPRanges
  13. Send Connectors
    1. Get-sendConnector | select Name, Enabled, RequireTLS, MaxMessageSize, AddressSpaces
  14. Outlook Web App Policies
    1. Get-OwaMailboxPolicy
  15. Public Folders
    1. Get-PublicFolder
Posted in IT | Tagged , | Leave a comment

Home Built Uninterruptible Power Supply (UPS)

A UPS can be a valuable tool to protect your electronics against power surges, brown outs, and black outs. I’ve always tried to use and maintain one at home for my own equipment. But they can be expensive and the batteries don’t seem to last long (duration of charge and overall lifetime). So I decided to build one of my own.

WARNING: I did some research online and it seemed pretty simple to build one. I’m by no means an expert in electronics so use this information at your own risk. There is danger in working with these types of electronics. You can potential start a fire or short out your equipment if you wire things improperly.

A few tips before we start:

  1. If you don’t understand completely what you are doing please stop
  2. DC wiring must never be reversed (positive to negative). This will usually destroy electronics. AC is different as it alternates in voltage both ways.
  3. Be sure your equipment is off when working on it. Use a multi meter to test it. Be sure to be set to AC or DC.
  4. Be very sure to use the correct gauge wiring when connecting components. Wires that can’t handle a high load will short out and burn up. This can cause a fire.
    1. Use online resources like wire guides to figure out what gauge you need
    2. The length of the wire is very important too. Short and direct runs are best.
  5. Don’t trust all information you see online. Including what you are reading now. This is a learning experience for me and I’ll be upgrading my UPS as I find flaws.

I decided on a pure sine wave inverted as I had issues with one that wasn’t a pure wave, it was a simulated sine wave. The difference is that some electronics don’t handle how quickly and sharply the voltages go up and down on the AC side. I had purchased a 2000 watt inverter and had issues powering up an old computer. It also had trouble with a electric fan. The fan is a great example showing how AC motors need the smooth sine wave increase and decrease in voltage to start and stop. (Think of moving a car with block wheels versus round wheels)

Parts List:








Wiring Diagram:

This system is pretty easy to build. You use the power inverter to power all of your AC equipment. The Iota 55 amp charger will charge the battery and power the inverter as long as it has power from the power grid. The battery is big enough to output enough amperage for the inverter (1,000 watts / 12 volts = 83 amps). The iota charger can’t supply enough power to the inverter if we have the maximum load on the inverter but this was OK with me as my power load is pretty small.


I’m using about 110 watts powering a few HP switches, cable modem, 2 PoE access points, a 4 disk enclosure, a Intel NUC, as well as a raspberry pi. I’m able to measure this with my bayite display. Then I was able to measure the load from my iota charger with a kill-a-watt AC power measuring device. The kill-a-watt came out to about 150 watts. So I’m seeing some loss in the conversion of AC to DC and this is normal. I’m sure there is even more loss from AC to DC and back to AC through the inverter. But this is the cost of a system like this.


Knowing my usage I estimated I would get about 8 hours of run time. A 100 amp/hour battery can output 100 amps (at 12 volts) for 1 hour or 10 amps for 10 hours. Since I’m using about 8 amps and you don’t want to discharge too low, i’d estimate 8 hours.


I did purchase a deep cycle battery. The difference between a regular sealed lead-acid battery and a deep cycle sealed lead-acid battery is the deep cycle will allow for a larger discharge and go down to a lower voltage without ruining the battery. If I remember correctly you don’t want to go below 10.8 volts charge on a lead-acid battery otherwise you will damage it. Speaking of this the iota charger had a separate accessory that can help keep batteries maintained properly. I had purchase this and installed it to help maintain my battery.


As for expansion I can add another battery to my setup to double my run time. You would want to connect it in parallel, not a series. In parallel the voltage stays the same and in a series it would double (this could cause damage to the inverter if it can’t accept much over 12 volts).


The gauge of the wire i used was very important. The lower the number the more amperage it will support but keep in mind the length is very important too. Too long and you can cause the same issue as having to thin of a wire. I tried to keep everything under 3 feet.



Posted in IT, Recent Projects | Leave a comment