Last Logon Date AD Users


The “lastLogon” Active Directory attribute is a property that is not replicated throughout the Domain Controllers.  This attribute is stored on the domain controller that received the authentication request and updated its property accordingly.  Because of this behavior, you have have experienced issues where Domain Controllers in other sites have old or empty lastLogon information.  This makes it difficult to be sure an account is truly inactive without verifying on each Domain Controller.  This can be a problem for environments that have tens or hundreds of Domain Controllers across its enterprise.

I was faced with a problem of writing a script, and like many, developed a seemingly simple solution.  The problem was the runtime was very long for the amount of Domain Controllers / User accounts for the environment.  I sought out to find the solution to this exceedingly long runtime, and I finally did.

This PowerShell will query each of your Domain Controllers only once and produce the most recent logon date/time.

To protect the informaiton about my directory, lets say my environment is between 5 and 10 Domain Controllers, with between 15 – 25k user accounts.  My runtime is between 4-5 minutes for the entire directory!  Which is a far cry faster than the hours other methods produce.

By default, this script will return the entire directory ($Username=*) and save to a CSV ($FileName).  You may call the function with a -Username to return a single account, and this will output on screen instead of a file.

    The purpose of this PowerShell script is to collect the last logon  
    for user accounts on each DC in the domain, evaluate, and return the 
    most recent logon value. 
        Author:   Jeremy Reeves 
        Modified: 02/14/2018 
        Notes:    Must have RSAT Tools if running on a workstation 
