Being that I am a creature of pure distilled vanity, I often reread my articles and blog posts after the fact to make sure that I wrote well and don’t have any typos. Well, when I reread my last post, I realized that I forgot to add the step of making a User Account with PowerShell DSC! Whoops!
As it turns out, actually creating a user with DSC had me ready to pull my hair off in frustration, so to save you intrepid Googlers from this pain, I’ve outlined what you SHOULDN’T do, and also have a working easy to understand demo in the bottom.
Add a User Resource to our DSC Config
So, building on our DSC Config from last week; when we want to add a new DSC Resource, the first start is to run Get-DSCResource and look at what Properties we can (and need to) focus on.
Get-DscResource User | select -ExpandProperty Properties
Based on what we see above, we should be good to add the following under xComputer
User LocalAdmin { UserName = $UserName DependsOn = '[xComputer]NewNameAndWorkGroup' Description = 'Our new local admin' Disabled = $false Ensure = 'Present' FullName = 'Stephen FoxDeploy' Password = '$Password' PasswordChangeRequired = $false PasswordNeverExpires = $true }
You can see that we added a few variables there, so let’s go add them all to our Parameter block.
Change our params block in this script to the following
param ( [string[]]$NodeName ='localhost', [Parameter(Mandatory)][string]$MachineName, [Parameter(Mandatory)][string]$WorkGroupName, [Parameter()][string]$UserName, [Parameter()][string]$Password )
Now let’s invoke it…
TestLab -MachineName DSCDC01 -WorkGroupName TESTLAB -Password 'myRootPw' -UserName 'Stephen FoxDeploy'
ERROR 😦
Write-NodeMOFFile : Invalid MOF definition for node 'localhost': Exception calling 'ValidateInstanceText' with '1' argument(s): 'Convert property 'Password' value from type 'STRING' to type 'INSTANCE' failed At line:37, char:2 Buffer: onName = 'TestLab'; };^
Fix: Convert property value from type string to type instance failed
What?!? Maybe my brain just doesn’t work so good, but I had a really hard time understanding what this message meant: Convert Property Value from type String to type Instance. So I took a peek at TechNet.
Checking TechNet, I see this example of how to use the DSC User Resource(I’ve actually bolded the part that was causing me grief, let’s see if you can figure it out quicker than I could!):
User UserExample
{
Ensure = “Present” # To ensure the user account does not exist, set Ensure to “Absent”
UserName = “SomeName”
Password = $passwordCred # This needs to be a credential object
DependsOn = “[Group]GroupExample” # Configures GroupExample first
}
Let’s look at the error message again…
“Convert property ‘Password’ value from type ‘STRING’ to type ‘INSTANCE’ failed
And now side-by-side…
Password = $passwordCred # This needs to be a credential object
Can’t convert property ‘Password’ value from type ‘STRING’ to type ‘INSTANCE’ failed
Hmm…I converting from a string failed, and TechNet says it needs to be a Credential object instead…! Wait! PowerShell is telling me I can’t give the PW as a string, it actually has to be a credential object! Not sure why it took me so long to understand this. Actually, if we take a look at the output from Get-DSCResource, it even says right there what type to provide the data in.
So, let’s replace our -PassWord ‘RootPW’ with a parenthesis with Get-Credential and see what happens…
TestLab -MachineName DSCDC01 -WorkGroupName TESTLAB -Password (Get-Credential -UserName 'FoxDeploy' -Message 'Enter New Password') -UserName 'FoxDeploy'
Fix: Storing passwords as plain text is not recommended
Running the cmd above gives us…some more blood in the water, but this is great because we’ve solved the first problem!
ConvertTo-MOFInstance : System.InvalidOperationException error processing property 'Password' OF TYPE 'User': Converting and storing encrypted passwords as plain text is not recommended. For more information on securing credentials in MOF file, please refer to MSDN blog: http://go.microsoft.com/fwlink/?LinkId=393729
Hey, at least we’re getting somewhere! This is probably one of the most informative error messages I’ve ever had in PowerShell! It’s saying hey, you can’t do this, here is a link with more info. I like it! Much better than before.
I did some googling and found this article on the topic, it turns out we need to specify a -ConfigurationData parameter, which will allow us to tell PS to, just this once, ignore our bad behavior password.
Let’s throw this bad boy on there…
$configData = @{ AllNodes = @( @{ NodeName = 'localhost'; PSDscAllowPlainTextPassword = $true } ) }
Now, to run it, one last time!
BLAM! Let’s apply it!
RESULTS
[[xComputer]NewNameAndWorkgroup] Checking if computer name is DSCDC01 [[xComputer]NewNameAndWorkgroup] Checking if workgroup name is TESTLAB [[xComputer]NewNameAndWorkgroup] in 0.2420 seconds. [[xComputer]NewNameAndWorkgroup] [[User]LocalAdmin] A user with the name FoxDeploy does not exist. [[User]LocalAdmin] in 2.4380 seconds. [[User]LocalAdmin] [[User]LocalAdmin] Configuration of user FoxDeploy started. [[User]LocalAdmin] Performing the operation 'Add' on target 'User: FoxDeploy'. [[User]LocalAdmin] User FoxDeploy created successfully. [[User]LocalAdmin] Configuration of user FoxDeploy completed successfully. [[User]LocalAdmin] in 2.5090 seconds.
Now, let’s look to see if our User was created
Ok, that’s damned sexy
Completed DSC File
This DSC config will rename our PC, join it to a workgroup, and then add a new local user to the machine. When you run this, you’ll be prompted to provide the PW for the new local user. Make sure to change the -Username value to a name you’d like to use.
configuration TestLab { param ( [string[]]$NodeName ='localhost', [Parameter(Mandatory)][string]$MachineName, [Parameter(Mandatory)][string]$WorkGroupName, [Parameter()][string]$UserName, [Parameter()]$Password ) #Import the required DSC Resources Import-DscResource -Module xComputerManagement Node $NodeName { xComputer NewNameAndWorkgroup { Name = $MachineName WorkGroupName = $WorkGroupName } User LocalAdmin { UserName = $UserName Description = 'Our new local admin' Ensure = 'Present' FullName = 'Stephen FoxDeploy' Password = $Password PasswordChangeRequired = $false PasswordNeverExpires = $true DependsOn = '[xComputer]NewNameAndWorkGroup' } } } $configData = 'a' $configData = @{ AllNodes = @( @{ NodeName = 'localhost'; PSDscAllowPlainTextPassword = $true } ) } #See whats needs to be configured # Get-DscResource User | select -ExpandProperty Properties | select -expand name TestLab -MachineName DSCDC01 -WorkGroupName TESTLAB -Password (Get-Credential -UserName 'FoxDeploy' -Message 'Enter New Password') -UserName 'FoxDeploy' -ConfigurationData $configData Start-DscConfiguration -ComputerName localhost -Wait -Force -Verbose -path .TestLab
Wrapping Up
So, it seems that most things in DSC look deceptively easy when you see the finished result, but working up to that is quite difficult. Instead this was more of a deep dive into using the User Resource with DSC. Join us again next week when we add our user to the Administators built-in Group, then make our machine into a Domain Controller.
Big Fan, keep up the good work. I felt like it may have been a little clearer if you noted when you first showed the code for $ConfigData, that this would be made part of the code by adding a ConfigurationData parameter to the command to make the MOF file. TestLab -ConfigurationData $ConfigData. It took me going back to the original article referenced to put this piece together. Maybe if I would have read the article before, you already taught the usage of this parameter there, I don’t know.
LikeLike
Why are you initializing the $configData variable ($configData = ‘a’) before giving it the actual hash table?
LikeLike
Dear Stephen, really great blog! I love it!
One question: in the param block you are using [string[]]$nodename? If you would use an Array of nodenames you also would have to use Arrays of Machinenames – or do I get something wrong?
LikeLike
Sorry, I’m on vacation right now, but will be back in the office this week! When I am, I’ll double check this and see if I missed something! Wouldn’t be the first time. 😃
LikeLike
Yep, you’re right. If you provided more than one $nodename, you’d need to also feed in an array of MachineNames, and the code doesn’t currently have a mechanism to handle indexing through an array. I really intended this to be used on a single machine at a time, but left in array support. Good catch!
LikeLike
FYI. If you still want to hardcode password in your script (like original one intended), you can still do it via below
$cred = [pscredential]::new(“dd”, (ConvertTo-SecureString “passwordhere” -AsPlainText -Force)) -as [pscredential]
LikeLike