PowerShell Desired State Configuration (DSC) Journey – Day 25

Edit:  This literally took me two days because Virtual Machine Manager is a complete pile.  Anyways….

When I left off last week I had gotten a Configuration to run on my VMM Template, with some work because I ran into a few issues.  I have updated my VMM Template to include all currently available DSC Modules (in case I need them) and added the DNS Settings to the Configuration I will be injecting.  I have also changed my Configuration file so that it doesn’t try to join the computer to the domain.  The need for credentials is complicating things so I have removed that for the time being.  The updated Configuration Script I am using for this next part is below.

[powershell]
$ConfigData = @{
AllNodes = @(
@{
NodeName = "localhost"
PSDscAllowPlainTextPassword = $True
}
)
}

Configuration DSCTemplate
{

Param
(

[pscredential]$Credential
)

Import-DscResource -Module xNetworking
Import-DscResource -Module xComputerManagement

Node $AllNodes.NodeName
{

xIPAddress IPAddress
{
InterfaceAlias = "Ethernet"
IPAddress = "100.198.100.152"
AddressFamily = "IPv4"
DefaultGateway = "100.198.100.1"
SubnetMask = 24
}

xDNSServerAddress DNSAddress
{
InterfaceAlias = "Ethernet"
Address = "100.198.100.2","100.198.100.3"
AddressFamily = "IPv4"
DependsOn = "[xIPAddress]IPAddress"
}

xComputer ComputerConfiguration
{
Name = "DSCTemplate"
DependsOn = "[xIPAddress]IPAddress"
}

}
}

DSCTemplate -ConfigurationData $ConfigData -Credential (Get-Credential)
[/powershell]

For my next adventure, I am going to be attempting Option 2.1 from this article about how to Automatically Configure Your Machines Using DSC At Initial Boot.  One other thing I should mention is that article states that you need to put the Unattend.XML in the root directory ( C:\ ).  With VMM there is actually a setting in the Template where you can specify the location of an answer file (that must be in your VMM library).  When I deploy my Template next I will be using this setting in VMM so we will see if the scheduled task actually gets created.

First step is to generate a compiled Configuration.

[powershell]

PS C:\Scripts> C:\Users\jacob.benson\SkyDrive\PowerShell\DSC\DSCTemplate.ps1
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters

Directory: C:\Scripts\DSCTemplate

Mode LastWriteTime Length Name
—- ————- —— —-
-a— 4/21/2014 2:28 PM 3016 localhost.mof
[/powershell]

Next step is to copy localhost.mof to pending.mof

[powershell]
Copy-Item C:\Scripts\DSCTemplate\localhost.mof C:\Scripts\DSCTemplate\Pending.mof
[/powershell]

Now, the instructions in the article say to inject the pending.mof file under the DSC Local Configuration Manager. However, farther down it says that “the above process will make localhost.mof the pending Configuration. Doesn’t say anything about pending.mof. So I guess we will see what happens.

I will be using the same Unattend.xml as before except VMM will be the one applying it.  I update my RunDSC.cmd to use the arguments specified in the article (which are also shown below).  I also removed the DSCTemplate.ps1 file from the C:\DSC directory at this time.

[powershell]

schtasks.exe /Create /SC ONSTART /TN "\Microsoft\Windows\Desired State Configuration\DSCRestartBootTask" /RU System /F /TR "PowerShell.exe -NonInt -Command ‘Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration –ClassName MSFT_DSCLocalConfigurationManager -MethodName PerformRequiredConfigurationChecks -Arg @{Flags = [System.UInt32]3 }’"

[/powershell]

I next deploy a VM from this Template, which includes the Unattend.xml file as part of the Template.  I have never tried to do this before so I have no idea if it will work or not.  Because of the multiple tests already performed the name of this VM is DSC-WEB-04.

First, the Unattend.xml must have worked because the Scheduled Task performed by the RunDSC.cmd file exists, and shows it last run when I booted the VM.  And that’s where the good news ends, because none of my Configuration was applied to the Server.  I copy the following text from the Action in the Scheduled Task and run it in a command prompt to see what happens.

[powershell]

<span style="font-family: Calibri; font-size: medium;"><span style="font-family: Calibri; font-size: medium;">Powershell.exe -NonInt -Command ‘Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration –ClassName MSFT_DSCLocalConfigurationManager -MethodName PerformRequiredConfigurationChecks -Arg @{Flags = [System.UInt32]3 }’"</span></span>

[/powershell]

And…it just returns me to the command prompt.  No errors.  No output of any kind.  Awesome.  Now the first thing I am going to do is re-enable the Analytic and Debug DSC logs (I have had to rebuild this template multiple times and I forgot to re-enable them).  I run the same thing in the command prompt and the same thing happens and nothing happens in the logs.

I then realize that I could just put this command in PowerShell and run it and see what happens.

[powershell]

Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration –ClassName MSFT_DSCLocalConfigurationManager -MethodName PerformRequiredConfigurationChecks -Arg @{Flags = [System.UInt32]3 }

[/powershell]

And holy crap.  It runs perfectly, with no errors.

dsc31

Back to my command prompt, if I remove the -NonInt -Command section from the command prompt, the task runs exactly as you would expect.

dsc32

So, the first thing I am going to do is to restart the Computer to change it’s Computer Name.  Then, I am going to edit the Scheduled Task removing the -NonInt -Command parameters from it.  I will then change the IPAddress back to DHCP and rename it to something completely random, and reboot again, seeing if the Scheduled Task does what it should.

Now, after I renamed it and rebooted, there are now 2 Scheduled Tasks in the Desired State Configuration folder in Task Scheduler.  The first is the Consistency task, and the second is the DSCRestartBootTask, but looking at it’s configuration, it is slightly different than the one my DSCRun.cmd is supposed to create.

[powershell]

PowerShell.exe -NonInt -Window Hidden -Command "Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration -ClassName MSFT_DSCLocalConfigurationManager -MethodName PerformRequiredConfigurationChecks -Arg @{Flags = [System.UInt32]2 }"

[/powershell]

Things that make you go….hmmmmm.  I have no idea what the difference the 2 or 3 makes at the very end of the command line, but obviously it means something.  Well let’s figure this out.  I delete the scheduled task and run the RunDSC.cmd file to generate the other scheduled task and run it.  Nothing happens.  Next I add the -Window Hidden parameter to the arguments of the scheduled task, leaving everything else to be the same.  I also noticed as part of this that the additional parameters in the action section has a special character before -ClassName.  That looks to be coming from the syntax of the RunDSC.cmd where it places a ‘ before Invoke-Command.  When I successfully ran it in PowerShell and the Command prompt I removed that formatting.  Putting that thought on hold I run the ScheduledTask with the -Window Hidden parameter and again nothing happens.  I delete the scheduled task, and change the RunDSC.cmd to remove the ‘ from the script and run it.

At this point I should mention that whatever process is creating that “New” DSCRestartBoot task, it is overwriting any changes I make to it, even after I delete it.  I only realized this after much testing.

I instead create  a new Scheduled Task using the RunDSC.cmd and call it MyTestTask.  Running it with it’s original configuration does nothing.  I change some things around, still nothing.  Run it in PowerShell, and it completes successfully and nothing gets configured.  I then wonder if something isn’t wrong with the .MOF file.  I go look in the C:\Windows\System32\Configuration directory, and holy crap.

