PowerShell Function Set-NetInterfaceName

August 13, 2008 at 3:08 PMAndy Schneider

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:          )
   6:         
   7:         $NicInterfaceName = gwmi Win32_NetworkAdapter | where {$_.netconnectionid -eq $oldName}
   8:         $NicInterfaceName.netconnectionid = $newName
   9:         $NicInterfaceName.put()
  10:         
  11: }

Posted in:

Tags:

ASCII Reinvented

July 31, 2008 at 3:07 PMAndy Schneider

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[$randomNumber.next(0,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]$randomObject.next(65,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!

Posted in:

Tags:

Using PowerShell to Demo C# and .NET

July 30, 2008 at 4:07 PMAndy Schneider

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
   4:  
   5: Name                           Value
   6: ----                           -----
   7: one                            1
   8:  
   9:  
  10: 271 >  $hash.one
  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.

Posted in:

Tags:

Editing Web.Config Files with PowerShell

July 29, 2008 at 11:07 PMAndy Schneider

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
   2:  
   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"
   8:  
   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)
  11:  
  12: #save a backup copy
  13: $xml.Save($backup)
  14:  
  15: #this was the trick I had been looking for
  16: $root = $xml.get_DocumentElement();
  17:  
  18: #Change the Connection String. Add really means "replace"
  19: $root.connectionStrings.add.connectionString = $connectionString
  20:  
  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.

Posted in:

Tags:

Credentials in the Console

July 23, 2008 at 9:07 PMAndy Schneider

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
   2:  
   3: cmdlet Get-Credential at command pipeline position 1
   4: Supply values for the following parameters:
   5: Credential
   6: PromptForCredential_UserAndy
   7: PromptForCredential_Password********
   8:  
   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"}
   5:         
   6:     Write-Host ""
   7:     $password = Read-Host  -AsSecureString "Password to access some resource"
   8:     
   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
   2:  
   3: Enter username to access some resource (no domain required): andy
   4:  
   5: Password to access some resource: **************
   6: 131 >
   7: 131 >  $cred.GetNetworkCredential() | fl *
   8:  
   9:  
  10: UserName : andy
  11: Password : secretpassword
  12: Domain   : MYDOMAIN

Posted in:

Tags:

Check a file into Team Foundation Server with PowerShell

July 14, 2008 at 7:07 PMAndy Schneider

We have recently started using Microsoft's Team Foundation Server for our projects. We are using SCRUM for our infrastructure projects (which has been an interesting learning endeavor and quite possibly deserves a blog post of its own sometime in the future)

Anyway, on the infra side, we typically don't use source control.  We use TFS mostly to manage our tasks for each sprint and our backlog items. However, we are starting to use PowerShell more and more in our build guides and giving our ops team PowerShell Scripts for deployments. Its only a matter of time until we will become just as dependent on Source Control as our development team is.

So I figured I would check into this. TFS actually has quite a nice little API that you can use with any .NET language, and so I started looking to see if I could use PowerShell to check in a script. Looks like its not that hard.

You need to install the Visual Studio 2008 SDK to get at the TFS assemblies, but that is not too hard.

In the SDK there are a few example solutions written in C# that show you how the basics for interacting with the API. I used the "VersionControlExample.cs" file and worked on re-writing some of it in PowerShell.

So here is my first crack at New-TFSItem

   1: param (
   2:     [string]$tfsServer = "TFSServerName",
   3:     [string]$tfsLocation = "$/TFS/Project",
   4:     [string]$localFolder ="c:\scripts",
   5:     [string]$file,
   6:     [string]$checkInComments = "Checked in from PowerShell"
   7: )
   8: $clientDll = "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.Client.dll"
   9: $versionControlClientDll = "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.VersionControl.Client.dll"
  10: $versionControlCommonDll = "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.VersionControl.Common.dll"
  11:  
  12: #Load the Assemblies
  13: [Reflection.Assembly]::LoadFrom($clientDll)
  14: [Reflection.Assembly]::LoadFrom($versionControlClientDll)
  15: [Reflection.Assembly]::LoadFrom($versionControlCommonDll)
  16:  
  17: #Set up connection to TFS Server and get version control
  18: $tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($tfsServer)
  19: $versionControlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
  20: $versionControlServer = $tfs.GetService($versionControlType)
  21:  
  22: #Create a "workspace" and map a local folder to a TFS location
  23: $workspace = $versionControlServer.CreateWorkspace("PowerShell Workspace",$versionControlServer.AuthenticatedUser)
  24: $workingfolder = New-Object Microsoft.TeamFoundation.VersionControl.Client.WorkingFolder($tfsLocation,$localFolder)
  25: $workspace.CreateMapping($workingFolder)
  26: $filePath = $localFolder + "\" + $file
  27:  
  28: #Submit file as a Pending Change and submit the change
  29: $workspace.PendAdd($filePath)
  30: $pendingChanges = $workspace.GetPendingChanges()
  31: $workspace.CheckIn($pendingChanges,$checkInComments)
  32:  
  33: #Delete the temp workspace
  34: $workspace.Delete()

There's probably a cool way to programmatically figure out where the dll's that need to be registered are, but I leave that as an exercise to the reader :)

