PowerShell DSC Journey – Day 17

Alright. So yesterday I worked through some issues in the Set-TargetResource Function and left off with a big one still remaining. Namely, that even though I have Ensure = Present in my test of the Function, it is running through the Ensure = Absent portion of the script if the Hardware Profile already exists, which doesn’t make any sense.

Here is the section of the Function in question.

And if I run this Test with a Hardware Profile that already exists, this is the output that I get.

And I know it’s removing it because the Hardware Profile disappears from VMM. The question is, why? I can see from the Verbose output that $Ensure is set to Present, so why is the Function ignoring that? As another test, if I run the same test but set $Ensure to Absent, it removes it. But that was really just a diversion to avoid thinking about the real problem because I am completely stumped.

I change my Else block to ElseIf($Ensure = “Present”) and that has no effect. It just jumps right into the If Absent block and deleted the Hardware Profile. Lacking ideas I start going through the xVMHyperV Set-TargetResource function searching for Ensure to see how they do it. And they are using -eq in the If statement instead of =. Does this really make a difference? Yes, that’s the answer. After I change it to that, it evaluates the parameter properly. I feel like this is a very simple mistake that I made solely because of my lack of experience and knowledge of PowerShell.

Doing a little research in PowerShell using Get-Help I discover the following:

  • = is an Assignment Operator.  It sets the value of a variable to a specified value
  • -eq is a Comparison Operator.  It compares to objects looking for an identical value
  • Thus, = does not equal -eq.  I feel dumb.  I should have known that.

That being said, when I run my test using an existing Hardware Profile now (with Ensure = Present), I get this for output.

Clearly there are a couple of issues here. The DVDDrive section looks OK, but then in the CPU section I get this beauty for Verbose output.

Yeah, that’s not real useful. Looking at this it appears that $HWProfile.CPCount is not what I think it is. Actually I think it’s because I used $HWProfile instead of the $ResourceHWProfile variable. This is going to require a fix in a lot of places. I make those changes and try my test again. I get the expected output, except that I get the same error about the VirtualNetworkAdapter that I got above. This goes back to my work in the previous blog post about how to handle this, because I don’t want to make this parameter mandatory. So, I need to add in some additional logic to my Function here.

And now when I run my test, I get exactly the output that I would expect.

Boom!
I do various tests with different CPU, DVDDrive and VMNetwork settings and everything looks good. I do make a change to one line of Verbose output, but other than that everything checks out fine. This is really cool. I have an actual functioning DSC Custom Resource. Tomorrow I am going to turn this thing into a Module, and try out an actual DSC Configuration and see if that actually works before adding in my additional parameters.

PowerShell DSC Journey – Day 16

When I left off yesterday I had a somewhat functioning Set-TargetResource function. I use functioning in the way that when something kind of works and doesn’t throw errors but doesn’t actually do anything functions. So, today I am going to be figuring out where I screwed up my logic and getting this Resource to work. And hopefully this doesn’t turn into a smaller version of War and Peace.

Here is my Set-TargetResource in it’s entirety.

The first problem I noticed is that when I run this test of my Set-TargetResource (in which I am testing for a Hardware Profile that doesn’t exist), here is the output I get.

Couple of things to note here. It didn’t find the Hardware Profile, so it created it. However, I didn’t specify anything for the DVD Drive, but the verbose output indicates that it was set to False, so it did not create a DVD Drive, so that needs to be fixed. The second issue is that the verbose output indicates that the CPU count set to 0 or not specified, CPU count should be at least 1. If I don’t specify a CPU count the Hardware Profile creation defaults to 1 CPU, so the message should indicate that. And lastly it just errored out on setting the VMNetwork because I didn’t specify one.

First things first, here is the section of code I have to handle the DVD Drive.

While thinking about this, it occurs to me that maybe I should set this to default to True, because I imagine that in almost every case you are going to want a DVD Drive on your server. I feel like that is a better solution than adding in another ElseIf section that has the Null case. Do people agree? I changed the parameter in the Set-TargetResource Function so that $DVDDrive = $True, and that means the existing code block should work fine.

Second minor issue is changing the Verbose text when the CPU Count is set to 0 or not specified. This particular section of code I changed from:

