Wednesday, June 13, 2012

Automate IT processes with C#

Before I begin, I would like to note that this process can automate anything...

We (the IT department) have been getting asked 4 times a day to reset IIS on a specific server. The issue is code related, so there is nothing we can do but wait on the programmers to correct the issue. In the meantime, I wanted to automate the process and make it amusing at the same time. There are 24 better ways to accomplish what I am trying to do, but decided to do the following:

  1. Create a batch script that does an "IISRESET"
  2. Convert the batch script to an EXE
  3. Write a C# app with a funny GUI that says "That was Easy"
  4. Embedd the IISRESET.EXE into the app
  5. Thinapp the app into its own virtual space
First things first, lets write the batch script that will accomplish the IISRESET on a remote server that anyone can run. First issue is that IISRESET doesn't have a username or password switch. Second, is that all users need permission to run an IISRESET which is bad. Third issue is that I can't even do a "RUNAS" because I want it fully automated and don't want any input from the user. So I decided to copy down "PSEXEC" from a file server to the users local computer and then run "PSEXEC" with the "IISRESET" command so that it runs from the server itself. Below is how I accomplished this:
@echo off
@color0E

:: Location of PSEXEC on File Server
set vxsrc=\\10.0.10.100\psexec\PsExec.exe

:: XCOPY Destination
set vxdest=%homepath%

:: XCOPY user profile location 
set vuserhome=%vxdest%

:: Where we copied the PSEXEC EXE
set vcommand=%vxdest%\PsExec.exe

:: Server we want to run IIS on
set vserver=10.0.10.175

:: PSEXEC credentials
set vuser=NGN\psexec_svc
set vpass=Psexec039482!

:: XCOPY PSEXEC to users home directory
xcopy /C /F /K /Y %vxsrc% %vxdest%

:: Run IISRESET with PSEXEC
%vcommand% \\%vserver% -u %vuser% -p %vpass% "iisreset"
Next is to convert the batch file into an EXE so people can't see the code. I understand you can decompile an exe, but we are going to do this multiple times. 

To convert the app I used "Bat to EXE Converter" by f2ko from CNET. Below is the download link:


Now on to the fun part with creating a GUI. I made my GUI with C# .NET 2.0 through Visual Studio 2010.  "I am not a programmer", so when it comes to proper button, form, and namespace names, I just use the default names and make them work. Below I embedded the IISRESET.EXE and unload it into a temp directory to be ran by the app.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Media;
using System.Reflection;
using System.IO;

namespace Form1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            SoundPlayer s = new SoundPlayer(textBox1.Text);
            s.Play();
            this.ExtractAndExecute();
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            this.ExtractAndExecute();
        }

        private void ExtractAndExecute()
        {
            byte[] contents;
            string filePath = Path.GetTempPath();
            filePath = Path.Combine(filePath, "IISRESET.exe");
            //using (FileStream fs = new FileStream(

            using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Form1.Resources.IISRESET.exe"))
            {
                contents = new byte[s.Length];
                s.Read(contents, 0, (int)s.Length);                
            }
            File.WriteAllBytes(filePath, contents);
            System.Diagnostics.Process.Start(filePath);
        }
    }
}
For the button image I used the below image
For the Sound that is played I used:
Now that I have the application built, I want to compress the app into one "EXE" file. This will make it easy and also have one more factor of compressing the EXE file. For this, I use VMWARE Thinapp. After thinapping the application, I now have one EXE named "IISRESET.EXE" that runs in its own virtual space.

This tutorial shows how to do a simple IISRESET, but anything can be accomplished with this method. You could fully automate anything and inject it into the "EASY" button.

Friday, June 8, 2012

Setting up Multiple PVS'd Citrix XA STA's with Unique UID's

Deciding to setup a new Citrix Provisioned XenApp Environment? One of the issues that users run into is the fact that all of your XenApp Servers look at the same vDisk. This in return gives every XA Server the same "STA UID". 

In this scenario we will act like we work for a small company with no money, and can only afford a Citrix XenApp 5 environment with a CSG (Citrix Secure Gateway). We will say that we have 3 XA servers named XA01, XA02, and XA03. Our CSG will be named CSG01. When we go through the CSG configuration tool on CSG01, XA01 is added as an STA with no issues. XA02 and XA03 although can't be added because they have the same STA UID. So how do we make this unique? I have seen other people write a simple PS or VBS script that can do this. Below is a PS script that Michael Stevelman wrote.
# Change XenApp 6 STA ID to MAC Address
# Created by Michel Stevelmans - http://www.michelstevelmans.com

# Set location of CtxSta.config file
$Location = "${env:ProgramFiles(x86)}\Citrix\system32\CtxSta.config"

# Get the MAC address of the first NIC
$Nics = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IpEnabled = TRUE"
foreach ($Nic in $Nics)
    {
    $Mac = $Nic.MacAddress
    Break
    }

# Remove colons from MAC Address
$Sta = $Mac.Replace(":", "")

# Replace STA ID with MAC Address
(Get-Content $Location) | Foreach-Object {$_ -replace '^UID=.+$', "UID=STA$Sta"} | Set-Content $Location

# Restart Citrix XML Service
Restart-Service CtxHTTP
I although, wanted to write a batch script that I could integrate in with my personality strings inside PVS with a string of "EnableUniqueStaUID", and set it to 0 or 1 depending on if I wanted to enable it. Below is my script that recreates the STA UID config file on server startup. 

