Comparing Lync Policies – or How to Flip Just About Any Array of Hashtables in PowerShell

If you are reading this blog and can read German, I don’t need to tell you about msxfaq.de, former Exchange and now Lync MVP Frank Carius’ online (but not very alphabetical) encyclopedia of Exchange and Lync – it probably gets more page views in a day than this blog ever has. Even if you cannot read German, you have still probably run into it when searching for Exchange or Lync topics and then seriously wished you could read German – machine translation only goes so far.

Anyhow, one of the most helpful things he’s put out there and that I use all the time is a Swap-Table script. I wasn’t able to turn it up with “flip table in PowerShell” or “pivot PowerShell table” or any of several variations, so this is a little attempt to make that wonderful file findable for the English-speaking world. Scroll to the bottom and look for the “Code” section. You can make it a function in your PowerShell profile by putting the contents of that text file inside the curly braces {} of the following (code not posted here because plagiarism is evil):

function Swap-Table {
# contents of swap-table.1.0.ps1 go here

}

It has been particularly useful for comparing ClientPolicies and ConferencingPolicies in Lync, as ClientPolicy has over 70 attributes! Once you have the function in your session and you’re connected to Lync Management Shell, it works like this:

Get-CsClientPolicy | Swap-Table | Out-GridView
Advertisements

PowerShell Summit Europe 2014 – Registration Ends September 10!

PowerShell, in my not quite humble opinion, is the best thing Microsoft has done in the past decade, with the second best being Lync. It is mere coincidence that my professional life currently revolves around both.

So, if you are a cheap flight away from Amsterdam, you have until September 10 to join several PowerShell legends like Jeffrey Snover, Don Jones, Richard Siddaway, Tobias Weltner and Steve Murawski, as well as about 60 regular PowerShell enthusiasts like me for three intense days at a price far lower than a regular Microsoft course at some random training center.

The summit starts September 29 (Monday) and goes through October 1, with most people arriving (and hanging out) Sunday evening.

Registration and more summit info: http://powershell.org/wp/community-events/summit/

 

NetLogon.log: Control what goes in, and get what you want out

Or, when you want some, but not all, of what Netlogon.log has to offer – especially when trying to track down the source of a user account’s lockouts or find subnets that haven’t been put into an Active Directory site yet.

At one point, we had Netlogon turned up to 11 on all our domain controllers – that is, DBFlag is set to 0x2080ffff, just like this TechNet article and everything else you see at first glance on the Internet has it. That quick looks makes it look like your options are EVERYTHING!!1!1 or nothing. Since that one Knowledge Base article literally IS the only place on the Internet where these flags are listed in detail, and Microsoft has recently started featuring the automated “Fix It” solution, here is the info for posterity (and convenience),
nicely formatted into tables.

Data Source: http://support.microsoft.com/kb/109626
Formatting into tables: Amanda Debler (me)
All descriptions as-is from Microsoft.

Basic Netlogon Flags

Flag Name Value Description
NL_INIT 0x00000001 Initialization
NL_MISC 0x00000002 Misc debug
NL_LOGON 0x00000004 Logon processing
NL_SYNC 0x00000008 Synchronization and replication
NL_MAILSLOT 0x00000010 Mailslot messages
NL_SITE 0x00000020 Sites
NL_CRITICAL 0x00000100 Only real important errors
NL_SESSION_SETUP 0x00000200 Trusted Domain maintenance
NL_DOMAIN 0x00000400 Hosted Domain maintenance
NL_2 0x00000800
NL_SERVER_SESS 0x00001000 Server session maintenance
NL_CHANGELOG 0x00002000 Change Log references
NL_DNS 0x00004000 DNS name registration

Verbose Netlogon Flags

Flag Name Value Description
NL_WORKER 0x00010000 Debug worker thread
NL_DNS_MORE 0x00020000 Verbose DNS name registration
NL_PULSE_MORE 0x00040000 Verbose pulse processing
NL_SESSION_MORE 0x00080000 Verbose session management
NL_REPL_TIME 0x00100000 replication timing output
NL_REPL_OBJ_TIME 0x00200000 replication objects get/set timing output
NL_ENCRYPT 0x00400000 debug encrypt and decrypt across net
NL_SYNC_MORE 0x00800000 additional replication dbgprint
NL_PACK_VERBOSE 0x01000000 Verbose Pack/Unpack
NL_MAILSLOT_TEXT 0x02000000 Verbose Mailslot messages
NL_CHALLENGE_RES 0x04000000 challenge response debug
NL_SITE_MORE 0x08000000 Verbose sites