To:

Of course after doing this I think, should I just set the CPUCount parameter to default to 1? I mean, you need a CPU for it to run, and it defaults to 1 anyways without even setting anything. I realize the code in the Else block is a little redundant, but for consistency sake I left it that way so somebody reading it wouldn’t be like “why did he set the hardware profile in the first section and not the second?”. I could also make CPUCount a mandatory parameter, but that seems a little excessive when it just defaults to 1 anyways. Hmm. I don’t think there is a good answer to this. I am going to set CPUCount to default to 1 and just change the Else statement to use -CPUCount $CPUCount for now. I don’t like the idea of hard coding parameter values into a DSC Configuration, but in this particular case I think it is justifiable.

Alright, now for the last problem with the VMNetwork. If I don’t specify it, it shouldn’t just freak out and throw and error, it needs to say something useful. Here is my current code.

The problem here is that I thought that checking to see if the parameter was not equal to Null mean it would also check to make sure it wasn’t empty. Clearly this is not the case. So how do I check for both cases? It appears based on some Google searches the best way to do this is to change it from If($VMNetwork -ne $null) to just If($VMNetwork) which checks for Null or Empty.

With those changes done, lets test again and see what happens (I deleted the Hardware Configuration created from the previous test).

Much better!

The one thing I don’t like about this, is that it still sets it set the CPUCount, when in all actuality it was already set by default. So, I am going to change that section again to this.

I verified that the Hardware Profile has 1 CPU, the DVD Drive is Enabled, and the Virtual Network is not set. Not setting a VMNetwork appears to mean that no settings in VirtualNetworkAdapters are set, which is good to know.

Ok, with that all done, I run it again because I remember there was a problem with this. Here are the important parts from this test.

Uh. What. That looks to be a massive fail on my part. Although, I don’t understand what the problem is here. It checks if it exists, it finds that it exists, and then it is going through the block where $Ensure = Absent, even though I have $Ensure = Present. Of course, then it is removing the profile as well, which is fun. I add this line to my code right above the line for If($Ensure = Absent), Write-Verbose “Ensure set to $Ensure”.

And I get this, which is even more puzzling.

It says that $Ensure = Present, so why the hell is it going through that block of code? I don’t understand! If I set it to Absent it does the same thing, which is fine, but why is it doing it when it is set to present????

I feel like this is a good place to stop for now so I can think a little about this and try to figure out what is going on.

PowerShell DSC Journey – Day 15

So when I left off yesterday I appeared to have my Get and Test TargetResource functions working properly, now it’s time to work on the Set-TargetResource function, which is probably where the fun will happen.

When I go to set the Resource, I have a couple of different scenarios I need to work through.

  • Hardware Profile exists, ensure equals absent
  • Hardware Profile exists, ensure equals present
  • Hardware Profile does not exist, ensure equals present

And here is what I came up with those 3 scenarios after working my way through it (and looking at the xVMHyperV Resource).  I almost immediately ran into a problem where there doesn’t seem to be a clear way to tell the Hardware Profile that it needs a DVD drive (at least not using the Set-SCHardwareProfile cmdlets).  A little research led me to the Set-SCVirtualDVDDrive cmdlets which can be associated with “a virtual machine, virtual machine template, or hardware profile used in a Virtual Machine Manager (VMM) environment.” which is perfect.  When I set the New DVD Drive I must specify a LUN and a BUS.  I am going to need some extra logic eventually to deal with what happens if you have multiple disks or whatever else, but for right now this will have to do.  I don’t even want to admit how long this took me to work through, but it appears to work.

CPU Count is easy and straightforward, which was nice.

However, I ran into the same problem with VMNetwork that I did with the DVDDrive (which doesn’t make me happy). But since I already had that experience it was much quicker to come up with what to do about it.

Now that that is done (I feel like I just went through the ringer), all I really need to do for the next part is copy and paste the commands from above into the area where I am creating a Hardware Profile if none exists.

