Friday, June 14, 2013

Join VSC Created Linked Clones to a Domain

By:Rik Hoffelder

Overview
One of the features of using Citrix's VSC with XenServer and NetApp storage is to take advantage of linked clones. The benefits of doing this are very rapid virtual desktop provisioning while consuming very little storage. When the process is run it doesn't have the ability to name the VM or join it to an Active Directory domain. Rather the machines are created with the typical Microsoft naming standard of some very long unusable name, even though the VM is labeled correctly in the XenCenter console. You could of course manually rename and join each machine to the domain, which is fine if you created just a few. But what if you just created 200? Wouldn't a nice PowerShell script help solve that problem for you? Of course it would, so I wrote one to help one of my customers do just that. Because I'm a nice guy who wants to give back to all those bloggers who have helped me by sharing their knowledge, here it is for you!

One of the major challenges in a script like this is finding the right machine to rename. Remember, as I noted above the machine is labeled in XenCenter correctly, but the actual name in the operating system is completely different. Because of this we need to use the IP address of the newly created VM in order to connect with PowerShell remotely to perform the rename and domain join process. In order to get that information from the VM we would need to know its real name, right? Not with the XenServer cmdlets for PowerShell. With this little toolset I can extract the IP address based on the VM label, not its actual name.

The script is designed to operate on PowerShell 3.0 in order to take advantage of improvements in remote management that doesn't require a lot of security changes and extra configuration to establish the remote session. In fact PowerShell 3.0 offers the capability of performing the VM rename and domain join in a single cmdlet with a single reboot, so it runs much faster than with PowerShell 2.0 that required two separate operations with two reboots to perform the same task.
Obviously the script requires the PowerShell cmdlets for XenServer. So you'll want to download that from here. Now because Citrix does a poor job of documenting the SDK, you'll need this article to help you register the Snapin with PowerShell.

Script Facts
In my script I use a temporary CSV file to build a list of VMs based off the user's input in the GUI. I then run a Foreach loop to process each VM from that CSV file. So make note, you'll either need to modify the script to use your own path or create a C:\TEST directory to store the CSV. The script also creates an import file named oddly enough, IMPORT.CSV in that same directory. The IMPORT.CSV file builds a list of the new VMs so that you may import them into a catalog in XenDesktop. Hey I tried to make this easy!

To run the script you must start PowerShell with elevated rights. In other words it must Run as Administrator from the management computer with the XenServer cmdlets installed and registered. This is a requirement to support the remote calls to the VM using PowerShell 3.0.
There are also a few places you'll need to edit the script to customize it for your environment. First, I create a dropdown menu of target OU aliases so the admin using the script doesn't have to remember a long distinguished name. So you will need to find the following section and update it to match your need:
    
[array]$OUs = "Windows 7 Admin OU - CORP Domain",
"Windows 7 OU - CORP Domain" <-NO COMMA HERE!!

Be sure to include the alias name in quotation marks "Alias" followed by a comma. The last line should not be followed by a comma as noted above.
Next you will need to locate this section to map the alias selected in the dropdown menu to the actual OU path. Update the items highlighted in red below to match your environmental need.
$OUPath = $Combobox.SelectedItem.ToString()

 
If ($OUPath -eq "Windows 7 Admin OU - CORP Domain"){
$OUPath = "OU=Win 7 Dedicated Admin,OU=Virtual Desktops,OU=Workstations,DC=CORP,DC=COM"
}
If ($OUPath -eq "Windows 7 OU - CORP Domain"){
$OUPath = "OU=Win 7 Dedicated,OU=Virtual Desktops,OU=Workstations,DC=CORP,DC=COM"
}


Last but not least, to build the import file you will need to know the connection string used by XenDesktop. To get that connection string you will need to perform an export from an existing catalog. Use this Citrix article as a reference if you aren't familiar with catalog export. Open the export file and use the data highlighted below:
 
  
To replace the value highlighted in red in the code snippet below:
        ### Build XD Catalog Import CSV ###
Write-Host "Building XenDesktop Import CSV"
$XDCsvHeader = "[VirtualMachinePath],[ADComputerAccount],[AssignedUsers]"
$XDConnectBase = "XDHyp:\connections\PVS Task Pool\"


Running the Script
That's it, you are not ready to kick the tires and give it a try. After launching the script a PowerShell-based GUI will open. Complete all of the fields and then click OK.
 

  
You will be prompted to enter the credentials of a user with rights to join machines to the domain:
           
                                                       

 
After clicking OK you will be prompted for the credentials of the local administrator for the VM. This is used to access the VM using PowerShell remoting.

                                     
           
