RSS Subscription 168 Posts and 2,769 Comments

Archive for September, 2008

Obtaining Exchange Information including Edge with PowerShell

So the last month I’ve been learning how to do PowerShell scripting. I’m new at scripting and figured I would share my first Exchange script due to sheer excitement. I’d like to give a special thanks to Shay Levy @ http://blogs.microsoft.co.il/blogs/ScriptFanatic. Shay has been a tremendous help over @ http://powershellcommunity.org when I run into issues that any typical novice runs into (especially with scripting). The other folks have been a great help as well.

I started this script last week and will be adding to it as I gain more knowledge. The skeleton of the script is complete with 1 function so far (to get disk information) and will allow me to easily add more functions to the script. I wanted to provide a method to dynamically check if a server was an Edge Server and prompt for authentication to allow PowerShell to obtain information from my Edge Servers.

With an Edge server, it is not going to be a part of your corporate domain . It may be a part of a forest/domain dedicated in your DMZ, but not your corporate forest/domain. Typically if you want to pull information from a domain-joined machine that is a part of your corporate domain, you just run a command against it and it’ll work if you have the access to do so. But since the Edge server is a part of a workgroup or DMZ forest/domain, it’s not that easy. This script will check if you are trying to check an Edge Server, and if so, it will display on the console that it is checking for Edge authentication and prompt you for credentials for that Edge Server.

Here’s the script I have so far:

?View Code POWERSHELL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# Define what paramters (-role) can be utilized when running the switch
Param(
[string] $role = "All"
)
 
# Define what values (Mbx, Cas, Etc.) can be utilized when using our -role parameter
switch ($role)
{
	Mbx { $role = "IsMailboxServer" }
	Cas { $role = "IsClientAccessServer" }
	Um { $role = "IsUnifiedMessagingServer" }
	Hub { $role = "IsHubTransportServer" }
	Edge { $role = "IsEdgeServer" }
}
 
# Test host to see if it replies to ping prior to allowing information gather functions to proceed.
function Ping-Host
{
       $result = Gwmi -Query "SELECT * FROM Win32_PingStatus WHERE Address='$server'"
       if ($result.statuscode -eq 0) {$true} else {$false}
}
 
# Function to display disk information.  If the server being checked is an Edge Server, you will be prompted for authentication.
function Get-DiskInformation
{
	""
	"Server: $server"
	if ($server.isEdgeServer)
	{
                $erroractionpreference = "SilentlyContinue"
		$cred = Get-Credential
		if ($cred) { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server -Credential $cred }
		else { Write-Warning "Could not obtain disk information for $server due to no credentials being provided" ; break }
                $erroractionpreference = "Continue"
	}
	else { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server }
 
	foreach($d in $disk)
	{
		$obj = New-Object PSObject
		$obj | Add-Member NoteProperty "Size(G)" ([math]::round($d.Size/1GB,2))
		$obj | Add-Member NoteProperty "Freespace(G)" ([math]::round($d.FreeSpace/1GB,2))
		$obj | Add-Member NoteProperty "Used(G)" ([math]::round($d.Size/1GB - $d.FreeSpace/1GB,2) )
		$obj | Add-Member NoteProperty "Freespace(%)" ([Math]::Round((($d.FreeSpace/1GB) / ($d.Size/1GB) * 100),2))
		$obj | Add-Member NoteProperty "Usedspace(%)" ([Math]::Round(((1 - ($d.FreeSpace/1GB) / ($d.Size/1GB)) * 100),2))
		$obj
	}
}
 
# Place servers into the $colServers variable which will later be fed into Functions
if ($role -ne "All" -and $role -ne "IsEdgeServer") { $colServers = Get-ExchangeServer | Where-Object {$_.$role -eq "True" -and $_.IsEdgeServer -ne "True"} }
elseif ($role -ne "All" -and $role -eq "IsEdgeServer") { $colServers = Get-ExchangeServer | Where-Object {$_."IsEdgeServer" -eq "True"} }
else { $colServers = Get-ExchangeServer }
 
if (!$colServers) { Write-Warning "There are no servers of the specified type to gather information for." }
else
{
	foreach ($server in $colServers)
	{
		if (Ping-Host -eq "$true")
		{
			Get-DiskInformation
		}
		else
		{
			Write-Warning "$server is not pingable"
		}
	}
}