dsc33

The only thing that used to be in here was the pending.mof and localhost.mof.old (which I added in case I would need it for these tests).  Well this certainly explains a lot.

I copy my pending.mof file over to the Template and run the scheduled task again. GLORY GLORY HALLELUJAH.  IT WORKS!  Here is what I used for my final, working, DSCRun.cmd file to create my scheduled task.

dsc34

At this point I literally feel like my brain is melting and I am going to call it a day and process this a little bit.  I feel like I need to write up an entire post about all the things I learned in this single article.

Next up, is undoing the DSC Configuration and saving this as a template with the new RunDSC.cmd and trying to deploy another VM and see if I can get it to actually run on boot.

 

 

 

PowerShell Desired State Configuration (DSC) Journey – Day 24

When I last left off I had created my VMM Template with an injected DSC .ps1 file on the Hard Disk.  My next objective is to deploy a VM from this Template and see if it configures the IP Address and joins it to the domain as it states in the injected DSC Configuration.

So, let’s deploy the VM and see what happens.  And keep your fingers crossed.  If this works on the first attempt I don’t even know what I will do.

dsc29

It got all the way to Changing Properties of the VM before crapping the bed with Error (23352) VMM cannot find the device or this device is not valid for a boot device.  Recommended action:  Make sure you specify a valid device as a boot device.  I have deployed a lot of VM’s through VMM, and I haven’t seen this before.  It had a valid hard drive as a boot disk so I am not sure what happened.

Researching the issue I come across this TechNet article (and many like it) where this is a known issue in VMM and deploying a Generation 2 VM from a Template.  So I am going to have to rebuild the template and try again.  Awesome.  Not.

Alright, after rebuilding the VMM Template using the VHD I have successfully deployed a VM based on the template that has the injected DSC Configuration.  So let’s fire this thing up and see what happens.

And naturally, nothing happens.  The scheduled task that was supposed to be created by the RunDSC.cmd file was not created, so the Configuration never ran.  Which also means the Unattend.xml didn’t run (or something isn’t configured properly).

The TechNet article states the following:

  • We have all necessary files ready. Now inject SampleIISInstall.ps1, RunDSC.cmd, and unattend.xml to the VHD. In our case, unattend.xml is copied under root directory while SampleIISInstall.ps1 and RunDSC.cmd are put under %SystemDrive%\DSC. If you boot up from the VHD, or create a VM from the VHD, you will see that IIS gets installed at first boot-up.

I verify that the all the files are where they should be.  I check the path in the Unattend.xml and verify that it goes to the correct place and both the RunDSC.cmd and DSCTemplate.ps1 files are in the directory.

<settings pass=”specialize”>
<component name=”Microsoft-Windows-Deployment” processorArchitecture=”amd64″ publicKeyToken=”31bf3856ad364e35″ language=”neutral” versionScope=”nonSxS” xmlns:wcm=”http://schemas.microsoft.com/WMIConfig/2002/State” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”>
<RunSynchronous>
<RunSynchronousCommand>
<Order>1</Order>
<Path>%SystemDrive%\DSC\RunDSC.cmd</Path>
</RunSynchronousCommand>
</RunSynchronous>
</component>
</settings>

It is at this point that I run the RunDSC.cmd file.  I previously looked for the scheduled task under the regular Task Scheduler Library, and didn’t see it.  After running the RunDSC.cmd I actually looked at the syntax and realized that the scheduled task is in the Desired State Configuration folder in the Task Scheduler Library.  Problem is that I didn’t look there previously so I don’t know if it was actually there already or not.

There isn’t anything useful in either the Desired State Configuration or Task Scheduler event logs so I guess we are just going to roll with this for now.  The scheduled task is set to run at startup so I reboot the VM and see what happens.

I didn’t get a screenshot in time but after I logged in Task Manager showed that Windows PowerShell was running as a background process which I hope is a good sign.  Or at least a sign of progress.

I quickly check the IP address of the VM and it’s not set properly.  The scheduled task does show that the last run time was a little over 3 minutes ago.

The DSC log doesn’t show anything.  Of course the Analytic and Debug logs aren’t enabled by default.  I should probably enable those on the template going forward.  The other event logs don’t have anything in them either (that I can find) so I am going to run the scheduled task manually and see what happens.

The scheduled task runs for about 5 seconds and then stops.  Which doesn’t seem nearly long enough.  Next troubleshooting step is to run the command that is in the scheduled task in PowerShell and see what happens.

And it seems that the latest WordPress updates broke the SyntaxHighlighter Plugin so this is going to have to work for now.

Powershell.exe -ExecutionPolicy RemoteSigned -File C:\DSC\DSCTemplate.ps1

And….I am an idiot.  I feel really dumb.  Here is the error.

At C:\dsc\DSCTemplate.ps1:20 char:43

+ Import-DscResource -Module xNetworking

Unable to load module ‘xNetworking’: module not found.

At C:\dsc\DSCTemplate.ps1:12 char:1

Of course that’s a problem Jacob!  I got so carried away with the other steps that I completely forgot about that (which I knew from previous experience with DSC).  I am already building another template to test whether that Scheduled Task gets carried so lets keep carrying on here.

I copy those Modules from my PC to c:\Program Files\WindowsPowerShell\Modules and restart PowerShell on my DSC Template.  I run Get-DSCResource and I now have the modules for xComputer, xDNSServerAddress,xFirewall and xIPAddress.

I am feeling crazy so let’s reboot and see what happens instead of testing the script again, since I know the scheduled task ran as it was supposed to.  After I login I quickly open up Task Manager and I see there are now two processes for Windows PowerShell and they are still running a minute after logging in.

dsc30

A quick check of the IP shows that it is still set for DNS.  There isn’t anything in the DSC log.  Both background processes are still running so I guess I will let them run for a few minutes and see what happens.  So after a break I come back and those same two processes are running and nothing has happened.  I end the processes and am going to run the .PS1 from PowerShell and see what happens.  And.  The very first thing that happens is that I get a credential box, which is probably why the script was hanging and doing nothing.  I enabled plain text passwords but never stored a credential, because I thought the credentials would be stored.  This is obviously flawed thinking because I never created a .MOF file to store that information, only a .PS1 file.

I enter my domain credentials and magic happens.  The IPAddress is set, the computer is renamed, but it fails to join the domain because “it either does not exist or cannot be contacted.”  Which doesn’t make any sense.  I have a feeling that is because after the IPAddress changed that the box popped up that asks you if you want to trust this network or share devices or whatever the hell it is that it says that you just always say OK to.  Turns out I was wrong (shocking I know).  The VLAN on my network adapter was set incorrectly, once I changed that, however it still didn’t work.

So I go to the network adapter settings and look….and…for the 2nd time today I feel like an idiot.  In my Configuration I set the IPAddress, but I sure didn’t configure the DNS Settings.  Which is clearly a problem.  So I set them manually and try to run my DSCTemplate.ps1 again.  And now, true magic happens.  The entire Configuration is applied successfully.  It asks me to reboot the server so I do that.  After the reboot everything looks wonderful.

I also spun up another VM based off of the template, and the Desired State Configuration task isn’t created on initial boot, so the unattend.xml isn’t working properly.  I don’t know enough about using a file like that and what might be causing that.  I will need to do some more investigation on that front.  For now I think this is a good place to stop.

 

 