Note: Added enabled true/false to output
Import-Module ActiveDirectory 
function Get-ADUsersLastLogon($Username="*") { 
    $FilePath_Prefix = "C:\temp\UserLastLogon-" 
    function Msg ($Txt="") { 
        Write-Host "$([DateTime]::Now)    $Txt" 
    #Cycle each DC and gather user account lastlogon attributes 
    $List = @() #Define Array 
    (Get-ADDomain).ReplicaDirectoryServers | Sort | % { 
        $DC = $_ 
        Msg "Reading $DC" 
        $List += Get-ADUser -Server $_ -Filter "samaccountname -like '$Username'" -Properties LastLogon | Select samaccountname,lastlogon,enabled,@{n='DC';e={$DC}} 
    Msg "Sorting for most recent lastlogon" 
    $LatestLogOn = @() #Define Array 
    $List | Group-Object -Property samaccountname | % { 
        $LatestLogOn += ($_.Group | Sort -prop lastlogon -Descending)[0] 
    if ($Username -eq "*") { #$Username variable was not set.    Running against all user accounts and exporting to a file. 
        $FileName = "$FilePath_Prefix$([DateTime]::Now.ToString("yyyyMMdd-HHmmss")).csv" 
        try { 
            $LatestLogOn | Select samaccountname, lastlogon, @{n='lastlogondatetime';e={[datetime]::FromFileTime($_.lastlogon)}}, Enabled, DC | Export-CSV -Path $FileName -NoTypeInformation -Force 
            Msg "Exported results. $FileName" 
        } catch { 
            Msg "Export Failed. $FileName" 
    } else { #$Username variable was set, and may refer to a single user account. 
        if ($LatestLogOn) { $LatestLogOn | Select samaccountname, @{n='lastlogon';e={[datetime]::FromFileTime($_.lastlogon)}}, Enabled, DC | FT } else { Msg "$Username not found." } 

Get Office 365 users with a specific license type via Powershell

It can sometimes be useful to get a list of Office 365 users with a specific license type via PowerShell. Instead of logging into the Office 365 portal and using a filtered view in the admin center, you can do it straight from the command line.

  • Connect to Office 365 via Powershell. If this cmdlet doesn’t work for you, follow this quick guide for instructions on installing the required PowerShell module.


  • Run Get-MsolAccountSku to get a list of the current licenses in your Office 365 tenant. Make a note of the AccountSkuId value for the license you want to filter on.


Get-MsolAccountSku Information
  • Now you can edit this short script to get the users matching that license. In this case, we’re getting users with the EnterprisePremium license.

Get-MsolUser | Where-Object {($_.licenses).AccountSkuId -match "EnterprisePremium"}

Replace EnterprisePremium with the AccountSkuID you’re trying to filter by. Since we’re using the -match operator we don’t need to type the entire AccountSkuID, we can just type enough of it to ensure that we’re only retrieving that specific one.

The script can be tweaked for specific use

Get-MsolUser -MaxResults 100000 | Where-Object {($_.licenses).AccountSkuId -match "EnterprisePremium"} | ft UserPrincipalName, IsLicensed | Out-File c:\temp\E3.csv

Get-MsolUser With Specific Office 365 License

Export these users to a text document

You can export these users to a text document using the Out-File cmdlet.

Get-MsolUser | Where-Object {($_.licenses).AccountSkuId -match "EnterprisePremium"} | Out-file C:\temp\EnterprisePremiumUsers.csv


Create a Dynamics NAV NST Instance with Powershell

How to create a NST instance with a powershell script:


Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted

Import-Module "C:\Program Files\Microsoft Dynamics NAV\100\Service\NavAdminTool.ps1" -DisableNameChecking

# Set varaibles for NST
$NAVServiceInstance = 'instance-name'
$DatabaseName = 'database-name'
$DatabaseServer = 'database-server'
$NAVServiceUser = 'service-account'
$NAVServiceUserPW = 'service-account-password'

$DefaultTimeZone = 'Server Time Zone'
$MaxUploadSize = 2047
$EnableTaskScheduler = 'False'


$SOAPMaxMsgSize = '5120'

$ChangeTimeout = $FALSE
$IdleClientTimeout = '01:30:00'

$NASArgument = 'JOBQUEUE'
$NASCodeunit = '450'
$NASMethod = ''
$DefaultCompany = ''

$LanguageID = '1044'
$Language = 'no-NO'

# NAV Service Account
$secpasswd = ConvertTo-SecureString $NAVServiceUserPW -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($NAVServiceUser, $secpasswd)

##Creating NST
New-NAVServerInstance $NAVServiceInstance -DatabaseName $DatabaseName `
                                          -DatabaseServer $DatabaseServer `
                                          -ManagementServicesPort 7045 `
                                          -ClientServicesPort 7046 `
                                          -ODataServicesPort 7048 `
                                          -SOAPServicesPort 7047 `
                                          -ServiceAccount user `
                                          -ServiceAccountCredential $mycreds `

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ServicesDefaultTimeZone `
                             -KeyValue $DefaultTimeZone `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ClientServicesMaxUploadSize `
                             -KeyValue $MaxUploadSize `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName EnableTaskScheduler `
                             -KeyValue $EnableTaskScheduler `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ServicesUseNTLMAuthentication `
                             -KeyValue $UseNTLM `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName SOAPServicesMaxMsgSize `
                             -KeyValue $SOAPMaxMsgSize `
                             -WarningAction SilentlyContinue
##Creating NAS
IF ($IsNAS) {  
Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ClientServicesEnabled `
                             -KeyValue FALSE `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName SOAPServicesEnabled `
                             -KeyValue FALSE `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ODataServicesEnabled `
                             -KeyValue FALSE `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ManagementServicesEnabled `
                             -KeyValue FALSE `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName NASServicesStartupArgument `
                             -KeyValue $NASArgument `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName NASServicesStartupCodeunit `
                             -KeyValue $NASCodeunit `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName NASServicesStartupMethod `
                             -KeyValue $NASMethod `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ServicesDefaultCompany `
                             -KeyValue $DefaultCompany `
                             -WarningAction SilentlyContinue

##Set Idle Client Timeout
IF ($ChangeTimeout) {  
Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ClientServicesIdleClientTimeout `
                             -KeyValue $IdleClientTimeout `
                             -WarningAction SilentlyContinue

##Set Services Language
IF ($IsNOR) {  
Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName DefaultLanguageId `
                             -KeyValue $LanguageID `
                             -WarningAction SilentlyContinue

Set-NAVServerConfiguration   -ServerInstance $NAVServiceInstance `
                             -KeyName ServicesLanguage `
                             -KeyValue $Language `
                             -WarningAction SilentlyContinue

#Add NAVService to portsharing and start Service.
#Import-Module $PSScriptRoot\NAVServerInstancePortSharing.ps1
#Enable-NAVServerInstancePortSharing $NAVServiceInstance


Synching Microsoft NPS (RADIUS) servers


Use the following powershell script to sync microsoft NPS (radius) servers.

Export-NpsConfiguration -Path C:\TEMP\LocalNPSExportedConfig.xml

$CurrentServerNPS = $env:computername

$NPServers = Get-ADGroupMember "RAS and IAS Servers"
$NPServers | ForEach-Object {

	$NPServerName = $_.Name

	if ($NPServerName -ne $CurrentServerNPS) {
		copy-item Export-NpsConfiguration -Path  \\$NPServerName\C$\TEMP\LocalNPSExportedConfig.xml
		Invoke-Command -ComputerName $NPServerName -ScriptBlock {Export-NPSConfiguration -Path C:\TEMP\BackupNPSExportedConfig.xml
		Invoke-Command -ComputerName $NPServerName -ScriptBlock {Import-NPSConfiguration -Path C:\TEMP\LocalNPSExportedConfig.xml


Quickly list all mailboxes to which a particular user has access

From Vasil Michev



  • List all mailboxes to which a particular user has Full Access permissions:

PS C:\> Get-Mailbox | Get-MailboxPermission -User vasil

Identity             User                 AccessRights

——–             —-                 ————

HuKu                 Vasil Michev         {FullAccess}

retail               Vasil Michev         {FullAccess}

sharednew            Vasil Michev         {FullAccess}

testplan2            Vasil Michev         {FullAccess}

WC                   Vasil Michev         {FullAccess}

  • List all shared/user/room/whatever mailboxes to which particular user has Full Access permissions:

PS C:\> Get-Mailbox -RecipientTypeDetails UserMailbox,SharedMailbox -ResultSize Unlimited | Get-MailboxPermission -User vasil

Identity             User                 AccessRights

——–             —-                 ————

HuKu                 Vasil Michev         {FullAccess}

retail               Vasil Michev         {FullAccess}

sharednew            Vasil Michev         {FullAccess}

testplan2            Vasil Michev         {FullAccess}

  • List all mailboxes to which members of a particular security group have access:

PS C:\> Get-Mailbox | Get-MailboxPermission -User secgrp

Identity             User                 AccessRights

——–             —-                 ————

Bathroom             secgrp               {FullAccess}

  • List all mailboxes to which a user has Send As permissions:

PS C:\> Get-Mailbox | Get-RecipientPermission -Trustee vasil

Identity                            Trustee                             AccessControlType                   AccessRights

——–                            ——-                             —————–                   ————

sharednew                           Vasil Michev                        Allow                               {SendAs}

  • List all user mailboxes to which members of a particular security group have access:

PS C:\> Get-Mailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited | Get-RecipientPermission -Trustee secgrp

Identity                            Trustee                             AccessControlType                   AccessRights

——–                            ——-                             —————–                   ————

HuKu                                secgrp                              Allow                               {SendAs}

  •  List all mailboxes to which a particular security principal has Send on behalf of permissions:

PS C:\> Get-Mailbox | ? {$_.GrantSendOnBehalfTo -match “vasil”}

Name                      Alias                ServerName       ProhibitSendQuota

—-                      —–                ———-       —————–

Bathroom                  bathroom             amspr03mb084     49.5 GB (53,150,220,288 bytes)

WC                        WC                   dbxpr03mb096     9.5 GB (10,200,547,328 bytes)

Find Mailbox and Online Archive size

Script to connect to Office 365 and collect the last logged on date/time, mailbox size and item count for each mailbox and archive mailbox for every user within your organization.

The following script was specifically written for a client recently which I thought would be good to share with the wider community. This will connect to Office 365 and collect the last logged on date/time, mailbox size and item count for each mailbox and archive mailbox for every user within your organization.


Those users without an archive mailbox will be returned as ‘No Archive’


For more information about this script please see the source at:

$Office365Credentials = Get-Credential
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "" -Credential $Office365Credentials -Authentication Basic -AllowRedirection
Import-PSSession $session
Write-Host "Gathering Stats, Please Wait.."
$Mailboxes = Get-Mailbox -ResultSize Unlimited | Select UserPrincipalName, identity, ArchiveStatus
$MailboxSizes = @()
foreach ($Mailbox in $Mailboxes) {
                $ObjProperties = New-Object PSObject
                $MailboxStats = Get-MailboxStatistics $Mailbox.UserPrincipalname | Select LastLogonTime, TotalItemSize, ItemCount
                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "UserPrincipalName" -Value $Mailbox.UserPrincipalName
                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "Last Logged In" -Value $MailboxStats.LastLogonTime
                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "Mailbox Size" -Value $MailboxStats.TotalItemSize
                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "Mailbox Item Count" -Value $MailboxStats.ItemCount
                if ($Mailbox.ArchiveStatus -eq "Active") {
                                $ArchiveStats = Get-MailboxStatistics $Mailbox.UserPrincipalname -Archive | Select TotalItemSize, ItemCount
                                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "Archive Size" -Value $ArchiveStats.TotalItemSize
                                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "Archive Item Count" -Value $ArchiveStats.ItemCount
                else {
                                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "Archive Size" -Value "No Archive"
                                Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name "Archive Item Count" -Value "No Archive"
                $MailboxSizes += $ObjProperties
$MailboxSizes | Out-GridView -Title "Mailbox and Archive Sizes"
Get-PSSession | Remove-PSSession


Customizing the RDS title “Work Resources” using PowerShell on Windows Server 2012



If you are running multiple Connection Brokers in High Availability mode, you must run this against the active broker. You can use this command:

Set-RDWorkspace -Name “XYZ Applications” -ConnectionBroker (Get-RDConnectionBrokerHighAvailability).ActiveManagementServer


Otherwise you can use:

set-RDWorkspace -Name “XYZ Applications” -ConnectionBroker

VMware get inventory


To export a list of usefull information about your environment use the following oneliner to export it to a CSV.

Get-VM | Select Name, PowerState, NumCPU, MemoryMB, ProvisionedSpaceGB, UsedSpaceGB, @{N=”ESX Host”;E={Get-VMHost -VM $_}}, @{N=”Datastore”;E={Get-Datastore -VM $_}}, @{N=”Guest OS”;E={Get-VMGuest -VM $_}} | Export-CSV d:\temp\report.csv



Copy WSUS approvals between Groups


Run the script below, then call it using the following syntax:

Copy-WsusGroupApprovals -WsusServerFqdn -SourceGroupName "OldServers" -TargetGroupName "NewServers"

You can optionally specify a port, the default being 8530. You can also specify to use a secure connection. The group names are both case sensitive though.

# ----------------------------------------------------------------------------------------------------------
# PURPOSE:    WSUS - Copy Approvals from one Group to another Group
# VERSION     DATE         USER                DETAILS
# 1           21/01/2016   Craig Tolley        First Version
# ----------------------------------------------------------------------------------------------------------

# Copies all approvals from the specified source group to the specified destination group. 
# Group names are case sensitive. 
# Unless specified the default WSUS port of 8530 will be used to connect. 
function Copy-WsusGroupApprovals


    [Int]$WsusServerPort = 8530,

    [Boolean]$WsusServerSecureConnect = $false,



    # Load the assembly required
        Write-Error "Unable to load the Microsoft.UpdateServices.Administration assembly: $($_.Exception.Message)"


    # Attempt the connection to the WSUS Server
        $WsusServer = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($WsusServerFqdn, $WsusServerSecureConnect, $WsusServerPort)
        Write-Error "Unable to connect to the WSUS Server: $($_.Exception.Message)"

    # Get all of the Wsus Groups, and check that the specified source and destination groups exist
    $Groups = $WsusServer.GetComputerTargetGroups()
    If ($Groups.Name -notcontains $SourceGroupName -or $Groups.Name -notcontains $TargetGroupName)
        Write-Error "Source or Destination group names cannot be found in the list of groups on the WSUS Server. Group names are case sensitive. Please check your names."
    $SourceGroupObj = $Groups | Where {$_.Name -eq $SourceGroupName}
    $TargetGroupObj = $Groups | Where {$_.Name -eq $TargetGroupName}

    # Get all of the updates on the server
    Write-Progress -Activity "Getting Details of all updates"
    $Updates = $WsusServer.GetUpdates()
    # Go through each of the updates. If the update has an approval for the source group, then create an approval for the destination group. 
    $i = 0
    $Approved = 0
    ForEach ($Update in $Updates)
        $i ++
        Write-Progress -Activity "Copying update approvals" -PercentComplete (($i/$($Updates.Count))*100) -Status "$i of $($Updates.Count)"
        if ($Update.GetUpdateApprovals($SourceGroupObj).Count -ne 0 -and $Update.GetUpdateApprovals($TargetGroupObj).Count -eq 0)
            Write-Host ("Approving {0} for {1}" -f $Update.Title, $TargetGroupObj.Name)
            $Update.Approve('Install',$TargetGroupObj) | Out-Null
            $Approved ++
    Write-Progress -Activity "Copying update approvals" -Completed

   Write-Output ("Approved {0} updates for target group {1}" -f $Approved, $TargetGroupName)