Once you click OK the script takes off and does its thing. When the script has completed it will notify you that the IMPORT.CSV file is ready for import. That's it, enjoy!

Join-Domain:XD.PS1
function
Show-MessageBox ($title, $msg) {

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") |
Out-Null


[Windows.Forms.MessageBox]::Show($msg, $title,
[Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]::Information,
[System.Windows.Forms.MessageBoxDefaultButton]::Button1,
[System.Windows.Forms.MessageBoxOptions]::DefaultDesktopOnly) |
Out-Null

}

 
[void]
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

 
[void]
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

 

 
## Create and build the form from the function ##

 
    $Form
=
New-Object
system.Windows.Forms.Form
        $Form.Text =
"VSC Rename & Domain Join"
        $Icon
=
[system.drawing.icon]::ExtractAssociatedIcon($PSHOME
+
"\powershell.exe")

        $Form.Icon = $Icon
        $Form.AutoScroll = $True
        $Form.AutoSize = $True
        $Form.AutoSizeMode =
"GrowAndShrink"
# or GrowOnly
        $Form.MinimizeBox = $False
        $Form.MaximizeBox = $False
        $Form.WindowState =
"Normal"
# Maximized, Minimized, Normal
        $Form.SizeGripStyle =
"Hide"
# Auto, Hide, Show
        $Form.ShowInTaskbar = $False
        $Form.Opacity =
1.0
# 1.0 is fully opaque; 0.0 is invisible
        $Form.StartPosition =
"CenterScreen"
# CenterScreen, Manual, WindowsDefaultLocation, WindowsDefaultBounds, CenterParent
        $Font
=
New-Object
System.Drawing.Font("Times New Roman",12,[System.Drawing.FontStyle]::Bold) # Font styles are: Regular, Bold, Italic, Underline, Strikeout
        $Form.Font = $Font

 
    $Label
=
New-Object
System.Windows.Forms.Label
        $Label.Text =
"Enter base Computer Name (i.e., PCX-IT-):"
        $Label.AutoSize = $True
        $Form.Controls.Add($Label)

 
    $Textbox
=
New-Object
System.Windows.Forms.TextBox
        $Textbox.Location =
New-Object
System.Drawing.Size(10,50)
        $Textbox.Size =
New-Object
System.Drawing.Size(260,300)
        $Form.Controls.Add($Textbox)
    
    $Label2
=
New-Object
System.Windows.Forms.Label
        $Label2.Text =
"Enter base Computer Number(i.e., 100):"
        $label2.Location =
New-Object
System.Drawing.Size(10,100)
        $Label2.AutoSize = $True
        $Form.Controls.Add($Label2)

 
    $Textbox2
=
New-Object
System.Windows.Forms.TextBox
        $Textbox2.Location =
New-Object
System.Drawing.Size(10,150)
        $Textbox2.Size =
New-Object
System.Drawing.Size(40,60)
        $Form.Controls.Add($Textbox2)

 
    $Label3
=
New-Object
System.Windows.Forms.Label
        $Label3.Text =
"Enter number of computers created (i.e., 100):"
        $label3.Location =
New-Object
System.Drawing.Size(10,200)
        $Label3.AutoSize = $True
        $Form.Controls.Add($Label3)

 
    $Textbox3
=
New-Object
System.Windows.Forms.TextBox
        $Textbox3.Location =
New-Object
System.Drawing.Size(10,250)
        $Textbox3.Size =
New-Object
System.Drawing.Size(40,60)
        $Form.Controls.Add($Textbox3)

 
    $Label4
=
New-Object
System.Windows.Forms.Label
        $Label4.Text =
"Enter Domain Name (i.e., contoso.com):"
        $label4.Location =
New-Object
System.Drawing.Size(10,300)
        $Label4.AutoSize = $True
        $Form.Controls.Add($Label4)

 
    $Textbox4
=
New-Object
System.Windows.Forms.TextBox
        $Textbox4.Location =
New-Object
System.Drawing.Size(10,350)
        $Textbox4.Size =
New-Object
System.Drawing.Size(260,300)
    $Textbox4.Text =
"na.erac.com"
        $Form.Controls.Add($Textbox4)

 
    $Label5
=
New-Object
System.Windows.Forms.Label
        $Label5.Text =
"Select the target OU:"
        $label5.Location =
New-Object
System.Drawing.Size(10,400)
        $Label5.AutoSize = $True
        $Form.Controls.Add($Label5)

 

[array]$OUs =
"Windows 7 Admin OU - CORP Domain",

"Windows 7 OU - CORP Domain"

 

