I recently had the need to be able to create a VM on a few of our Hyper-V servers at work. I found a great article by The Virtual PC Guy on how to create a new VM. I took what he had and extended the functionality a bit.  To manage Hyper-V, for now, we are pretty much stuck with WMI. However, Virtual Machine Manager will help us out a bunch. In the meantime, we can start with WMI. I figured it never hurts to know what is going on under the hood anyway.

One key piece of functionality that was not included was the ability to override the default location for new VM's when they are created. This is critical to us as we are using a SAN and have dedicated LUN's for our clustered VM's. So now that you have the background, here's the code


.posh li.li2, .posh li.li1 { list-style-type:decimal-leading-zero; white-space:nowrap;}.syntax .posh { overflow: auto; background-color:#eaeaea; border:1px solid #dddddd; border-width: 1px 1px 0px 1px;}.syntax .poshcredit {background-color:#eaeaea;border-bottom:1px solid #dddddd;color:#666666;font-size:85%;overflow:hidden;padding:0.5em 1em;}/* GeSHi (c) Nigel McNie 2004 ( */.posh .de1, .posh .de2 {font-family: 'Courier New', courier, monospace; font-weight: normal;}.posh {font-family: monospace;}.posh .imp {font-weight: bold; color: red;}.posh li {background: #ffffff;}.posh li.li2 {background: #f8f8f8;}.posh .kw1 {color: #666699; font-weight: bold;}.posh .kw2 {color: #333399; font-weight: bold; font-style: italic;}.posh .kw3 {color: #660033;}.posh .co1 {color: #666666; font-style: italic;}.posh .comulti {color: #666666; font-style: italic;}.posh .es0 {color: #000099; font-weight: bold;}.posh .br0 {color: #66cc66;}.posh .st0 {color: #009900;}.posh .nu0 {color: #cc66cc;}.posh .me1 {color: #003366;}.posh .me2 {color: #003366;}.posh .re0 {color: #0066cc; font-style: italic;}.posh .re1 {font-style: normal;}.posh .re2 {color: #000066;}.posh .re3 {color: #660033; font-weight: bold;}.posh .re4 {color: #003366;}
  1. function New-HyperVVM {
  2.         param   (
  3.                         [string]$Hypervhost = "localhost",
  4.                         [string]$Vm = "VM Courtesy of PowerShell",
  5.                         [string]$location = "C:MyVirtualMachines$vm"
  6.                         )
  7.         $wmiClassString = "\" + $Hypervhost + "rootvirtualization:Msvm_VirtualSystemGlobalSettingData"
  8.         $wmiclass = [WMIClass]$wmiClassString
  9.         $newVSGlobalSettingData = $wmiClass.CreateInstance()
  10.         $newVSGlobalSettingData.psbase.Properties.Item("ExternalDataRoot").value = $location
  11.         $newVSGlobalSettingData.psbase.Properties.Item("ElementName").value = $Vm
  12.         $VSManagementService = gwmi MSVM_VirtualSystemManagementService -namespace "rootvirtualization" -ComputerName $Hypervhost
  13.         $GlobalSettings  = $newVSGlobalSettingData.psbase.GetText(1)
  14.         $VSManagementService.DefineVirtualSystem($GlobalSettings, $ResourceSettings)
  15. }
downloadThis Script brought to you by PoshCode

This is up on Poshcode. I highly recommend you be a good PowerShell community member and start sharing your code up here. If you haven't heard about their plans for this site, its awesome!

You can hear Joel and Hal talk about it on this episode of the  PowerScripting podcast.

Start Posting to – NOW !!!

Hal posted a comment a while back asking if I would consider putting some of the functions i had been blogging about up on I had heard of it and even used some of the scripts up there but for whatever reason I hadn't bothered to post anything. Hal finally convinced me to put some code up there. I have to say, its ridiculously easy.

My 0.02 is this should be the default shared online repository for Powershell scripts and functions. So I have one question for you.. Why are you still reading this and not posting your latest and greatest script up to PoshCode?

Go and do it.. now !




   1: PS C:\> Get-GreatestSwimmerEver
   4: NumberGoldMedalsIn2008    : 8
   5: NumberWorldRecordsIn2008  : 7
   6: LifetimeOlympicGoldMedals : 14
   7: FirstName                 : Michael
   8: LastName                  : Phelps
   9: GreatestSwimmerEver       : True
  13: PS C:\>

A thank you note from an intern

We had several high school students working for us this summer as interns. It was a great experience. I just had to share a section of a thank you note I received from one of them the day they completed their internship.

"I thought it would never be possible for me like coding again, after a year of programming class. But after a week of PowerShell, I liked coding again .. but only in PowerShell !!"

I love the fact that Powershell helped me get a high school student interested in scripting, and that it is quite literally "Changing the world one line at a time."

PowerShell Function Set-IPAddress

I recently came across the need to configure IP information on a Network interface using a script. This is definitely something that can be accomplished with NETSH,  but I was looking for a way to do it with native PowerShell using Get-WmiObject. If there is a choice, I tend to prefer to stick with native Powershell Cmdlets and functions rather than running cmd based tools, but that is just my personal opinion and you can take it or leave it.

My goal was to be able to specify a Network Interface by name, ie "Local Area Connection" and pass it an IP address, mask, gateway, and 1 or 2 DNS servers. In order to accomplish this there were two WMI classes that I needed. The first is Win32_NetworkAdapter. The second is Win32_NetworkAdapterConfiguration. The real key (pun intended) was to figure out a way to join the information from these two classes so that I knew for sure that I was working with the same network interface. It turns out that both classes have a property called InterfaceIndex.

Win32_NetworkAdapter has a property called netconnectionid which is equal to the name of the Adapter, ie "Local Area Connection".

With that information, here is a function that you can use to specify settings for a NIC based on the name of the NIC.

   1: function Set-IPAddress {
   2:         param(  [string]$networkinterface,
   3:                 [string]$ip,
   4:                 [string]$mask,
   5:                 [string]$gateway,
   6:                 [string]$dns1,
   7:                 [string]$dns2,
   8:                 [string]$registerDns = "TRUE"
   9:          )
  12:         #Start writing code here
  13:         $dns = $dns1
  14:         if($dns2){$dns ="$dns1,$dns2"}
  15:         $index = (gwmi Win32_NetworkAdapter | where {$_.netconnectionid -eq $networkinterface}).InterfaceIndex
  16:         $NetInterface = Get-WmiObject Win32_NetworkAdapterConfiguration | where {$_.InterfaceIndex -eq $index}
  17:         $NetInterface.EnableStatic($ip, $mask)
  18:         $NetInterface.SetGateways($gateway)
  19:         $NetInterface.SetDNSServerSearchOrder($dns)
  20:         $NetInterface.SetDynamicDNSRegistration($registerDns)
  22: }

Hope you find this useful.


PowerShell Function Set-NetInterfaceName

Now that you are able to set an IP address given a name of a network interface, you may want to change that name.

Here's a quick little function to do that.

   1: function Set-InterfaceName {
   2:         param(  
   3:                 [string]$oldName,
   4:                 [string]$newName
   5:          )
   7:         $NicInterfaceName = gwmi Win32_NetworkAdapter | where {$_.netconnectionid -eq $oldName}
   8:         $NicInterfaceName.netconnectionid = $newName
   9:         $NicInterfaceName.put()
  11: }

ASCII Reinvented

I mentioned earlier that I had told a colleague about using Hashtables in PowerShell and C#.  Later on in the afternoon, he came across a situation where he needed to generate a random string of characters.

He came up with this little snippet of code.  (I think he may have a been a little high on Hash . . . . . tables)

   1: function new-array {$args}                                                                                        
   2: $CharacterArray = new-array A B C D E F G H I J K L M N O P Q R S T U V X Y Z;                                    
   3: $CharacterHashTable = new-object System.Collections.Hashtable                                                                                                                                                                     
   4: for ($i=0; $i -ilt $CharacterArray.Count; $i++) {$CharacterHashTable.Add($i, $CharacterArray[$i])}                
   5: $randomNumber = new-object System.Random                                                                          
   6: $randomCharacter = $CharacterHashTable[$,25)]                         

"Wow!  That is great!  You basically reinvented ASCII !"

Then I showed him this neat little trick :)

   1: PS >  [char]65
   2: A

He came back with these two lines to generate a random character.

   1: $randomObject = New-Object System.Random
   2: $randomChar = [char]$,90)

It turns out that if you know ASCII exists, it can indeed be used to generate random characters :)

However, I have to give some credit for coming up with a neat little subset of ASCII in a couple lines of PowerShell. Not too shabby!

Using PowerShell to Demo C# and .NET

I have an intern working with us and he is writing some PowerShell and C# WinForms to automate some of our administrative tasks. He had a problem with a C# app and when he started asking me about it, it sounded like a hash table was just what the doctor ordered.

He had seen hash tables in PowerShell but wasn't quite sure how they would work in C# (he had only cracked open C# maybe 5 weeks ago). Rather than firing up Visual Studio and typing in a bunch of "blah-blah/yadda-yadda" I was able to demo a "C# version" of hash tables in PowerShell.

Once he saw this, he was able to understand how to use hash tables in C#. 

   1: 268 >  $hash = New-Object System.Collections.Hashtable
   2: 269 >  $hash.Add("one",1)
   3: 270 >  $hash
   5: Name                           Value
   6: ----                           -----
   7: one                            1
  10: 271 >  $
  11: 1
  12: 272 >

I just love how PowerShell is continuing to bridge a gap between Developers and Admins. These are obviously different disciplines and both require different skills, but the more we can learn about each one, the more effective we can be in our respective roles.

Editing Web.Config Files with PowerShell

A couple days ago, a colleague of mine (who is a recent PowerShell convert) asked me if there was some cool way in PowerShell to update a web.config file based on the particular environment that the app was being deployed to (dev, test, or production)

I went in to this knowing config files are just XML files and I also knew that PowerShell can do some pretty cool work with XML and I figured there must be a way.

I found a great article called Deployment made simple using Powershell by Omar Al Zabir up on Code Project.

He has a script that does a number of things, but what I was particular interested in was changing the database connection string in a web.config file.

Luckily, Omar had the answer.

I tweaked it a bit and came up with the following:

   1: #Code to update a connection string on a web.config file –yes this can be used to change a lot more stuff too J
   3: #Set the Connection String and the path to web.config (or any config file for that matter)
   4: $connectionString = "Data Source=old-db;Initial Catalog=mydb;Integrated Security=True;User Instance=True"
   5: $webConfigPath = "C:\Inetpub\wwwroot\myapp\web.config"
   6: $currentDate = (get-date).tostring("mm_dd_yyyy-hh_mm_s") # month_day_year - hours_mins_seconds
   7: $backup = $webConfigPath + "_$currentDate"
   9: # Get the content of the config file and cast it to XML and save a backup copy labeled .bak followed by the date
  10: $xml = [xml](get-content $webConfigPath)
  12: #save a backup copy
  13: $xml.Save($backup)
  15: #this was the trick I had been looking for
  16: $root = $xml.get_DocumentElement();
  18: #Change the Connection String. Add really means "replace"
  19: $root.connectionStrings.add.connectionString = $connectionString
  21: # Save it
  22: $xml.Save($webConfigPath)

The one thing I added was the backup copy by saving the current file with the current date and time appended to the end of the file name.

So next time you are tempted to edit a config file with notepad, open up the shell and take a whack at it.

Credentials in the Console

A while back on the Windows PowerShell Team blog, there was a post that describes how to force Get-Credential to prompt for a username and password in the console itself rather than popping up a Windows dialog box asking for a username and password.

The change is a key in the registry, and is permanent, unless of course you change it back to the original setting. The other minor complaint was that the there was no space between where the user needs to type a username and password, and the text prompting for the text.

   1: 119 >  $c = Get-Credential
   3: cmdlet Get-Credential at command pipeline position 1
   4: Supply values for the following parameters:
   5: Credential
   6: PromptForCredential_UserAndy
   7: PromptForCredential_Password********
   9: 120 >

You can see that the "PromptForCredential" and my username - "Andy" just run together. 

You can accomplish this another way with much more control over the user experience and you don't have to hack the registry.

So here is my quick and dirty function. I put in a very basic check to see if someone added their domain or not, and added it if necessary.

   1: function Get-Cred {
   2:     Write-Host "";
   3:     $username = Read-Host "Enter username to access some resource (no domain required)"
   4:     if ($username -notlike "MYDOMAIN\*"){$username = "MYDOMAIN\$username"}
   6:     Write-Host ""
   7:     $password = Read-Host  -AsSecureString "Password to access some resource"
   9:     $credential = New-Object System.Management.Automation.PSCredential($username,$password)
  10:     return $credential
  11: }

And here it is in action:

   1: 130 >  $cred = Get-Cred
   3: Enter username to access some resource (no domain required): andy
   5: Password to access some resource: **************
   6: 131 >
   7: 131 >  $cred.GetNetworkCredential() | fl *
  10: UserName : andy
  11: Password : secretpassword
  12: Domain   : MYDOMAIN