PowerShell Desired State Configuration (DSC) Journey – Day 23

After yesterday’s shenanigans I am ready to move on to the next step.  Here is my current Configuration.

$ConfigData = @{
    AllNodes = @(
                @{
                    NodeName = "localhost"
                    PSDscAllowPlainTextPassword = $True
                    }
                )
}

Configuration DSCTemplate
{

    Param
    (

        [pscredential]$Credential
    )

    Import-DscResource -Module xNetworking
    Import-DscResource -Module xComputerManagement

    Node $AllNodes.NodeName
    {

        xIPAddress IPAddress
        {
            InterfaceAlias = "Ethernet"
            IPAddress = "100.198.100.152"
            AddressFamily = "IPv4"
            DefaultGateway = "100.198.100.1"
            SubnetMask = 24
        }

        xComputer ComputerConfiguration
        {
            Name = "DSCTemplate"
            DomainName = "domain.com"
            Credential = $Credential
            DependsOn = "[xIPAddress]IPAddress"
        }

    }
}

DSCTemplate -ConfigurationData $ConfigData -Credential (Get-Credential)

Now, following along with this article I am currently testing Option 1 – Inject a DSC Configuration (.ps1) into a VHD.  A couple of things from that example that I need to add to my Configuration.  It starts with Powershell -c .  I have no idea what this does and can’t find any information on it but I will include it anyways.  If I run that itself in PowerShell I just get the help syntax.  And I need to add the line to start the DSC Configuration.  Here is the updated Configuration.

Powershell -c{
$ConfigData = @{
    AllNodes = @(
                @{
                    NodeName = "localhost"
                    PSDscAllowPlainTextPassword = $True
                    }
                )
}

Configuration DSCTemplate
{

    Param
    (

        [pscredential]$Credential
    )

    Import-DscResource -Module xNetworking
    Import-DscResource -Module xComputerManagement

    Node $AllNodes.NodeName
    {

        xIPAddress IPAddress
        {
            InterfaceAlias = "Ethernet"
            IPAddress = "100.198.100.152"
            AddressFamily = "IPv4"
            DefaultGateway = "100.198.100.1"
            SubnetMask = 24
        }

        xComputer ComputerConfiguration
        {
            Name = "DSCTemplate"
            DomainName = "domain.com"
            Credential = $Credential
            DependsOn = "[xIPAddress]IPAddress"
        }

    }
}

DSCTemplate -ConfigurationData $ConfigData -Credential (Get-Credential)

Start-DSCConfiguration -Path .\DSCTemplate -ComputerName localhost -Wait -Force -Verbose

}

I have saved the above Configuration as DSCTemplate.ps1 .  This is the file I will be injecting into the VHD Template in Virtual Machine Manager (I hope).  First step is to download the unattend.xml from that article.  Second step is to create a RunDSC.cmd file.  Using the example in the article this is the contents of my RunDSC.cmd.

schtasks.exe /Create /SC ONSTART /TN "\Microsoft\Windows\Desired State Configuration\DSCRestartBootTask" /RU System /F /TR 
"PowerShell.exe -ExecutionPolicy RemoteSigned -File c:\dsc\DSCTemplate.ps1"

On my Template VM I create a C:\DSC directory and copy the RunDSC.cmd and DSCTemplate.ps1 files.  The unattend.xml gets copied under the root directory (C:\).  Now, time to create a template from this VM, deploy it and see what happens.  That’s where we will pick up tomorrow.

PowerShell Desired State Configuration (DSC) Journey – Day 22

Update 4/15/2014:  I retyped this exact Configuration in a new PowerShell window this morning and everything works fine.  I have compared them side by side and the code is absolutely identical.  The one from yesterday still errors, the one from today compiles like it should.  Frustration level = high.

In my last post I talked about what my next plans were.  To that end I have put together a list of the steps I think need to be done to make it happen.  First step is the creation of a Template disk to use in Virtual Machine Manager (VMM).  This template is Server 2012 R2 with all the latest updates.  There are a lot of articles you can find out there on how to create a template so I am not going to go into those steps.  One other thing I have done is created an Application Profile, a Guest OS Profile, and a Hardware Profile to be used for my DSC Template.

My current thoughts on “first steps” is the following:

  • Build the server template with nothing but the OS.  This template is called SERVER2012R2_DSC_TEMPLATE
  • Build the simple Configuration I want to test with
  • Push the Configuration server template
  • Ensure changes are made to the Configuration
  • Revert those changes back to the way it was originally
  • Follow the steps in Option 1 in this article to inject the DSC Configuration into a .VHD
  • Convert my server template into a VMM template
  • Deploy a server from this template and see if the Configuration actually happens

My server template is built and completely updated.  The only things that I have modified are:

  • It has a DHCP Address
  • All Windows Firewall’s have been disabled
  • The time zone is set to Central Standard Time
  • IE ESC is set to Off
  • Windows Updates are set to download only

Here is the Configuration I am going to test with.  The lack of parameters in the Configuration is by design.  I am trying to keep things as simple as possible for now.  Of course all I want to do is set an IP Address and join to the domain.  But do you think I can get my Configuration to a .MOF?  Of course not, because no matter what I do, all it tells me is that

ConvertTo-MOFInstance : System.InvalidOperationException error processing property 'Credential' OF TYPE 'xComputer': Converting and storing encrypted passwords as plain text is not 
recommended for security reasons. If you understand the risks, you can add a property named “PSDscAllowPlainTextPassword” with a value of “$true” to your DSC configuration data, for each 
node where you want to allow plain text passwords. For more information about DSC configuration data, see the TechNet Library topic, http://go.microsoft.com/fwlink/?LinkId=386620.
At line:35 char:9
+   xComputer
At line:180 char:16
+     $aliasId = ConvertTo-MOFInstance $keywordName $canonicalizedValue
+                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessProperty,ConvertTo-MOFInstance
Errors occurred while processing configuration 'DSCTemplate'.
At C:\windows\system32\windowspowershell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:2203 char:5
+     throw $errorRecord
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (DSCTemplate:String) [], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessConfiguration

Even though I have this is my ConfigData setup:

$ConfigData = @{
                 AllNodes = @(
                             @{
                                 NodeName = "localhost";
                                 PSDscAllowPlainTextPassword = $true;
                             }
                            )
                 }

I’ve tried everything I can possibly think of, and absolutely cannot get anything but this freaking error.  Unbelievable.  If I run it without the -Credential parameter it creates the .MOF with no problems.  I’ve tried all manner of formatting and placement within the Configuration.  I’ve copied and pasted from TechNet articles and Steve Murawski’s example here.  I just don’t get it.  It shouldn’t be this hard.

Funny enough, if I Google search for “Converting and storing encrypted passwords as plain text is not recommended for security reasons. If you understand the risks, you can add a property named “PSDscAllowPlainTextPassword” ” the only thing that comes up is my previous blog article.  Which is certainly not encouraging.

I am running all of this on my Windows 8.1 computer that has WMF 5.0 Preview installed.  Hoping for some better insight (or that maybe WMF 5.0 is causing this issue) I run the exact same Configuration script on a Windows 2012 R2 server (without WMF 5.0).  This is what I got for the error.