Posted in:

Tags:

Stuffing the output of the last command into an automatic variable

June 26, 2008 at 5:06 AMAndy Schneider

UPDATE:

/\/\o\/\/ and Joel Bennet both chimed in and provided a much more elegant solution, just override out-default. This is really what I had wanted to do, but didn't know how. Thanks to both /\/\o\/\/ and Joel for your input. Please do check out their comments on this post. But here is the code they provided:

   1: # From /\/\o\/\/
   2:  
   3: function out-default {
   4:     $input | Tee-Object -var global:lastobject | 
   5:     Microsoft.PowerShell.Utility\out-default
   6: }
   7:  
   8: # And from Joel 
   9: # In case you are using custom formatting
  10: # You will need to override the format-* cmdlets and then
  11: # add this to your prompt function
  12:  
  13: if($LastFormat){$LastOut=$LastFormat; $LastFormat=$Null }
  14:  
  15:  
  16:  

A couple of days ago, an intern that is working for us, was helping me with a Powershell script to manage our Hyper V Cluster. The script ran fine but we were querying 7 different computers and then rolling up all the output into a custom object, so the thing took a while to run. It was just long enough to be annoying. During this process, he asked if there was a way to have PowerShell automatically store the output of the last command in a variable automatically.

I first went down the road of using Tee-object,  According to the built-in help,

The Tee-Object cmdlet send the output of a command in two directions (like the letter T). It stores the output in a file or variable, and also sends it down the pipeline. If Tee-Object is the last command in the pipeline, the command output is displayed in the console.

Here's Tee-Object in action

PS C:\> get-process notepad | tee-object -variable note

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6596    60     0.06   6984 notepad


PS C:\> $note

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6596    60     0.06   6984 notepad


PS C:\>

But of course, my Intern (rightfully so) declares this to be unsatisfactory. He wants it to just happen automagically. Picky intern, huh?

Then on the bus ride home this afternoon, I was thinking about Jeffrey's post on Push-Noun. He basically shows us how to set up a loop that goes forever, taking input and executing it only for a specific noun in Powershell.

Considering this, I realized I could do something similar for my issue. So here's the code, stolen from Push-Noun.

function Set-LastObjectAvailable {
while ($TRUE)
{
    Write-Host "[LASTOBJECT]> " -NoNewLine
    $line = $Host.UI.ReadLine().trim()
    switch ($line)
    {
    "exit"   {return}
    "quit"   {return}
    "?"      {"Just type a command and the output will be displayed and stored in `$lastobject" }
    {$_.StartsWith("!")}
             {
                $Cmd = $_.SubString(1)
                Invoke-Expression $line |Tee-Object -varialbe lastobject | Out-Host
             }
    default  {

                Invoke-Expression $line |Tee-Object -Variable lastobject | out-host
             }
    }
}
}

And here it is in action:

PS C:\> Set-LastObjectAvailable
[LASTOBJECT]> get-process notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6600    60            6984 notepad
     52       2     1256       4412    58     0.06  10152 notepad