$Combobox
=
New-Object
System.Windows.Forms.ComboBox

$Combobox.Location =
New-Object
System.Drawing.Size(10,450)

$Combobox.Size =
New-object
System.Drawing.Size (500,300)

Foreach ($Item
in
$OUs){

$Combobox.Items.Add($Item)
}

$Form.Controls.Add($Combobox)

 
    $Label6
=
New-Object
System.Windows.Forms.Label
        $Label6.Text =
"Enter XenServer Host or Pool Master:"
        $label6.Location =
New-Object
System.Drawing.Size(10,500)
        $Label6.AutoSize = $True
        $Form.Controls.Add($Label6)

 
    $Textbox6
=
New-Object
System.Windows.Forms.TextBox
        $Textbox6.Location =
New-Object
System.Drawing.Size(10,550)
        $Textbox6.Size =
New-Object
System.Drawing.Size(260,300)
        $Form.Controls.Add($Textbox6)

 
    $Label7
=
New-Object
System.Windows.Forms.Label
        $Label7.Text =
"Enter XenServer Username:"
        $label7.Location =
New-Object
System.Drawing.Size(10,600)
        $Label7.AutoSize = $True
        $Form.Controls.Add($Label7)

 
    $Textbox7
=
New-Object
System.Windows.Forms.TextBox
        $Textbox7.Location =
New-Object
System.Drawing.Size(10,650)
        $Textbox7.Size =
New-Object
System.Drawing.Size(260,300)
        $Form.Controls.Add($Textbox7)

 
    $Label8
=
New-Object
System.Windows.Forms.Label
        $Label8.Text =
"Enter XenServer Password:"
        $label8.Location =
New-Object
System.Drawing.Size(10,700)
        $Label8.AutoSize = $True
        $Form.Controls.Add($Label8)

 
    $Textbox8
=
New-Object
System.Windows.Forms.TextBox
        $Textbox8.Location =
New-Object
System.Drawing.Size(10,750)
        $Textbox8.Size =
New-Object
System.Drawing.Size(260,300)

$Textbox8.PasswordChar =
'*'
        $Form.Controls.Add($Textbox8)

 
    $OKButton
=
New-Object
System.Windows.Forms.Button
        $OKButton.Location =
New-Object
System.Drawing.Size(100,800)
        $OKButton.Size =
New-Object
System.Drawing.Size(90,60)
        $Font1
=
New-Object
System.Drawing.Font("Times New Roman",10,[System.Drawing.FontStyle]::Regular) # Font styles are: Regular, Bold, Italic, Underline, Strikeout
        $OKButton.Font = $Font1
        $OKButton.Text =
"OK"
        $OKButton.Add_Click({$x=$TextBox.Text;$Form.Close()})

 

$Form.Controls.Add($OKButton)

$Form.ShowDialog()

 
###Build Computer Name Varibles ###
$BaseCompName
=
$Textbox.Text
$BaseCompNum
=
$Textbox2.Text
[String]$ComputerName =
[string]$BaseCompName +
[string]$BaseCompNum
$NumComputers
=
$Textbox3.Text
[Decimal]$b =
[Decimal]$BaseCompNum +
[Decimal]$NumComputers

 
### Varibles to join PC to domain ###
$Credential
=
Get-Credential
-Message
"Enter Domain Account"
-UserName
"CORP\USER"
$Domain
=
$Textbox4.Text

 
### XenServer Varibles ###
$XenHost
=
$Textbox6.Text
$XenUser
=
$Textbox7.Text
$XenPwd
=
$Textbox8.Text

 
### Translate OU selection to actual OU distinguishedName ###
$OUPath
=
$Combobox.SelectedItem.ToString()

 

If ($OUPath
-eq
"Windows 7 Admin OU - NA Domain"){

$OUPath
=
"OU=Win 7 Dedicated Admin,OU=Virtual Desktops,OU=Workstations,DC=CORP,DC=COM"
}