There’s one piece of the script I want to disuss. It’s the function for Get-DiskInformation. I used to have the function written as such.

?View Code POWERSHELL
1
2
3
4
5
6
7
8
9
10
11
12
13
function Get-DiskInformation
{
	""
	"Server: $server"
	$(if ($server.isEdgeServer) { Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server -Credential (Get-Credential) }
	else { Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server }) | `
	Format-Table -autosize DeviceID,VolumeName,`
	@{Label="Size(G)";Expression={[math]::round($_.Size/1GB,2)}},`
	@{Label="Freespace(G)";Expression={[math]::round($_.FreeSpace/1GB,2)}},`
	@{Label="Used(G)";Expression={[math]::round($_.Size/1GB - $_.FreeSpace/1GB,2)}},`
	@{Label="Freespace(%)";Expression={[Math]::Round((($_.FreeSpace/1GB) / ($_.Size/1GB) * 100),2)}},`
	@{Label="Usedspace(%)";Expression={[Math]::Round(((1 - ($_.FreeSpace/1GB) / ($_.Size/1GB)) * 100),2)}}
}

Now the issue with how I used to have it written is that it forces Format-Table and a specific style. So essentially, I am forcing the output to look a certain way. There’s no way for the user to dynamically change the way the output appears and will mess up the formatting if you wanted to place the data into an Excel sheet for example.

Now let’s take a look at the way I have it in the final script I posted.

?View Code POWERSHELL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Get-DiskInformation
{
	""
	"Server: $server"
	if ($server.isEdgeServer)
	{
                $erroractionpreference = "SilentlyContinue"
		$cred = Get-Credential
		if ($cred) { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server -Credential $cred }
		else { Write-Warning "Could not obtain disk information for $server due to no credentials being provided" ; break }
                $erroractionpreference = "Continue"
	}
	else { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server }
 
	foreach($d in $disk)
	{
		$obj = New-Object PSObject
		$obj | Add-Member NoteProperty "Size(G)" ([math]::round($d.Size/1GB,2))
		$obj | Add-Member NoteProperty "Freespace(G)" ([math]::round($d.FreeSpace/1GB,2))
		$obj | Add-Member NoteProperty "Used(G)" ([math]::round($d.Size/1GB - $d.FreeSpace/1GB,2) )
		$obj | Add-Member NoteProperty "Freespace(%)" ([Math]::Round((($d.FreeSpace/1GB) / ($d.Size/1GB) * 100),2))
		$obj | Add-Member NoteProperty "Usedspace(%)" ([Math]::Round(((1 - ($d.FreeSpace/1GB) / ($d.Size/1GB)) * 100),2))
		$obj
	}
}

As you can see, it looks quite different. What I did here was create a PowerShell object. This allows me to add data to this object which in return allows me to pipe our command into many different outputs such as Format-Table, Format-List (which will be default since there are >=4 lines of data being returned), Excel, etc.. I’ve also added other error checking such as if you don’t enter a user/password for your Edge Server, it won’t throw a bunch of errors on the screen. Instead, it will provide a nice warning message letting you know credentials were not provided.

Let’s take a look at an example at pulling only disk information for a Mailbox (and yes this is a testlab and Exchange is running on my DC which I would never do in production!).

So what happens if we didn’t specify the -role command or specified All for the role command? The same thing will happen since -role switch is automatically assumed and the default for the command is All.

Since we selected All, it found an Edge Server in the environment but the script runs a ping test on all servers before trying to obtain information. Since our Edge Server was down, we were notified of this. Now after bringing my Edge Server online, let’s run a -role All again.

We still pulled information for our domain-joined Exchange Server, but now that our Edge server is up and we can ping it, it prompts us for authentication. On the screen, the server that it is prompting for is OCS-EXCEdge which is the name of our Edge Server. So let’s enter our credentials for this OCS-EXCEdge server.

We now authenticated with the Edge Server and obtained disk information. If we instead hit cancel on the authentication prompt, we would get:

And the last thing I want to show is another safe guard in case you enter a wrong server type.

Share