So I guess now we need to test this thing and see if it blows up. I am going to start with my original 3 tests and then add in some variations (providing those still work). I run the first one for an existing Hardware Profile and find out quickly I need to specify the Ensure parameter. Which means I should probably make that a mandatory property (I think?). I don’t see anyone leaving that out unintentionally but maybe I should add in some logic to check for that? I guess I will figure that out later.

Winning! Test #2! This does not go as well.

Alright, so it’s basically exiting after checking if the Hardware Profile exists. In this test, the profile does not exist, so it should jump down to the Else block and create it, however that is not happening. I am ashamed to admit this but I had an extra closing bracket in the top half of the script, so it was exiting before creating the profile. When I run it again, it works, sort of. The profile is created, but it throws errors because I didn’t specify a CPU count or a VMNetwork, because they aren’t mandatory. So, I need to do some updates here I think.

For the second test, it created the Hardware Profile, with no errors, however it added “VERBOSE: Hardware Profile DSCWEB Hardware Profile 2 is Absent” after displaying the Hardware Profile, which uh….doesn’t make any sense?

Looking at the verbose messages closer, I have a couple of issues that I need to get cleaned up. Which will be a good thing to do tomorrow.

PowerShell DSC Journey – Day 14

When I left off yesterday, I had modified my Get-TargetResource to work (hopefully) with all the new properties I had setup for my Custom Resource. Now, I am going to test this, and DSC willing I will be able to move on to the Target Resource functions today before I finish this thing out.

First I am going to run my 3 previous tests that I knew worked and see if they still work. And…I am getting mildly competent at DSC it seems.

Alrighty then, on to Test 2. Still winning!

For Test 3 I am creating a new Test with the new Properties to see what happens. And halfway into writing this test I realize that is a dumb idea. It’s dumb because I already demonstrated above that with a valid Hardware Profile and a valid VMM Server name, I can return the current properties that I have setup. On to Test-TargetResource!

Ah. It’s in Test-TargetResource that I see all my additional properties show up.

While looking at what needed to be copied from the Get-TargetResource function I realized (maybe for the second time) that there was no reason for this section of code. So I got rid of it.

I looked at this for a while before trying out to figure out what to do. First thing I thought was that I need to modify the section that tests the Hardware Profile. Or something. This is really making my brain hurt. I should probably start doing these at the beginning of the day instead of at the end. Anyways, here is the Test-TargetResource Function in its entirety.

Well. Let’s fill the screen with red text.

Well that wasn’t red, but I certainly don’t want it to return False if the Hardware Profile exists so I add this change to the script. If($HWProfile -ne $Name){Return $False} . Run it again but I get the same error and it’s clear to me why. I haven’t specified the $Ensure parameter. So let’s try that out. Short version, that didn’t help, but that’s because I don’t have a DVD drive setting or anything else, so of course they are returning False. Duh. Dummy.

Let’s try this test, with the settings that I know are accurate.

Now we are talking. I am not going to bore you with a bunch more examples, but here is a summary of tests.
Ensure = Absent, Returns False
CPUCount = 4, Returns False
DVDDrive = False, Returns False
VMNetwork = Blah, Returns False
Name = Blah, Returns False

Today was a good day. Tomorrow it will be time for the Set-TargetResource Function.

PowerShell DSC Journey – Day 13

When I left off yesterday I had a functioning Custom Resource. By functioning I mean that I can run some tests without errors (unless it should error) and that it can create a new Hardware Profile name if one doesn’t exist. Other than that, it’s pretty much worthless. So, let’s add in a few more properties to the Resource and then update the Get, Set, and Test functions accordingly. I am going to out on a limb here and say that this isn’t all going to happen today.

I am going to add 3 additional Properties to my Resource. I should also mention that I got rid of Depends On, because nothing seems to use it.

And of course it wouldn’t be a blog of mine if it didn’t immediately start off with an error.

Alright, so I can’t use CPU’s but I can use CPUs. Got it. That seems kind of silly to me, but whatever. 2/2 on errors so far.

Alright, so I guess I need to include all the properties when I update it. Truth be told I couldn’t remember if I needed to or not so I decided to just wing it.
So, I run that with all the properties and it seems to work. However, when I open the .psm1 file, nothing has changed. That does not give me a warm and fuzzy feeling. After I do some looking around, the schema.mof has been updated with the new values, but not the .psm1 file, which I guess makes sense.

