PowerShell is Logical… Usually

Alternative title: questions I’d ask Jeffrey Snover in an AMA.

I use PowerShell at work on a daily basis. It’s not usually anything complicated, but I couldn’t do my job effectively without it. Office 365 is great, but the GUI is slow, and PowerShell is much faster. Managing Active Directory is easy, but managing objects at any scale is always quicker once scripted, especially when you want to repeat tasks.

Today, I needed to deploy a change to local user accounts on our clients. We have a standard local user account for use at events, so non-staff can temporarily use a laptop without accessing company data.

Our domain policies dictate all accounts must have passwords, and password complexity requirements are applied, even to local accounts. To satisfy this, there’s a standard, unprivileged account, set up on each device that requires it, with a uniform password.

Last year, I rolled out a deployment server, because why deploy hardware manually when you don’t have to? Unfortunately, the new deployment system and the old method of deploying a local account don’t work well together. The password used for the local account conflicts with the configuration settings applied by the deployment server.

This is easy to fix. It requires a change to the password. You can do this individually on computers, but that takes a lot of time and disturbs staff. Therefore, PowerShell is the answer.

There are four PowerShell cmdlets for managing local users:

  • New-LocalUser
  • Get-LocalUser
  • Set-LocalUser
  • Remove-LocalUser

I needed to:

  • Check for the existence of the local user account
  • Create the account if it doesn’t exist
  • Update the account if it does exist

So, I wrote a script that could do this, which I could then distribute. I found it didn’t work consistently, and I had to do a bit of digging into Microsoft’s PowerShell documentation to find the cause. It was simple, but it’s a little illogical.

PowerShell has different types of parameters. Sometimes it’s looking for an integer (a number), sometimes a string (free text, essentially), sometimes a boolean (something that’s true, or something that’s false), sometimes a switch parameter (requires no further input).

There are two differences between the Set-LocalUser cmdlet and the New-LocalUser cmdlet.

First, the New-LocalUser cmdlet has the following parameter:

-UserMayNotChangePassword

Whereas the Set-LocalUser cmdlet has the following parameter:

-UserMayChangePassword

Essentially, these switches do the same thing, but in reverse, and it’s difficult to remember which way round it is. Why can’t they both just use the same syntax?

However, there’s a second issue, affecting both these cmdlets, and another one:

-PasswordNeverExpires

In the New-LocalUser cmdlet, these parameters are switch parameters. You include them and there is no further input.

In the Set-LocalUser cmdlet, these parameters are boolean – you have to set them to $True or $false.

It’s easy enough to fix, but it’s odd logic, eh?

Printing to a file with PowerShell

I need to print a large number of files to PDF. I don’t have time to open each file individually and do it manually, so I needed to find a way to automate the process. PowerShell provided me a solution, with a little ingenuity.

I’ve been trying to tackle a very tricky issue recently. I have a folder of PDF files sitting in a folder, and on a regular basis, the files in that folder need to be password protected and then moved elsewhere.

Acrobat Pro has a great feature called Actions, where you can set a workflow for documents. Normally, I would pick the folder containing these files and use a saved workflow to encrypt each file with a pre-determined password.

Unfortunately, the contents of some of these files (fillable forms) prevent that workflow from applying properly, meaning some files can’t be password protected in this manner. Since they need to be encrypted, it was important to find another way to do this.

I discovered that if I used a PDF printer to print the file to PDF again, the resulting file could be encrypted without issue. This was a good start, but not of much use if I needed to open each file and print the file manually. I needed something that could be automated. Specifically, I needed to be able to:

  • Automatically select the correct printer;
  • Specify a folder and work through each PDF in that folder;
  • Print each PDF to a file without any interaction during the process (so the output path must also be automatic);
  • Avoid interfering with the default print device beyond this task.

So, I turned to PowerShell and Adobe’s PDF printer (which comes with Adobe Acrobat’s licensed products).

Adobe’s PDF printer is useful because it’s possible to specify a default output directory in the printer preferences, and thus avoid a pop-up appearing for each PDF prompting for a save location. Free PDF printers like CutePDF don’t have this option (although I believe they are available in their paid-for products).

After that, everything can be done in PowerShell. Here’s the code I wrote:

$defaultprinter = Get-WmiObject -Query "SELECT * FROM win32_Printer WHERE default=$true"
$PDFprinter = Get-WmiObject -Query "Select * From Win32_Printer Where Name = 'Adobe PDF'"
$PDFprinter.SetDefaultPrinter()
Dir C:\test\*.pdf | Foreach-Object { Start-Process -FilePath $_.FullName -Verb Print }
$defaultprinter.SetDefaultPrinter()

The first line retrieves the current default printer. This is so that, at the end of the task, the default printer is restored (it will be changed for the duration of this task).

The second line retrieves the Adobe PDF printer. On my computer, it’s called “Adobe PDF”. If the named printer doesn’t exist, the script will return a horrible looking error and fail (I’ve no real need for error handling in this script).

The third line of this script sets the Adobe PDF printer from the second line as the default printer.

The fourth line of the script takes a specified directory and, for each PDF in that directory, prints the document. As the default printer is the Adobe PDF printer, that’s the printer that is used. In the Adobe PDF printer, I’ve already set a couple of options:

  • Disabled the option to show the PDF as it’s created (I want the task to run in the background as far as possible);
  • Specified an output directory where the PDFs will be saved to;
  • Set the PDF quality settings (in this case, “Standard”, for compatibility).

The final line restores the default printer, as it’s not likely I would want the Adobe PDF printer set as the default printer.

For me, that’s not the end of the process as I then need to go back in to Acrobat and encrypt the files, but the PowerShell script makes this a much quicker process than it would otherwise be.

I’d love to be able to encrypt the files from the same script, but unfortunately I’ve not found a way to do that. Nevertheless, a time-consuming problem made easier.