Categories
PowerShell

Compare the Exchange Alias to the Primary SMTP Address

Here’s a data quality report for an Exchange environment. This command looks for Alias values that do not match the “username” portion of the PrimarySMTPAddress values. Any discrepancies are returned.

Get-Mailbox -ResultSize unlimited | where {$_.PrimarySmtpAddress.ToString() -notlike $_.Alias.ToString()+"@*"} | ft name, Alias, PrimarySmtpAddress

Categories
PowerShell

Search Active Directory for a list of email addresses

I’m occasionally asked to provide a list of account names based on a list of email addresses. This is pretty straight foward when the list of addresses contains only primary email addresses.

Get-Content C:\scripts\users.txt | %{Get-ADUser -Filter {mail -like $_} -Properties *} | ft name, displayName, eduPersonPrimaryAffiliation, PasswordLastSet -AutoSize

However, it’s very important to point out that the previous command will only match on the primary email address. Most of the time our list of address does not contain only primary email addresses – it may contain one of several secondary addresses the user has, which Active Directory refers to as proxyAddresses.

To make sure we search through all accounts for all their email address, we have to modify the input file and also modify our command a bit.

  1. Prefix the string smtp: to be beginning of each address in your file.
    1. For example, if you have address address@domain.edu in your file, modify that line to smtp:address@domain.edu
  2. Run the following PowerShell command – it’s all one line.
    1. Get-Content C:\scripts\users.txt | %{Get-ADUser -Filter {proxyAddresses -like $_} -Properties *} | ft name, displayName, mail, eduPersonPrimaryAffiliation, PasswordLastSet -Autosize

That output will include the primary email address for each account, so you can validate the results against your input file.

Categories
Office 365 PowerShell

Connecting to Office 365 with automation

In order to automate management activities for Office 365, it’s imperative to connect to the remote environments without human interaction. Typically this type of automated job will be run with Task Scheduler. However, due to differences between Windows authentication and Office 365 authentication, this is not quite as straightforward as running a Task Scheduler job with a service account in a pure Windows Server environment.

Fortunately there is a method of creating a file-based credential that securely associates an Office 365 user account with a local or Active Directory user account. We can then provide that credential file to the Office 365 connection string in our scripts. This allows us to schedule our scripts in Task Scheduler, run them as the local or Active Directory user account, and gain access to Office 365 resources.

  1. First, it’s important to logon to the server where the Task Scheduler will run, and to logon there as the user account that will be running the Task Scheduler job (i.e., domain\userA). This can be either a local user account or an Active Directory user account.
  2. Now we create the credential file using the Export-Clixml cmdlet. We must specify the Office 365 user account we plan to use in our scripts (i.e., cloudUser@tenant.onmicrosoft.com), and we’ll be prompted for the password on that cloud account when we execute the following command. An XML credential file will be produced in the path we specify in the Export-Clixml cmdlet.
    Get-Credential "cloudUser@tenant.onmicrosoft.com" | Export-Clixml C:\credentials\office365_credential.xml

    1. Note that the credential file we create here is tied to this account on this machine, and can only be used on this machine.
  3. Now that the credential file has been created, we can log out of the server and log back in with our standard administrative account if desired.

Now that we have the credential file, we can use it in our scripts to gain access to any Office 365 resource where this Office 365 user account (i.e., cloudUser@tenant.onmicrosoft.com) has permissions as long as we run those scripts as the associated user account (i.e., domain\userA). Keep in mind that the credential file will be unusable if it is copied to another computer, or even if it is used by any other user account on the computer where it was created!

Here is a sample connection string to connect to Exchange Online using our credential file as long as it’s run by the domain\userA account.

$credential = Import-Clixml C:\credentials\office365_credential.xml
$Session = New-PSSession `
-ConfigurationName Microsoft.Exchange `
-ConnectionUri https://outlook.office365.com/PowerShell-LiveID?PSVersion=4.0 `
-Credential $credential `
-Authentication Basic `
-AllowRedirection
Import-PSSession $Session

Here is a sample connection string to connect to Microsoft Online using our credential file.

Import-Module MSOnline
$credential = Import-Clixml C:\credentials\office365_credential.xml
Connect-MsolService –Credential $credential

Categories
PowerShell

compare two lists with PowerShell

I am asked occasionally to compare a list of employee ID numbers to find the differences or the matches. Notepad++ does not do a great job of this, so I put together a quick PowerShell solution.

