Posts RSS Comments RSS 117 Posts and 170 Comments till now

Archive for January, 2008

Calculated Properties

nicad49 asked
“Can you explain the last part of each script? Specifically the @{n=’Servers’;…}etc. I understand that we are creating some sort
of an array, but if I run it as is, I don’t get any values returned.”

This is great question. I see examples like this used all the time without explanation. Unless you had the chance to review all the
help files from all the different CMDLets there is good chance you missed some REALLY cool stuff. This is one of those things
hidden in the bowels of Help files and Books.

Before I go into my specific example, lets look at this excerp from http://technet.microsoft.com/en-us/library/bb978655.aspx

“You can also use Select-Object to add calculated properties to an object. To add a calculated property, use the Property
parameter and type a hash table for the parameter value. Create an Expression key in the hash table and assign to the key an
expression that calculates a value. The hash table can also have a Name key.”

Basically what we can do is add a property to an object that is calculated. This calculation could be from other properties or just a
change to the way the data is configured. Here is how you use this feature @{name=”SomeName”;expression={”Script block used
to provide value(s)”}}.

Some important things to note:
* The Name can be anything, even a valid property name.
* Expression is a scriptblock. What does this mean? It means that you can put any code you want in there… even an entire script
and what ever is returned is put in the property you created.

Now that we have some foundation lets look back at my code. Here is the first example.

  1. $MF = (New-Object -com MetaFrameCOM.MetaFrameFarm)
  2. $MF.Initialize(1)
  3. $MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,AppName,DistinguishedName,
  4.   @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
  5.   @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsByServer.Csv -noType

Now the first problem maybe cut/paste not working correct so make sure line three is all one big long line.

Next lets zoom in on the Important line… number three

  1. $MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,AppName,DistinguishedName,
  2.   @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
  3.   @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsByServer.Csv -noType

First we have the $MF.Servers. This returns an array of Citrix Server Objects with all sorts of properties. We only really care about
the Applications and the Server name so we go to the next step.

Select-Object ServerName -expand Applications. This code takes each one of the MFCom Server objects and Strips out everything
but the ServerName and the Applications. The reason for -expand is because Applications is an array of MFCom Application
Objects and we want them all.

Here is the biggy
Select-Object ServerName,AppName,DistinguishedName,@{n=’Users’;e={$_.Users | %{$_.UserName}}},@{n=’Groups’;e={$_.Groups | %{$_.GroupName}}}
This section takes the object we Created with the ServerName and the Applications and Passes on the ServerName, but extracts
information about the applications we want to know like AppName and DistinguishedName. The problem is that we want the users
and groups to. The problem with this is these properties are (like Server and Application) arrays of MFCom User or MFCom Group
objects. Enter calculated fields.

Lets look each calculated field
@{n=’Users’;e={$_.Users | %{$_.UserName}}}
This basically says create a property named Users and add the values from $_.Users, but only give me the UserName… not all the
other properties.
@{n=’Groups’;e={$_.Groups | %{$_.GroupName}}}
Same thing except for with Groups.

Perhaps multilevel Citrix Nesting isn’t the best place to get your teeth wet on calculated properties, but I hope I cleared it up a bit
for you.

An Interactive Case for Powershell (Yet more Citrix Fun!)

I was recently in a discussion on Brian Madden Forums about listing Citrix Information and exporting to CSV. It seemed like a
perfect fit for Powershell so I converted the VBScripts to Powershell (of course taking an 85 line script to 3 lines. Convert is
hardly the correct term.)

Here is the Forum Topic
http://www.brianmadden.com/Forum/Topic/95285

Here is my Code. There were three scripts, so I made three as well.

  1. # Apps by Server CTXApps_by_Server_w_Users
  2. $MF = (New-Object -com MetaFrameCOM.MetaFrameFarm)
  3. $MF.Initialize(1)
  4. $MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,AppName,DistinguishedName,
  5.        @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
  6.        @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsByServer.Csv -noType
  1. # Apps with Servers
  2. $MF = New-Object -com MetaFrameCOM.MetaFrameFarm
  3. $MF.Initialize(1)
  4. $MF.Applications | Select-Object AppName,DistinguishedName,
  5.       @{n="Servers";e={$_.Servers | foreach{$_.ServerName}}} | export-Csv C:\AppsWithServer.Csv -noType
  1. # Apps with Servers and Users CTXApps_w_Servers_w_Users
  2. $MF = New-Object -com MetaFrameCOM.MetaFrameFarm
  3. $MF.Initialize(1)
  4. $MF.Applications | Select-Object AppName,DistinguishedName,
  5.       @{n="Servers";e={$_.Servers | foreach{$_.ServerName}}},
  6.       @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
  7.       @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsWithServerandUsers.Csv -noType

If you notice in my three scripts they all start with the same two lines. Effectively these are one liners that could be used
interactively. I think this does a great job of showing Citrix Admins how nicely Powershell will fit in to their daily lives.
Things that use to take 100s of lines of script writing can now be done interactively at a shell.

Get-InstalledSoftware (what software is installed?)

nishant left a comment on my “Powershell, Remote Registry and You! Part 1 (Overview)” post.

Nishant asked “I want a complete list of software installed on a remote machine using Powershell.”

I decided to post on this because it is brought up a lot. Unfortunately, getting a complete list of install applications is not that straightforward. Some applications do not store information in the Registry so the best we can do is list software that provides Uninstall regkey info. That is what the script below does. While this is not perfect it does a pretty good job getting the info.

  1. Param($srv=$env:ComputerName)
  2. $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Srv)
  3. $key = $regKey.OpenSubkey("Software\Microsoft\Windows\CurrentVersion\Uninstall",$false)
  4. $key.GetSubKeyNames()

It is common to see people use Win32_Product to list installed apps, but it is important to point out this only list applications installed by an MSI installer.

Perhaps the best options is to combine both of these options.

  1. # Get-InstalledSoftware
  2. Param($srv=$env:ComputerName)
  3. $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Srv)
  4. $key = $regKey.OpenSubkey("Software\Microsoft\Windows\CurrentVersion\Uninstall",$false)
  5. Write-Host
  6. Write-Host "Getting Software in Uninstall Key" -Fore Green
  7. Write-Host ("-"*60) -Fore Gray
  8. $key.GetSubKeyNames()
  9. Write-Host
  10. Write-Host "Getting Software From Win32_Product" -Fore Green
  11. Write-Host ("-"*60) -Fore Gray
  12. get-wmiobject Win32_Product -comp $srv | foreach{$_.Name}
  13. Write-Host

Consortio Services Podcast

I had the chance to spend some time with the Consortia folks last night discussing Powershell and its place for the administrator in the next couple of years. Please stop by and listen to the Podcast.

http://www.cstechcast.com/home.aspx?Episode=8

More Info:
Consortio Services was founded by 3 technology experts who have over 35 years of combined expertise in nearly all facets of Information Technology. They have gained real world knowledge in such diverse areas as Networking Systems infrastructure, Telephony support, Operating System maintenance, Database Architecture, Design, and Administration, and Software design and support.

Eric Beehler - Network Technologies Consultant
Eric Johnson - Database Technologies Consultant
Joshua Jones - Operating Systems Consultant

Visit the website here
http://www.consortioservices.com/index.shtml

Secure String Followup

As a follow up on Converting Secure String discussed here: http://bsonposh.com/modules/wordpress/?p=66

Joel “Jaykul” Bennett released this script: http://www.powershellcentral.com/scripts/116

This is a slightly more secure way to store passwords in a text file for use later. Check it out.

Format-XML (Journey to Pretty XML output)

I ran across a need to use Export-CliXML to produce human readable XML. Since, I never really cared what the output looked like so I let the blob go.

Now I care :)
I sent a message to the Powershell Dev Team and as always they are super speedy in response. (less than 1hr… that is INSANE.)

Enter Format-XML
http://blogs.msdn.com/powershell/archive/2008/01/18/format-xml.aspx

Help for Export-Clixml
http://technet.microsoft.com/en-us/library/bb978636.aspx

Thanks Again Jeffrey and Team!

p.s. I dont believe I ever said the output (Export-CliXML) looked like crap… just not Pretty :)

Converting Secure String

I recently heard a question about ConvertTO-SecureString and ConvertFrom-SecureString.

These CMDLets may be a little confusing so let’s talk a little about them.

Before you read the rest, it is key to understand what a Secure String actually is.

A Secure String is simple text that is encrypted in memory. This lets you store passwords or other secure data in memory without having to concern yourself with someone snooping your session or dumping your memory contents to get your data.

More Info Here
http://msdn2.microsoft.com/en-us/library/system.security.securestring.aspx

ConvertTo-SecureString: http://technet.microsoft.com/en-us/library/bb978707.aspx
I think this where it gets a little confusing for people. ConvertTo is meant to take an encrypted string and store it as a Secure String. Specificially for the output from ConvertFrom-SecureString. It will allow you use -asPlainText w/ -Force if you just want to convert a piece of text to Secure String.

ConvertFrom-SecureString: http://technet.microsoft.com/en-us/library/bb978629.aspx
This is used to convert a Secure String to text. It is import to note…. this is NOT the original text but the string representation of the encryption. This is a great tool for exporting the Secure String to a file in an encrypted form.

One other note before I show the code. By default (unless you provide a key) it uses Windows Data Protection API (DPAPI). This is VERY important. This process it is fairly secure, but can only decrypted by you on that specific machine. On the flip side… using a Key is not near as secure and some would argue security by obscurity.

Now… lets look at what we have… nothing magic here. We have three functions Export-EncryptedText. Import-EncryptedText, and Get-EncryptedText.
Export-EncryptedText: This gets a Secure String and exports to a file
Import-EncryptedText: This imports a Secure String Text from a file and returns a Secure String
Get-EncryptedText: This converts a Secure String into the orginal Text

We use “Read-Host -AsSecureString” to create convert our text to a secure string.

  1. function Export-EncryptedText{
  2.     param($text,$file,$key)
  3.     if($key){ConvertFrom-SecureString -SecureString $text -key $key | out-file $file}
  4.     else{ConvertFrom-SecureString -SecureString $text | out-file $file}
  5. }
  6. function Import-EncryptedText{
  7.     Param($file,$key)
  8.     $textFromFile = Get-Content $file
  9.     if($key){ConvertTO-SecureString $TextFromFile -key $key}
  10.     else{ConvertTO-SecureString $TextFromFile}
  11. }
  12. function Get-EncryptedText($text) {
  13.     $Ptr = [System.Runtime.InteropServices.Marshal ]::SecureStringToCoTaskMemUnicode($text)
  14.     $result = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)
  15.     [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($Ptr)
  16.     $result
  17. }
  18.  
  19. # Example Use
  20. $dataToStore = Read-Host -AsSecureString
  21. "My String To Encrypt Here"
  22.  
  23. # Key… needs to be a 16, 24, or 32 byte array
  24. $key = (200..231)
  25.  
  26. # Use this to put the password in a file
  27. Export-EncryptedText -text $dataToStore -file c:\data\testfile.secure -key $key
  28.  
  29. # To get the password back you do this
  30. $myText = Import-EncryptedText c:\data\testfile.secure -key $key
  31.  
  32. # To see the text use Get-SecurePass
  33. Get-EncryptedText $myText