Netlogon Control Flags

Flag Name Value Description
NL_INHIBIT_CANCEL 0x10000000 Don’t cancel API calls
NL_TIMESTAMP 0x20000000 TimeStamp each output line
NL_ONECHANGE_REPL 0x40000000 Only replicate one change per call
NL_BREAKPOINT 0x80000000 Enter debugger on startup

Right now, I just want a clear look at account lockouts and subnetless IPs, even on our busiest DCs. A very busy DC can blow through a 100MB log file allowance in a few hours, and even with Netlogon.bak, collection and filtering would have to happen several times a day to make sure we see all the bad logons.

“But why would you want to capture LESS diagnostic information?!?” Because heavy logging can cause its own problems – read and think about this article from the Directory Services team before you implement “All Logging, All The Time”. It explains how to enable (and disable) logging for all facets of Directory Services.

As a compromise, I’m just going to turn off all those ‘[SITE]’ messages, since they are most of the entries in netlogon.log, and don’t provide any information I need right now. So, 0x2080ffff - 0x00000020 = 0x2080ffdf. I tried the nice .NET/PowerShell way, but it failed against a 2003 server. Back to the old-fashioned way – still possible from within a PowerShell script:

(all one line) reg add "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v DBFlag /t REG_DWORD /d 0x2080ffdf /f

Make sure that your log file is the size you want it to be (in this case, 100 megabytes):

(all one line)reg add "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v MaximumLogFileSize /t REG_DWORD /d 100000000 /f

And to check our work…

(all one line) reg query "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v DBFlag

(all one line) reg query "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v MaximumLogFileSize

Finish up by stopping and starting the Netlogon service, again, the old-fashioned way with NT Service Controller. You need to use sc.exe in PowerShell, because sc is an alias for Set-Content:

sc.exe \\$computerName stop netlogon

sc.exe \\$computerName start netlogon

