Here’s a neat little feature I wasn’t aware of until recently. You can make a .NET Dictionary object case-insensitive. i.e. myDictionary[“ABC”] returns the same as myDictionary[“aBc”]. You can read more at:
Thursday, September 1, 2011
Wednesday, August 31, 2011
Returning Multiple Disparate Result Sets as XML From SQL Server Using FOR XML EXPLICIT
<orderformdata> <orders> <order> <orderdate>1/1/2011</orderdate> <ordertypeid>1</ordertypeid> <orderamount>1000.00</orderamount> </order> <order> <orderdate>1/1/2011</orderdate> <ordertypeid>1</ordertypeid> <orderamount>1000.00</orderamount> </order> </orders> <references> <ordertypes> <ordertype id="1" name="Professional Services"/> <ordertype id="2" name="Product"/> <ordertype id="3" name="Support Contract"/> </ordertypes> </references> </orderformdata>
Using SQL Servers FOR XML clause, it’s quite easy to output a result set as XML. With the EXPLICIT directive you can have a great amount of control over how the XML is rendered. However, the examples tend to show how to create XML from a single result set or nested records (say orders and order details). So how to return the above?
Monday, August 22, 2011
Fun with WCF, WSDL & the F5 (Updated)
So this week we pushed out an early iteration of the latest version of our integration tool to a customer as a proof-of-concept for evaluation. One of the features they were most interested in evaluating is the web service interface. Our current customers interact with the existing version of the integration tool through FTP and a UI.
Although the backend functionality is fairly stable, the web service layer itself is not that mature. Not too big a deal as it’s a fairly thin wrapper over our existing service layer. We created, tested and delivered a test .NET client that seemed to work fine and so were reasonably confident that there wouldn’t be too many hiccups.
Not so much. We spent the better part of the day simply trying to get their tool to retrieve and understand our WSDL. The good news is that we finally got it to work.
Tuesday, August 16, 2011
Setting Up a Test Reverse Proxy/SSL Accelerator Environment (or for that matter, a budget production one)
Given that, according to our operations people, BIG-IP boxes start at 5 figures and rapidly go up, they seemed unwilling to purchase one for me. Additionally, for security issues, and to remain in compliance with our SAS-70, I don’t have access to our production environment for testing.
Tuesday, July 26, 2011
Execute-Remotely
One of the really powerful features of PowerShell is the ability to run commands remotely. I used this when I wanted to loop through my test servers from the build machine and run my MSI installs.
The following script loops through an array of machines and returns a list of .log files under the C:\Windows folder. One thing to note. The remote session doesn't have access to any of the local script variables, so we pass them as parameters using the -Args argument of the Invoke-Command cmdlet, receiving them using the param statement inside the remote script block.
$AgentServers = @("MYSERVER1", "MYSERVER2");
ForEach ($AgentServer in $AgentServers) {
$Session = New-PSSession -ComputerName $AgentServer;
$FilePattern = "*.log";
$Arguments = $FilePattern
Invoke-Command -Session $Session -Args $Arguments -Script {
param($FilePattern)
Set-Location "C:\Windows"
Get-ChildItem -Include $FilePattern -Recurse
}
Remove-PSSession -Session $Session
}
Now the first time you run the remote script above, it may well fail. Why? Because you forgot to enable remoting on the target machine. On each machine you want to run remote sessions on you’ll have to run:
Enable-PSRemoting
Note that you’ll have to start PowerShell as administrator to perform this.
Also take note that we’re killing each session using Remove-PSSession when we’re done with it as there is a 5 session limit on each remote server and it’s pretty easy to hit that if you forget to close out prior ones.
On that note, there’s a quick trick on clearing out all those orphaned sessions:
Get-PSSession | Remove-PSSession
Get-PSSession will return a list of all open session objects, piping them to Remove-PSSession which subsequently closes them out.
Monday, July 25, 2011
Run-Script
As I mentioned in an earlier post, just like the DOS command line has batch files, PowerShell can run PowerShell script files. These have the .PS1 extension.
So go ahead, throw a bunch of PowerShell commands together into a .PS1 file in NotePad and save it. Next, navigate to the folder you saved it to and double-click on it. Awesome, you just ran your first PowerShell script! What do you mean it didn’t run? It came up in NotePad?
Oh yeah. By default, for security reasons, double-clicking on a PowerShell script doesn’t run it. To do that you have a couple of options. First is to open up PowerShell and run the script from PowerShell's command line. To do that you simply type:
C:\MyScripts\MyCoolScript.ps1
If your current folder is already C:\MyScripts you’ll type:
.\MyCoolScript.ps1
Note that we prefaced the script file with “.\”. Without that, PowerShell thinks it’s a built-in command and yells that it doesn’t recognize it.
OK, you’ve typed it in, hit enter and away it goes, no? Except that all you see is:
File C:\MyScripts\MyCoolScript.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details.
At line:1 char:11
+ .\test.ps1 <<<<
+ CategoryInfo : NotSpecified: (:) [], PSSecurityException
+ FullyQualifiedErrorId : RuntimeException
$%!#$%!^&*#…OK, ok, breath…again…deep breath…Yet another security “feature”. With this one type at the command line:
Set-ExecutionPolicy RemoteSigned
Or
Set-ExecutionPolicy Unrestricted
You only need to run this once and it will allow you to run scripts. When setting RemoteSigned, any local scripts will run as-is, but remote scripts will need to be digitally signed. Like Enable-PSRemoting, you’ll need to open PowerShell as administrator.
But I’m not an administrator! The execution policy is not a true security setting, it’s simply there to help prevent the inadvertent running of malicious scripts. You can actually set the execution policy when you open PowerShell from the command line by using the –ExecutionPolicy argument, using the same RemoteSigned or Unrestricted value. This will only set the execution policy for that session.
Note that when using the Set-ExectuionPolicy you can set the scope of the setting using the –Scope argument to be either the current process (-Scope Process, same as setting it on the command line), current User (-Scope CurrentUser) or the local machine (-Scope LocalMachine). The default value is LocalMachine.
Sunday, July 24, 2011
Run-Executable
OK, so one of the promises of PowerShell is that not only does it do all this cool new stuff, but all your favorite DOS commands are aliases of the new PowerShell commands, but that you can also run your old executables just like you used to….
Well not so fast….
Running a straight executable such as MyExe.exe, works just fine as long as you’re in the exe’s folder or it resides in a folder in the PATH environment variable.
Try passing to start passing it command line arguments and things start to get squirly.
This is due to how PowerShell handles command line arguments. PowerShell has three types of arguments:
- Positional – these arguments are expect to be in a specific position such as:
- Run-Command “Param1Value”
- Named – these arguments are proceeded by the parameter name and can be in any position as follows (note that I’m passing a string literal to Param2 and a variable value to Param1:
- Run-Command –Param2 “Param2Value” –Param1 $Param1Value
- Switch Parameters – These parameters are either bolean parameters that are either present or not such as:
- Run-Command –SwitchedOn
PowerShell tries to treat executable paramters the same way. This works great if your executable uses the same format, but if you’re trying to run MsiExec you’ve got a problem.
MsiExec /i MyInstall.msiWill work fine, but try:
$MyCustomInstallFolder = “D:\Custom Program Files”
MsiExec /I MyInstall.msi TARGETDIR=$MyCustomInstallFolder
Not so much. the problem is is that what is actually getting passed is:
MsiExec /i MyInstall.msi “TARGETDIR=D:\Custom Program Files”MsiExec ends up ignoring the TARGETDIR parameter because PowerShell didn’t recognize TARGETDIR as a named parameter and treated it as a positional parameter, surrounding it with quotes because the expanded string contained spaces.
After fighting with this for quite some time (and doing a fair amount of Googling), I ended up writing the following function that utilizes the .NET Process object to execute an executable, passing a string variable for the entire command line.
Function Start-Process
{
Param([string]$Executable,
[string]$Arguments,
[string]$WorkingFolder,
[int]$Timeout = 240000,
[switch]$ShowStandardOutput)
Write-Host ("Starting Process, {0}" -F $Executable);
Write-Host ("Command Line Args: {0}" -F $Arguments);
Write-Host ("Working Folder: " -F $WorkingFolder);
Write-Host ("Timeout: {0}" -F $TimeOut);
Write-Host ("Show Std Out: {0}" -F $ShowStandardOutput);
$ProcessInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo;
$ProcessInfo.FileName = $Executable;
$ProcessInfo.WorkingDirectory = $WorkingFolder
$ProcessInfo.UseShellExecute = $false;
if ($ShowStandardOutput) {
$ProcessInfo.RedirectStandardOutput = $true;
}
else {
$ProcessInfo.RedirectStandardOutput = $false;
}
$ProcessInfo.RedirectStandardError = $false;
$ProcessInfo.Arguments = $Arguments;
$ProcessInfo.CreateNoWindow = $false;
$ProcessInfo.ErrorDialog = $false;
$Process = New-Object -TypeName System.Diagnostics.Process;
$Process.StartInfo = $ProcessInfo;
$Process.EnableRaisingEvents = $false;
$Process.Start();
if ($ShowSTandardOutput) {
$Output = $Process.StandardOutput.ReadToEnd();
$Output;
}
if (-not $Process.WaitForExit($Timeout)) {
$Process.Kill;
$ProcessInfo;
throw "Start-Process - Process timed out";
}
if ($Process.ExitCode -ne 0) {
$ProcessInfo;
throw "Start-Process - Process failed with exit code - " + $Process.ExitCode
};
}
Since then I’ve read that you can actually run Cmd.exe, passing the entire command line (executable and arguments) as a string, thus doing something similar to what I’m doing with the Process object.