While looking at this, I realize that I need to change all 3 of those from an attribute of Read to an attribute of write, because I may need to write those attributes if it’s a new Hardware Profile.
So, I make that change and update the Resource and here’s the new Schema.MOF.

I am trying to figure out if I need to add in all the Parameters into the Get-TargetResource function, and it looks like I only need to declare the Parameters that are Mandatory, which I already have. All I think I need to do is add some logic for getting my other new parameters and returning those values. After poking around the xVMHyperV Resource this is what I come up with to add to the Get-TargetResource Function.

I am somewhat concerned that this probably shouldn’t go in the body of the Function, but probably in the section where I test for the Hardware Profile, but for right now I am going to leave it. I should mention at this time that I also made this change to the section where I test for the VMM Server.

The else part is not needed since it will just error out anyways if the server is incorrect, but for right now I am going to leave it.

I then make these changes to the returnValue section to return the correct values (or I hope at least).

I am fully expecting this to blow up in my face and it’s near the end of the day, so I am going to stop here and pick up on this again tomorrow.

PowerShell DSC Journey – Day 12

Before I get started working on the Set-TargetResource function, it occurred to me over the weekend that instead of using $Name -cin $HWProfile.Name I should be using $Name -match $HWProfile.Name because otherwise I could just put a name like “Web” and who knows what would happen. Probably not anything good.

So, I make that change and run my first test using a valid Hardware Profile name and of course it fails. Of course!

After verifying that the Hardware Profile didn’t get deleted I think that match probably doesn’t do what I think it does. Reading through the about_comparison_operators help file I think that I need contains and contained in. So let’s make this change and try again. It also doesn’t work, and after doing some testing, it appears I need to reverse my comparison.

Awesome!

Now, let’s move along to the entire point of this post. Alright, so we need all of our checks and things we used previously (I think? I mean shouldn’t it just bomb out on Test and Get and never reach Set anyways?). I will stick them in there anyways especially since I know they work.
So here is what I have after doing that.

First thing I notice is the DependsOn parameter. I don’t see this parameter listed in any of the existing Resources anywhere, so I am just going to remove it from the Set-TargetResource. The only thing I need to Set right now is the Hardware Profile name if it doesn’t exist, so I will need to modify one section of the script here.

And…I think that is it for now. So let’s test this baby out and see how much red text I get.
Test 1:

That was expected. This test is where things are going to get real. For the second time in two days I feel like a DSC Wizard!

So pumped. I verify that it does in fact show up in the VMM GUI. I won’t bore you with the picture but I promise you that it is there. Now I will run my last test just for the sake of consistency.

For right now I am OK with it trying to check the HardwareProfile even if it can’t find the VMM server, but I will probably end up adding in more logic for testing that better at some point.

Well. I have a somewhat functioning (but not very useful) Custom Resource now. Tomorrow we will start adding in more Hardware Profile parameters!

PowerShell DSC Journey – Day 10

When we left off yesterday I believed that I had a functioning Get-TargetResource function, without yet actually getting to the part where I Get anything. Which begs the question whether or not I actually need all that other stuff in there, or if it belongs in the Test-TargetResource. I honestly have no idea, I am just working off of the examples I have at my disposal. So, let’s see today if we can get some stuff returned!

Looking through examples of existing DSC Resources, I don’t see Ensure or DependsOn anywhere, except in what appears to be one special case, so I guess DSC just knows how to use them? I looked at some of Steve Murawski’s Stack Exchange Resources and that just confused me even more so I am just going to keep plugging along here. I am going to set Ensure = “Present” because the Hardware Profile should already exist (I guess?). When I get further along into creating a new Hardware Profile I imagine I will need to set this to “Absent” if the Hardware Profile does not exist. So, this is what I have.

And if I run one my Test-TargetResource functions that I know should work, here is what I get:

HOLY CRAP IT WORKED ON THE FIRST TRY AND I AM REALLY EXCITED AND YOU CAN’T MAKE ME STOP TALKING IN ALL CAPS!!!! Well, that’s all for today, we will pick it up tomorrow with working on the Test-TargetResource.