Here’s a table of the result codes to interpret the [LOGON] entries (from Section “Netlogon Log File Error Codes” http://technet.microsoft.com/en-us/library/cc776964.aspx, copied in case that particular link goes away):

Log Code Description
0x0 Successful login
0xC0000064 The specified user does not exist
0xC000006A The value provided as the current password is not correct
0xC000006C Password policy not met
0xC000006D The attempted logon is invalid due to a bad user name
0xC000006E User account restriction has prevented successful login
0xC000006F The user account has time restrictions and may not be logged onto at this time
0xC0000070 The user is restricted and may not log on from the source workstation
0xC0000071 The user account’s password has expired
0xC0000072 The user account is currently disabled
0xC000009A Insufficient system resources
0xC0000193 The user’s account has expired
0xC0000224 User must change his password before he logs on the first time
0xC0000234 The user account has been automatically locked

The most important ones to distinguish between are 0xC000006A (bad password was entered this time – these ARE the droids you’re looking for) and 0xC0000234 (a logon attempt has been made with a user account that has been locked out, but this says nothing about whether this current attempt used a good or bad password)

Continue reading

Deprovision and UnDeprovision Users and Groups with PowerShell

Since I’ve got deprovisioning problems on the brain for some reason, here are a few little things that do work as expected.

Using Quest’s Active Directory Management Shell, connected to ARS (Connect-QADService -Proxy) as a user allowed to deprovision objects, Deprovision-QADUser works like a champ on any user who could be deprovisioned via the ARS MMC or the web interface. However, there is no UnDeprovision-QADUser or UndoDeprovision-QADUser or even Deprovision-QADUser -Undo.

There are also no cmdlets that parallel the Deprovision/Undo Deprovision functions available for groups in the ARS MMC and web interface.

The edsvaDeprovisionType and edsvaUnDeprovision attributes are accessible via the -ObjectSettings parameter of the Set-QADUser and Set-QADGroup cmdlets. So, here’s what I’ve added to my profile to remedy these slight shortcomings of the otherwise amazing QADMS:


# Makes deprovisioning groups by script similar to deprovisioning users.

function Deprovision-QADGroup {
Param($Group)

Get-QADGroup -Identity $Group | Set-QADGroup -ObjectAttributes @{edsvaDeprovisionType=1}

}

# For undeprovisioning groups. Would work the same for undeprovisioning users.

function Undeprovision-QADGroup {
Param($Group)

Get-QADGroup -Identity $Group | Set-QADGroup -ObjectAttributes @{edsvaUnDeprovision=1}

}

# Undeprovision user.

function Undeprovision-QADUser {
Param($User)

Get-QADUser -Identity $User | Set-QADUser -ObjectAttributes @{edsvaUnDeprovision=1}

}

Before you get carried away, remember that what Deprovision-QADGroup actually does is as much up to you or your ARS developer/consultant as the effects of Deprovision-QADUser are; likewise, you have to do a bit of work for what Undo Deprovision does.

Modifying GrantSendOnBehalfTo Without Tears

If you run the cmdlet

Set-DistributionGroup "Sales Team" -GrantSendOnBehalfTo jonestom

you will overwrite the current contents of that distribution group’s GrantSendOnBehalfTo list. Oops.

One of my colleagues, a very old Exchange hand and a new PowerSheller, asked me if there was a simpler way to add one mailbox owner ($trustee) to the GrantSendOnBehalfTo attribute of another Exchange mailbox or distribution group ($target) than having to get both items, extract $trustee.distinguishedname and the current value of $target.GrantSendOnBehalfTo (and save it to another variable, $currentTrustees), append the trustee’s distinguished name to the current list of trustees ($currentTrustees += $trustee.DistinguishedName), then finally replace the old value of $target.GrantSendOnBehalfTo with $currentTrustees (Set-DistributionGroup $target -GrantSendOnBehalfTo $currentTrustees)

Yes, there is, at least as of Exchange 2010 SP1.

Set-DistributionGroup "Sales Team" -GrantSendOnBehalfTo @{add='domainA\jonestom','domainB\smithjane'}

Removing an unwanted trustee works the same way:

Set-DistributionGroup "Sales Team" -GrantSendOnBehalfTo @{remove='domainA\jonestom'}

Because combining these operations often does not end well, here is a cmdlet I made for the help desk to replace a departing colleague with his successor:

function Replace-DLGrantSendOnBehalfTo {
Param($oldTrustee,
	$newTrustee,
	$targetDistributionGroup
	)
Set-DistributionGroup $targetDistributionGroup -GrantSendOnBehalfTo @{add=$newTrustee}
Set-DistributionGroup $targetDistributionGroup -GrantSendOnBehalfTo @{remove=$oldTrustee}
}

For greater efficiency, it can be part of a pipeline, preceded by the names of the target distibution groups and surrounded by a foreach statement. A similar cmdlet can be made for mailboxes. There is probably a way to make it more generic; I might add that later.

The only article I’Ve seen that showed the @{add=$foo}, @{remove=$bar} way to modify this: http://blogs.technet.com/b/manjubn/archive/2012/10/02/grantsendonbehalfto-permissions-for-mailbox-overwrites-existing-permission.aspx

An article that shows the longer way around I described at the beginning, but gives a good explanation of the difference between GrantSendOnBehalfTo and SendAs permissions in Exchange: http://exchangeserverpro.com/exchange-2010-grant-send-behalf-permissions-distribution-group

Finding disconnected mailboxes the PowerShell and EMS way

Like most IT books, Microsoft Exchange Server 2010 Inside Out is a huge doorstop of a book. Since my boss was kind enough to purchase it, and I’m by far the department’s foremost reader of English and its least-experienced Exchange admin, I’ve decided to try to read the whole thing, a chapter a day.

It’s already paid off – I solved a problem using it just a day after reading the fantastic “Exchange Management Shell” chapter.

When a user gets transferred to a location in another domain in our Active Directory forest, he gets a new user account in that domain, but his old mailbox is moved to the Exchange server for that location in the new domain. Occasionally, something goes wrong and the mailbox ends up in a disconnected state – in this case, the old account was deleted before the mailbox was connected to the new one. The real problem was that the help desk staff member who did the move couldn’t remember which mailbox database (or even server) he moved the mailbox to…

Exchange Management Shell and Tony Redmond to the rescue!

The script below is adapted from “Exchange Server 2010 Inside Out”, page 132, mostly to overcome the pipelining problem Tony describes in the “Code changes required by remote PowerShell” section on page 120. I ran this, as I run everything I can, in an implicit Exchange remoting session in my trusty PowerGUI, rather than in Exchange Management Shell itself. Make sure you are either implicitly remoted to an Exchange server or in Exchange Management Shell before running it.

$mbxServers = (Get-MailboxServer | ForEach-Object { $_.name })
Foreach ($mbxserver in $mbxServers) {Get-MailboxStatistics -Server $mbxserver `
| where {$_.disconnectdate -ne $null } | select displayname,OriginatingServer, `
DisconnectDate,LastLoggedOnUserAccount,LastLogonTime,TotalItemSize,Database `
| Export-Csv c:\temp\$mbxserver-disconnectedmailboxes.csv -NoTypeInformation }

The main weakness is that it will only find disconnected mailboxes on 2007 or 2010 servers, but that’s because Get-MailboxStatistics only works on those newer mailboxes.

Tony Redmond’s blog: http://thoughtsofanidlemind.wordpress.com/

Getting ints out of Exchange 2010 disk space statistics Strings when remoting

July 2015 update: This still applies to Exchange 2013 – I’ve not tested it yet with Exchange 2016, though…

I’m back, this time with Exchange stuff. It’s been awhile, mostly due to my company’s IT department having found out what happens when DNS and AD do not get along and everything starts falling back to NTLM authentication, as well as a long Thanksgiving vacation back to the States. However, I’ve begun a project to overhaul our old user provisioning process, and ActiveRoles Server is at the center of it.

User provisioning means email provisioning, and in an Exchange environment, that means Exchange cmdlets.

The PowerShell cmdlets for Exchange 2010 are about 90% awesome. Why not 100%? Little stuff like returning disk space statistics from Get-MailboxDatabase -Status or Get-MailboxStatistics as strings when doing implicit remoting, not the ByteQuantifiedSize type returned when you use Exchange Management Shell on the server itself, or as a simple int.

This is because ByteQuantifiedSize is not a regular .NET type, but a Microsoft.Exchange.Data type, so instead of rendering it as a plain-vanilla int when the Microsoft.Exchange.Management.PowerShell.E2010 snap-in isn’t loaded, PowerShell renders it as a String. Microsoft wants us to use implicit remoting instead of the snap-in when running remotely. Make up your minds, Exchange team!

Technet forum discussion about this inconsistency: Exchange Server 2010: why string here and int there with EMC?

Example:

Get-MailboxDatabase -Server US1234 -Status `
| Select-Object AvailableNewMailboxSpace `
| Sort-Object -Descending


AvailableNewMailboxSpace
------------------------
94.88 MB (99,483,648 bytes)
1.58 GB (1,696,464,896 bytes)

Wait… 1.58 GB is bigger than 94.88 MB, right? Not if they’re strings.

However, this can be overcome. I’m posting this because all the ways I could find are stuck deep inside rather long scripts. This method is from http://poshcode.org/1902, by Karl Mitschke (see line 120). I chose to leave it as bytes rather than converting to MB or GB, as the destination for this data is a SQL Server table that will immediately be used for the user provisioning project, but might be used for other things in the future.

Our mailbox databases are named for the sites they’re at: for example, one of the ones at the US-Dallas site would be US-Dallas-MD01. The sitename can be extracted with Split(‘-‘).

Get-MailboxDatabase -Status `
| Select-Object @{Name="Sitename"; `
Expression={$_.Name.split('-')[0]+'-'+$_.Name.split('-')[1]}}, `
Name, Server, `
@{Name="Available"; `
Expression={[int]($_.availablenewmailboxspace.split("(")[1].Split()[0])}} `
| Sort-Object -Property @{Expression="Sitename";Descending=$false}, `
@{Expression="Available";Descending=$true}

That finally got me what I was looking for.

Here’s a function for converting those disk space strings to integers – I’ve added it to the script I use to start Exchange implicit remoting sessions from my workstation.

Function Get-IntFromExchangeNumberString {
Param($NumberString)
 [int]($NumberString.split("(")[1].Split()[0])
}

If there is a better and/or more concise way to do any of what I’ve done, feel free to put it in the comments or post a link!