ConvertTo-MOFInstance : System.InvalidOperationException error processing property 'Credential' OF TYPE 'xComputer': Converting and storing an encrypted password as plaintext is allowed only if 
PSDscAllowPlainTextPassword is set to true.
At line:32 char:9
+   xComputer
At line:164 char:16
+     $aliasId = ConvertTo-MOFInstance $keywordName $canonicalizedValue
+                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessProperty,ConvertTo-MOFInstance
Errors occurred while processing configuration 'DSCTemplate'.
At C:\windows\system32\windowspowershell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:2088 char:5
+     throw $errorRecord
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (DSCTemplate:String) [], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessConfiguration

A slightly different error message here but the same issue still stands.  I have it set as $true.  I have also tried $True and it doesn’t matter.  I really don’t understand what I am doing wrong here.

I mean look, I am clearly not crazy here:

PS C:\Windows\system32> 
$ConfigData = @{  
                AllNodes = @(       
                                @{
                                 NodeName = "localhost"
                                 PSDscAllowPlainTextPassword = $true    
                                 }
                             ) 
}

PS C:\Windows\system32> $ConfigData

Name                           Value                                                                                                                                                                                 
----                           -----                                                                                                                                                                                 
AllNodes                       {System.Collections.Hashtable}                                                                                                                                                        

PS C:\Windows\system32> $ConfigData.AllNodes

Name                           Value                                                                                                                                                                                 
----                           -----                                                                                                                                                                                 
NodeName                       localhost                                                                                                                                                                             
PSDscAllowPlainTextPassword    True

As a last ditch effort, on my Pull server (2012R2) I downloaded version 1.0 of the xComputerManagement Resource and restarted the server.  I am hoping that Version 1.2 is the problem I guess, because I am completely out of ideas.  And..nope.  Exact same freaking error. I am done for the day.

 

PowerShell Desired State Configuration (DSC) Journey – Day 21

I have written 20 articles about what I have learned about DSC so far.  At this point I am familiar enough with the technology that I have started to think about how I am going to demo this technology in my organization.  I could demo the basic features that are available without going into anything in depth, but I want this demo to be a complete and total WOW reaction.  I have spent some time this week figuring out how I want to proceed with the demo and this is what I have decided.

We use System Center Virtual Machine Manager to deploy VM’s.  I want to demonstrate using VMM and DSC the following:

  • Deploy a “farm” consisting of 4 Web Servers and 1 SQL Server.  This may require me to configure VMM Service Provisioning.
  • Use VMM Templates (including possibly a service template) to do the initial building of the VM, naming it, and joining it to the domain.
  • After that I want it to automatically configure its Local Configuration Manager for settings I have decided on
  • This includes reaching out to a Pull server and getting the appropriate Configuration
  • Applying that Configuration
  • Breaking the servers, and them fixing themselves based off of their DSC Configuration

Too ambitious?  Not ambitious enough?  Any high level steps I missing?

All that being said, I am going to continue to post about anything relating to this process (even if it’s VMM specific) under the DSC Learning Journey on Twitter, as eventually it will all tie in together.

PowerShell Desired State Configuration (DSC) Journey – Day 20

Yesterday I was driving the struggle bus around because I don’t understand how hash tables work.  Thankfully for me Steven Murawski was kind enough to put an example up of how to use the Configuration Data hash table in a DSC Configuration which you can find here.

Using that example, I modified my Configuration as below and was able to Invoke it with no errors!  I sure just stop here and call it a day, but what fun would that be?

Configuration NewServer
{
    param(

    [string]$DomainName='domain.com',

    [pscredential]$Credential

    )

    Import-DSCResource -Module xComputerManagement

    Node $AllNodes.NodeName
    {

        xComputer DomainJoin
        {
            Name = $Node.Name
            DomainName = $DomainName
            Credential = $Credential
        }
    }

}

$ConfigData = @{   
    AllNodes = @(        
        @{     
            NodeName = "DSCTEST-02";                             
            PSDscAllowPlainTextPassword = $true; 
         } 
    )  
} 

NewServer -ConfigurationData $ConfigData -Credential (Get-Credential)

After looking at this I literally spent about an hour thinking about it.  This works, but is it going to do what I want it to do?  No.  I need to be able to specify the ComputerName, and have it renamed to that.  And then a light bulb went off, and this happened.

Configuration NewServer
{
    param(

    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [string[]]$ComputerName,

    [string[]]$NodeName="localhost",

    [string]$DomainName='domain.com',

    [pscredential]$Credential

    )

    Import-DSCResource -Module xComputerManagement

    Node $NodeName
    {

        xComputer RenameComputer
        {
            Name = $ComputerName
            Credential = $Credential
        }

        xComputer DomainJoin
        {
            Name = $ComputerName
            DomainName = $DomainName
            Credential = $Credential
            DependsOn = "[xComputer]RenameComputer"
        }
    }

}

$ConfigData = @{   
    AllNodes = @(        
        @{     
            NodeName = "localhost";                             
            PSDscAllowPlainTextPassword = $true; 
         } 
    )  
} 

NewServer -ConfigurationData $ConfigData -ComputerName DSCTEST02 -Credential (Get-Credential)

And I got this awesome error for my trouble.  And yes I know I complicated matters by adding another block of code in there to rename the computer, but that’s the least of my concerns right now.

S C:\Scripts> C:\Users\jacob.benson\SkyDrive\PowerShell\DSC\NewServer.ps1
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Add-NodeKeys : The key properties combination 'DSCTEST02' is duplicated for keys 'Name' of resource 'xComputer' in node 'localhost'. Please make sure key properties are unique for each 
resource in a node.
At line:176 char:9
+         Add-NodeKeys $keyValues $keyNames $keywordName
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : DuplicateKeyInNode,Add-NodeKeys
Write-NodeMOFFile : Invalid MOF definition for node 'localhost': Exception calling "ValidateInstanceText" with "1" argument(s): "Convert property 'Name' value from type 'STRING[]' to type 
'STRING' failed
 At line:26, char:2
 Buffer:
leVersion = "1.2";
};^
ins
"
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:1457 char:17
+ ...             Write-NodeMOFFile $name $mofNode $Script:NodeInstanceAlia ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : InvalidMOFDefinition,Write-NodeMOFFile


    Directory: C:\Scripts\NewServer


Mode                LastWriteTime     Length Name                                                                                                                                              
----                -------------     ------ ----                                                                                                                                              
-a---          4/8/2014   3:27 PM       2658 localhost.mof.error                                                                                                                               
Errors occurred while processing configuration 'NewServer'.
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:2203 char:5
+     throw $errorRecord
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (NewServer:String) [], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessConfiguration

Alright, well I am pretty sure it doesn’t like the fact that I am renaming my computer in one block, and then using the same name in the other block.  It didn’t occur to me until just now that my code block for DomainJoin is already renaming the computer itself so there is no need to rename it in another code block.  After removing that code block and some more trial and error, this is the Configuration that I got to Inovke and create the .MOF file.  Whether or not this actually ends up working is a whole other matter.  The other issue from looking at the errors I am getting when trying to use [String[]]$ComputerName and the examples on TechNet is that it doesn’t look like using multiple ComputerName is going to work, which throws a wrench into what I am trying to accomplish.  I think I need to sit down and map this out because I have confused even myself at this point.