If ($OUPath
-eq
"Windows 7 OU - NA Domain"){

$OUPath
=
"OU=Win 7 Dedicated,OU=Virtual Desktops,OU=Workstations,DC=CORP,DC=COM"
}

 
### Check for Comps.CSV, if it exists remove it! ###
$CSV
=
Test-Path
C:\Test\Comps.CSV

 
if ($CSV
-eq
"True") {
    Remove-Item
C:\Test\Comps.CSV
}

 
$Column
=
"Name,IP"
Add-Content
C:\Test\Comps.CSV
$Column

 
#Import-Module "C:\Program Files\Microsoft System Center 2012\Virtual Machine Manager\bin\psModules\virtualmachinemanager\virtualmachinemanager"

 
Add-PSSnapin
XenServerPSSnapIn
Connect-XenServer
-Server
$XenHost
-UserName
$XenUser
-Password
$XenPwd

 
### Loop through each computer name and get its IP address. Add the name and IP to the CSV ###
Do {$vm
=
Get-XenServer:VM
-Server
$XenHost
-NameFilter
$ComputerName
    ;$arr
=
Get-XenServer:VM_guest_metrics.Networks
-VMGuestMetrics
$vm.guest_metrics
    ;$IP
=
$arr["0/ip"]
    ;$AddComp
=
$ComputerName
+
","
+
$IP
    ;Add-Content
C:\Test\Comps.CSV
$AddComp

 
    ;[Decimal]$NewCompNum =
[Decimal]$BaseCompNum +
1
    ;[String]$BaseCompNum =
[String]$NewCompNum
    ;$ComputerName
=
$BaseCompName
+
$BaseCompNum
}
Until ($NewCompNum
-eq
$b)

 
### read from CSV and list comps ####
$VMComps
=
Import-CSV
C:\Test\Comps.CSV
$LocalCred
=
Get-Credential
-Message
"Enter the Local Admin Account"
-UserName
"ADMINISTRATOR"

 
### Rename/restart each VM ###
Foreach ($VMComp
in
$VMComps) {

Rename-Computer
-ComputerName
$VMComp.IP -NewName $VMComp.Name -Force
-LocalCredential $LocalCred


Add-Computer
-ComputerName
$VMComp.Name -Credential $Credential
-DomainName
$Domain
-OUPath
$OUPath
-LocalCredential
$LocalCred
-Restart
-Options
JoinWithNewName
}

 

 
### Build XD Catalog Import CSV ###
Write-Host
"Building XenDesktop Import CSV"
$XDCsvHeader
=
"[VirtualMachinePath],[ADComputerAccount],[AssignedUsers]"
$XDConnectBase
=
"XDHyp:\connections\PVS Task Pool\"

 
$XDCSV
=
Test-Path
C:\Test\Import.CSV

 
if ($XDCSV
-eq
"True") {
    Remove-Item
C:\Test\Import.CSV
}

 
Add-Content
C:\Test\Import.CSV
$XDCsvHeader

 
Foreach($VMComp
in
$VMComps){

$XDCsvLine
=
$XDConnectBase
+
$VMComp
+
".vm,NA\"
+
$VMComp
+
"$"

Add-Content
C:\Test\Import.CSV
$XDCsvLine
}
Write-Host
"XenDesktop Import CSV Complete."
Write-Host
"The file, IMPORT.CSV, is located on C:\TEST\"

 

 
### Remove host affinity so it'll failover properly! ###
Foreach ($VMComp
in
$VMComps){

Set-XenServer:VM.Affinity
-VM
$VMComp
-Server
$NULL
}

 
####### Done! #####
Write-Host
"Completed"



More information on PowerShell


Read more!

Wednesday, September 5, 2012

Why one VPX is a bad Choice

By:Rick Davis


Usually, you get exactly what you pay for, so when you skimp on device redundancy, that's exactly what happens.

Server virtualization has brought us efficient and cost effective disaster recovery options.   Snapshots, v-motion, and storage replication are the underpinnings of this redundancy.  However, its incorrect to apply these server techniques to virtualized Application Delivery Controllers (ADCs).  The constraints of memory utilization, product licensing, and time/complexity-to-recovery are nuances which uniquely apply to ADC devices and keeps them from enjoying the same methods of redundancy that virtualized server workloads leverage. 

A redundant and available architecture always starts with TWO appliances.  It's just not appropriate to architect an enterprise quality high availability solution with less.   It might look good on paper, but the recovery time will clearly exceed estimates- and likely won't work at all.

In some business continuity architectures, 3 devices may be appropriate.  And in the future, Spotted Clustering, across multiple data centers may change best practices, but that's still a ways away and will require additional licensing.  In the mean time, the best practice solution should always start with a pair of devices in each data center.     

Read more!

Wednesday, January 11, 2012

Using Rich HTML with Send-MailMessage

By:Rik Hoffelder
The Send-MailMessage CMDLET can be a useful tool in many ways. However it has some limitations that can be easily overcome with a little PowerShell scripting and HTML tags. I was recently asked by a customer for some ideas to help automate a notification message when a new employee joins the company. This was to be a part of a larger auto-user provisioning script that would send a welcome message to the new employee with various company information and helpful links. This particular task was performed manually by an administrator using a custom template in Outlook (.OFT file). Our mission was to convert the OFT and manual process into something we could do with Send-MailMessage.

Before I go too much further into the solution I want to thank and credit my customer, Paul Savage of McCarthy Building Companies, for pulling this all together and sharing his code with us. Thank you Paul!

First it is important to understand that Send-MailMessage allows you to use HTML in the message body using the –HtmlAsBody switch. Next we take advantage of the Get-Content and Out-String cmdlets to build the Body parameter of the message. This allows us to create a txt file with HTML tags that contain the message content and provide the same formatting that was used in the body of the OFT. So with the assistance of Paul's Sharepoint Developer we were able to create the message body text file as shown below:

Body.Txt

<style>

.colorchange {

    color:#4F81BD;

}

</style>

<html xmlns="http://www.w3.org/1999/xhtml">

<body>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt">Welcome to Company Name Here!&nbsp; We are glad you are here!&nbsp;&nbsp; Below are some helpful items to get you started.</p>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt">Our company provides a library of documents to help you with your daily tasks and responsibilities <a href="http://intranet.company.com/employee%20%documents ">by clicking here.</a>.</p>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt">The <a href=" http://intranet.company.com/hr">Human Resources Policies Manual</a> should be thoroughly reviewed.&nbsp; By replying, you are acknowledging receipt and consent to the Policies outlined in the Manual.</p>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt">In addition to policies noted above, please take a moment to review and understand a few additional guidelines when using our Information Systems.&nbsp; They are: </p>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt">&nbsp;1.&nbsp; Use of the Mass Distribution Function Company E-Mail System- <a href="http://intranet.company.com/hr/Mass+Email+Distribution.doc">http:// intranet.company.com/hr/Mass+Email+Distribution.doc</a><br />

&nbsp;2.&nbsp; Spam E-Mails- <a href="http:// intranet.company.com/hr/Email-SPAM-Filtering-Service.aspx">http:// intranet.company.com/hr/Email-SPAM-Filtering-Service.aspx</a> <br />

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt">Again, the intent of these policies and procedures are to provide for necessary safeguards that protect all employees interests and our company.&nbsp;&nbsp; If you have any questions on the policies or procedures, feel free to contact the I.T. Help Desk at 800-555-1234.&nbsp; They will forward your questions to the appropriate personnel who can assist you.</p>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt">Thank you and welcome aboard!</p>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:10pt"><b>Jane Doe</b> | <span class="colorchange">Director</span></p>

<p style="font-family:Tahoma, Geneva, sans-serif; font-size:8pt; color:#808080">Human Resources | Company Name, Inc.<br/>(800) 555-1234</p>

</body>

</html>

Next we reference the body file in the script as shown below:

SendWelcomeEmail.PS1

$messageParameters = @{

Subject = "Welcome Aboard!"

Body = Get-Content "C:\body.txt" | out-string

From = "Welcome@company.com"

To = "NewEmployee@company.com"

SmtpServer = "smtp.company.com"

}

Send-MailMessage @messageParameters –BodyAsHtml

With a little bit of tweaking you can easily pass a variable to the script by adding the following:

SendWelcomeEmail.PS1 NewEmp

(Example .\SendWelcomeEmail.PS1 john.doe@company.com)

Param(

    [string] $NewEmp = ""

)

function ValidateParams

{

$validInputs = $true

$errorString = ""

if ($NewEmp -eq "")

{

    $validInputs = $false

    $errorString += "`missing parameter: The employee email address parameter is required. Please enter a valid email address. Example: john.doe@company.com"

}

if (!$validInputs)

{

    Write-error "$errorString"

}

return $validInputs

}

### Validate the parameters ###

$ifValidParams = ValidateParams;

if (!$ifValidParams) { exit; }

$messageParameters = @{

Subject = "Welcome to the Company!"

Body = Get-Content "C:\body.txt" | out-string

From = "Welcome@company.com"

To = &NewEmp

SmtpServer = "smtp.company.com"

}

Send-MailMessage @messageParameters –BodyAsHtml


 

Good luck and happy scripting!




More information on PowerShell

Read more!

Monday, July 18, 2011

RightFax Integration Module in a Collective

By:Rik Hoffelder


The RightFax Shared Services Collective is a wonderful feature that allows load balance and failover in a shared nothing architecture. There is an exception to this however the Integration Module cannot run on multiple nodes when sharing the same Inbox. When this is done you end up with duplication of faxes.

As shown below a typical environment will use two or more collective servers with the IM. It reads from Inboxes on a clustered file share with all application servers feeding to this location. This makes for a great high availability solution, but doesn't solve the IM problem.



To workaround this problem most administrators disable the IM service on all but one collective server. In the event of a primary server failure they manually start the service on another collective member. This is an acceptable workaround however it has two flaws. The first is that it’s a manual process. The second issue occurs when the original server is recovered. You have to remember to shutdown the IM service on the other server.

As a field consultant I have encountered this situation on numerous occasions. In a few cases the project required that I provide some mechanism to fail this over automatically. There are a few ways to accomplish this outside of RightFax depending on what you have available. So for those of you that have struggled with this situation here’s how I accomplished it using a VB Script. I also have lightly tested PowerShell version. Note that neither script is sanctioned or supported by OpenText.

The script, RFMonitor.VBS, uses a CSV input file named RFMonitor.INI. This file provides the services and servers used as input varibles for the script. The fields are RF Service Monitored, Primary Server, Secondary Server. I run the script as a scheduled task on a separate server (management server) every 10 minutes.

It essentially pings the primary to make sure it’s up, if not it fails over to the secondary immediately. If the primary is up it verifies the services are running, if not it fails over. It also checks to see if the IM service is running on both servers. If so, it stops it on the secondary. This has been tested extensively and is running in a couple of production environments.

RFMonitor.INI

RFPROD,FAXSRV02,FAXSRV01,
RFSERVER,FAXSRV02,FAXSRV01,
RFDOCTRANS,FAXSRV02,FAXSRV01,
RFPAGE,FAXSRV02,FAXSRV01,
RFQUEUE,FAXSRV02,FAXSRV01,
RFREMOTE,FAXSRV02,FAXSRV01,
RFRPC,FAXSRV02,FAXSRV01,
RFIsoConv,FAXSRV02,FAXSRV01,
CapaSync,FAXSRV02,FAXSRV01,
RFEMAIL,FAXSRV02,FAXSRV01,
RFMIME,FAXSRV02,FAXSRV01,
RFWORK1,FAXSRV02,FAXSRV01,
RFWORK2,FAXSRV02,FAXSRV01,
RFWORK3,FAXSRV02,FAXSRV01,


RFMonitor.VBS

On Error Resume Next
'##### Open and parse CSV for varibles #####
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("rfmonitor.ini", ForReading)
Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine
arrFields = Split(strLine, Chr(44))
strSvc = arrFields(0)
strMainSrv = arrFields(1)
strBkupSrv = arrFields(2)
Call CheckMainSrv()
Loop
'##### Check Main Server Availability #####
Sub CheckMainSrv()
strComputer = "."
Set objShell = CreateObject("WScript.Shell")
Set objWshScriptExec = objShell.Exec("ping " & strMainSrv)
Set objStdOut = objWshScriptExec.StdOut

While Not objStdOut.AtEndOfStream
strLine = objStdOut.ReadLine
If InStr(strLine,"Reply from") Then
Call CheckBkupSrv()
Else
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 1, strMainSrv & " " & "is unreachable, starting services on" & " " & strBkupSrv
Call StartBkupSrv()
End If
Wend
End Sub

'##### Check Backup Server Availability #####
Sub CheckBkupSrv()

strComputer = "."
Set objShell = CreateObject("WScript.Shell")
Set objWshScriptExec = objShell.Exec("ping " & strBkupSrv)
Set objStdOut = objWshScriptExec.StdOut

While Not objStdOut.AtEndOfStream
strLine = objStdOut.ReadLine
If InStr(strLine,"Reply from") Then
Call CheckSvcType()
Else
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 1, strMainSrv & " " & "is unreachable, checking services on" & " " & strMainSrv
Call StartMainSrv()
End If
Wend

End Sub

'##### Check Service Type #####
Sub CheckSvcType()

If strSvc = "RFPROD" Then
Call CheckIMSvc()
ElseIf strSvc = "RIGHTFAXONFAX011024" Then
Call CheckGPSvc()
ElseIf strSvc = strSvc Then
Call CheckSvcState()
End If
End Sub

'##### Check State of Services Fail As Needed #####
Sub CheckSvcState()

strQ1 = "Select * from Win32_Service"
strQ2 = "Where Name = '" & strSvc & "'"
strQuery = strQ1 & " " & strQ2

Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strMainSrv & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery(strQuery)
For Each objService in colListOfServices
If ObjService.State = "Stopped" Then
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 1, objService.DisplayName & " " & "service has stopped on" & " " & strMainSrv
Call SvcFail()
Else
End If
Next

End Sub
'##### Check Integration Module service state #####
Sub CheckIMSvc()
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strMainSrv & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery("Select * from Win32_Service Where Name ='RFPROD'")
For Each objService in colListOfServices
If ObjService.State = "Stopped" Then
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 1, objService.DisplayName & " " & "service has stopped on" & " " & strMainSrv
Call StartRFProdSvc()
Else
Call CheckDupIMSvc()
End If
Next
End Sub
'##### Check Get PAID service state #####
Sub CheckGPSvc()
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strMainSrv & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery("Select * from Win32_Service Where Name ='RIGHTFAXONFAX011024'")
For Each objService in colListOfServices
If ObjService.State = "Stopped" Then
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 1, objService.DisplayName & " " & "service has stopped on" & " " & strMainSrv
Call StartGPSvc()
Else
Call CheckDupGPSvc()
End If
Next
End Sub
'##### Start RFPROD Service Routine #####
Sub StartRFProdSvc()
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strBkupSrv & "\root\cimv2")
Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RFPROD'")
For Each objService in colServiceList
errReturn = objService.StartService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been started on" & " " & strBkupSrv & " " & "Failover Completed!"
Next
End Sub
'##### Start GetPaid Service Routine
Sub StartGPSvc()

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strBkupSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RIGHTFAXONFAX011024'")

For Each objService in colServiceList
errReturn = objService.StartService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been started on" & " " & strBkupSrv & " " & "Failover Completed!"
Next
End Sub

'##### Check RFPROD Serivce on second node, stop if running #####
Sub CheckDupIMSvc()

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strBkupSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RFPROD'")

For Each objService in colServiceList
If ObjService.State = "Running" Then
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 2, objService.DisplayName & " " & "Service is running on" & " " & strBkupSrv & " " & "stopping service to prevent duplicates!"

errReturn = objService.StopService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been stopped on" & " " & strBkupSrv
Else
End If
Next
End Sub

'##### Check RFPROD Serivce on second node, stop if running #####
Sub CheckDupGPSvc()

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strBkupSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RIGHTFAXONFAX011024'")

For Each objService in colServiceList
If ObjService.State = "Running" Then
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 2, objService.DisplayName & " " & "Service is running on" & " " & strBkupSrv & " " & "stopping service to prevent duplicates!"

errReturn = objService.StopService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been stopped on" & " " & strBkupSrv
Else
End If
Next
End Sub

'##### Other Service Failure #####
Sub SvcFail()

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strMainSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RFPROD'")

For Each objService in colServiceList
If ObjService.State = "Running" Then
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 2, objService.DisplayName & " " & "Service is running on" & " " & strMainSrv & " " & "stopping service to failover!"

errReturn = objService.StopService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been stopped on" & " " & strMainSrv
Else
End If
Next

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strMainSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RIGHTFAXONFAX011024'")

For Each objService in colServiceList
If ObjService.State = "Running" Then
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 2, objService.DisplayName & " " & "Service is running on" & " " & strMainSrv & " " & "stopping service to failover!"

errReturn = objService.StopService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been stopped on" & " " & strMainSrv
Else
End If
Next
Call StartRFProdSvc()
Call StartGPSvc()
End Sub


'##### Main Server Fail, Start Backup Server #####
Sub StartBkupSrv()

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strBkupSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RFPROD'")

For Each objService in colServiceList
If objService.State = "Stopped" Then
errReturn = objService.StartService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been started on" & " " & strBkupSrv
Else
End If
Next

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strBkupSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RIGHTFAXONFAX011024'")

For Each objService in colServiceList
If objService.State = "Stopped" Then
errReturn = objService.StartService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been started on" & " " & strBkupSrv
Else
End If
Next
End Sub


'##### Backup Server Fail, Start Main Server #####
Sub StartMainSrv()
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strMainSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RFPROD'")

For Each objService in colServiceList
If objService.State = "Stopped" Then
errReturn = objService.StartService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been started on" & " " & strMainSrv
Else
End If
Next

Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strMainSrv & "\root\cimv2")

Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='RIGHTFAXONFAX011024'")

For Each objService in colServiceList
If objService.State = "Stopped" Then
errReturn = objService.StartService()
Set LogError = Wscript.CreateObject("Wscript.Shell")
LogError.LogEvent 0, objService.DisplayName & " " & "service has been started on" & " " & strMainSrv
Else
End If
Next
End Sub


RFMonitor.PS1 (PowerShell Version)

Like I noted earlier, this has barely been tested and never used in a production environment. If you have something better or cleaner, please feel free to share. This was one of the first PS scripts I wrote over 6 years ago, so I’m sure it could use some improvements.


