Monthly Archives: December 2011

Powershell: Working with strings – Data Manipulation


In my first post, PowerShell: Working with Strings I talked about the types of strings and how to make them. Now lets talk about how to manipulate that data.

What it comes down to is one of two types of string data. It’s either a single string, or a collection of strings. Get-Content returns a collection of strings as do many other cmdlets and most cmd programs (ipconfig)  so we are going to work with these two sets.


$str1 = "hello world!"

$str2 = "hello","world"

The $str2 is just a way to simulate what Get-Content would give us.

First, lets start by looking to see what we can do with a basic string:


$str1 | gm

You’ll notice you have a System.String object and a bunch of methods to work with. One thing to keep in mind about a String is that its immutable which basically means you can’t change the value of the variable, you’ll need to store it to a new variable with the new value you want. Lets try to change our basic string to upper case:


$str1.ToUpper()

$str1

You’ll notice when we just type $str1 its back to lower case (ie, immutable).  So if we wanted to keep that we’d need to store it some place, but lets keep it as it is.

Looking at my original string you’ll see its all lower case, so lets change that h to an H. There are a few ways we can do it, and since we only have one H in this string, we have even more options. Most of these are a bit over kill, but they allow me to show an assortment of methods.


#simple replace

$str1.replace("h","H")

#sub string grabs from index to end in this case

#we just slap an H in front, good if there are multiple h's

"H" + $str1.substring(1)

"H$($str1.substring(1))"

#now in to the over kill

$str1.Insert(0,"H").Remove(1,1)

$str1.Remove($str1.IndexOf("h"),1).Insert(0,"h")

There are a few neat things to note here, the first being tacking on to methods.

$object.method().method().method() and so on

Which would work something like this

(($object.method()).method()).method()

Too many parentheses to follow? The point is, as you tack them on the new method works on the result of the previous, in our case it’s always a string, but that could change depending on what you are doing.

Another thing is the nesting of methods which can be handy, but some times harder to follow, in a script, it usually worth while to add a few extra characters to make it readable.

As you noticed when you did the Get-Member (shorthand gm) on the string, there were a lot of methods and it had the definition for each but most were packed in and it was hard to read, so if you are curious you can go to MSDN or you can type a method in without the () like so:


$str1.substring

Doing so will spit out something like this:

OverloadDefinitions

——————-

string Substring(int startIndex)

string Substring(int startIndex, int length)

Which shows the different ways to call the method. The first item is the return type, in both cases here it’s a string. Then it shows the name of the method and its param options. We have two cases here, a StartIndex and a StartIndex and Length. In the first case we provided a starting point and it goes to the end of the string, in the next case we provide a starting point and a distance.


$str1.substring(6)

$str1.substring(6,5)



So far everything we've been doing is .NET methods from the String object but PowerShell has some neat ways to handle this as well.



$str1 -replace "h", "H"

This works just as well but there a two very important notes here the first being that the .NET methods are case-sensitive and PowerShell is not, try this:


$str1 -replace "E", "h"

$str1.replace("E","h")

In the first case the e was replaced and in the second it was not.

The other thing to keep in mind is that many of the PowerShell comparison operators use regular expressions rather than just simple character replacement as the .NET methods do. I’ll go in to RegEx and the different operators in my next post on strings.

Now, I mentioned two types of string data and really only talked about one. The reason I made mention of both is so that you don’t try to do these operations on the return of a get-content or on the output of a command app.

One Thing I would like to point out is the Length property on our objects $str1 and $str2.


$str1.length

$str2.length

$str2[0].length

You’ll notice that the return on $str2.lenth is 2, which is how many items are in the array. This may seem obvious now, but when working with PowerShell you may not always know ahead of time if it’s an array or not (Another post on arrays some day.)

If however you have data like that and would like to make it a single string, you can do so with Join. Again you have the .NET and PowerShell way to do this.


$str2 -join " "

[string]::join(" ",$str2)

You’re probably wondering where the [string]::join came from. That’s a static method on the String class that was not listed by the gm we did before. If you looked at the MSDN docs you’ll see some methods with an S next to them, indicating they are static, but, who wants to leave PowerShell?


gm -i $str1 -Static

Thankfully the Get-Member cmdlet has a Static param that will show us the static methods available to an object of that type! Have I mentioned before how much I love Get-Member?

There is a ton more that can be done with strings but, I hope this enough to allow you to dig in to it more on your own!

Until next time (RegEx!)

Creating DataStores From EMC Storage for your VMWare ESX environment with PowerCLI


I recently built my VMWare ESX environment out and we’ve got a VNX 5700 for our SAN. At some point we’ll be implimenting Site Recovery Manager and it was recommended that we use 1TB LUNs which is fine, but I’ve ended up with a lot of LUNs to map. Doing this in the client would just suck and the CLI is ok, but its a lot of tracking down to give them meaningful names.

How I prefer to name my DataStores is to use something that lets me know where it lives, like this VNX_P100_L15. This indicates the storage array (only have one VNX) and then the Pool that the LUN lives in (to indicate its performance level) and then the LUN number.

If you were to try and do this by hand its rather painful, so I wrote a script to do this for me.

Things you’ll need:

  • NaviSecCLI in your system path and security configured (would be easy to add perms to this if needed).
  • PowerCLI installed and connected (Connect-VIServer).
  • The IP of an SP on your VNX (any EMC array should work here).
  • The Storage Group you use for your VNX.
  • The Storage accessible to your environment (Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs will make sure all hosts see the storage)
## EMC info
$sg = "<your Storage Group Name, Case matters"
$ip = "IP of an SP"
## VM info
$vmhost = Get-VMHost "Any ESX Hostname" ##any host since all should see storage

###EMC data gathering
## get ALU/HLU
$rtn = (naviseccli -h $ip storagegroup -list -gname $sg) -match "\s+\d+\s+\d+"

$luns = @()
$rtn | %{$luns += ,(-split $_)}

##[0] = host(HLU), [1] = storage(ALU)

##Find what pool the LUN belongs to.
for($i=0;$i -lt $luns.count; $i++)
{
    $pool = ((naviseccli -h $ip lun -list -l $luns[$i][1]) -match "pool name")[0].substring(10).trim()
    ## in my case remove "pool" in pool names are something like "Pool 100", you're might be different
    $pool = $pool.replace("Pool ","")

    ##Build Lun Names
    $luns[$i] += "VNX_P$($pool)_L$($luns[$i][1])"
}

## VNX part
##get host lun numbers with the lun itself.
$VMLUNS = $VMHost | Get-SCSILun |?{$_.vendor -eq "DGC" -and $_.capacitymb  -gt 1024} | select *,HOSTLUN
$VMLUNKey = (get-view $vmhost).Config.StorageDevice.ScsiTopology | ForEach {$_.Adapter} | ForEach {$_.Target} | ForEach {$_.Lun}
$VMLUNS | %{$key = $_.key; $_.HOSTLUN = $VMLUNKey |?{$_.scsilun -eq $key} | select -exp lun -un}

##currently used canonicals to make sure you dont try to create a used datastore
$inuse = Get-Datastore | where {$_.type -eq 'vmfs'} | get-view | %{$_.info.vmfs.extent} | select -ExpandProperty diskname
$vmluns = $VMLUNS | ? {$inuse -notcontains $_.canonicalname}

##Create the DataStores
foreach($vmlun in $vmluns)
{
    $hostlun = $vmlun.HOSTLUN
    $lun = $luns | where { $_[0] -eq $hostlun}

    New-Datastore -VMHost $vmhost -Name $($lun[2]) -Path $($vmlun.canonicalname) -BlockSizeMB 2 -Vmfs
}

## rescan so all hosts see new DataStores
Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs

You’ll notice I set the BlockSizeMB to be 2, which limits VM disk to 512GB, which is generally enough (good middle ground for SRM)

Im new to ESX so any feedback would be welcomed.

Hope it helps!

Using WMI to link a Disk Volume to a Physical Disk with PowerShell


Recently someone asked how to map a disk volume to a physical disk. At first I figured this would be real easy and just involve two WMI calls. Come to find out, it’s not that easy, but not impossible.


$diskdrive = gwmi win32_diskdrive

foreach($drive in $diskdrive)

{

out-host -InputObject "`nDrive: deviceid-$($drive.deviceid.substring(4)) Model - $($drive.model)"

##partition

$partitions = gwmi -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=`"$($drive.DeviceID.replace('\','\\'))`"} WHERE AssocClass = Win32_DiskDriveToDiskPartition"

foreach($part in $partitions)

{

Out-Host -InputObject "`tPartition: $($part.name)"

$vols = gwmi -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=`"$($part.DeviceID)`"} WHERE AssocClass = Win32_LogicalDiskToPartition"

foreach($vol in $vols)

{

out-host -InputObject "`t`t$($vol.name)"

}

}

}

Not too bad, but could be better. There is a GetRelated method that PowerShell tack on to WMI objects which should allow you to do the Associate stuff with PowerShell rather than writing WMI queries but I havent figured that out.