Configuration NewServer
{
    param(

    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [string]$ComputerName,

    [string[]]$NodeName="localhost",

    [string]$DomainName='domain.com',

    [pscredential]$Credential

    )

    Import-DSCResource -Module xComputerManagement

    Node $AllNodes.NodeName
    {

        xComputer DomainJoin
        {
            Name = $ComputerName
            DomainName = $DomainName
            Credential = $Credential
        }
    }

}

$ConfigData = @{   
    AllNodes = @(        
        @{     
            NodeName = "localhost";                             
            PSDscAllowPlainTextPassword = $true; 
         } 
    )  
} 

NewServer -ConfigurationData $ConfigData -ComputerName DSCTEST02 -Credential (Get-Credential)

My brain hurts and I am distracted so I am going to call it quits on this project for today.  More tomorrow.  Probably.  Maybe.

PowerShell Desired State Configuration (DSC) Journey – Day 19

While I have been exploring using DSC I have always been thinking about how I can best utilize it in my environment.  We are a predominantly Hyper-V environment, and currently use Virtual Machine Manager(VMM) to handle building all of our VMs.  In my last post I explored creating a VM, but quickly ran into a couple of issues.  The memory and disk settings default to dynamic, which isn’t what we use except in a couple of specific situations.  You also have to specify an install directory to store the hard disks and specify a specific host.  In VMM it will “suggest” a host on the cluster as well as a default location which almost never ends up being used.  I came across this article  a while back and decided to give this a whirl to see if it would work better for what I wanted.  Another option of course is just to build all the servers you want and then push out a Configuration to them to do what needs to be done or to download one from a Pull server.  I don’t have strong feelings right now about doing it either way, so I am just going to try this and see how it goes.

First, I have built a new Server 2012R2 VM in VMM.  I am going to turn this into a template after injecting the DSC Configuration files and then deploy a VM using the specified Configuration to see what happens.  The template will just be called Server2012R2_DSC_Template.  I’ve installed all the latest updates, including KB 2883200 as specified in the article.

I have previously created the Configuration below.  I can tell you right now it won’t work the way that I intended before I looked at this more closely.  For one thing, if I want this thing to join to the domain, I am going to need to store the password in plain text as it talks about in the xComputer Management Module Resource TechNet article.

Configuration NewServer
{
    param(

    [Parameter(Mandatory = $True)]
    [ValidateNotNullorEmpty()]
    [string[]]$ComputerName,

    [string[]]$DomainName='domain.com',

    [pscredential]$Credential

    )

    Import-DSCResource -Module xComputerManagement

    Node $ComputerName
    {

        xComputer DomainJoin
        {
            Name = $ComputerName
            DomainName = $DomainName
            Credential = $Credential
        }
    }

}

If I try to Invoke that Configuration I get the error below.  This doesn’t really make sense because even I use localhost for instance I get the same error.  This probably is going to require a blog article on its own to explore it further but for now I am just going to deal with it because the TechNet article also provides a work around.

PS C:\Scripts> C:\Users\jacob.benson\SkyDrive\PowerShell\DSC\NewServer.ps1
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
ConvertTo-MOFInstance : System.InvalidOperationException error processing property 'Credential' OF TYPE 'xComputer': Converting and storing encrypted passwords as plain text is not 
recommended for security reasons. If you understand the risks, you can add a property named “PSDscAllowPlainTextPassword” with a value of “$true” to your DSC configuration data, for each 
node where you want to allow plain text passwords. For more information about DSC configuration data, see the TechNet Library topic, http://go.microsoft.com/fwlink/?LinkId=386620.
At C:\Users\jacob.benson\SkyDrive\PowerShell\DSC\NewServer.ps1:20 char:9
+   xComputer
At line:180 char:16
+     $aliasId = ConvertTo-MOFInstance $keywordName $canonicalizedValue
+                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessProperty,ConvertTo-MOFInstance
Errors occurred while processing configuration 'NewServer'.
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:2203 char:5
+     throw $errorRecord
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (NewServer:String) [], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessConfiguration

Using the information in the article I add this to the bottom of my Configuration file:

$ConfigData = @{   
                 AllNodes = @(        
                              @{     
                                 NodeName = $ComputerName 
                                 # Allows credential to be saved in plain-text in the the *.mof instance document.                             
                                 PSDscAllowPlainTextPassword = $true 
                              } 
                            )  
              } 

NewServer -ComputerName DSCTEST02 -Credential Get-Credential -ConfigurationData $ConfigData

And I get this tremendous error message.

PS C:\Scripts> C:\Users\jacob.benson\SkyDrive\PowerShell\DSC\NewServer.ps1
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
ValidateUpdate-ConfigurationData : all elements of AllNodes need to be hashtable and has a property 'NodeName'.
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:1164 char:30
+ ...  $dataValidated = ValidateUpdate-ConfigurationData $ConfigurationData
+                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : ConfiguratonDataAllNodesNeedHashtable,ValidateUpdate-ConfigurationData

Well, that doesn’t make any sense.  I copied and pasted directly from the article, and just changed localhost to $ComputerName.  I put $ComputerName inside of double quotes, and still the same error.  I am extremely puzzled.  Google isn’t of any help with this error message either.  I then try replacing NodeName in the AllNodes hashtable with ComputerName because that is what I am using and in the example it’s called NodeName.  And I still get the same error.  Even crazier is that if I comment out that entire NodeName line I still get the same error message.  Then I thought I was onto something because I realized I had forgotten the semi-colon after NodeName = $ComputerName, but that didn’t make any difference either.

I try about a 40 different things, none of which work.  I think this is as good a place as any to leave off today.  Hopefully my subconscious will figure out what stupid thing I am doing wrong and I will pick this up again tomorrow.

PowerShell Desired State Configuration (DSC) Journey – Day 18

Yesterday I created a VHD to use for building a VM.  Today I am going to take that VHD (and the previously created VMSwitch) and create a Virtual Machine using the xVMHyperV Resource.

Here are all the Properties for this Resource

PS C:\Scripts> Get-DscResource -Name xVMHyperV | Select-Object -ExpandProperty Properties

Name                                            PropertyType                                                                        IsMandatory Values                                         
----                                            ------------                                                                        ----------- ------                                         
Name                                            [string]                                                                                   True {}                                             
VhdPath                                         [string]                                                                                   True {}                                             
DependsOn                                       [string[]]                                                                                False {}                                             
Ensure                                          [string]                                                                                  False {Absent, Present}                              
Generation                                      [string]                                                                                  False {Vhd, Vhdx}                                    
MACAddress                                      [string]                                                                                  False {}                                             
MaximumMemory                                   [UInt64]                                                                                  False {}                                             
MinimumMemory                                   [UInt64]                                                                                  False {}                                             
Path                                            [string]                                                                                  False {}                                             
ProcessorCount                                  [UInt32]                                                                                  False {}                                             
RestartIfNeeded                                 [bool]                                                                                    False {}                                             
StartupMemory                                   [UInt64]                                                                                  False {}                                             
State                                           [string]                                                                                  False {Off, Paused, Running}                         
SwitchName                                      [string]                                                                                  False {}                                             
WaitForIP                                       [bool]                                                                                    False {}

