Monday, December 22, 2014

Mini-Orca: Getting Product Name and Code from an MSI

Was poking around the script source for the DSC package resource and found this nice way to get the product name and code for an MSI.  Before this I’d been using Orca to load the file and look at its properties.  This way looks much nicer.

$sig = @'

[DllImport("msi.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]

private static extern UInt32 MsiOpenPackageW(string szPackagePath, out IntPtr hProduct);

 

[DllImport("msi.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]

private static extern uint MsiCloseHandle(IntPtr hAny);

 

[DllImport("msi.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]

private static extern uint MsiGetPropertyW(IntPtr hAny, string name, StringBuilder buffer, ref int bufferLength);

 

private static string GetPackageProperty(string msi, string property)

{

    IntPtr MsiHandle = IntPtr.Zero;

    try

    {

        var res = MsiOpenPackageW(msi, out MsiHandle);

        if (res != 0)

        {

            return null;

        }

           

        int length = 256;

        var buffer = new StringBuilder(length);

        res = MsiGetPropertyW(MsiHandle, property, buffer, ref length);

        return buffer.ToString();

    }

    finally

    {

        if (MsiHandle != IntPtr.Zero)

        {

            MsiCloseHandle(MsiHandle);

        }

    }

}

public static string GetProductCode(string msi)

{

    return GetPackageProperty(msi, "ProductCode");

}

   

public static string GetProductName(string msi)

{

    return GetPackageProperty(msi, "ProductName");

}

'@

$MsiTools = Add-Type -PassThru -Namespace Microsoft.Windows.DesiredStateConfiguration.PackageResource `

    -Name MsiTools -Using System.Text -MemberDefinition $sig

 

$MsiTools::GetProductName("C:\SharePointPreReqs\sqlncli.msi")

### Result: Microsoft SQL Server 2012 Native Client

 

$MsiTools::GetProductCode("C:\SharePointPreReqs\sqlncli.msi")

### Result: {49D665A2-4C2A-476E-9AB8-FCC425F526FC} 

 

Wednesday, December 17, 2014

Get the Domain Given a SecurityIdentifier (SID)

Somebody asked how to get the domain name, given the SID of an object.  There’s at least a couple of ways. 

One uses the Get-ADDomain command in the ActiveDirectory module.  This works because the Identity parameter accepts the domain SID (nice).

Another way is to use the Translate method of the SecurityIdentifier class to translate to an NTAccount which has the account name in the format of Domain\Account.

### Create a new Security Identifier Object

$sid = New-Object System.Security.Principal.SecurityIdentifier "S-1-5-21-2627401586-940742709-677887653-1013"

 

### Get the AD Domain for that SID

Get-ADDomain -Identity $sid.AccountDomainSid

 

### Tranlsate to an NTAccount (REDMOND\FOO)

$sid.Translate([System.Security.Principal.NTAccount])

 

Wednesday, December 03, 2014

Using WMI and XML to Read Hyper-V Key-Value Pair Data

Here is the follow-up to the post about using DSC to set a key-value pair item for Hyper-V and its integration services.

The code sample below shows how to use WMI and XML to read the specific item from the key-value pair data.

The only real trick to this is to create a string that can be cast to XML, which just means creating the root node (kvpItems in the sample below) then inserting the string we get from WMI.

The next part is to use XPath to pick out just the item we need.  Having worked with FIM long enough I have developed a liking to XPath, but could have just as easily skipped XML and XPath and gone with a quick –like operator (though it wouldn’t be a nice).

Here’s the sample code:

###

### Get a VM

### then get its Key-Value Pair items

### then put them into an XML DOM

### then use XPath to find a specific key-value pair

###

$vmName = 'HoofHeartedVM'

do{

    Write-Verbose "$(Get-Date) Waiting for the FIM Service to reach the started state."

 

    $vm  = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName='$vmName'"

    $kvp = Get-WmiObject -Namespace root\virtualization\v2 -Query "Associators of {$Vm} Where AssocClass=Msvm_SystemDevice ResultClass=Msvm_KvpExchangeComponent"

 

    $kvpItems = [xml]"<KvpItems>$($kvp.GuestIntrinsicExchangeItems)</KvpItems>"

    $kvpItem  = Select-Xml -Xml $kvpItems -XPath "/KvpItems/INSTANCE[PROPERTY/VALUE[.='FimServiceStatus'] and PROPERTY/VALUE[.='Running']]"

    Start-Sleep -Seconds 60

}

until($kvpItem)

 

Using DSC to Communicate with the Hyper-V Host from the Guest

There are some great posts describing how to use Hyper-V to trade data between the Hyper-V Host and the VM without relying on network connectivity.  This comes in handy when a VM is not available on the network, WinRM is not available, or security is just a little too tight for comfort.

Here’s some of those posts:

In my environment I want the Hyper-V host to know when a VM reaches the state where the FIM Service is running.  They way I do it is to use DSC to create a registry key.  Pretty simple.

I’ll follow-up with the script to consume the Key-Value pairs on the Hyper-V host.

configuration SampleHypervKvpConfiguration

{

    node (hostname)

    {

        Service FimService

        {

            Name     = "FimService"

            State    = "Running"

        }

 

        Registry HypervKvpForFimService

        {

            Ensure    = "Present"

            Key       = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Auto"

            ValueName = "FimServiceStatus"

            ValueData = "Running"

            DependsOn = '[Service]FimService'

        }

    }

}

 

SampleHypervKvpConfiguration -OutputPath $env:temp\SampleHypervKvpConfiguration

 

Start-DscConfiguration -Path $env:temp\SampleHypervKvpConfiguration -Verbose -Wait

 

Tuesday, December 02, 2014

Solve the 'Circular DependsOn exists' Error

The blog title is a suggestion posted on Connect for PowerShell:

Solve the 'Circular DependsOn exists' Error

The DependsOn parameter enables PowerShell Desired State Configuration (DSC) to resolve references in the configuration and order them properly.  This works quite well in my experience building a DSC configuration for FIM but there are a couple cases in FIM that break this functionality:

  • Set objects that refer to each other
  • Forest Configuration objects where the forests trust each other

In pre-DSC days I would just solve this by doing multiple passes to order them properly.  Seems like a simple enough ask but my guess is that the existing DSC resources don’t run into this problem yet because they don’t represent objects with circular dependencies (lucky me). 

My current workaround is just to exclude the properties that cause the problem, which for now results in a broken configuration.  My next step is to cheat by using a Script resource in my DSC configuration to fix up the items that required hacking.

At the end of the day I really can’t complain.  I’ve generated a 40,000 line DSC script using thousands of FIM objects, resulting in a 6MB MOF file.  At this scale I was expecting something to tip over but so far am pretty impressed.  Now, time to run that MOF file through the LCM, what could possibly go wrong?

Monday, December 01, 2014

Having Too Much to DependsOn

In building a Desired State Configuration document for a FIM Service deployment I’ve come across a limit when using the DependsOn parameter:

StrongConnect : DependsOn link exceeded max depth limitation '1024'.

This happened when I had a configuration item that depended on more than 1024 other items.  In this case, it was a FIM Set object with thousands of ExplicitMember values.  The configuration is valid in FIM, so the resulting DSC configuration I’d generated was what I wanted but I guess past the limits for what DependsOn was intended for.

The workaround was just to omit the items, which could cause the configuration to fail late if the ExplicitMember items don’t actually exist in the FIM Service when the configuration runs.  For now I might just create those items out-of-band from DSC.