Friday, August 30, 2013

Zip files and folders with PowerShell

One of the more frustrating things in this day of PowerShell v3 is not having cmdlets that can simply manipulate ZIP archives.

It is right there in the Windows GUI, but is it easy to automate?  Nope.

In fact if you search around you will find lots of different ways to handle this, one getting more complex than the next.  You will also find community projects that attempt to do the same thing.

One common reference that I ran across was this:

http://blogs.msdn.com/b/daiken/archive/2007/02/12/compress-files-with-windows-powershell-then-package-a-windows-vista-sidebar-gadget.aspx

From the spectacular David Aiken. I have to admit, it is not the first time he has saved my bacon.

His solution is built in, no funky community add-ins, nothing strange I can’t follow, and best of all it is compact – it is really small.

I have already had to do some things with Shell.Application with PowerShell, so I figured I would give it a shot.

Well, I immediately ran into some issues. 

One, his use of –Recurse.  Not necessary.  Use Get-ChildItem and pipe in the folder and then entire folder is zipped.  Perfect.  So you can work the input just about any way you like and it will simply pass whatever you pipe to it.

Two, file locking.  This tripped my up for hours.  And lots of other folks that have found his solution too.  His little 500 millisecond wait before advancing on to the next is simply not real nor flexible based on varying file sizes.

I found all kinds of folks commenting on the same thing and developing all kinds of fancy solutions to handle it.  But, in the end I found something really simple, and it was buried right there in the shell.application all along.  Simply test for the existence of the item you are zipping in the zip archive.

So simple.  One little do loop.  With my crafty Until ( $zipPackage.Items() | select {$_.Name -eq $file.Name} )

And, I only modified the Add-Zip, since David already had a fail safe to create the .ZIP if it didn’t already exist.

I am going to update my use of his usage example as well.

In my case I have a number of XML files.  Each is in a unique Folder.  There are other files and folders with the XML files as well (this is an OVF, for you OVF fans).

I want to create the .ZIP one level up from the folder where the XML is.

$ovfFolder = Get-Item $xmlFile.PSParentPath 

$zipFullName = $ovfFolder.Parent.FullName + "\" + $xmlFile.BaseName + ".zip"

Get-ChildItem $ovfFolder | Add-Zip $zipFullName

Now, below is my modification to the original Add-Zip function.

function Add-Zip  # usage: Get-ChildItem $folder | Add-Zip $zipFullName
{
    param([string]$zipfilename)

    if(!(test-path($zipfilename)))
    {
        set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
        (dir $zipfilename).IsReadOnly = $false   
    }
    $shellApplication = new-object -com shell.application
    $zipPackage = $shellApplication.NameSpace($zipfilename)
    foreach($file in $input)
    {
        $zipPackage.CopyHere($file.FullName)
        do {
            Start-sleep 2
        } until ( $zipPackage.Items() | select {$_.Name -eq $file.Name} )
    }
}

Wednesday, August 28, 2013

PowerShell to test if a network connection is up and on the domain

If you have noticed I have been spending a lot of time working with deployments, deploying, and scripting configurations.
In fact, I have spent nearly two years, off and on, working on this in various ways and permutations from Windows Azure VMRole (the now dead non-persistent one) to SCVMM Service Templates.
The thing that makes this type of scripting unique is that the scripts are executed within the OS of the VM, not externally from some manager that uses a PowerShell remoting session or the like.
This means that each script has no knowledge of anything beyond the boundaries of the OS where the script is running.
Now, I assume that many of you are aware of the Hyper-V Synthetic Nic, and that the Synthetic NIC driver comes to life later in the boot process (not in 2012 R2 generation 2 VMs, but that is different).
The problem is one of timing.  Your script could be running prior to your network being awake an functional.
Here is a little script that I use to test my domain joined machines prior to continuing when I have a need for domain connectivity (such as executing a command using a domain credential).

Do { $upTest = ( Get-NetConnectionProfile | where {$_.IPv4Connectivity -ne "NoTraffic"} ) } until( $upTest.NetworkCategory -eq "DomainAuthenticated" )
If you want to take this to the next level and identify the IP address and physical NIC (say you have multiple NICs and you need to bind to the IP of the domain NIC or the NIC itself in some configuration.

$mgmtNetProfile = Get-NetConnectionProfile | where {$_.NetworkCategory -eq "DomainAuthenticated" }  # Assuming only one NIC is domain joined.
$mgmtNetIpAddress = Get-NetIPAddress -InterfaceIndex $mgmtNetProfile.InterfaceIndex -AddressFamily IPv4

Monday, August 19, 2013

Hyper-V WMI v2 porting guide

Ben Armstrong of the Hyper-V team just released (on the TechNet Wiki) a v2 namespace porting guide.

To all of my DevOps and developer friends out there, this is a highly useful guide.  Because if you have not noticed, moving from the v1 namespace to the v2 is not a simple change of the namespace.

And if you have not heard yet, the v1 namespace is GONE with the 2012 R2 release.

So, spend some time over at the TechNet Wiki page;

http://social.technet.microsoft.com/wiki/contents/articles/19192.hyper-v-wmi-v2-porting-guide.aspx

and then go over to Taylor Brown’s blog and check his updates of his v1 namespace examples to the v2 namespace:

http://blogs.msdn.com/b/taylorb/