Based on all of that information and what I have done so far, here is what I have added to my Configuration:

    xVMHyperV $Name
    {
        Name = $Name
        VHDPath = (Join-Path -Path C:\Scripts\VHDS -ChildPath $($Name))
        Ensure = "Present"
        DependsOn = @("[xVHD]OSDisk",
                      "[xVMSwitch]DSCSwitch")
        Generation = $Generation
        SwitchName = $SwitchName
        State = "Running"
        ProcessorCount = 2
        MaximumMemory = 4096
        RestartIfNeeded = $True
    }

I am pretty sure I have everything covered there, but I am sure I am overlooking something, so let’s Invoke this Configuration and see what breaks :).  Yay Errors!

PS C:\Scripts> C:\Users\jacob.benson\SkyDrive\PowerShell\DSC\BuildTestVM.ps1
Write-NodeMOFFile : Invalid MOF definition for node 'localhost': Exception calling "ValidateInstanceText" with "1" argument(s): "Convert property 'Name' value from type 'STRING[]' to type 
'STRING' failed
 At line:72, char:2
 Buffer:
imumMemory = 4096;
};^
ins
"
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:1383 char:17
+                 Write-NodeMOFFile $name "localhost" $Script:NoNameNodeInstanceAl ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : InvalidMOFDefinition,Write-NodeMOFFile

    Directory: C:\Scripts\BuildTestVM

Mode                LastWriteTime     Length Name                                                                                                                                              
----                -------------     ------ ----                                                                                                                                              
-a---          4/2/2014   1:59 PM       4088 localhost.mof.error                                                                                                                               
Errors occurred while processing configuration 'BuildTestVM'.
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:2088 char:5
+     throw $errorRecord
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (BuildTestVM:String) [], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessConfiguration

So the Name Property of the VMHyperV Resource can’t take an array of names, it has to be one.  So let’s try an idea from this article on reusing existing Configuration scripts in PowerShell.

    ForEach ($VM in $Name){
    xVMHyperV $VM
    {
        Name = $VM
        VHDPath = (Join-Path -Path C:\Scripts\VHDS -ChildPath $($VM))
        Ensure = "Present"
        DependsOn = @("[xVHD]OSDisk",
                      "[xVMSwitch]DSCSwitch")
        Generation = $Generation
        SwitchName = $SwitchName
        State = "Running"
        ProcessorCount = 2
        MaximumMemory = 4096
        RestartIfNeeded = $True
    }
    }

The .MOF file is created with no errors.  Now let’s try to build this thing and see what happens.  As I expected, it hated my MaximumMemory number.

PowerShell provider MSFT_xVMHyperV  failed to execute Test-TargetResource functionality with error message: Cannot validate argument on parameter 'MaximumMemory'. The 4096
argument is less than the minimum allowed range of 33554432. Supply an argument that is greater than or equal to 33554432 and then try the command again.
    + CategoryInfo          : InvalidOperation: (:) [], CimException
    + FullyQualifiedErrorId : ProviderOperationExecutionFailure
    + PSComputerName        : localhost

4GB is equal to 4294967296 Bytes so let’s try that and see what happens.  I also really hope it doesn’t set the memory for this VM as Dynamic.  New errors!

PowerShell provider MSFT_xVMHyperV  failed to execute Test-TargetResource functionality with error message: Generation  should match virtual disk extension
C:\Scripts\VHDS\DSC-APP-01
    + CategoryInfo          : InvalidOperation: (:) [], CimException
    + FullyQualifiedErrorId : ProviderOperationExecutionFailure
    + PSComputerName        : localhost

Umm.  It does…..what the crap!  Screenshot for proof!

dsc28

I am guessing it is a syntax issue because I didn’t include the .vhdx in my path because I was hoping it would just find it and recognize it, but that clearly didn’t happen.  After much trial and error that I spared you from, I came up with this as my VHDPath.

VHDPath = (Join-Path -Path C:\Scripts\VHDS\$VM -ChildPath "$VM_DISK1_c.vhdx")

And then I got this slightly different error.

PowerShell provider MSFT_xVMHyperV  failed to execute Test-TargetResource functionality with error message: C:\Scripts\VHDS\DSC-APP-01\.vhdx does not exists
    + CategoryInfo          : InvalidOperation: (:) [], CimException
    + FullyQualifiedErrorId : ProviderOperationExecutionFailure
    + PSComputerName        : localhost

Sidenote:  I clearly need to go back and re-read my PowerShell in a Lunches book to figure out why things with parameter names in them don’t work the way I expect them to.  Here is the next VHDPath I will attempt:

VHDPath = (Join-Path -Path C:\Scripts\VHDS\$VM -ChildPath "$($VM)_DISK1_c.vhdx")

GREAT SUCCESS!

PS C:\scripts> Start-DscConfiguration -Wait -Verbose .\BuildTestVM
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' =
root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer localhost with user sid S-1-5-21-738551990-92959840-526660263-26310.
VERBOSE: [localhost]: LCM:  [ Start  Set      ]
VERBOSE: [localhost]: LCM:  [ Start  Resource ]  [[File]VMFolder]
VERBOSE: [localhost]: LCM:  [ Start  Test     ]  [[File]VMFolder]
VERBOSE: [localhost]:                            [[File]VMFolder] The destination object was found and no action is required.
VERBOSE: [localhost]: LCM:  [ End    Test     ]  [[File]VMFolder]  in 0.0100 seconds.
VERBOSE: [localhost]: LCM:  [ Skip   Set      ]  [[File]VMFolder]
VERBOSE: [localhost]: LCM:  [ End    Resource ]  [[File]VMFolder]
VERBOSE: [localhost]: LCM:  [ Start  Resource ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]: LCM:  [ Start  Test     ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]:                            [[xVMSwitch]DSCSwitch] Checking if Switch DSCTest is Present ...
VERBOSE: [localhost]:                            [[xVMSwitch]DSCSwitch] Switch DSCTest is Present
VERBOSE: [localhost]: LCM:  [ End    Test     ]  [[xVMSwitch]DSCSwitch]  in 0.8770 seconds.
VERBOSE: [localhost]: LCM:  [ Skip   Set      ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]: LCM:  [ End    Resource ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]: LCM:  [ Start  Resource ]  [[xVHD]OSDisk]
VERBOSE: [localhost]: LCM:  [ Start  Test     ]  [[xVHD]OSDisk]
VERBOSE: [localhost]:                            [[xVHD]OSDisk] Vhd C:\Scripts\VHDs\DSC-APP-01\DSC-APP-01_DISK1_C.Vhdx is present:True and Ensure is Present
VERBOSE: [localhost]: LCM:  [ End    Test     ]  [[xVHD]OSDisk]  in 0.8050 seconds.
VERBOSE: [localhost]: LCM:  [ Skip   Set      ]  [[xVHD]OSDisk]
VERBOSE: [localhost]: LCM:  [ End    Resource ]  [[xVHD]OSDisk]
VERBOSE: [localhost]: LCM:  [ Start  Resource ]  [[xVMHyperV]DSC-APP-01]
VERBOSE: [localhost]: LCM:  [ Start  Test     ]  [[xVMHyperV]DSC-APP-01]
VERBOSE: [localhost]: LCM:  [ End    Test     ]  [[xVMHyperV]DSC-APP-01]  in 0.7470 seconds.
VERBOSE: [localhost]: LCM:  [ Start  Set      ]  [[xVMHyperV]DSC-APP-01]
VERBOSE: [localhost]:                            [[xVMHyperV]DSC-APP-01] Checking if VM DSC-APP-01 exists ...
VERBOSE: [localhost]:                            [[xVMHyperV]DSC-APP-01] VM DSC-APP-01 does not exists
VERBOSE: [localhost]:                            [[xVMHyperV]DSC-APP-01] Creating VM DSC-APP-01 ...
VERBOSE: [localhost]:                            [[xVMHyperV]DSC-APP-01] VM DSC-APP-01 created and is Running
VERBOSE: [localhost]: LCM:  [ End    Set      ]  [[xVMHyperV]DSC-APP-01]  in 5.0730 seconds.
VERBOSE: [localhost]: LCM:  [ End    Resource ]  [[xVMHyperV]DSC-APP-01]
VERBOSE: [localhost]: LCM:  [ End    Set      ]
VERBOSE: [localhost]: LCM:  [ End    Set      ]    in  7.7190 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 7.787 seconds

