Find me in different places

Entries in powershell (5)


Configuring Exchange 2013 with Powershell- Part 4: Kemp Load Balancer

I have been a little delayed in posting lately, I am hoping to finish all of the posts for Exchange 2013 now that CU 1 has been released and we can now actually deploy it.

I won't spend too much time talking about the load balancer themselves, but they are highly recomended by me, they are a small company and are quckly becoming the standard for Lync and Exchange.

To do the setup its really easy. Lets first setup our arrays

$kempurl = ""
[array]$kempVSIP = "", "", ""
[array]$kempVSPort = "443", "443", "25"
[array]$kempVSProt = "tcp", "tcp","tcp"
[array]$kempCheckPort = "https", "https", "smtp"
[array]$kempServiceType = "http", "http","gen"
[array]$kempVsDefaultGateway = "", ""
[array]$kempRealServerPort = "443", "443", "25"
[array]$kempcheckURL ="","","" #Make sure this has the same number of URLs as services being created, for SMTP leave null

and then another array of the IPs of our CAS

[array]$kempRealServers = "", ""

For this example I am just creating 3 virtual services, 1 for OWA, EAC, EWS, AS; 1 for Outlook Anywhere; and finally 1 for SMTP. Note that this is an example, There are definaly reasons to create seperate services for each directory. If you want to learn more I highly recommend listening to this epsoide of the UC Architects podcast with Greg Taylor.