PowerShell DSC Journey – Day 9

When I left off I had modified my Get-Target resource function to include steps for checking to make sure SCVMM cmdlets existed, and that the specified VMM Server was a valid VMM Server. Next, I need to make sure that the given Name of the Hardware Profile exists.

The issue I run into here is that the Get-SCHardwareProfile doesn’t have a Name parameter (because that would make too much sense) only an ID value.

So, knowing the ID I can do this.

To make this work, I am going to need to get the ID of the profile using the name, without really using the name. I thought this was going to be complicated, but it turns out to be easier than I thought.

So, to test this let’s do this:

And here is some output from testing this.

Alright, so let’s add some tests into the Get-TargetResource Function before we populate the $returnValue section.

Let’s test the first one and see what happens.

What the what? I clear all my variables (because opening a new session is too easy) and try again. Same thing. Fine, I will open a new tab in ISE. That also doesn’t work. Well, what the hell, I was just using this and it was working fine! After reading through this, I see what I am doing wrong. I need to replace Get-SCHardwareProfile -VMMServer (Get-SCVMMServer -ComputerName $VMMServer) with $HWProfile = Get-SCHardwareProfile -VMMServer $SCVMMServer . And, that doesn’t work so well either.

Alright. So let’s back up here for a second. Get-SCVMMServer -ComputerName MY-VMM-SERVER works fine. If I set $VMMServer = “MY-VMM-SERVER” and then run Get-SCVMMServer -ComputerName $VMMServer it works fine. If I then run $HWProfile = Get-SCHardwareProfile -VMMServer $VMMServer it works fine. So, uh, why isn’t this working for me in my Get-TargetResource function? The function is doing the exact same thing, and failing. I have no idea why.

I add the following two lines to the type of the script (underneath the parameter declaration) just to make sure nothing screwy is happening (and it isn’t).

I do some more testing, and something screwy is going on. It looks like once a connection is made to the VMMServer it isn’t able to make the connection a second time in the same script? Does that even make sense? I change the $SCVMMServer parameter to return the entire SCVMMServer object, and then see if the $VMMServer matches $SCVMMServer.Name like this.

Once I do that, it works like magic!

Alright, let’s test the next scenario.

Well, that’s great and all, except that it didn’t throw an error and stop, which it needs to. So, I add the following line to my function (I place it after the Verbose line, because I figured out that once it does the Throw, there isn’t anything else that happens after that).

Now, this is better.

And finally for my last test.

This is great and all, except that it shouldn’t try to find the Hardware Profile if it can’t contact the VMMServer. I check the $ErrorActionPreference and it is set to continue, I want to set this (in the function) to stop. There is no reason for it to keep going if it encounters an error. I add $ErrorActionPreference = “Stop” to the beginning of the function and try my test again.

I will spare you the text, but it failed to connect to the VMMServer, and then stopped. Up next, filling out the return values section and testing that out.

PowerShell DSC Journey – Day 8

Alright. Picking up where I left off yesterday, this time I am seriously going to work on the Get, Test and Set Functions for my first custom DSC Resource. I promise I won’t get myself sidetracked by why I need the full path to import my Module :).

First things first, here is the code that was generated by the DSC Resource Designer for the Get-TargetResource Function.

Off the top of my head, some things I need to do: Ensure SCVMM Cmdlets exist. Make sure the VMM Server is valid. See if the Hardware Profile name already exists, if so, return the appropriate values. I am also going to add regions to each section of this script so that they can be easily collapsed when working on other sections.

First task, lets make sure that the SCVMM cmdlets exist and are available. I pretty much took this section and modified it from the xVMHyper-V Resource.

And if I run this on my desktop, I don’t get any kind of error which is what it should do. Now let me copy and paste this into a VM that doesn’t have the SCVMM cmdlets and this happens.

Well, that was easy enough :). Next thing, let’s make sure that the $VMMServer is an actual VMMServer. I start out by doing this (for testing purposes to see if this is going to work).

Followed by this (because I want to see what the output type is).

The name property is a string like the $VMMServer parameter is, so I should be able to just compare them and call it good.