That was pretty impressive how quickly that took.  And of course it created it with Dynamic Memory.  Blah.  Not what I wanted to see.

I am not sure what I will cover tomorrow but I think I have a pretty good idea!  It will more than likely have something to do with this article.

 

PowerShell Desired State Configuration (DSC) Journey – Day 17

Yesterday I created  vSwitch on Hyper-V to use as part of building some VMs in the very near future (like this week).  Today I am going to create a VHD to use to build the VM.

Running Get-DSCResource I see that there are 2 similarly named Resources xVHD and xVHDFile.  What is the different between the two?  I have no idea.  Let’s see if I can figure it out.

PS C:\Scripts> Get-DscResource -Name xVHD | Select-Object -ExpandProperty Properties

Name                                            PropertyType                                                                        IsMandatory Values                                         
----                                            ------------                                                                        ----------- ------                                         
Name                                            [string]                                                                                   True {}                                             
Path                                            [string]                                                                                   True {}                                             
DependsOn                                       [string[]]                                                                                False {}                                             
Ensure                                          [string]                                                                                  False {Absent, Present}                              
Generation                                      [string]                                                                                  False {Vhd, Vhdx}                                    
MaximumSizeBytes                                [UInt64]                                                                                  False {}                                             
ParentPath                                      [string]                                                                                  False {}                                             

PS C:\Scripts> Get-DscResource -Name xVHDFile | Select-Object -ExpandProperty Properties

Name                                            PropertyType                                                                        IsMandatory Values                                         
----                                            ------------                                                                        ----------- ------                                         
FileDirectory                                   [CimInstance[]]                                                                            True {}                                             
VhdPath                                         [string]                                                                                   True {}                                             
DependsOn                                       [string[]]                                                                                False {}

I think it’s pretty clear that xVHD is the Resource that I want to use, but what is xVHDFile for?  Looking on the TechNet page for the Hyper-V Resource in the example section it says this about the xVHDFile Resource: “Customize VHD by copying a folders/files to the VHD before a VM can be created. Example below shows copying unattended.xml before a VM can be created”.  OK then, that makes sense (I suppose).

Before I create my xVHD Configuration, I recommend that you check out the comment for this Resource by Aleksandar Nikolić where he points out that right now there isn’t a way to get a Generation 1 VM that uses a .VHDX disk.  I start to build the Configuration, using parameters for the Resource Properties (most of them anyways) and immediately wonder if this is going to create a Fixed or Dynamic disk.  I have a suspicion it is going to create a Dynamic Disk (which is what Hyper-V defaults to).  I will find out soon enough!

Here is my Configuration.

Configuration BuildTestVM
{

Param
(
    [Parameter(Mandatory)]
    [ValidateNotNullorEmpty()]
    [String[]]$Name,

    [Parameter(Mandatory)]
    [ValidateNotNullorEmpty()]
    [String[]]$FilePath,

    [Parameter(Mandatory)]
    [ValidateNotNullorEmpty()]
    [ValidateSet("VHD","VHDx")]
    [String[]]$Generation,

    [Parameter(Mandatory)]
    [ValidateNotNullorEmpty()]
    [String[]]$MaxSize,

    [Parameter(Mandatory)]
    [ValidateNotNullorEmpty()]
    [String]$SwitchName,

    [Parameter(Mandatory)]
    [ValidateNotNullorEmpty()]
    [String]$SwitchType
)

Import-DscResource -Module xHyper-V

    xVMSwitch DSCSwitch
    {
        Name = $SwitchName
        Ensure = "Present"
        Type = $SwitchType
        AllowManagementOS = "True"
    }

    xVHD OSDisk
    {
        Name = "$Name_DISK1_C"
        Path = $FilePath
        Ensure = "Present"
        Generation = $Generation
        MaximumSizeBytes = $MaxSize
    }

}

BuildTestVM -Name DSC-APP-01 -FilePath C:\Scripts\VHDs -Generation "VHDx" -MaxSize 51200 -SwitchName DSCTest -SwitchType Internal

And it fails miserably with these errors.

PS C:\Scripts> C:\Users\jacob.benson\SkyDrive\PowerShell\DSC\BuildTestVM.ps1
Write-NodeMOFFile : Invalid MOF definition for node 'localhost': Exception calling "ValidateInstanceText" with "1" argument(s): "Convert property 'Path' value from type 'STRING[]' to type 
'STRING' failed
 At line:39, char:2
 Buffer:
 {
    "Vhdx"
};
};^
ins
"
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:1383 char:17
+                 Write-NodeMOFFile $name "localhost" $Script:NoNameNodeInstanceAl ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : InvalidMOFDefinition,Write-NodeMOFFile

    Directory: C:\Scripts\BuildTestVM

Mode                LastWriteTime     Length Name                                                                                                                                              
----                -------------     ------ ----                                                                                                                                              
-a---          4/1/2014   2:26 PM       2066 localhost.mof.error                                                                                                                               
Errors occurred while processing configuration 'BuildTestVM'.
At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:2088 char:5
+     throw $errorRecord
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (BuildTestVM:String) [], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessConfiguration

Looking at my Configuration again, this is what I get for using Copy-Paste for my Parameter blocks :).  I changed my Parameter blocks for $FilePath, $Generation and $MaxSize from [String[]] to [String] and everything works as expected.

I then run the command below, and gets lots of fun output.

