Creating Hyper-V Symbolic Links using Powershell

January 31, 2012 1 comment

As an MCT I’m required to download preconfigured VM’s and supporting VHD’s from Microsoft which are then imported into base images running Server 2008 R2 and HyperV.  Prior to importing the VM’s, symbolic links need to be created in each VM’s root Virutal Hard Disk folder.  Symbolic links are shortcuts that are associated with base or middle tier VHD’s.  Microsoft even includes a .bat file that automates their creation.  An example of one of these .bat files is below:

@ECHO OFF
@ECHO Creating Symlinks necessary to import VM’s
rem ************  to create Symlink for Base Image
rem ************substitute VHDName.vhd with the name of the base image needed
mklink “Path:\Source.vhd” “Path:\Base.vhd”
pause

So it occurred to me that if a .bat file could automate the creation of one or more symbolic links, then perhaps a Powershell script could automate the execution of one or more individual .bat files.  Creating these symbolic links is a repetitive act of having to browse to each root VM’s folder, execute the .bat file and then close the corresponding cmd prompt.  This is surely another instance where time could be better spent elsewhere and so I turn to my old friend Powershell for a script to automate this mundane task.

The first step is define the path to the root VM folders.

$path = "C:\Program Files\Microsoft Learning\6419\Drives\"

Now we run Get-ChildItem on the folder using the -Include parameter to filter for .bat files only and then -Recurse to browse sub folders and files.   These results are then placed into the $vhds array variable.

$vhds = Get-ChildItem $path -Include *.bat -Recurse

The next step is to execute the .bat files by piping the $vhds array containing the required .bat files to Start-Process.  One of the problems I encountered was that, although Start-Process was able to execute the .bat files it wasn’t passing it’s results down the pipeline.  Most cmdlets do return output but on occasion certain cmdlets needs to be encouraged to do this using the -PassThru  parameter.