Exchange 2007 Unicast NLB Issue on Hyper-V

I ran into an issue with Exchange 2007 Network Load Balancing (NLB) Unicast Mode when running on Hyper-V running on full install of Server 2008 x64.  To first understand the problem, it is useful to understand how NLB Unicast works.

When running Unicast, the NIC that has NLB enabled will have its’ MAC Address overwritten.  When you add a second NLB member to your NLB cluster, that new member’s NLB enabled NIC will have it’s MAC Address overwritten with the same MAC address as the other NLB enabled NIC.

Let’s take a look at our EXCAS1 server which contains both the Client Access Server and Hub Transport Server role.

As we can see, we have two NICs which are required when doing Unicast.  The reason you need two NICs is because, as stated, each NLB enabled NIC will have its’ MAC address overwritten and both NLB Node’s NLB enabled NICs will have the same MAC Address.  Because of this, we need a second NIC on each server so both NLB nodes can still communicate with each other.

Taking a look at our NLB NIC, we have a MAC Address of 00-15-5D-01-70-18.

How did we get this MAC Address?  Taking a look at our “Hyper-V Synthetic Network Adapter” in Hyper-V Manager, we can see we assigned our NLB NIC a dynamic MAC Address.

So let’s go ahead and enable NLB on our NLB enabled NIC.

After clicking OK, we can see that our MAC Address on our NLB NIC has changed from 00-15-5D-01-70-18 to 02-BF-CO-A8-01-FB.  Any other NLB nodes that are added to our NLB cluster will have its’ NLB enabled NIC’s MAC changed to 02-BF-C0-A8-01-FB as well.

Go into the Properties of our NLB and let’s go ahead and configure NLB.  And yes, my test lab’s domain is fark.com (will be changing back to shudnow.net once I get my new hardware and re-configure my lab environment).

The Cluster IP configuration is the IP configuration that will be used for our NLB IP.  They are not the IPs that are currently assigned to our existing NICs; although you will be adding this IP to your NLB NIC in just a bit.

On the Host Parameter’s tab, we’ll want to put in the IP Address that currently lives on the existing NLB NIC.

The Port rules are configured as such.  After configuring your port rules, Click OK to Continue.

We now have to add the Cluster IP to our NLB NIC.  This process will automatically be done for new nodes which join the NLB cluster.

Because we specified our host name for our Cluster NLB to be mail.fark.com, we will want to ensure a host record for mail.fark.com points to 192.168.1.251.

Now here’s where I ran into an issue with Hyper-V.  From our EXCAS1 server, we can ping mail.fark.com properly.

But from another machine (our DC), we cannot successfully ping mail.fark.com

Initially, I thought this may be the issue Andy Grognan ran into here.  But unfortunately, it was not.

I then tried to disable the NLB option.  I was then able to ping 192.168.1.251 which is still bound to the IPv4 settings of the NLB NIC.  This made me think it was an option with modifying the MAC Address.

I found out that a Hyper-V Synthetic Network Adapter does not allow you to dynamically modify the MAC address.  A legacy network adapter does, but a Hyper-V Legacy Network Adapter does but does not support x64.  This means we need to figure out a way to get this working with a Hyper-V Synthetic Network Adapter.

I shut down my EXCAS1 server and went into the properties of the Hyper-V Synthetic Network Adapter that was used for NLB.  I took note of the newer NLB MAC Address and modified the Hyper-V network settings to have a Static MAC Address of 02-BF-C0-A8-01-FB.

I booted my EXCAS1 server back up and went back on my DC1 server and tested a ping to mail.fark.com.  It worked!

And to test from our DC1 to our web browser to ensure OWA works through our NLB  hostname?  That works too!

Share

Two new Qualified IP-PBXs for OCS (CUCM and Seltatel)

Two new IP-PBXs (Cisco CUCM 4.2, 5.1 and 6.1 and Seltatel) have qualified in the Open Interoperability program here.  The new support adds support only for DirectSIP but not Dual Forking.  Because of the enabled functionality for DirectSIP, you no longer need a supported Voice Gateway between an OCS Mediation Server and a Qualified IP-PBX that supports DirectSIP.