Now lets get back to configuring, Before we continue, make sure you have already configured your load balancer and can get a simple connection first, look at this post for help connecting. Now lets add our new virtual services.

        $c = Get-Credential
        for($i = 0; $i -lt $kempVsIP.Length; $i ++) {

        $vs = $kempVsIP[$i]
        $port = $kempVsPort[$i]
        $name = $kempVsName[$i]
        $checkType = $kempCheckPort[$i]
        $defaultGateway = $kempVsDefaultGateway[$i]
        $servicetype = $kempServiceType[$i]
        $realserverport = $kempRealServerPort[$i]
        $checkurl = $kempcheckURL[$i]

            $uri = $kempurl+"/access/addvs?vs=$vs&port=$port&prot=$prot&NickName=""$name""&checktype=$checktype&checkport=$port&DefaultGW=$defaultgateway&VStype=$servicetype&ForceL7=yes&transparent=no"

            if($checkurl -ne ""){
                $uri += "&checkURL=$checkurl"

            $returnXML = Invoke-RestMethod -Uri $uri -Credential $c

            foreach($r in $kempRealServers){
                $uri = $kempurl+"/access/addrs?vs=$vs&port=$port&prot=$prot&rs=$r&rsport=$realserverport"

                Invoke-RestMethod -Uri $uri -Credential $c
            $uri = $kempurl+"/access/showvs?vs=$vs&port=$port&prot=$prot"
            $returnXML = Invoke-RestMethod $uri -Credential $c
            $status = $ReturnXML.Response| Select -ExpandProperty code
            if($status -eq "ok"){

So lets talk through this FOR loop, First, I found that trying to build the long string using the indexes of the arrays to cause inconsistant results, so I just set them to local variables then build the string. Next we just build the string and create the VS, add the check URL and the servers and thats it. Note that in this example we set Layer 7 transparency to off, there are many times where using L7 transparency will benifit your organization.

Thats it, the Kemp Technologies solution is pretty awesome and decently priced. If you haven't checked it out go download the demo here. Up next we will install Exchange 2013!


Setting up your Kemp Load Balancer for PowerShell

This is going to be a quick post about how to setup your Kemp Load Balancer to accept PowerShell commands.
In PowerShell V3 Microsoft added a new cmdlet called Invoke-RestMethod This command will make an HTTP post to whatever URL and then pass back the data. This lines up perfectly with Kemp releasing its RESTful API, To get the API documentation visit Kemp's site here

The first step is to upgrade your load balancer to the latest version. Kemp has been adding some new features recently so its helpful to be on the latest version. However 6.0-38 is the minimum version required

The main issue I ran into is that Kemp is requiring a https connection to make API calls. This cmdlet has an issue with self signed certs. Looking around I found a few posts of people having trouble, see here. One person here recommends creating a x509 cert but I was unable to get that to work. My workaround was using my third party wildcard cert and giving the load balancer an internal dns record instead of accessing it through the IP like the documentation suggests. My LB now has an internal dns pointer from to the management IP. Now I can make https REST calls to the dns name and it works like a charm.

So enough of talking lets get some data.
First lets set some variables

$kempurl = ""
$kempVSIP = "" #use a VIP of an existing service
$kempVSPort = "443" #the assigned port to the above VIP
$kempVSProt = "tcp" #the protocol of the Virtual Service
$c = Get-Credential # This is the credential to your load balancer

Now that we have our variables lets create a variable for the URL

$uri = $kempurl+"/access/showvs?vs=$kempVSIP&port=$kempVSPort&prot=$kempVSProt"

Now that we have our string created lets make the call

$returnXML = Invoke-RestMethod $uri -Credential $c

We use this command to see what we got back

$status = $ReturnXML.Response| Select -ExpandProperty code
 if($status -eq "ok"){ #Makes sure the post succeeded

If you want to see what the raw data looks like in your browser just type in $uri to show the URL and paste it in your browser so you can see all of the data in raw XML form.

To dig a little further you can just keep going down the tree, For example to get the Real Server IP addresses for a service just modify line 3 above to this:

$returnXML.Response.Success.Data.Rs | Select Addr

Or to get the Name of the Virtual Service:

$returnXML.Response.Success.Data | Select NickName

If you want to learn more about XML data in PowerShell I recommend reading through this


In the next post I will talk about how to create the Virtual Services we need for Exchange. But having this setup is critical to the next step

NOTE: PowerShell doesn't handle errors very well, mostly by throwing 503 errors. If you run into this type: $URI to get the URL and paste it into your browser, you will get a better explaination of what is going wrong, normally it is either an authentication issue, or it cannot find the virtual service.


Configuring Exchange 2013 with PowerShell- Part 3: Storage

So we are doing really good, we have our networking configured and the Prereqs installed, lets get to the storage.

NOTE: This is designed around using Equallogic storage, if you are using a different kind of storage you can take some of the principals and apply them to your environment.
First lets setup our variables:

$ServerName = "EX2013-01"
$lUNNames = "$serverName-DB1", "$serverName-DB2", "$serverName-Logs"
$lUNSizes = "700000","700000","100000" # Size in MB
$driveNames = "DB1", "DB2", "Logs"
$driveLetters = "K", "L","M"
$eqlGroupIP = ""
$eqlDiscoveryIP = ""
$storagePoolName = "default"

Just like when we configured our networking, make sure everything is in the right order and that the same number of items are in each array.

For this next section, make sure you have either used the default HIT Kit install directory or change the first line to the install location. Dell has a really good PowerShell tutorial that can be downloaded alongside the HIT kit.

First we need to import the module:

import-module -name "C:\Program Files\EqualLogic\bin\EqlPSTools.dll"

Next lets connect to the group. NOTE: This requires you know the credentails for the group.

Connect-EqlGroup -GroupAddress $eqlGroupIP -Credential (Get-Credential)

Now that we are connected lets make some volumes, do note that this sets the server that the script is running on with read and write access. If this doesn't follow your organization settings change the New-EqlVolumeAcl cmdlet.

        $iqn = (Get-InitiatorPort).NodeAddress

        for($i = 0; $i -lt $lUNNames.Length; $i++)
                New-EqlVolume -VolumeName $LunNames[$i] -VolumeSizeMB $lUNSizes[$i] -StoragePoolName $storagePoolName -SnapshotReservePercent 100
                New-EqlVolumeAcl -VolumeName $LunNames[$i] -InitiatorName $iqn -AclTargetType volume_and_snapshot

Now lets enable iSCSI Initiator and refresh the group to discover our newly created volumes.

        Start-Service msiscsi
        Set-Service msiscsi -StartupType Automatic

        New-IscsiTargetPortal -TargetPortalAddress $eqlDiscoveryIP
        Start-sleep -Seconds 3
        Get-iscsiTargetPortal | Update-IscsiTargetPortal
        Start-sleep -Seconds 8

Here is the fun part, This can be a little confusing so I will first explain what these 17 lines does.
First we will connect the first volume in the $LUNNames variable then initalize the disk as GPT style, bring it online, format it assign the drive letter, use 64kb allocation size and name it, then go on to the next volume until it has configured all of the volumes in the array. This step took a decent amount of time to perfect, it is only designed to run once, if you have an error you will need to delete the volumes from the SAN and start over. Disks don't like getting initalized twice.

        for($i = 0; $i -lt $lUNNames.Length; $i++)
            $volume = $lUNNames[$i]
            Get-IscsiTarget | where{$_.NodeAddress -like "*$volume"} | Connect-IscsiTarget -IsMultipathEnabled:$true -IsPersistent:$true
          [int]$disknumber =  Get-Disk| Where {$_.BusType -match "iSCSI" -and $_.OperationalStatus -eq "Offline"}| Select -ExpandProperty Number

           start-sleep -Seconds 1

            Initialize-Disk -Number $disknumber -PartitionStyle GPT
            Set-Disk -number $disknumber  -IsOffline:$false

            start-sleep -Seconds 1

            New-Partition -DiskNumber $disknumber -UseMaximumSize -DriveLetter $driveLetters[$i]  | Format-Volume -AllocationUnitSize 64kb  -FileSystem NTFS -NewFileSystemLabel $driveNames[$i] -Confirm:$false
        start-sleep -Seconds 1


So thats it, this step will create, connect and configure the storage for your Exchange server. Each piece of this can be pasted together in order and run as one script which is the way I designed it. Next up we will configure a Kemp Load Balancer.


Configuring Exchange 2013 with Powershell- Part 1: Networking

When starting the process of configuring Exchange 2013 servers the first place I wanted to start was networking, I wanted to be able to just pass in some MAC addresses of the NICs and give them IPs, I also wanted everything to happen automatically. Using Powershell 3 made this easier then it used to be in older versions.

The first step to build 3 arrays. The first will hold the MAC address, the second the new names, the third the new IP addresses. An example can look like this:

[array]$macAddresses = "00-15-5D-00-0C-58","00-15-5D-00-0C-59",,"00-15-5D-00-0C-5A","00-15-5D-00-0C-5B","00-15-5D-00-0C-5C","00-15-5D-00-0C-5D"
[array]$nicNames =    "Management", "DAG", "iSCSI 1", "iSCSI 2", "iSCSI 3", "iSCSI 4"
[array]$ipAddresses = "", "", "", "", "", ""

The second is to make a loop that will go through and configure the NICs. One thing to note: make sure you have exactly the same number of data in all 3 loops in the correct order otherwise the NICs will be incorrectly configured.

for($i=0; $i -lt $macaddresses.Length; $i++){         
     [string]$macaddress = $macAddresses[$i]          
     Get-NetAdapter | Where-Object {$_.MacAddress -eq "$macaddress"} | Rename-NetAdapter -NewName $nicNames[$i] 
     New-NetIPAddress -InterfaceAlias $nicNames[$i] -IPAddress $ipAddresses[$i] -PrefixLength 24 

This loop will through and name the NIC based on the corresponding MAC address and then give it the IP , also setting the subnet mask. All of the cmdlets going forward are based on the interface name, it is crucial to have the interface name set correctly.

Now being able to change the name and IP is cool, but what about all of the other things that needs to get done to an interface? DNS, Jumbo packets? We can do all of that in Powershell! For example for a DAG NIC, you might want to do the following:

Set-DnsClient -InterfaceIndex DAG -RegisterThisConnectionsAddress: $false
#Disables NIC features for replication network that are not needed
Set-NetAdapterBinding -Name DAG -DisplayName "Client*" -Enabled $false
Set-NetAdapterBinding -Name DAG -DisplayName "File*" -Enabled $false

How about for the iSCSI network?

Set-DnsClient -InterfaceIndex iSCSI -RegisterThisConnectionsAddress: $false
#Enable Jumbo Packets
Set-NetAdapterAdvancedProperty -Name iSCSI -RegistryKeyword “*JumboPacket” -Registryvalue 9014

Now lets put it all together as one script:

[array]$macAddresses = "00-15-5D-00-0C-58","00-15-5D-00-0C-59",,"00-15-5D-00-0C-5A","00-15-5D-00-0C-5B","00-15-5D-00-0C-5C","00-15-5D-00-0C-5D"
[array]$nicNames =    "Management", "DAG", "iSCSI 1", "iSCSI 2", "iSCSI 3", "iSCSI 4"
[array]$ipAddresses = "", "", "", "", "", ""

for($i=0; $i -lt $macaddresses.Length; $i++)
     [string]$macaddress = $macAddresses[$i]
     Get-NetAdapter | Where-Object {$_.MacAddress -eq "$macaddress"} | Rename-NetAdapter -NewName $nicNames[$i] 
     if($nicNames[$i] -eq "Management")
         New-NetIPAddress -InterfaceAlias $nicNames[$i] -IPAddress $ipAddresses[$i] -PrefixLength 22 -DefaultGateway
         Set-DnsClientServerAddress -InterfaceAlias $nicNames[$i] -ServerAddresses,
     if($nicNames[$i] -like "DAG")
        Set-DnsClient -InterfaceIndex $nicNames[$i] -RegisterThisConnectionsAddress: $false
        New-NetIPAddress -InterfaceAlias $nicNames[$i] -IPAddress $ipaddresses[$i] -PrefixLength 28
        #Disables NIC features for replication network
        Set-NetAdapterBinding -Name $nicNames[$i] -DisplayName "Client*" -Enabled $false
        Set-NetAdapterBinding -Name $nicNames[$i] -DisplayName "File*" -Enabled $false                
     if($nicNames[$i] -like "iSCSI")
       Set-DnsClient -InterfaceIndex $nicNames[$i] -RegisterThisConnectionsAddress: $false
       New-NetIPAddress -InterfaceAlias $nicNames[$i] -IPAddress $ipaddresses[$i] -PrefixLength 24
       #Enables Jumbo Packets for iSCSI NICS only
       Set-NetAdapterAdvancedProperty -Name $nicNames[$i] -RegistryKeyword “*JumboPacket” -Registryvalue 9014
write-host "Configured NIC:"$nicnames[$i]

So by running this 32 line script you can guarantee that all of you NICs are configured correctly. It is also much faster then manually doing each step.

As I have said before, my ideas may not follow best practices, make sure you do your research to make sure configure your adapters correctly.

In my next post I will talk about installing the prerequisites for Exchange 2013

NOTE: You will either need to be running Server 2012 or have already installed WRM 3.0 to use these cmdlets


Why Use Powershell?

As I was working away the other day on my very long Exchange 2013 configuration Powershell script I was asked why I was spending so much time working on the script that I would only use one time, and that it would be 'faster' if I just did everything manually. This really started me thinking, "Why am I spending so much time on this script?" It came to me later in the day while I was trying to diagnose an issue in another system, that if that system was configured in PowerShell I could go back and see the steps that were taken and that would make finding the problem easier.
Another time last year I started getting complaints of problems with OWA in the 2010 deployment, I knew the problem had to due with the virtual directory somehow. But to find which server had the problem I had to stop IIS on each CAS and check until I figured out which one of the three was not working, the issue had to do with a step that was missed when it was first deployed, if it was configured using PowerShell all of the servers would have been configured exactly the same. 

This brings me to the current project that I am working on, I understand that I probably spent more time writing it then I would have saved if I had done everything manually. But if I ever leave the organization anyone can go back and see exactly how Exchange was deployed, and mostly it was a challenge I gave myself. I wanted to see if I could completly setup Exchange without needing to do anything manually; so far I am at 520 lines and I haven't found a single step that I couldn't find a work around for.

I think Powershell is a great tool that Microsoft has built, I have been really impressed with all of the changes they made in Powershell V3, it is a huge leap forward.

Last night I was watching Jeffrey Snover give the keynote at the NIC conference, I found what he said really interesting, he talked about how the industry is changing and how to be ahead of the curve. PowerShell is so powerful and how it has changed the way people do their jobs, it has changed my job. I really enjoyed his thought about how to stay relevant you have to constantly re-invent yourself.  You can watch it here. Thanks to everyone who posted the link on Twitter. If you want to learn more about PowerShell I recommend watching this, I have only watched half so far, but it is really good, there are a ton of things I didn't know it could do. Everyday I find a new awesome feature.

Have a favorite PowerShell feature or cmdlet? Post it in the comments or just tweet it with the hashtag #PowerShell