[LASTOBJECT]> $lastobject

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6600    60            6984 notepad
     52       2     1256       4412    58     0.06  10152 notepad


[LASTOBJECT]> ?
Just type a command and the output will be displayed and stored in $lastobject
[LASTOBJECT]> gps notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6600    60            6984 notepad
     52       2     1256       4412    58     0.06  10152 notepad


[LASTOBJECT]> $lastobject.count
2
[LASTOBJECT]> exit
PS C:\>

This isn't exactly bulletproof. I think I would like to have an automatic variable that stores the output of the last command that was executed, just in case you want to mess with it and you don't want to have to run it again. Basically just override out-host to use a Tee-Object -variable lastcommandoutput or something along those lines.

Posted in:

Tags:

Sysadmin Meme

June 16, 2008 at 3:06 PMAndy Schneider

Shay Levi called me out with a blog post that was started by Mind of Root.  Just to summarize:

A meme  consists of any unit of cultural information, such as a practice or idea, that gets transmitted verbally or by repeated action from one mind to another.

So here it goes:

How old were you when you started using computers?

11

What was  your first machine?

The first machine I got to use was an Apple IIE. Later on, my family purchased a Mac IIsi.

What was the first real script you wrote?

This really isn't scripting, but my first introduction to any type of programming was on a Mac in Junior High School where we learned to use Hyper Card. It was basically a computerized flip book, and I had  a guy walk across the screen and open a door.

The first real script was a batch file that ran a bunch of installers silently for a workstation build for our EE building's computer lab in college.

What languages have you used?

  • Powershell
  • VBScript
  • Ruby
  • C# (I'm calling this scripting since you can use inline C# with the new Add-Type Cmdlet in the Powershell V2 CTP2 :) )

What was your first professional Sysadmin gig?

I worked for a small consulting firm called Starr Technologies and we did IT for several Venture Capital firms in Menlo Park, CA.

If you knew then what you know now, would you have started in IT?

Absolutely! I studied Electrical Engineering in college, but transistors, resistors, and capacitors just never really did it for me. My senior year I took a class on Internet communications and haven't turned back sense. Looking back though, I wish I had taken more CS classes though.]

If there is one thing you learned along the way that you would tell new Sysadmins, what would it be?

Two things.

Get involved in the community. This is something I am learning about now. The Powershell community is really the first "online" community I have actively participated in and I am loving it. It is so much easier to learn with people who are passionate about the same things as you are.

Second is find a great mentor whom you can trust. This probably is not your manager, and may not be someone you work with directly. Buy him/her lunch. People rarely turn down free food, even if they are crazy busy. If people like what they do, they would love the opportunity to teach people. I have a mentor for my professional career as well as for my personal life. I also love to teach younger people as well. It's a two way street: always teach, and always be willing to be taught.

What is the most fun you have had scripting?

The best part of scripting is when someone asks you if you can do x, y, or z and you come back to them and say, "Hey, I wrote this little script for you." I love watching their jaw drop after I show them the one or two lines of Powershell I used to make it happen.

Who Am I calling out ?

/\/\o\/\/ - the PowerShell Guy

Marco Shaw

Kirk Munro

Jeffrey Snover

Posted in:

Tags:

Building Script Cmdlets as objects

June 15, 2008 at 6:06 AMAndy Schneider

I have been using CTP2 on my computers for sometime and I recently came across a situation where I needed a function that would easily accept parameters from the pipeline and also as a standard parameter. This was the perfect excuse to start playing with Script Cmdlets. These things have all kinds of cool attributes that you can use to make your scripts easier to use, which is all wonderfulness.

So I cracked open Powershell and started playing. It took me a little while but I figured out how to convert my function over to a ScriptCmdlet.

But there was a problem. I had to read lots of documentation to figure it out. What I love about Powershell is how it is so discoverable with use of get-member.

Buried in Powershell is something called a CommandInfo object. This object describes a command and what it can do. Wouldn't it be cool if these objects had information about the parameters, whether or not they accepted values from the pipeline, and what position they were in. The list of options goes on and on.

