Amazon Contextual Product Ads

Wednesday, June 29, 2011

Finding Orphaned Delegates in Exchange 2007 using a powershell script

Over the years, I have worked at many companies and one thing I have noticed about all of these companies is, they tend to wind up having a lot of the same issues. One in particular is the topic of today's blog.

Have you ever sent an invitation from Outlook to another mailbox only to receive a Non Delivery Report (NDR) from someone else's mailbox. Then, when you checked, you discovered the other mailbox doesn't even exist in your network. It may have existed at one time, but it definitely no longer exists.

This is a common issue for many companies using Exchange 2007 simply because they do not typically do their "due diligence" when it comes to cleaning up removed users.

You see what happens is, a user will often have a delegate who manages his or her calendar. Then, one day, that delegate drops off of the face of the earth. Either they were terminated, quit, or quite possibly were abducted by aliens and the admins who knew about it, removed the delegate's account from Active Directory. What they did not do was remove the delegation rights to their bosses calendar.

This leaves something we like to call an Orphaned Delegate. Now, when an invitation is sent to that boss, or an existing meeting is responded to that includes that boss, Exchange will try to direct the message to the Orphaned Delegate, which it will fail to do, and an NDR is generated.

If it is an email with a large list of recipients, it can be difficult to figure out who has the Orphaned Delegate assigned to their calendar. That is where the following script comes in.

From this script, you will do a couple of things.
  1. Capture any errors and silenetly continue as many of the results we are looking for will actually produce an error which we need to capture.
  2. Create an output file.
  3. Specify which domain controller to work with.
  4. Get the mailboxcalendarsettings for every mailbox that has a delegate assigned to it.
  5. Try to find an existing mailbox that matches the delegate.
  6. If no match is found for that delegate, write it to the output file next to the name of the mailbox which has the orphaned delegate.
Once you have that information, you can go into that user's mailbox and remove the Orphaned Delegate to stop the issue from occuring.

The script looks as follows:

$ErrorActionPreference = "SilentlyContinue"
$out = "c:\Scripts\Orphans.txt"
$inp = "Mailbox`tOrphaned Delegate"
Out-File $out -inputObject $inp
$dc = "DC01.domain.com"
$del = get-mailboxcalendarsettings -ResultSize Unlimited -domaincontroller $dc |Where {$_.ResourceDelegates -ne $null}
foreach ($usr in $del)
{
 $Err = $null
 $iden = $usr.Identity
 $mbx = get-mailbox $iden
 $name = $mbx.DisplayName
 $delegates = @($usr.ResourceDelegates)
 ForEach ($usr2 in $Delegates)
 {
  $mbx2 = get-recipient $usr2 -ErrorVariable Err
  If ($Err -ne $null)
  {
   $inp = "$name`t$err"
   Out-File $out -inputObject $inp -Append 
  }
 }
}

Thursday, June 23, 2011

A couple of useful PowerShell WMI scripts using dot sourced functions

I work a lot with functions stored in a dot sourced ps1 file because it makes scripting so much easier and more efficient. For instance, if I have several scripts that need to perform the same test, such as a Ping test to determine whether a server is responding to ICMP requests, I simply call the function from the dot sourced file without having to rewrite it over and over again.
So for the purposes of today's example, I have stored all of my functions in a file at the following location:
\\Server\scripts\functions.ps1

The Pinghost function looks as follows:
Function Pinghost  ([string] $Hostname )
{
    $status=get-wmiobject win32_pingstatus -Filter "Address='$Hostname'" | Select-Object statuscode
    if($status.statuscode -eq 0)
    {$result = "$Hostname is REACHABLE"}
    else
    {$result = "$Hostname is NOT reachable"}
        $result
}




So my code in the script would look as follows:
. \\Server\Scripts\Functions.ps1

$server = "MyServer01.domain.com"
Pinghost $server

This would return one of the following results:


MyServer01.domain.com is REACHABLE

or

MyServer01.domain.com is NOT reachable


Let's say you want to see whether a service is installed on a server and determine it's status. To do this, I use a function called Get-RemoteService. The function is in the same functions.ps1 file so the file would look as follows:


Function Get-RemoteService
{
 param($serverName,$serviceName)
 $svc = get-wmiobject win32_service -computer $serverName -filter "name='$serviceName'"
 If ($svc -eq $Null)
 {
  $svc = "No such Service on this server."
 }
 $svc

}

Function Pinghost  ([string] $Hostname )
{
    $status=get-wmiobject win32_pingstatus -Filter "Address='$Hostname'" | Select-Object statuscode
    if($status.statuscode -eq 0)
    {$result = "$Hostname is REACHABLE"}
    else
    {$result = "$Hostname is NOT reachable"}
        $result
}


As you can see, you can stack the functions in any order you want in the functions.ps1 file. When you dot source it, all functions in this file become available to the script that is calling them. This script would look something like this:


. \\Server\Scripts\Functions.ps1

$server = "MyServer01.domain.com"
$service = "Wins"
Get-RemoteService $server $Wins


This returns one of the following results:


ExitCode : 0
Name : WINS
ProcessId : 2100
StartMode : Auto
State : Running
Status : OK


or

No such Service on this server.



These are just a few of the wmi functions I use day to day. The point here is not so much in the functions themselves but in the practice of dot sourcing those functions. Dot sourcing helps me to centralize all of my functions so I don't have to reinvent the wheel each time I write a new script. The common needs of my scripts can reutilize the functions with far less code and with less potential for typoes/user error. Once my functions test out and prove error-free, I can safely reuse them over and over again.



If you do a lot of administrative scripting, I can not recommend dot sourcing your functions enough.


-Edit-
-As a side note, I was reminded that there are other ways to do these scripts in PowerShell v2.0. I should specify that most if not all of these scripts will be PowerShell v1.0 as that is what my experience is in and it will allow for backward compatibility. If you know of a way to do these scripts in v2.0, your comments would be very helpful to folks looking specifically for that version. Until I actually start working with 2.0, it will be a safe assumption that these scripts were written for PowerShell v1.0.

Thanks Tyler for the Heads Up.

Tuesday, June 14, 2011

PowerShell script to Dynamically choose a Domain Controller

I write a lot of scripts that require a domain controller to be specified. Problem is, I have over 90 domain controllers to choose from and they seem to be adding and removing them all the time. So I figured, the best way would be to create a dynamic list of domain controllers in the same site as the updates I am trying to apply. So if I am creating a mailbox in a site called NewYork, I would only want the domain controllers in the NewYork site to show up. I chose to do this through a function so the code could be easily reused for the many scripts that require a domain controller be specified.

The function looks as follows:

Function ChooseDC
#This funtion dynamically creates a list of Domain Controllers in a specific site (within the current forest) and lets you set the $DC value to one of them.
{
 #Prompt the user to select a domain controller
 Write-Host "Please select a domain controller:" -fore Yellow
 #Create an array that list all domain controllers in site called SITENAME (replace with your site)
 @([Array]$myServers = (([System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()).Sites | Where {$_.Name -eq "SITENAME"}).Servers)

 #sort the Array on the server names 
 [Array]$Servers = $myServers | Sort-Object Name
 #Iterate through each server in the array (starting at array item 0 through the last item in the array)
 For ($s = 0;  $s -le ($Servers.Count -1); $s +=1)
 {
  #Capture the server name to the $ServerName variable
  [String]$ServerName = $Servers[$s].Name
  #Create a string to output to the screen saying to pick that number for that server
  [String]$Out = "Enter " + ($s +1) + " for " + $ServerName
  #Output the string to the screen
  Write-Host -Fore Yellow $out 
 }
 #After all domain controllers are listed, prompt user to choose which domain controller by it's number that they want to work with.
 [int]$Result = Read-Host "Make your choice now"
 #Set an integer variable to the result of their choice minus 1. This will sync the choice with the proper array item for that Domain Controller.
 [int]$item = ($Result -1)
 #set $DC to equal that server name
 [String]$DC = $Servers[$item].Name
 #return the value of the variable, $DC, to the calling script.
 Return $DC

}

So, if you needed to see if a specific user had replicated yet to a specific domain controller, you could create a simple script that includes the function above and then the following after the closing "}".

#run the function and assign it's result to $DC
$DC = ChooseDC

#replace UserName with the User Name of the user you are checking for.
get-User -id "UserName" -domaincontroller $DC |fl DisplayName


Save the script as something like c:\finduser.ps1 and open powershell (in this case the Exchange Management Shell). Change directory to C: (CD\).
Running the script would look as follows:
cd\
[PS] C:\>.\finduser
Please select a domain controller:
Enter 1 for NY0DC01.domain.com
Enter 2 for NY0DC02.domain.com
Enter 3 for NY0DC03.domain.com
Enter 4 for NY0DC04.domain.com
Enter 5 for NY0DC05.domain.com
Enter 6 for NY0DC06.domain.com
Make your choice now: 2
If the user does not exist on the domain controller, you will get an error similare to the one below:
Get-User : The operation could not be performed because object 'UserName' could not be found on domain controller 'NY0DC02.
domain.com'.
At C:\test.ps1:36 char:9
+ get-User <<<<  -id "UserName" -domaincontroller $DC |fl DisplayName
    + CategoryInfo          : InvalidData: (:) [Get-User], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : 41716ACC,Microsoft.Exchange.Management.RecipientTasks.GetUser
If the user does exist, it would return the DisplayName of the user. You can save the function in a dot sourced function file and make it accessible to all of your scripts without having to put it in each one. To dot source it, just save the function in a file and call it something like functions.ps1. To make it even more available, let's save it to a shared folder. So \\servername\scripts would be a good place to put it. Now, my script that uses the dot sourced function script would look as follows:
#call the dot sourced function script
. \\NYFileSrv01\Scripts\functions.ps1
#this makes all functions in the functions.ps1 script available to your script.

#call your function
$DC = choosedc
#use the result of the function ($dc) in your get-user statement
get-User -id "UserName" -domaincontroller $DC |fl DisplayName

All in all, this is a pretty useful function. Also, using dot sourcing is an excellent way to keep all your functions organized and available without having to rewrite or cut and paste them every time. I highly recommend dot sourcing all functions.

Wednesday, June 8, 2011

Script to Change the Primary SMTP address in Exchange 2007

Throughout the years, I have worked for many companies. One thing that pretty much all of these companies did was to change their Domain Names from time to time. It is a pretty tedious process to change the Primary SMTP address on several users, especially for large companies that decide to rebrand.

If you had to do it manually, it could take several days to even weeks and leaves a lot of room for human error. Luckily, we don't have to do it manually. In Exchange 2007, we can do it with a PowerShell script.

The following script will gather the current Primary SMTP address, strip off the domain, and replace it with the new domain. The old Primary SMTP address will remain attached to the user, but as a secondary address, so email will not be interrupted to that user as senders learn to use the new address.

You will need to run the script in the Exchange Management Shell:

#specify a domain controller to work with
$dc = 'USDC01.domain.com'
#Create an output file to capture the results
$Output = "c:\output.txt"
Out-File $Output -InputObject "OldPrimarySMTPAddress`tNewPrimarySMTPAddress"
#specify the OU you wish to pull the users from
$OU = "domain.com/Users" #replace with the OU you want to work with
#specify the domain of the new SMTP address you want to change it to
$NewDomain = "NDomain.com"
#get a list of all mailboxes in the OU
$list = get-mailbox -OrganizationalUnit $OU -resultsize Unlimited -DomainController $DC
#Iterate through the list
foreach ($user in $list)
{
 $mb = Get-mailbox $user -DomainController $dc
 #capture current primary smtp address
 $SMTP = $mb.PrimarySmtpAddress
 [string]$Local = $SMTP.Local
 [string]$OldDomain = $SMTP.Domain
 [string]$CPSMTP = $Local + "@" + $OldDomain
 #captur new primary smtp address
 [string]$NPSMTP = $Local + "@" + $NewDomain
 #capture the old and the new SMTP addresses to the output file
 [string]$iobject = $CPSMTP + "`t" + $NPSMTP
 Out-File $Output -InputObject $iobject -Append
 #set the new primary smtp address on the mailbox and remove the flag to use the email address policy (if you do not do this, the email address will revert to whatever the policy has set to)
 Set-Mailbox $user -PrimarySmtpAddress $NPSMTP -EmailAddressPolicyEnabled $false -DomainController $DC
}

Hopefully, this script proves as useful to you as it was for me time and again.

Tuesday, June 7, 2011

PowerShell script to gather various information on Domain Controllers

I recently got tasked with upgrading all of my Windows 2003 domain controllers to Windows 2008 R2. Now, since I have domain controllers all over the world and IT staff in each country who will be performing the work for their particulare divisions, I needed a quick and easy way to see which servers were on which OS and what site they belonged to and I needed it on demand so I could keep up with the project as it moved forward. PowerShell made it easy for me.

The following script will create an output file, give it a tab delimited header, and then populate it with various bits of information for all available domain controllers in the domain. By available, I mean the server needs to be up and online at the time the script is run. To gather for more than one domain, you will need to run this on a domain controller in each domain.

This script will need to be run in the Active Directory Module for Windows Powershell.
#first, let's create the output file
$out = "c:\Output.txt"
out-file $out -inputobject "Name`tOS`tSite`tFSMO Roles`tLDAP Port`tGlobal Catalog"
#now, let's get a list of all Domain Controllers
$dcs = get-addomaincontroller -filter { name -like "*" }
#next, you want to iterate through each domain controller and capture it's information.
foreach ($dc in $dcs)
{
#get the operating system of the current DC
   $OS = $dc.OperatingSystem
#get the name of the current DC
   $name = $dc.Name
#get the AD site the DC is in
   $site = $dc.Site
#get the FSMO roles of the DC (if any)
   $FSMO = $dc.OperationMasterRoles
#get the LDAP port the DC listens on
   $ldap = $dc.LdapPort
#determine whether the DC is a global catalog server or not
   $gcq = $dc.IsGlobalCatalog
   if ($gcq -eq $true) 
   {
      $gc = "Yes"
   }
   Else
   {
      $gc = "No"
   }
#write out all of the results to a single, tab delimited line
   $inp = "$name`t$os`t$site`t$FSMO`t$ldap`t$gc"
#write the tab delimited line to the output file
   out-file $out -inputobject $inp -append
}

That's it. Pretty cut and dry. You can open the output.txt file in Excel since it is tab delimited and it will line up nice and neat in the proper columns.

Welcome to my Active Directory and Exchange Powershell Scripting blog

Over the years I have accumulated quite a few scripts revolving around the administration of Active Directory and Microsoft Exchange. These scripts have saved me countless hours and headaches when dealing with what could be mundane and tedious tasks. I will try to post a new one at least weekly, perhaps more often as time permits. If you have specific questions regarding scripts you would like to see, just let me know and I'll see what I've got. If I don't have one that does what you are looking for, time permitting, I will see if I can put one together for you.

If it had not been for the many scripters over the years who have made their scripts freely available, I probably would not have learned as much as I have regarding scripting, and so, I am passing the lessons learned on to you.

Most, if not all, of these scripts will have to be run in one of the following PowerShell modules: the Active Directory Module for Windows PowerShell or the Exchange Management Shell, depending on which technology we are scripting. Also, most of these scripts will be for PowerShell v1.0 until I actually start working with 2.0.

The format of my posts will be a little different than most scripting sites. Basically, I will give a brief description of why I wrote the script and then without too much fluff, I will post the script with comments inlaid so that when you copy and paste the script, the description of the various parts of the script will go with you. That way you can easily find what does what and make adjustments as your environment or needs require.

If my scripts seem longer than necessary, that is by design. One liners are great, but they are difficult to learn from, and even more difficult to document. I prefer to lay each part out on it's own line so I can comment on it and also so it is more readable. If you are new to scripting, I feel this format will be more beneficial to you.

I hope you find this information useful and visit my blog often.

Chris Allen, AKA Gus Gallows