PS C:\Scripts> Start-DscConfiguration C:\Scripts\BuildTestVM -Wait -Verbose
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' =
root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer localhost with user sid S-1-5-21-738551990-92959840-526660263-26310.
VERBOSE: [localhost]: LCM:  [ Start  Set      ]
VERBOSE: [localhost]: LCM:  [ Start  Resource ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]: LCM:  [ Start  Test     ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]:                            [[xVMSwitch]DSCSwitch] Checking if Switch DSCTest is Present ...
VERBOSE: [localhost]:                            [[xVMSwitch]DSCSwitch] Switch DSCTest is Present
VERBOSE: [localhost]: LCM:  [ End    Test     ]  [[xVMSwitch]DSCSwitch]  in 1.0110 seconds.
VERBOSE: [localhost]: LCM:  [ Skip   Set      ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]: LCM:  [ End    Resource ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [localhost]: LCM:  [ Start  Resource ]  [[xVHD]OSDisk]
VERBOSE: [localhost]: LCM:  [ Start  Test     ]  [[xVHD]OSDisk]
VERBOSE: [localhost]: LCM:  [ End    Test     ]  [[xVHD]OSDisk]  in 0.0620 seconds.
PowerShell provider MSFT_xVHD  failed to execute Test-TargetResource functionality with error message: Cannot bind argument to parameter 'Name' because it is an empty string.
    + CategoryInfo          : InvalidOperation: (:) [], CimException
    + FullyQualifiedErrorId : ProviderOperationExecutionFailure
    + PSComputerName        : localhost

VERBOSE: [localhost]: LCM:  [ End    Set      ]
The SendConfigurationApply function did not succeed.
    + CategoryInfo          : NotSpecified: (root/Microsoft/...gurationManager:String) [], CimException
    + FullyQualifiedErrorId : MI RESULT 1
    + PSComputerName        : localhost

VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 1.675 seconds

It doesn’t think it has a name.  That’s weird.  I am going to guess it’s because my syntax is wrong, because I always forgot how to do this properly when trying to use a parameter name in text like this.  Luckily for me the TechNet example has the proper way to do it so I don’t have to go look it up myself.  I change what I had before to this and try it again.

Name = "$($Name)_DISK1_C"

More fun output!

VERBOSE: [localhost]: LCM:  [ Start  Set      ]  [[xVHD]OSDisk]
VERBOSE: [localhost]:                            [[xVHD]OSDisk] Checking if C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx is Present ...
VERBOSE: [localhost]:                            [[xVHD]OSDisk] C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx is not Present
VERBOSE: [localhost]:                            [[xVHD]OSDisk] C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx is now Present
Failed to create the virtual hard disk.
The size specified for 'C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx' is too small. The smallest valid size for a virtual hard disk is 3MB.
A parameter that is not valid was passed to the operation.
    + CategoryInfo          : InvalidArgument: (Microsoft.Hyper...l.VMStorageTask:) [], CimException
    + FullyQualifiedErrorId : InvalidParameter,Microsoft.Vhd.PowerShell.NewVhdCommand
    + PSComputerName        : localhost

VERBOSE: [localhost]: LCM:  [ End    Set      ]  [[xVHD]OSDisk]  in 4.5570 seconds.
The PowerShell provider MSFT_xVHD threw one or more non-terminating errors while running the Set-TargetResource functionality. These errors are logged to the ETW channel called
Microsoft-Windows-DSC/Operational. Refer to this channel for more details.
    + CategoryInfo          : InvalidOperation: (:) [], CimException
    + FullyQualifiedErrorId : NonTerminatingErrorFromProvider
    + PSComputerName        : localhost

VERBOSE: [localhost]: LCM:  [ End    Set      ]
The SendConfigurationApply function did not succeed.
    + CategoryInfo          : NotSpecified: (root/Microsoft/...gurationManager:String) [], CimException
    + FullyQualifiedErrorId : MI RESULT 1
    + PSComputerName        : localhost

VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 6.437 seconds

Couple of things here.  I made the size of my VHDx file in MB instead of Bytes, so I need to fix that.  But seriously, why does it need to be in Bytes?  Is anyone really going to need to configure the size of a .VHDx down to the Byte level?  Is their some underlying code in Hyper-V that it can’t convert or recognize this itself?  I also find it humorous that the Local Configuration Manager reports that the VHDx is now Present, and then it says that it failed to create the disk.  Looking in my $FilePath directory no VHDx file was created.  It also tells us that errors were logged to the DSC/OperationalLog so let’s check that out.  Pretty boring.  It’s the exact same stuff that is in the Verbose output.  Although that raises an interesting question, if I run it without -Verbose what gets output on the screen.  Let’s try it.  All the important stuff still gets logged.  Alright, I have changed my VHDx size from 51200 to 53687091200 which is 50GB in Bytes.  Which really seems silly when it’s typed out like that.  Round 3!

PS C:\Scripts> Start-DscConfiguration C:\Scripts\BuildTestVM -Wait -Verbose
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' =
root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer OM808-IT-293 with user sid S-1-5-21-738551990-92959840-526660263-26310.
VERBOSE: [OM808-IT-293]: LCM:  [ Start  Set      ]
VERBOSE: [OM808-IT-293]: LCM:  [ Start  Resource ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [OM808-IT-293]: LCM:  [ Start  Test     ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [OM808-IT-293]:                            [[xVMSwitch]DSCSwitch] Checking if Switch DSCTest is Present ...
VERBOSE: [OM808-IT-293]:                            [[xVMSwitch]DSCSwitch] Switch DSCTest is Present
VERBOSE: [OM808-IT-293]: LCM:  [ End    Test     ]  [[xVMSwitch]DSCSwitch]  in 0.8730 seconds.
VERBOSE: [OM808-IT-293]: LCM:  [ Skip   Set      ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [OM808-IT-293]: LCM:  [ End    Resource ]  [[xVMSwitch]DSCSwitch]
VERBOSE: [OM808-IT-293]: LCM:  [ Start  Resource ]  [[xVHD]OSDisk]
VERBOSE: [OM808-IT-293]: LCM:  [ Start  Test     ]  [[xVHD]OSDisk]
VERBOSE: [OM808-IT-293]:                            [[xVHD]OSDisk] Vhd C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx is present:False and Ensure is Present
VERBOSE: [OM808-IT-293]: LCM:  [ End    Test     ]  [[xVHD]OSDisk]  in 0.6640 seconds.
VERBOSE: [OM808-IT-293]: LCM:  [ Start  Set      ]  [[xVHD]OSDisk]
VERBOSE: [OM808-IT-293]:                            [[xVHD]OSDisk] Checking if C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx is Present ...
VERBOSE: [OM808-IT-293]:                            [[xVHD]OSDisk] C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx is not Present
VERBOSE: [OM808-IT-293]:                            [[xVHD]OSDisk] C:\Scripts\VHDs\DSC-APP-01_DISK1_C.Vhdx is now Present
VERBOSE: [OM808-IT-293]: LCM:  [ End    Set      ]  [[xVHD]OSDisk]  in 1.7650 seconds.
VERBOSE: [OM808-IT-293]: LCM:  [ End    Resource ]  [[xVHD]OSDisk]
VERBOSE: [OM808-IT-293]: LCM:  [ End    Set      ]
VERBOSE: [OM808-IT-293]: LCM:  [ End    Set      ]    in  3.4900 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 3.553 seconds

And like I was afraid of it doesn’t create  a Fixed disk type, it just creates a disk with a size of 4,096 KB.  I have a feeling I know how I could fix that but let’s not get carried away right now 🙂

After doing this I also realized that I need to create a folder for the VM and put the VHD in that folder, so I add this to my Configuration Script.

    File VMFolder
    {
        DestinationPath = (Join-Path -Path C:\Scripts\VHDs -ChildPath $($Name))
        Ensure = "Present"
        Force = $True
        Type = "Directory"
    }

    xVHD OSDisk
    {
        Name = "$($Name)_DISK1_C"
        Path = (Join-Path -Path C:\Scripts\VHDs -ChildPath $($Name))
        Ensure = "Present"
        Generation = $Generation
        MaximumSizeBytes = $MaxSize
        DependsOn = "[File]VMFolder"
    }

And it works and everything looks great.  Tomorrow I will create a VM (or 3 or 4) using my VM Switch and this VHD file and see what else I can break.