I think the best way to explain is to show some code that could exist.

   1: # Build Up A parameter object
   2: $param.Name = "File"
   3: $param.HelpMessage = "Please Enter a file name"
   4: $param.acceptsValueFromPipeline = $TRUE
   5:  
   6: # Build a block that will go into the processBlock of the ScriptCmdlet
   7: $scriptblock = "Get-Content `$File"
   8:  
   9: #Build The CmdLet
  10: $cmdlet.verb = "Get"
  11: $cmdlet.noun = "DemoTextFile"
  12: $cmdlet.p1 = $param # p1 is short for parameter1 in the scriptCmdlet
  13: $cmdlet.beginBlock = "write-host `"Beginning`""
  14: $cmdlet.processBlock = $scriptblock
  15: $cmdlet.endBlock = "write-host `"Ending`""
  16: $cmdlet.Write()

At the very end, the $cmdlet.write() method would write the code for you.

Well, now here comes the fun part (With warnings)

This code was written hacked together and is absolutely not guaranteed to work and I cannot be held liable if it eats your cat or kills your computer. The purpose here is to really see if there would be any interest in furthering this. Eventually i would like to see these properties get added to the System.Management.Automation.FunctionInfo class, or create a new class that inherits from FunctionInfo.

First, we can use Add-Type to create new types with inline C#

   1: Add-Type @"
   2: namespace Getpowershell {
   3:     public class Scriptcmdlet {
   4:         public string noun;
   5:         public string verb;
   6:         public string processBlock;
   7:         public string beginBlock;
   8:         public string endBlock;
   9:         public Getpowershell.Parameter p1;
  10:         public Getpowershell.Parameter p2;
  11:  
  12:         public string Write() {
  13:  
  14:             System.Text.StringBuilder sb = new System.Text.StringBuilder();
  15:             sb.Append("Cmdlet " + this.verb + "-" + this.noun + " { \n");
  16:             sb.Append("param (\n");
  17:             sb.Append("[Parameter(\n");
  18:             if (this.p1.acceptsValueFromPipeline == true) { sb.Append("ValueFromPipeline=`$true," +"\n"); }
  19:             if (this.p1.Mandatory == true) { sb.Append("Mandatory,\n"); }
  20:             sb.Append("HelpMessage=\"" + this.p1.HelpMessage + "\"]\n");
  21:             sb.Append("$" + this.p1.Name);
  22:             sb.Append(")\n");
  23:             sb.Append("Begin { \n" + this.beginBlock + "\n}\n");
  24:             sb.Append("Process { \n" + this.processBlock + "\n}\n");
  25:             sb.Append("End { \n" + this.endBlock + "\n}\n}\n");
  26:             return sb.ToString();
  27:         }
  28:     }
  29:     public class Parameter {
  30:             public string Name;
  31:             public bool acceptsValueFromPipeline;
  32:             public string HelpMessage;
  33:             public bool Mandatory;
  34:         }
  35:     
  36: }
  37: "@

Now I can do this code over again but I need to set up some objects with my new types first.

   1: $cmdlet = New-Object Getpowershell.Scriptcmdlet
   2: $param = New-Object Getpowershell.Parameter
   3:  
   4: # Build Up A parameter object
   5: $param.Name = "File"
   6: $param.HelpMessage = "Please Enter a file name"
   7: $param.acceptsValueFromPipeline = $TRUE
   8:  
   9: # Build a block that will go into the processBlock of the ScriptCmdlet
  10: $scriptblock = "Get-Content `$File"
  11:  
  12: #Build The CmdLet
  13: $cmdlet.verb = "Get"
  14: $cmdlet.noun = "DemoTextFile"
  15: $cmdlet.p1 = $param # p1 is short for parameter1 in the scriptCmdlet
  16: $cmdlet.beginBlock = "write-host `"Beginning`""
  17: $cmdlet.processBlock = $scriptblock
  18: $cmdlet.endBlock = "write-host `"Ending`""
  19: $cmdlet.Write()

In theory, you can run $cmdlet.write() and it will echo out the text for a new Script Cmdlet..