$vhds | %{ start-process "$_" -PassThru |

These results are then pipelined to Foreach-Object where I found it necessary to pause the script and allow the .bat file to properly execute using Start-Sleep 1.  Without the pause there is a quick blip on the screen and then the .bat file exists.  I remember reading a script in a recent blog article by Greg Caporale regarding printing .pdf’s where he incorporated Start-Sleep ( or it’s alias sleep ) to provide enough time for the .pdf files to print before closing Adobe Reader.   The Foreach-Object also passes down the pipline each individually open cmd process to Stop-Process which exits the open cmd prompt.

$vhds | %{ Start-Process "$_" -PassThru |
        %{ Start-Sleep 1 ; $_} | Stop-Process }

I then wrapped the whole thing into a function called it Import-SymbolicLinks and defined a $Path parameter to make the script that much more flexible by accepting parameter input. Below if the final script.

Function Create-SymbolicLinks {            

 Param(
  $path = "C:\Program Files\Microsoft Learning\6419\Drives\"
  )            

 $vhds = Get-ChildItem $path -Include *.bat -Recurse
 $vhds | %{ Start-Process "$_" -PassThru |
            %{ Start-Sleep 1 ; $_} | Stop-Process }
 }            

Create-SymbolicLinks

The obvious next step is to script the import of these VM’s as well as create snapshots.

Advertisements
Categories: Powershell Tags: ,

Query Security Log Using Powershell

January 13, 2012 7 comments

I’ve just completed a script that will parse the Windows Security Event log for Event ID’s of type 4624 (user logons).    Once the events have been retrieved the script then creates and outputs a  custom object populated with the following properties:

  • Account Name
  • DateTime
  • Type  ( Interactive,Network,Unlock)

The script is composed of 2 functions:

  • Find-Matches
  • Query-SecurityLog

Query-SecurityLog is the main function and is responsible for creating and outputing the custom object.  Find-Matches is a helper function used to parse the event log message property for the following pattern: “Account Name:      SomeUserName”.    Prior to incorporating the Find-Matches function I had used the -Match operator but then realized that pattern was referenced twice  in the message and that the -Match operator returns only the first matching expression.  So, if you need to find more than one occurrence of a pattern in raw text, you have to switch over to the RegEx object underlying the -Match operator and use it directly.

This left me with 2 possible option that could be used to discover one or more matching values:

  • Select-String (as used in Find-Matches)
  • [regex]

The Find-Matches function was something I already had in my repository of scripts so I went that route, however after reading Chapter 13: Text and Regular Expression in the Powershell.com ebook I realized this could of been done with less code by using [regex].

$Pattern = [regex]"(?i)\baccount name:\s+\w+\b"
$Content = $Pattern.matches($i.message) | select -prop value | %{$_.value}

The example below demonstrates how Select-String is used in the Find-Matches function to output all matching patterns:


I then used an if\else statemtment block to determine  how many matches were returned and then set the $account parameter accordingly.

if($content.Count -eq 2) {
$account = $content[1]} else {$account =  $content }

I was also suprised to see just how many non-user account logon events were recorded and decided to remove any events that referenced the accounts below:

  • System
  • IUSER
  • LOCAL
  • NETWORK
  • $ENV:Computername

As well as filter the events based on the following Logon Types and assign the $logontype variable accordingly:

  • Interactive (2)
  • Network (3)
  • Computer Unlocked (7)

This was done using the following code:

$notmatch = "System|IUSR|LOCAL|NETWORK"
if($account -notmatch $notmatch)  {                        

    if($i.Message | Select-String -Pattern "Logon Type:\s+[2]") {
    $logontype = "Interactive" }
    if($i.Message | Select-String -Pattern "Logon Type:\s+[3]") {
    $logontype = "Network" }
    if($i.Message | Select-String -Pattern "Logon Type:\s+[7]") {
    $logontype = "Computer Unlocked" }
    }

It was then just a matter of creating and populating the custom object.  There are 2 ways to populate an object once created.  One way is to create the object and then use the Add-Member cmdlet to populate it and the other is to use the -Property property along with splatting ( @{} ).

  • $Obj | Add-Member -Noteproperty NameOfProperty  ValueOfProperty
  • -Property @{ NameOfProperty = “Value” }

I choose to go with the Splatting option as it requires less code.

$obj = New-Object PSObject -Property @{
       User = $user
       Date = $Date
       LogonType = $LogonType
       }

The end result was the following code.

Function Find-Matches {                        

 Param($Pattern)
 Process {
  $_ | Select-String -pattern $Pattern -AllMatches |
   select -ExpandProperty matches |
   select -ExpandProperty value
  }
 }                        

Function Query-SecurityLog {               

    Param(
        [int]$MaxEvents,
        [array]$global:Users = @(),
        [string]$Comp = $env:computername,
        $notmatch = "System|IUSR|LOCAL|NETWORK"            

        )                 

    if($MaxEvents)
    { $events = Get-WinEvent -LogName security -MaxEvents $MaxEvents |
    Where-Object{$_.id -eq "4624"}
    } Else {  $events = Get-WinEvent -LogName security  |
    Where-Object{$_.id -eq "4624"}}            

    Foreach($i in $events) {                        

    $content = $i.message| Find-Matches -Pattern "account name:\s+\w+"
    if($content.Count -eq 2) {
    $account = $content[1]} else {$account =  $content }
    $account = (($account -split ":")[1]) -replace "\s+",""                        

    if($account -notmatch $notmatch) {                        

        if($i.Message | Select-String -Pattern "Logon Type:\s+[2]") {
        $logontype = "Interactive" }
        if($i.Message | Select-String -Pattern "Logon Type:\s+[3]") {
        $logontype = "Network" }
        if($i.Message | Select-String -Pattern "Logon Type:\s+[7]") {
        $logontype = "Computer Unlocked" }                        

   $user = $account
   $Date = $i.TimeCreated
   $obj = New-Object PSObject -Property @{
       User = $user
       Date = $Date
       LogonType = $LogonType
       }            

  $Global:Users += $Obj                        

       }
    }                        

 write-output $Global:Users | Select User,Date,LogonType |
             Sort Date -Descending | Format-Table -Auto
}                        

Query-SecurityLog -MaxEvents 1000

The output of Query-SecurityLog  is:

Categories: Powershell Tags:

Powershell Objects and .PS1XML Files

January 9, 2012 1 comment

Anyone who uses powershell on a regular basis knows that the output of any powershell cmdlet is of an object type.  Powershell produces objects. For example let’s look at the objects produced by two cmdlets that essentially do the same thing: Get-Eventlog and Get-WinEvent.   The below example uses the GetType() method to display the object type for an eventlog.

(Get-Eventlog -List | Select -First 1).Gettype() | FT -auto
(Get-WinEvent -Listlog Application).Gettype() | FT -auto

Both produce different objects, Eventlog and EventLogConfiguration.  The output of these objects is associated with a powershell formating file (.ps1xml) which defines the properties they will display.

The example below shows the output as it pertains to Get-Eventlog and Get-Winevent.

(Get-Eventlog -List | Select -First 1)
(Get-WinEvent -Listlog Application)

Although they both reference the same event log (Application) they are configured to display different properties, even though some of them reference the same values, like Log and LogName or Entries and RecordCount.

I then was curious which formatting files were associate with each object type, so I decided to view which .ps1xml files existed and ran the following command to browse the $pshome directory and filter for only *.ps1xml files.

dir $pshome\* -include *.ps1xml

My next step was to parse these files using Select-String  for references to the object types (Eventlog and EventLogConfiguration) but decided that the process should be automated and created a function that would create a custom object with the properties of my choosing.   This would also be useful to demo for the workshop I’m putting together called “Using Powershell to View Events and Event Logs” (not sure I did the title so it may change shortly).

Anyway, the result of all this was the creation of the Get-EventLogBaseObjects function.

Function Get-EventLogBaseObjects {
 $Array = "Get-EventLog","Get-WinEvent"
 $global:newarray =@()                                                

     Switch ($Array) {                                                

 "Get-Eventlog" {
    $GetEventLog = (get-eventlog -list | Select -first 1).gettype()
    $XMLFile = Select-String -Path $PSHOME\*.ps1xml -Pattern $GetEventlog |
        Select -First 1
    $Obj = New-Object PSObject -Property @{
            Cmdlet = "Get-Eventlog"
            SystemType = $GetEventLog.UnderlyingSystemType
            XMLFile = $XMLFile.filename
            }
    $global:newarray += $obj
    }                                    

 "Get-WinEvent" {
    $GetWinEvent = (Get-WinEvent -Listlog Application).gettype()
    $XMLFile = Select-String -Path $PSHOME\*.ps1xml -Pattern $GetWinEvent |
        Select -First 1
    $Obj = New-Object PSObject -Property @{
            Cmdlet = "Get-WinEvent"
            SystemType = $GetWinEvent.UnderlyingSystemType
            XMLFile = $XMLFile.filename
            }
    $global:newarray += $obj
     }
 }
 write-output $global:NewArray | Select Cmdlet,SytemType,XMLFile |
        Format-Table -AutoSize
}                        

Get-EventLogBaseObjects
Categories: Powershell Tags: ,

Determine Pipeline Input

January 7, 2012 Leave a comment

There was a recent post on the Powershell Forums titled Finding Function Name in which the user wanted to know if there was anyway to determine which cmdlet or function was being used to send input down the pipeline.  In order to assist I first needed to do some research as I was unsure how to make this determination.  It just so happened that the first response was by Kazun (PS MVP..enough said.. ) and he suggested using $MyInvocation.Line. 

So upon doing a google search I came across the following article PowerShell Deep Dive: Using $MyInvocation by Kirk Munro which went into great detail on how to use the $MyInvocation variable to make a similiar distinction.  From that article I came up with  the following script to demo $MyInvocation.  It includes 4 functions.  The one that anaylzes the pipeline input is Out-Name.   Here is the script…

Function Get-Svc {            
      Get-Service | select -First 2            
    }            
Function Get-Proc {            
      Get-Process | Select -First 2            
    }            
Function Get-File {            
      GCI c:\ | Select -First 2            
    }            

Function Out-Name {            
    param($name)            

    Begin {             
       $global:Invocation = $MyInvocation.line             
    }            

    Process{             
       if( $global:Invocation -match "Get-Svc" ) {            
       $_.name             
       }            
       Elseif ( $global:Invocation -match "Get-Proc" ) {            
       $_.vm            
       }             
       Else { Write-Host "Get-Svc was not used" }                   
    }            
  }            

Get-Svc | out-Name            
#Get-Proc | out-name            
#Get-File | Out-Name
 

The script was created soley to demo $MyInvocation but could be altered (as with any script) to make it fit your needs.

Categories: Powershell Tags:

Powershell Execution Policies

January 16, 2011 Leave a comment

Windows Powershell execution polices let you determine the conditions under which Powershell loads configuration files (.ps1xml), module script files (.psm1) and scripts (.ps1).    By defaut Powershell doesn’t permit any of these files as it’s default execution policy is set to Restricted.  The available execution policies are as follows and can be found by running Help About_Execution_Policies Read more…

Categories: Powershell Tags:

Migrate DHCP Server Settings Using Powershell

January 10, 2011 Leave a comment

During my preparation to teach the “Updating Your Skills to Server 2008 R2” class, I saw lot’s of potential to use Powershell to automate some of the mundane tasks, such as creating files and assigning permissions (Module 2) or restoring an AD Computer Object (Module 4) to the more advanced such as using the new Server Migration Tools to migrate the DHCP service from DC1 to SVR1 (Module 1). Read more…

Categories: Powershell Tags:

Appending a Function to a Powershell Profile

December 29, 2010 Leave a comment

Powershell profiles provide the opportunity to extend custom functionality either via the console or a scripting engine like PowerGui or the ISE.  They are normal PS scripts and end with a .ps1 file extension. Like all PS scripts they can be edited in notepad, ISE, PowerGUI (my favorite) or any other script\text editor. Read more…

Categories: Powershell