#Script Notes: Ping server, if alive Check service state on main from ini file, start on bkup if stopped,
#checks for RFPROD and GetPaid specificially.

####Check Main and Backup for duplicate RFAXONFAX011024 running, stop on Backup
Function CheckGPDup {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFAXONFAX011024'"
$State = $colitem.state
if ("$State" -eq "running")
{write-host "Stopping RFPROD service on Backup server to prevent duplicate faxes."
$colItem1 = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFAXONFAX011024'"
ForEach ($objItem in $colItem1) {$objItem.StopService()}}
else
{write-host "No duplicate RFAXONFAX011024 service running on" $Bkup"."}
}


####Check Main and Backup for duplicate RFPROD running, Stop on Backup
Function CheckRFProdDup {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFPROD'"
$State = $colitem.state
if ("$State" -eq "running")
{write-host "Stopping RFPROD service on Backup server to prevent duplicate faxes."
$colItem1 = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFPROD'"
ForEach ($objItem in $colItem1) {$objItem.StopService()}}
else
{write-host "No duplicate RFPROD service running on" $Bkup"."
ChkGPDup}
}


####Failover (Stop on main, start on Backup) for any failed service
Function FailOverRFProdGP {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Main" –filter "Name= 'RFPROD'"
ForEach ($objItem in $colItem) {$objItem.StopService()}

$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Main" –filter "Name= 'RFAXONFAX011024'"
ForEach ($objItem in $colItem) {$objItem.StopService()}

$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFPROD'"
ForEach ($objItem in $colItem) {$objItem.StartService()}

$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFAXONFAX011024'"
ForEach ($objItem in $colItem) {$objItem.StartService()}

write-host "RFPROD and RFAXONFAX011024 has been started on" $bkup"."}


####Check all other services, per ini
Function CheckSvcs {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Main" –filter "Name= '$Name'"
$State = $colitem.state
if ("$State" -eq "stopped")
{write-host "Failing over IM and GP to" $Bkup"."
FailOverRFProdGP}
else
{write-host "Service" $Name "is already running on" $Main"."
CheckRFProdDup}
}


####Start RFAXONFAX011024 on Backup
Function StartGPBkup {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFAXONFAX011024'"
ForEach ($objItem in $colItem) {
$objItem.StartService()}
write-host "RFAXONFAX011024 has been started on" $bkup"."}

####Start RFPROD on Backup
Function StartRFProdBkup {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Bkup" –filter "Name= 'RFPROD'"
ForEach ($objItem in $colItem) {
$objItem.StartService()}
write-host "RFPROD has been started on" $bkup"."}

####Check RFAXONFAX011024
Function CheckGP {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Main" –filter "Name= 'RFAXONFAX011024'"
$State = $colitem.state
if ("$State" -eq "stopped")
{write-host "Starting RFAXONFAX011024 service on Backup server."
StartGPBkup}
else
{write-host "Service RFAXONFAX011024 is already running on" $Main"."
CheckSvcs}
}

###Check RFPROD
Function CheckRFProd {$colItem = get-wmiobject -class "Win32_Service" -namespace "root\cimv2" -computername "$Main" –filter "Name= 'RFPROD'"
$State = $colitem.state
if ("$State" -eq "stopped")
{write-host "Starting RFPROD service on Backup server."
StartRFProdBkup}
else
{write-host "Service RFPROD is already running on" $Main"."
CheckGP}
}

#### Open file and pasre varibles ####
$INIFILE = Import-CSV -path "C:\Documents and Settings\rhoffelder\desktop\rfmonitor.ini"

ForEach ($objItem in $INIFILE) {
$Name = $objItem.Name
$Main = $objItem.Main
$Bkup = $objItem.Bkup

#### Ping Main Srver ####
$ping = new-object System.Net.NetworkInformation.Ping
$status = $ping.send($Main)
write-host $status
if($status) {CheckRFProd}

#### Ping Backup Server ####
else {$ping = new-object System.Net.NetworkInformation.Ping
$status = $ping.send($Bkup)
write-host $status
if($status) {FailOverRFProdGP}
Else {"Both servers offline!"}}}
write-host "finished!"





More information on RightFax




Read more!
Microsoft Virtualization, Citrix, XENServer, Storage, iscsi, Exchange, Virtual Desktops, XENDesktop, APPSense, Netscaler, Virtual Storage, VM, Unified Comminications, Cisco, Server Virtualization, Thin client, Server Based Computing, SBC, Application Delivery controllers, System Center, SCCM, SCVMM, SCOM, VMware, VSphere, Virtual Storage, Cloud Computing, Provisioning Server, Hypervisor, Client Hypervisor.