$dupes = @()
[System.Collections.ArrayList]$arrA = Get-Content U:\listA.txt
[System.Collections.ArrayList]$arrB = Get-Content U:\listB.txt
foreach ($itemA in $arrA) {
if ($arrB -match $itemA) {
$arrB.Remove($itemA)
$dupes += $itemA
}
}

Now $arrB contains only items from listB.txt that do not also appear on listA.txt. Also $dupes contains the items that exist in both files.

Categories
Office 365 PowerShell

Rotate images in ADFS 3.0

ADFS 3.0 is otherwise known as ADFS 2012 R2 since it is available only on Server 2012 R2. As I gain some experience with it, one of the nice configuration options is the ability to use PowerShell to customize the sign-in page.

Among the customizations we’ve made is one to help keep our sign-in page from looking stale over time. I wrote this simple PowerShell script to rotate the large “illustration” image occasionally. It runs as a Scheduled Task, and pulls approved images randomly from a file system folder. The script also logs which image was in place at any given time in case that happens to be interesting to someone at some point.

cd X:\path\images
$RandomImage = Get-ChildItem | Get-Random | %{((Get-Item $_).VersionInfo).FileName}
(Get-Date -format G) + " $RandomImage" | Out-File X:\path\Logs\IllustrationRandomizer.log -Append
Set-AdfsWebTheme -TargetName Custom_Theme -Illustration @{path=$RandomImage}

Categories
PowerShell

PowerShell error with Get-ADUser user -Properties *

After upgrading some of our servers to Server 2012 R2, we’ve discovered a bug in the PowerShell 4.0 Get-ADUser cmdlet. When running the command Get-ADUser username -Properties *, the cmdlet returns the following error:

Get-ADUser : One or more properties are invalid.
Parameter name: msDS-AssignedAuthNPolicy
At line:1 char:1
+ Get-ADUser username -Properties *
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (username:ADUser) [Get-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Comm
ands.GetADUser

Mike F Robbins researched the error in the following blog post, and determined that the issue occurs with PowerShell 4.0 run against Server 2008 R2 domain controllers. The issue is that two attributes, AuthenticationPolicy and AuthenticationPolicySilo, exist in a Server 2012 R2 Active Directory but do not exist in a Server 2008 R2 Active Directory. The Server 2012 R2 RSAT tools expect the attributes to exist in both environments, so an error is returned in the Server 2008 R2 Active Directory environment.

http://mikefrobbins.com/2013/11/07/windows-8-1-rsat-powershell-cmdlets-get-aduser-get-adcomputer-one-or-more-properties-are-invalid/

As Mike points out, a good workaround is to use PowerShell implicit remoting to process the commands on the domain controllers themselves. However, in the meantime I’m able to process the command from other downlevel OS machines, which is fine in my case for now.

I’ve submitted the case to Microsoft Premier who has confirmed the bug and escalated to the Platforms Team. I’ll post updates here as I get them.

Categories
PowerShell

SQL Server PowerShell Module (SQLPS)

SQL Server provides a Windows PowerShell module called sqlps that is used to import the SQL Server components into Windows PowerShell. The sqlps module loads two Windows PowerShell modules:

  • A SQL Server provider, which enables a simple navigation mechanism similar to file system paths. You can build paths similar to file system paths, where the drive is associated with a SQL Server management object model, and the nodes are based on the object model classes. You can then use familiar commands such as cd and dir to navigate the paths similar to the way you navigate folders in a command prompt window. You can use other commands, such as ren or del, to perform actions on the nodes in the path.
  • A set of cmdlets, which are commands used in Windows PowerShell scripts to specify a SQL Server action. The SQL Server cmdlets support actions such as running a sqlcmd script containing Transact-SQL or XQuery statements.

More details here: http://msdn.microsoft.com/en-us/library/hh245198.aspx

Although the SQLPS module is installed along with SQL Server, you do not have to install SQL Server to obtain the module. You simply need to install three stand-alone packages from the Microsoft® SQL Server® 2012 Feature Pack, available here:
http://www.microsoft.com/en-us/download/details.aspx?id=29065

Install the following packages in this order:

  1. Microsoft® System CLR Types for Microsoft® SQL Server® 2012 (SQLSysClrTypes.msi)
  2. Microsoft® SQL Server® 2012 Shared Management Objects (SharedManagementObjects.msi)
  3. Microsoft® Windows PowerShell Extensions for Microsoft® SQL Server® 2012 (PowerShellTools.msi)

Be sure to select the appropriate package platform for each, either x86 or x64.

To load the sqlps module in Windows PowerShell:

Import-Module sqlps

(You can include the -DisableNameChecking parameter if you’re concerned about suppressing the Encode-Sqlname and Decode-Sqlname warning.)

Thanks to Max Trinidad for his article explaining this information. http://www.maxtblog.com/2012/09/create-powershell-smo-scripts-without-installing-sql-server/

Categories
PowerShell

ActiveDirectory module and UAC

I have noticed a few odd behaviors with PowerShell’s ActiveDirectory module, one regarding the msDS-UserPasswordExpiryTimeComputed attribute of the Get-ADUser cmdlet, and another regarding the New-ADServiceAccount cmdlet.

On a brand new Server 2008 R2 domain, the following command returns values for only a small percentage of the accounts in the domain but the vast majority of accounts return with no value.

Get-ADUser -filter * -Properties “msDS-UserPasswordExpiryTimeComputed” | ft name, “msDS-UserPasswordExpiryTimeComputed” –AutoSize

Also on this domain, issuing the following command often results in an “Access is denied” error, but not always. In fact, typically if I receive the error and then leave my RDP session to the computer open for 30 to 60 minutes and try command again (hit the Up Arrow key on the keyboard), the command runs without error.

New-ADServiceAccount svc_test

This seems to indicate a general problem with the ActiveDirectory PowerShell module.

The following article pointed me in the right direction for a workaround.
http://social.technet.microsoft.com/Forums/en-US/windowsserver2008r2general/thread/5947c0eb-9549-4161-afa1-edb451fc742f

The problem appears to be a bug in the way the ActiveDirectory module behaves when User Account Control (UAC) is enabled. The workaround is to disable UAC, which requires a reboot. When UAC is left enabled, even when PowerShell is run with elevated permissions, the problem still occurs.

I’ve confirmed the experiences above both on domain controllers and on member servers, and in two different Active Directory forests.

Categories
PowerShell

Look for orphaned Active Directory home directories

This PowerShell script will iterate through all home directory folders in our Windows file share server and search Active Directory for a homeDirectory path value that ends with that folder name (it actually looks for *\<folderName> so it will only return exact matches).

The script has to be run from a location that has network access and read permissions on the physical volume on the file share server, and also requires the Active Directory PowerShell module to be loaded.

$strHomeServer = "\\server"
$strUsersPath =  "\l$\users\"
$arrDirectories = Get-ChildItem $strHomeServer$strUsersPath | where {$_.attributes -match "directory"}
Write-Host "Number of home directories: $arrDirectories.count" | Out-File C:\scripts\homeDirAudit.txt -Append
$arrDirectories | %{
$objDirectory = $_
# \5c is the LDAP escape sequence for the \ character
#
this info from help about_ActiveDirectory_Filter
$searchString = "*\5c" + $_
$objUser = (Get-ADUser -Filter {homeDirectory -like $searchString} -Properties homeDirectory)
if (-not $objUser) { "$objDirectory not found!" }
else { $objUser | select @{n='Folder Name';e={$objDirectory}}, name, homeDirectory }
} | Out-File C:\scripts\homeDirAudit.txt -Append

Categories
PowerShell

Detecting disconnected Exchange mailboxes

Here’s how to detect Active Directory accounts that had an Exchange 2010 SP1 mailbox at one time, but the mailbox has since been disconnected, aka disabled.

Essentially find all accounts where a msExchWhenMailboxCreated value exists and a homeMDB value does not exist.

Get-ADUser -LDAPFilter "(&(msExchWhenMailboxCreated=*)(!homeMDB=*))" -Properties CanonicalName, msExchWhenMailboxCreated | sort CanonicalName | ft CanonicalName, msExchWhenMailboxCreated -AutoSize

– or-

Get-ADUser -Filter {(msExchWhenMailboxCreated -like "*") -and -not (homeMDB -like "*")} -Properties CanonicalName, msExchWhenMailboxCreated | sort CanonicalName | ft CanonicalName, msExchWhenMailboxCreated -AutoSize

This allows us to know before creating a mailbox for a user whether we should first bother to look for a disconnected mailbox that may already exist for the user.