What my script accomplishes:
  • Figures out if your are running x86 or x64
  • Finds the active NIC and copies the MAC into a variable
  • Parses out a clean version of the MAC and appends it to "STA"
  • Recreates the STA config file with the new STA UID
Below is my Script...

@echo off
@color 0E
::
:: Script by Keith Smith
:: smith.itpro@gmail.com
::
:: Add this script to the personalities of your Provisioned Servers
:: This will delete and recreate the CtxSta.config file with a unique STA UID
::
set ctxstax86="C:\Program Files\Citrix\system32\CtxSta.config"
set ctxstax64="C:\Program Files (x86)\Citrix\system32\CtxSta.config"
set configlocation=null

:: Create a unique STA UID with the STA and the MAC
:getstauid

FOR /F "skip=3" %%G IN ('getmac') DO set MAC=%%G
For /F "tokens=1,* delims=-" %%a in ("%MAC%") DO set MACa=%%a
For /F "tokens=2,* delims=-" %%b in ("%MAC%") DO set MACb=%%b
For /F "tokens=3,* delims=-" %%c in ("%MAC%") DO set MACc=%%c
For /F "tokens=4,* delims=-" %%d in ("%MAC%") DO set MACd=%%d
For /F "tokens=5,* delims=-" %%e in ("%MAC%") DO set MACe=%%e
For /F "tokens=6,* delims=-" %%f in ("%MAC%") DO set MACf=%%f
set cleanmac=%MACa%%MACb%%MACc%%MACd%%MACe%%MACf%
set stauid=STA%cleanmac%

::check location of file if it 32 bit or 64 bit or not found
:check
                If exist %ctxstax86% set configlocation=%ctxstax86%
                If %configlocation%==%ctxstax86% goto startscript 
                If exist %ctxstax64% set configlocation=%ctxstax64%
                If %configlocation%==%ctxstax64% goto startscript
                goto ctxstana

:: Delete the old instance of the CtxSta.config and replace with a unique one
:startscript
cls
DEL /Q /F %configlocation% 

echo [GlobalConfig] >> %configlocation%
echo. >> %configlocation%
echo UID=%stauid% >> %configlocation%
echo. >> %configlocation%
echo TicketVersion=10 >> %configlocation%
echo. >> %configlocation%
echo TicketTimeout=100000 >> %configlocation%
echo. >> %configlocation%
echo MaxTickets=100000 >> %configlocation%
echo. >> %configlocation%
echo LogLevel=0 >> %configlocation%
echo. >> %configlocation%
echo MaxLogCount=10 >> %configlocation%
echo. >> %configlocation%
echo MaxLogSize=20 >> %configlocation%
echo. >> %configlocation%
echo LogDir=C:\Program Files\Citrix\logs\ >> %configlocation%
echo. >> %configlocation%
echo ; Allowed Client IP addresses >> %configlocation%
echo ; To change, substitute * with client IP addresses. Use ";" to seperate IP addresses/address ranges. >> %configlocation%
echo ; To specify a range of IPs always use StartIP-EndIP. >> %configlocation%
echo ; For example, AllowedClientIPList=192.168.1.1;10.8.1.12-10.8.1.18;123.1.2.3 >> %configlocation%
echo. >> %configlocation%
echo AllowedClientIPList=* >> %configlocation%
echo. >> %configlocation%
echo ; SSL only mode >> %configlocation%
echo ; If set to on, only requests sent through HTTPS are accepted >> %configlocation%
echo SSLOnly=off >> %configlocation%
echo. >> %configlocation%

:: Restart Citrix XML Service
net stop CtxHTTP && net start CtxHTTP

cls
goto endscript

:ctxstana
cls
goto endscript

:endscript
exit
I do agree that my script is a little bit longer than a PS script, but it gets the job down, and I know exactly what it is doing. Now that my STA's have different UID's, I can add all three XA servers to my CSG.
  • XA01 - STA005056A1502A
  • XA02 - STA603FAE356386
  • XA03 - STA287BBA142194
To check your STA UID, you can easily do it by running one of the two commands depending on your OS architecture:
  • x86
    • TYPE "C:\Program Files\Citrix\system32\CtxSta.config"
  • x64
    • TYPE "C:\Program Files (x86)\Citrix\system32\CtxSta.config"
Below is an example of the above command(s) output:
c:\>TYPE "C:\Program Files\Citrix\system32\CtxSta.config"
[GlobalConfig]

UID=STA005056A1502A

TicketVersion=10
TicketTimeout=100000
MaxTickets=100000
LogLevel=0
MaxLogCount=10
MaxLogSize=20
LogDir=C:\Program Files\Citrix\logs\
; Allowed Client IP addresses
; To change, substitute * with client IP addresses. Use ";" to seperate IP addre
sses/address ranges.
; To specify a range of IPs always use StartIP-EndIP.
; For example, AllowedClientIPList=192.168.1.1;10.8.1.12-10.8.1.18;123.1.2.3
AllowedClientIPList=*
; SSL only mode
; If set to on, only requests sent through HTTPS are accepted
SSLOnly=off

Where’s my enabled Users?

Where’s my enabled Users? I’m going through and fine tuning our Proodpoint Spam solution and noticed one of the filters needs to be updated....