What does this mean to you?  You can connect the IP-PBX to the OCS Mediation Server via Direct SIP and have your supported IP-PBX trunk the DID numbers you will be using for Enterprise Voice directly to OCS.  This is Standalone Mode.

As time goes on, vendors will add Dual Forking support to their IP-PBXs and become qualified in the Open Interoperability Program.  An example is Cisco CUCM 7 which adds support for Dual Forking (Coexistence Mode).  Dual Forking allows an incoming call to be forked to OCS.  This means that you can share an extension between your hard phone and OCS.  When an incoming call comes in, you can have CUCM 7 ring your hard phone as well as trunk the call to OCS.  OCS will then ring all SIP enabled endpoints. When one end point picks up the phone, the IP-PBX and OCS know how to talk to each other to let them know that an endpoint has picked up and all other endpoints stop ringing.

For outbound, it’s similar in the fashion that OCS and CUCM (and other IP-PBXs that support Dual Forking) will know how to talk to each other so that when OCS places an outbound call, it will ring the other user’s SIP enabled endpoints as well as tell CUCM to ring the user’s hard phone.  Just as with incoming calls, when a user picks up an endpoint, all other endpoints stop ringing.

Share

Automatic Logons, Directors, and Client Redirections

The Resource Kit states that external users cannot be re-directed to their correct pool without the use of a Director.  This is partially untrue.  Directors are always optional in any circumstance even if you have multiple pools with external access. After talking with Tom Laciano over at LCSKid as well as a PSS escalation engineer, I think I have a pretty good understanding of the Director Role and how Pools redirect and proxy users and figured I would share with you as I have seen several posts where people have been confused about when it’s needed.

Any pool will provide redirect functionality out of the box for both internal and external users.  Now to understand how you should allow internal users to connect to pools, you must first understand how to allow Communicator 2007 clients to logon automatically (manual mode is possible but I will talk about Automatic).  You can read about how to configure Automatic Logon here.  It essentially consists of creating one of two records:

  • _sipinternaltls._tcp.<domain> – for internal TLS connections
  • _sipinternal._tcp. <domain> – for internal TCP connections (performed only if TCP is allowed)

Both of these SRV records would point to an A record which matches a Common Name (CN) or a Subject Alternative Name (SAN) on the certificate of your Front End Server.

You may want to load balance these DNS requests across multiple pools, but unfortunately, this won’t work.  But why not?  Can’t we create 2 SRV records with the same weight so they get load balanced across multiple pools?  Well, no.  The communicator client was not coded to take 2 SRV records with the same weight and allow for the use of Round Robin.  Because of this, you are only allowed to have one SRV point to a dedicated pool.  If this pool ever goes down, you can change where the SRV record is pointed.  The client will begin to utilize the new DNS changes and therefore use the new Pool once the Minimum TTL has passed for that DNS record and the client will re-query DNS and find the new Pool to log on to.

You may recall a screen below when configuring your pool:

If you take a look at the deployment guide, it states the following:

If this server or pool will also be used to authenticate and redirect requests for automatic sign-in, then select the Use this server or pool to authenticate and redirect automatic client logon requests check box. When you configure automatic client logon, you must designate one (and only one) Enterprise pool or Standard Edition Server to authenticate and redirect client sign-in requests.

As you can see, only one Pool should be designated  to authenticate and redirect client-sign in requests.  This is a bit misleading.  It doesn’t really mean that if you are deploying multiple pools you must only place a checkmark in that box for a single pool.  All it really means is that you should only have one SRV record pointed to a Pool at any given time.  In other words, in your entire environment, you should have only one SRV record for automatic logon total.

The reality of that checkmark is that it doesn’t make any modifications to OCS and regardless if you place a checkmark in the option discussed above, your Pools will be able to redirect users.  All it does is enable some additional screens during Setup to allow you to make additional and optional configuration modifications to your Pool; none of which are detrimental to your OCS install.  This is what I verified with the PSS escalation engineer I dealt with recently for a different issue.

Personally, for every Pool you configure, I would go ahead and place a checkmark in that box.  You will then be able to see all the options during Setup that you otherwise would not see.

Now because of all the above functionality with all Pools having the native functionality to redirect users, why would you want to use a Director? A Director is simply a Pool that has no users.  It’s not even required to disable the other roles on the server but is rather an optional configuration step.

One benefit of having a Director is by having your SRV record point to this one Director or Pool of Directors.  If you have a large environment, by not having a Director, you are having one of your Pools be hammered with client requests which can significantly reduce the performance of your Pool which are housing users.  So by having your SRV record point to a Director, you are reducing the performance hit on your other Pool by allowing your Director to absorb client logon requests which will then be redirected to your pool.

Another item to be aware of, is that for external users, when traffic comes from an Access Edge Server, the header information includes information about the connecting client.  Because of this, when external traffic gets sent from an Access Edge to the next-hop Pool or Director, that Pool or Director will authenticate the user and proxy SIP traffic to the correct Pool rather than re-directing the client.  This is why I stated that the Resource Kit states that external users do not get re-directed (which is true), but you do not need a Director. Having a Director in a large environment will allow this proxying to be done on a Director which will reduce the peformance hit on your next-hop Server or Pool by allowing your Director to absorb the performance hit from proxying this SIP traffic.

Another benefit to having a Director is if the server gets DOS attacked, only the Director gets taken offline and not one of the Pools which are housing users.

I would recommend housing your Director on a server that is located close to where the majority of your users are located.  So if the 75% of your users are located in North America and 25% are located in Europe, and you have one pool for North America and one Pool for Europe, I would create a Director in North America.  The Director Role can be deployed on a Standard Edition Server or an Enterprise Edition Server; although Enterprise Edition will most likely be overkill.

Share

Public IM Connectivity and Multiple SIP Domains

The main point of this article was to clear up some confusion as to how connectivity to a PIC provider works with multiple SIP domains as PIC providers ignore Subject Alternative Name (SAN) names in a certificate.  So if we have multiple SIP domains, how exactly do we achieve PIC federation with multiple SIP domains?  Well, read on and I’ll tell you!

Public IM Connectivity takes advantage of federation to allow users to talk with Yahoo, MSN, and AOL.  Allowing your users to communicate with these providers is not all or nothing.  You can choose to allow users to communicate with one PIC provider, two PIC providers, or all three.

If you take a look at the OCS doumentation, it clearly states that for Federation, you need a DNS record of _sipfederationtls._tcp.<domain>, over port 5061.  <domain> will be your SIP domain.  When you install an Access Edge, you specify an External Access Edge FQDN.  So let’s say our main SIP Domain is exchange.shudnow.net.  We may specify our External Access Edge FQDN as sip.exchange.shudnow.net.  So our Federation DNS record will be _sipfederationtls._tcp.shudnow.net that points to sip.exchange.shudnow.net.  This is possible because both namespaces match up.  You cannot have an SRV record point to an A record that belongs to a different namespace.

So, what do we do for all our other SIP domains?  Well, when we install the Edge, we will specify that we have multiple SIP domains and those SIP domains get added to the Subject Alternative Name (SAN) of the certificate.  This allows us to create different Federation DNS entries and point them to an A record that matches the FQDN specifed in one of the SAN entries of the certificate assigned to the Access Edge role.

But as stated earlier, PIC providers ignore SAN names.  So how do we allow federation to multiple SIP domains?  These SRV records are only used for open/enhanced federation where we want to allow domains to automatically federate with our OCS organization.  PIC does not use open/enhanced federation.  It uses something called Direct Federation which is where you specify what SIP domains belong to a specific Access Edge FQDN.

So let’s say in my organization, I have the following SIP domains:

  • exchange.shudnow.net
  • sales.shudnow.net
  • marketing.shudnow.net

When setting up PIC on Microsoft’s Licensing Website, you would specify that for each of these SIP domains, that the PIC provider would use the Common Name of the External Access Edge FQDN.  This may be sip.exchange.shudnow.net.  So instead of PIC accessing the SRV records for each domain which wouldn’t work unless PIC starts to not ignore SANs, each time it needs to communicate with a specific domain, it will always use the Common Name of the External Access Edge FQDN.

Because of this, your SRV records are not needed unless you plan on doing Open/Enhanced Federation. You can read more about Federation and PIC from the Edge Server Deployment Guide here.

Thanks to Tom Laciano over at LCSKid for clearing up some of this for me.

Share