Here is some output from get-member on $cmdlet and $param

   1: PS C:\Users\andys\Desktop> $cmdlet | fl *
   2:  
   3:  
   4: noun         : DemoTextFile
   5: verb         : Get
   6: processBlock : Get-Content $File
   7: beginBlock   : write-host "Beginning"
   8: endBlock     : write-host "Ending"
   9: p1           : Getpowershell.Parameter
  10: p2           :
  11:  
  12:  
  13:  
  14: PS C:\Users\andys\Desktop> $param | fl *
  15:  
  16:  
  17: Name                     : File
  18: acceptsValueFromPipeline : True
  19: HelpMessage              : Please Enter a file name
  20: Mandatory                : False
  21:  
  22:  
  23:  
  24: PS C:\Users\andys\Desktop> $cmdlet.Write()
  25: Cmdlet Get-DemoTextFile {
  26: param (
  27: [Parameter(
  28: ValueFromPipeline=$true,
  29: HelpMe
ssage="Please Enter a file name"]
  30: $File)
  31: Begin {
  32: write-host "Beginning"
  33: }
  34: Process {
  35: Get-Content $File
  36: }
  37: End {
  38: write-host "Ending"
  39: }
  40: }
  41:  
  42:  
  43: PS C:\Users\andys\Desktop>

Known issues:

I have no idea if the Mandatory option will produce proper code for the Cmdlet

Right now it will only support adding one parameter which is the p1 property of the $cmdlet object

Posted in:

Tags:

Managing Hyper-V with WMI

June 12, 2008 at 3:06 PMAndy Schneider

Dung K Hoang has an excellent series of blog posts on how to manage Hyper-V with WMI.

I have taken a couple of his scripts and built a function that takes input from the pipeline and gives me an object that has three properties, a HostServer, VirtualMachine, and a Switch.

   1: function Get-HyperVInfo {
   2:     begin {
   3:         $VmSwitchinfo = @();
   4:     }
   5:     process {
   6:         $computer = $_
   7:         $ListofVMs = gwmi -namespace root\virtualization Msvm_ComputerSystem -filter "ElementName <> Name" -computer $computer
   8:         $ListofSwitches = gwmi -namespace root\virtualization Msvm_VirtualSwitch -computer $computer
   9:         $ListofSwitchPorts = gwmi -namespace root\virtualization Msvm_SwitchPort  -computer $computer
  10:         foreach ($Switch in $ListofSwitches)
  11: {
  12:             $SwitchGUID = $Switch.Name
  13:             $SwitchDisplayName = $Switch.ElementName
  14:             $PortsOnSwitch = $ListofSwitchPorts | where {$_.SystemName -match $SwitchGUID} 
  15:  
  16:             foreach ($Port in $PortsOnSwitch)
  17: {
  18:                 $PortPath = $Port.__PATH
  19:                 $ListofConnections = gwmi -namespace root\virtualization Msvm_ActiveConnection -computer $computer
  20:                 $a = $ListofConnections | where {$_.Antecedent -like $PortPath}
  21:                 if ($a -ne $NULL)
  22: {
  23:                     $LANEndPoint = $a.Dependent 
  24:                     foreach ($VM in $ListofVMs)
  25: {
  26:                         $VMGUID = $VM.Name
  27:                         $VMDisplayName = $VM.ElementName
  28:                         if ($LanEndPoint -like "*$VMGUID*")
  29: {
  30:  
  31:                             $vminfo = "" |Select-Object VirtualMachine ,HostServer, switch
  32:                             $vminfo.Switch = $SwitchDisplayName
  33:                             $vminfo.VirtualMachine = $VMDisplayName
  34:                             $vminfo.HostServer = $_
  35:                             $vmswitchinfo += $vminfo
  36:  
  37:                         }
  38:                     }
  39:                 }
  40:             }
  41:         } 
  42:  
  43:  
  44:     }
  45:     end {
  46:     $vmswitchinfo
  47:     }
  48: }

To use this function you can just do something like this

"HVSERVER1","HVSERVER2","HVSERVER3" | Get-HypervInfo

I have used this script to get a bunch of information on all the VM's in my Hyper V Cluster. Note that this is just using WMI so I can query my Server Core machines that are running Hyper V.

Many Many thanks to Dung for this info. I would not have even been able to get started without his help.

Posted in:

Tags: