Tim D'Annecy


#Windows #Microsoft #Powershell

Exchange has the ability to limit sending permissions on Distribution Groups. Finding which Distribution Groups have user sending permissions assigned to them can be very time consuming using the portal.

To make it quicker, you can list the accounts that have the ability to send to a specific distribution group using PowerShell.

$FormatEnumerationLimit=-1 # This allows the property to be expanded in format-table


Get-DistributionGroup -Identity XXX | Format-table -Wrap -AutoSize -property name,acceptmessagesonlyfrom # Change XXX to the Distribution Group SAM account name



I company I work for just completed an O365 tenant migration. After email had been moved to a new Exchange tenant, we noticed that users continued to use their Outlook apps on their phones. They were also continuing to chat on the old tenant's Teams and were using all of the Office apps on the web using their cached logins.

This caused a headache. Some users were in the new environment with the correct domain—others didn't notice the “@onmicrosoft.com” and were having issues with SSO apps that had been migrated.

We needed to revoke all of the cached login tokens force log out all users. This quick Powershell command did the trick:


$users = Get-AzureAdUser -All $true

$users | foreach {
    Revoke-AzureADUserAllRefreshToken -objectID $_.objectID

After this runs, all users will be required to log in again. This forced them to go into the new tenant and solved our SSO issues.


#Windows #Powershell

Here's a script that I'm using to roll out the Quest ODM agent on PCs in my environment that do not have access to the LAN. I used Atera Service Desk to deploy it for internet-only installation.

Agent install and hosting

This script requires that you download the Device Agent from the Quest Migration for Active Directory downloads page:

Once that's complete, you will need to upload the file to a publicly accessible file share. I used Azure Files to create a storage container and provide direct access to the file. This URI will be pointed to in the script, so you cannot use something like OneDrive or SharePoint without special configuration.


Before running this script, you will need to change the following XXX values:

Function Invoke-ODM_Agent_Install {

	$InstallCheck = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Quest\On Demand Migration For Active Directory\ODMAD_AD" -ErrorAction SilentlyContinue
  If ($null -eq $InstallCheck) {
    Write-Host 'Downloading ODM agent.' 
    $QuestODMMSIURI = 'XXX' # Change to use your own Azure Files URI
    $QuestODMdest = 'C:\Temp\ODM\QuestODM.msi' 
    Invoke-WebRequest -Uri $QuestODMMSIURI -OutFile (New-Item -Path $QuestODMdest -Force)

    Write-Host 'Installing ODM agent.'
    cmd /c "msiexec.exe /I `"C:\Temp\ODM\QuestODM.msi`" /qn SERVICEURL=https://us.odmad.quest-on-demand.com/api/ADM AUTHKEY=XXX" # Change to use your own authkey from Quest
    Write-Host 'Finished installing ODM agent.' 
  ElseIf ($null -ne $InstallCheck) {
    Write-Host 'ODM agent is already installed.'
  Else {
    Write-Host 'ERROR!' 




#Windows #Powershell #Meraki

I wrote up a quick and dirty Powershell script today that adds a split-tunnel VPN connection, asks the user for connection info, dials the connection, then configures static routes.

# Add-MerakiVPN.ps1
# Creates a split-tunnel VPN connection and adds static routes.
# Tim D'Annecy 2021-09-08

function Add-MerakiVPN {
    $ServerAddress = 'blahblahblah.dynamic-m.com' # Change this value to match your Meraki hostname
    $ConnectionName = 'Meraki VPN'
    $PresharedKey = 'blah' # Change this value

    Add-VpnConnection `
        -Name $ConnectionName `
        -ServerAddress $ServerAddress `
        -TunnelType L2tp `
        -EncryptionLevel Optional `
        -SplitTunneling `
        -AllUserConnection `
        -L2tpPsk $PresharedKey `
        -AuthenticationMethod Pap, MSChapv2 `

    $StaticRoutes = @(
        '', # Change these to match your internal subnets

    try {
        rasphone.exe -d $ConnectionName
        Start-Sleep -Seconds 30
        $StaticRoutes | foreach {
            New-NetRoute -DestinationPrefix $_ -InterfaceAlias $ConnectionName
    catch {
        Write-Error 'There was an error adding the VPN connection'


#Powershell #Windows

This one-liner imports a CSV formatted with at least the header Name and a list of user names. It outputs to a CSV with the SamAccountName and Enabled properties.

import-csv ".\in.csv" | ForEach-Object  { Get-ADUser -Identity $_.Name -Property samaccountname,enabled } | Select-Object -Property samaccountname,enabled | Export-Csv -Path ".\out.csv" -NoTypeInformation -Append


#Windows #Powershell

I found this post on Reddit and wanted to save the command for my notes.

Running this command in Powershell will give you the PC's currently connected SSID. This is handy for troubleshooting network issues when connected remotely through a PSSession.

netsh wlan show interfaces | select-string SSID

#powershell #Exchange

If you're using Microsoft Exchange Online, there's no way to currently see when a Mail Contact was created on the web dashboard.

I wanted to know when an address was added as a Mail Contact in one of our tenants, but I also wasn't able to get an audit trail using the Microsoft Compliance center.

As a workaround, this Powershell command will give the basic info for “WhenCreated”.

Get-Recipient -RecipientTypeDetails MailContact -ResultSize Unlimited | sort WhenCreated | select Name,Alias,WhenCreated

#Windows #Powershell

# Uninstall-UmbrellaRoamingClient
# Tim D'Annecy 2021-06-04
# Removes Cisco Umbrella Roaming Client.

function Uninstall-UmbrellaRoamingClient {
    param ()
    $UmbrellaStatus = (Get-Service -Name 'Umbrella_RC').Status

    If ( $UmbrellaStatus = 'Running' ) {
        Write-Host 'Roaming Client was running. Stopping now.'
        Stop-Service -Name 'Umbrella_RC' -Force
        try {
            #wmic Product where "name='Umbrella Roaming Client'" call uninstall
            (Get-WmiObject -Class win32_product -Filter "Name='Umbrella Roaming Client'").Uninstall()
            Write-Host 'Umbrella successfully uninstalled using wmic call.'
            Start-Sleep -Seconds 5
        catch {
            Write-Host 'Umbrella could not be uninstalled.'
    else {
        Write-Host 'Roaming Client was not running. Exiting.'


#Windows #Powershell

While most organizations are moving files to cloud-based solutions, I'm working for a client who wants to keep everything in-house. In this environment, some users had a private folder under a previous drive letter mapping, others didn't have anything at all.

ADUC screenshot of Profile tab

I created this quick and dirty Powershell script to automate the cleanup process for existing users.

This script gets all users from AD, sets their HomeDirectory attribute in AD to a fileshare and mounts it on the U: drive, and creates private folders with the correct ACL permissions.

# Assign-PrivateDrive
# Tim D'Annecy 2021-07-09
# Creates shared drive folder with correct permissions and sets AD property.

function Assign-PrivateDrive {

    Import-Module ActiveDirectory
    $driveLetter = 'U:'
    $PrimaryDC = 'sample.dc'
    $activeOUDN = 'OU=Users,DC=sample,DC=local'

    $users = Get-ADUser -Filter { Enabled -eq $true }  -SearchBase $activeOUDN -Properties * 
    foreach ($user in $users) {
        $UserSAM = $user.SamAccountName
        $fullPath = "\\samplefs\share\Private\{0}" -f $UserSAM
        Set-ADUser -Server $PrimaryDC -Identity $UserSAM -HomeDrive $driveLetter -HomeDirectory $fullPath 
        if (!(Test-Path -Path $fullPath )) {
            Write-Host "Creating directory at $fullPath"
            New-Item -path $fullPath -ItemType Directory
            $acl = Get-Acl $fullPath

            $FileSystemRights = [System.Security.AccessControl.FileSystemRights]"Modify"
            $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow
            $InheritanceFlags = [System.Security.AccessControl.InheritanceFlags]"ContainerInherit, ObjectInherit"
            $PropagationFlags = [System.Security.AccessControl.PropagationFlags]"InheritOnly"
            $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule ("sample\$UserSAM", $FileSystemRights, $InheritanceFlags, $PropagationFlags, $AccessControlType)
            Write-Host 'Setting permissions on folder.'
            Set-Acl -Path $fullPath -AclObject $acl 
        else {
            Write-Host "Skipping $($user.Name) . Directory found at $fullPath"


#Windows #Powershell

I found a great way to have something similar to the GNU program top on Windows in Powershell in this Stack Exchange post.

While(1) {ps | sort -des cpu | select -f 15 | ft -a; sleep 1; cls}

It's not the exact same app, but it gives you the top 15 running processes sorted by CPU load.