Wednesday, March 20, 2019

Creating local user accounts using PowerShell in a mixed-Windows Server 2012R2/2016 environment

I recently wrote a PowerShell script that, among other things, creates some local user accounts. I developed this script to run on a server running Windows Server 2016, where it worked perfectly. However, when I ran this script on a server running Windows Server 2012R2, it failed with an error that a cmdlet that it used was not found. Specifically, the cmdlet that was not found was New-LocalUser.

As it turns out, versions of PowerShell before v5.1, which is included in Windows Server 2016, there was no PowerShell-native way to create local user accounts. If you need to create a local user in a script on Server 2012R2 or earlier, you will need to use either the ADSI (Active Directory Services Interfaces) or net user commands.

ADSI

ADSI is the Active Directory Services Interface, which provides a COM interface to Active Directory objects and services. Although it sounds like this is only for working with AD, it can also be used to create local accounts by treating the local computer as its own domain. You can use a snippet like this to create a local account using the ADSI PowerShell interface:

$computer = [ADSI]"WinNT://$Env:COMPUTERNAME,Computer"
$account = $computer.Create("User", "username")
$account.SetPassword("password") #or $( Read-Host -Prompt "Password" ) if interactive
$account.SetInfo()
$account.UserFlags = 64 #ADS_UF_PASSWD_CANT_CHANGE
$account.SetInfo()


There are a number of flags that can be set on the new account using the .UserFlags attribute. The MSDN documents don't give numeric values for these flags (though you could calculate them yourself, given knowledge of how binary flags work); thankfully, these can be found online, as in this post from Will Steele on the TechNet forums. You can add flags together either by directly adding their numeric values, or by binary-ORing them together. Be sure to call .SetInfo() on your new local account object to save these changes.

Net User

The net command, or more specifically its subcommand net user, is an older way of creating user accounts from the Windows command line. net user is simple to use by hand, and simple to embed in a script. This command can be called either directly from cmd.exe, or in a PowerShell (or even batch) script with a snippet like this:

net user username password /add [options]

And that's it. This creates the new user, assigns its password, and adds it to the Users group on the local computer. There are various options for this command that can be placed after the final "/add", including /expires, which allows you to specify the date on which the account should expire, and /times, which lets you dictate within what time ranges the account is allowed to log on.

Unfortunately, net user does not have options for everything. For example, you cannot use the net user command to set the account's password to never expire; this must be done using the wmic command, like so:

WMIC USERACCOUNT WHERE Name="username" SET PasswordExpires=FALSE

Putting it together

So, say that you want to write one script that will create local user accounts on servers running Windows Server 2012R2 and 2016, and you want to use the "best" way available on each platform. You can use a snippet like this to detect which version of PowerShell is running, and take the appropriate actions:
if($PSVersionTable.PSVersion.Major < 5 -or ($PSVersionTable.PSVersion.Major >= 5 -and $PSVersionTable.PSVersion.Minor < 1)){
    # New-LocalUser not supported!

    $computer = [ADSI]"WinNT://$Env:COMPUTERNAME,Computer"
    $account = $computer.Create("User", "username")
    $account.SetPassword("password") #or $( Read-Host -Prompt "Password" ) if interactive
    $account.SetInfo()
    $account.UserFlags = 64 #ADS_UF_PASSWD_CANT_CHANGE
    $account.SetInfo()

    #etc.
}
else{
    $password = ConvertTo-SecureString -String "password" #or Read-Host -AsSecureString if interactive
    New-LocalUser -Name "username" -Password $password -PasswordNeverExpires

    #etc.
}

Friday, March 1, 2019

SCCM Gotcha: Task Sequence Media Size and FAT32

In SCCM, you have the option to create Task Sequence Media, which allows you to build a flash drive, DVD, or other bootable media that you can use to deploy operating system task sequences when no network connection is available (specifically, this is "stand-alone media" mode, versus "bootable media" and other options). This is very useful for off-site situations where you may not have any access to your organization's central network, or for computers whose NICs are not working correctly, but still need to be imaged.

In our environment, we tend to use USB flash drives for Task Sequence Media, and I was recently asked to create one for the first time with my newly-granted SCCM Administrator permissions. So, I went through the Create Task Sequence Media wizard, following the steps in the Microsoft docs, but it repeatedly failed, complaining that the USB media did not have enough space available to hold the task sequence. But that didn't make sense - I was using a 64 GB flash drive, and yet when I looked at it in the Disk Management console, I saw that it was only formatted with a single 32 GB partition.

It turns out that the reason for this is that Microsoft does not support partition sizes of greater than 32 GB on FAT32-formatted drives on anything other than Windows NT 3.51. As Wikipedia notes, FAT32 can actually support up to 8 TB or 16 TB partitions, so it's not totally clear why Microsoft chooses to impose this limit. Some conspiratorialists online claim that this was a move to force users onto Microsoft's then-new NTFS filesystem; others say that it has to do with USB flash drives' internal specifications. Whatever the reason, the fact remains that the SCCM Task Sequence Media wizard only offers to format a USB flash drive as FAT32.

This means that if you have a task sequence whose contents are near to or greater than 32 GB, you are likely to encounter the same problems I did. In our case, it is because of the vast range of hardware that our imaging drives must support, causing the driver packages to take up almost 20 GB, but in situations with a lot of software - CAD, Adobe Creative Cloud, etc. -  you could hit the limit with just one driver package.

The solution seems to be not to ask the wizard to create a bootable USB flash drive for you. Instead, choose the second option, "CD/DVD set." The default media size is 4.7GB, but you can increase this, all the way up to "unlimited." Then, using the Browse button, you select the location to which the wizard should save the resulting .ISO file, and continue through the wizard like normal. Once the .ISO is built, you can use a tool like Rufus to copy it to a USB flash drive of an appropriate size and make it bootable. Once you've confirmed that it's working, you can use another tool like ImageUSB to capture a binary copy of that flash drive, and then write it concurrently to a batch of drives.

Tableau, TabPy, and the Case of No Input Rows

 I haven't scientifically confirmed this or anything, but it sure seems like if you pass an empty dataframe to a TabPy script, then no m...