Wednesday, April 22, 2015

Install-MSAs.ps1 -- Automatically Create and Configure Managed Service Accounts with this PowerShell Script

I have brought up using Managed Service Accounts on this blog in the past (SQLync Cheatsheet, or The Quick Rundown on SQL Server and Lync Server 2013) and, for me, now is a great time to revisit the topic.

Due to unforeseen circumstances, I made the tough decision to blow my lab away and start from scratch. Well, honestly, I do this about 6 times a year, so it's more like I'm looking for an excuse. Anyway, I decided that I would do two very special things with this lab:

  • Configure everything almost entirely in PowerShell -- whenever possible
  • Follow recommended best practices (when it doesn't greatly slow down other work)

To a lot of IT folks, that probably sounds like a nightmare. To others, "impossible." (Honestly, it is technically impossible even with only using Microsoft products... there's a small number of things that just cannot be done). But hopefully you're like me and would enjoy the heck out of it. It's a constant puzzle and you're always finding new pieces. I just love when you execute a script that you're really not sure of and it runs without any of that dastardly red text.

When it came time to install SQL, I realized that the recommended best practice is to have an MSA for every service. That's a minimum of two per instance. With SCOM, SCCM, SCVMM, SC Orch, Lync, and SharePoint... that's a lot of services.

So of course I scripted it.

PowerShell Script: Install-MSAs.ps1

Using the Script



Assuming you put the script in C:\Scripts, you can just run

.\Install-MSAs.ps1 "SvcSqlR-SQL02"

and it will create the MSA and install it on the local computer. You can then configure it for a service, such as those needed for SQL to run, like so:



You can also specify more than one MSA name. For example, you could do this:

$MSANames = "MSA1","MSA2"

and then put that variable after the script name. Or, if you want to get fancy, you can do this:


All I did was create a text file on my desktop that looks like this:


...and then I get to delete all those accounts that I made for your amusement. That's right... all of this is for YOU so you better appreciate it. Or whatever.

To delete or view the MSAs in Active Directory Users and Computers, see below. You must first enabled "Advanced Settings" under the View drop-down menu.


Script Content


Overview:


I'm just going to paste the script here because it's not very long and you won't have to download anything.

Here is the high-level process:

  • Check if the AD PowerShell module is installed; install it, if needed
  • Check to see if a service account with the name you provided exists; skip creating that MSA if so
  • Create the MSA, associate it with the local computer, and install it
  • Display which MSAs were created and which failed to be provisioned

Code:



param($MSANames)

$hostname = hostname

$InstallState = (Get-WindowsFeature -Name RSAT-AD-PowerShell).InstallState

if ($InstallState -ne "Installed") {
    Install-WindowsFeature -Name RSAT-AD-PowerShell -WarningAction SilentlyContinue | Out-Null
    sleep 1
}

Import-Module ActiveDirectory
sleep 1

$CreatedMSAs = @()
$NotCreatedMSAs = @()

foreach ($name in $MSANames) {
    
    Try {
        Get-ADServiceAccount -Identity $name | Out-Null
        sleep 1
        Write-Host "An account with the name '$name' already exists! This account cannot be created.`n`n" -ForegroundColor Red
        $NotCreatedMSAs += $name
    } catch [System.Exception] {
        New-ADServiceAccount -Name $name -Enabled $true -RestrictToSingleComputer
        sleep 1
        Add-ADComputerServiceAccount -Identity $hostname -ServiceAccount $name
        sleep 1
        Install-ADServiceAccount $name
        sleep 1
        $CreatedMSAs += $name
    }
}

$count = 0
if ($CreatedMSAs -ne $null) {
    Write-Host "Managed Service Accounts have been installed on this computer." -ForegroundColor Green
}
foreach ($name in $CreatedMSAs) {
    $count += 1
    Write-Host "MSA #$count :      $name" -ForegroundColor Cyan
}

if ($NotCreatedMSAs -ne $null) {
    Write-Host "`nThe following MSAs were not able to be created, most likely because an account with that name already exists."
    foreach ($name in $NotCreatedMSAs) {
        Write-Host $name
    }
}

Friday, April 10, 2015

Where to Start with PowerShell -- It's not as scary as you think

I opened PowerShell. Now what?

Yes. I want to talk about the most basic elements of PowerShell... but I'm also not just writing this for the people who are just now seeing that little blue box for the first time.














Whoops. Wrong blue box.


There we go.

Beyond those who are just venturing out from the cmd prompt or looking to get into automation, I've come to realize there are a LOT of IT professionals who use PowerShell on a regular basis yet they have very little idea what it is they are actually doing. They paste the same commands in day after day, probably know which parameters do what, and it just becomes a tiny piece of the backdrop of their workday. Heck, maybe they even get an error once in a while... but this can be fixed by just "doing it manually" through the UI.

To these folks--and I know this because I was one of them not long ago--this is enough. It's enough to get the job done, and it's enough to put "Intermediate PowerShell" on a resume. Oh, then you're in a world of trouble. Because one day, you're going to have some new task, with a new set of variables, and a new set of cmdlets to run... and not all cmdlets have an equivalent in the GUI... and the error messages for that cmdlet may have been designed by a moron.


PowerShell: What is it? What's it for?

I don't ask that to be inane or condescending. I didn't start getting better with PowerShell until I knew how to answer that question. Sure, I can Google it and echo back that it's a command-line interface created to interact with the .NET architecture, but that means nothing to an infrastructure person.

Because think about it: there already is a command-line interface. Good old cmd.exe has been serving Windows operating systems well for much longer than it was even referred to as the "cmd prompt." It can even start reaching into the .NET universe and do its thing. It can add little bits here and remove little bits there until it gets the job done. 

If you were cleaning out your closet with the cmd prompt, you'd get it done by inspecting every dirty shirt or smelly sock and then moving it to wherever it needs to go, like a hamper or a bio-hazard containment facility. Well, if you were cleaning out your closet with PowerShell, it would be like having a team of specialized engineers whose sole purpose in life was to eradicate your dirty laundry.


But why is it so much better? Because it was designed for a different purpose and a lot went into it to get to that point. PowerShell is object-oriented, meaning that can view things as a whole or even collections of things. It can store that information as the object itself rather than storing it as a collection of characters and numbers. It's not just a logical arrangement of the data like in cmd prompt. The information being stored is the data. 

Going back to my horrible closet analogy, you could use PowerShell to clean up the whole closet at once. You could decide to magically repaint all your underwear purple. You could turn all those old magazines (that you never even read once) into boxes of Raisin Bran. 

Heck, the closet would be an object too, right? Just turn it into a new closet. Or a spaceship. Whichever is more exciting for you.

The Super Basics

The stuff in this section is what you gotta know if you want to be able to say, "Yes, I know at least a little of PowerShell beyond the fact that it is named PowerShell."

Use PowerShell ISE or be dumb


"What's that program you're running?" asked the person on our team who was brought on to write PS scripts.

"Oh, nothing..." I said, smugly. The joke's on him because it is not nothing.


ISE stands for Integrated Scripting Environment. It is the tool to use when you want to turn a string of commands into an actual script. I won't go too far into this in today's post, but it really is amazing. I mean, I've actually tried looking for something else out there, out of pure astonishment at ISE's functionality, but I don't think it exists. The team behind this really did stellar work.

Just because it is used for scripting does not mean you cannot just run commands out of it. This is the excuse I hear most often as to why they opted for "regular" PowerShell over ISE. You see that blue area of the screen in the picture above? They made it that color blue for a reason. It works just like the "regular" version.

Actually, I take that back. It works WAY better than the command-only window. Here's why:


As you start typing out the parameters of a cmdlet, a nifty thing called IntelliSense will kick in. It will provide a list of parameters that you can use and sometimes even suggest the value to input.

Just FYI, IntelliSense does have to process a lot of data, so it may occasionally "time out". Just hit backspace and retype the dash.

A command is what you run and a cmdlet is what makes it happen: 

When you hit Enter on your keyboard after typing stuff into PowerShell, you just ran a command. The terminology gets mixed up even by the most guru-y of the gurus, but it's an important distinction. A command is made up of 2 parts: the cmdlet and the parameters. Take a look at the command below:

Add-Computer -DomainName "hyperi2.net" -Credential "hyperi2\Administrator" -Restart

Sometimes there will be only one parameter. Sometimes the command will be so long that you don't remember how old you were when you started typing it. Sometimes you won't have any parameters and the cmdlet and the command are the same thing.

One tiny extra consideration: cmdlet may also be referring to the syntax as well as the first part of the command. For example, someone might say, "How do you run that cmdlet?" If you asked how to run a command, a pretentious annoying person might answer back, "Press Enter!" And then they'd do that annoying laugh that goes on for way too long.

Every cmdlet = Verb-Noun: 

Say what you're going to do (the "verb") and then which object (the "noun") to do it to. Let's use the same example as before:

Add-Computer -DomainName "hyperi2.net" -Credential "hyperi2\Administrator" -Restart

If someone was panicking and said, "What am I going to do? How do I fix this?!" the answer would be a cmdlet. The verb is what to do and "this" is the noun. So, in order to fix the problem, I am going to add the computer to the domain. This may seem incredibly obvious, but there's often a lot of context to sort through.

Also important to note: parameters can almost always be re-arranged in a command. What if it began "Add-Computer -Restart"? That's not as clear, but it will still work.*

I chose this example specifically because it leaves a lot of unanswered questions. I mean, we know that we are adding a computer to something. But to what? The domain? Which computer? Is DomainName specifying the domain that the machine is already a part of? That last one seems strange, but there are a lot weirder cmdlets out there.

PowerShell and Windows are released at the same time, and versions matter:

Here's a helpful breakdown:

PowerShell 1.0 was released with Windows XP and Server 2003
PowerShell 2.0 was released with Windows 7 and Server 2008 R2
PowerShell 3.0 was released with Windows 8 and Server 2012
PowerShell 4.0 was released with Windows 8.1 and Server 2012 R2
...and I'm sure you could have guessed that 5.0 will be out with the next kernel

To be honest, I wasn't using PowerShell back in the XP days, so I can't really speak to that. However, the differences between version 2.0 and 3.0 are HUGE. Not only did they add a tremendous amount of cmdlets, they also loosened up the syntax and made alterations to already existing cmdlets. This doesn't matter much if you've stuck with 2.0, but it can be a real pain when you're used to 3.0 and find yourself in a situation where you have to use 2.0.

One of my favorite parameters in a cmdlet is -Recurse for Get-ChildItem. Well, I once structured the logic of a very important script around the fact that I could use it. When the script started failing and after I spent several anger-filled hours losing my mind, I realized that this parameter was released for 3.0.

One very important thing to note is that you can use the next versions up in some cases. For example, if you're on Windows 7 with Service Pack 1, you can skip all the way from 2.0 to 4.0. If you can upgrade, you should upgrade.

You can find your PowerShell version simply by typing $PSVersionTable and hitting enter.



Intermediate Basics

It's okay to ask for help

Given that we are using a shell, many people open PowerShell for the first time and type "help." You would assume that you'd get some helpful information about how to get started and perform your first simple tasks. Instead, what you get is a help file that describes the help system. That can be confusing, but it's honestly a huge freaking clue that you're going to be using it a LOT.

It also tells you right away that you're going to need to update your help files. And that doesn't mean just "regularly". What I mean is that you might open up a help file and see this:


So, let's do it. You do need an internet connection or at least a means to transfer the files over. Remembering that cmdlets are a combination of verbs and nouns, we can update help files simply by typing Update-Help. You should do this right away after installing a fresh instance of PowerShell, and then about every week after that. I know it's hard to remember to do that with our busy schedules. If only there were some way to do it automatically... (spoiler alert: use PowerShell)

After running Update-Help, you'll see lots and lots of help files being downloaded, like so:


Then, you might get some errors. That's okay.


Chances are that the home location for a module has been moved or about a million other possible problems can come up. Just be sure that the error doesn't say that it can't reach the internet at all.

Modules: A fancy word for "things"

Without any modules, PowerShell wouldn't do anything. Even the most basic cmdlets wouldn't exist. So to say that they rev .NET into overdrive is an understatement. 

I don't want to go too deeply into modules right now, because I'd rather focus on how to find and load them, but just know that modules are incredibly simple. Soon enough, you'll be writing your own scripts and functions and setting up how to use them. Well, string a few of those together with a similar purpose, add some help file documentation, and then package it for easy access and you have a module.

So, with how important modules are, don't be surprised when we run Get-Module, which returns the ones that are loaded, and see the screen just explode with text. I mean, it's going to be a massacre. See?


Wait, what?! 2 of them? Do we need to add them in or something? Do you have to do it every time?

The answer is technically yes to those last two questions, but Microsoft was a sport and made it incredibly easy. There are LOTS of modules installed but loading all of them at start-up would be really taxing. It does take time to process each one simply because the .NET library is so large. You will also notice that ISE is tremendously faster at this task.

Let's do an experiment. Those of you who have access to a computer with the Active Directory module installed can follow along.

1. First, open regular old PowerShell (not ISE). 
2. Once you are able to type, type Get-Module and hit enter. You'll notice that the system lags behind as it gets all the background gears and sprockets into place. It feels like it takes forever just to get going.
3. After a bit, the shell will be back to normal speed and you can try out some simple commands like "Set-Location C:\" and "Get-ChildItem". It should be responding much more quickly now.
4. Now we're going to run an AD command. Type out "get-adcom" and hit tab. We're trying to get it to complete a cmdlet for us that belongs to a module that isn't loaded. It searches through the loaded modules first, doesn't find "Get-ADComputer" inside those, so it starts searching through modules that are not loaded. There will be additional lag time as it goes looking, but then it will give you the full cmdlet.
5. You can finish the command by typing "Get-ADComputer -Filter *" and hitting enter. It will return every single computer in the domain. If you have a lot of computers, this will take a while and you won't even be able to read them all.
6. Type Get-Module again. You'll notice that the module "ActiveDirectory" has now appeared. Even if you were to delete the command without hitting the enter key, the module would still be loaded. This is because it automatically loads the module you need as soon as its able to.

One last thing on modules: there are obviously countless modules available online, but there actually are a lot already installed and waiting to be loaded. To get a list of all the modules on your computer, type Get-Module -ListAvailable.

Finding the Right Cmdlets

I'll tell you right now: often times, the best way to figure out how to do something in PowerShell is just to Google it. However, the best way to learn what is and isn't possible in PowerShell is to dig into it and try to find answers on your own.

In the shell, type "gcm". This is going to give you a LOT of output because it is showing you every cmdlet, function, and some aliases. By the way, "gcm" is an alias itself. It is an abbreviated way to type out Get-Command. To see more aliases, type Get-Alias. You can even create your own.

Earlier today, I was trying to figure out how to set the connection-specific DNS suffix for a network interface. It might seem really difficult to find the right combination of cmdlet and parameters, but not really. I just typed "gcm *dns*" to return every cmdlet that contains that letter combination. I knew that I would be using a set- cmdlet because I was actually making a modification. Here's what popped up:



By the way, you can use multiple wildcards (*) in a search, so I could have done "gcm set*dns*client*" to narrow down the search a bit more.

Then, I needed to poke around in that cmdlet to find the right parameters. And that leads me into our final section...

Advanced Basics: "shcm"

This will change your PowerShell life.

shcm is an alias for Show-Command.

When I was looking for the command to set a DNS suffix, I picked a cmdlet that sounded like it might work, Set-DnsClient, and then put "shcm" in front of the cmdlet. Here is the window that popped up along with the command I typed underneath it.


You'll notice that I entered information into the fields that it provided just like I would type out a command. Once I had it how I wanted it, I just hit "Copy" down at the bottom and pasted it into my script. This is how it came out:

Set-DnsClient -InterfaceAlias "Ethernet" -ConnectionSpecificSuffix hyperi2.net -PassThru -RegisterThisConnectionsAddress $True

You can also just hit "Run" if you will only be using it one time.

That is substantially easier than trying to build the command one parameter at a time. It even tells you what type of information to enter if you mouse over one of the fields. If you hit the blue "?" at the top right, it will give you a modifiable, scalable, and searchable version of the help file.

You'll eventually have commonly-used cmdlets and their parameters memorized, but this is an absolutely crucial tool for learning at any level of scripting.


I hope this has been informative for you, and I'd like to thank you for watching--err, reading. I think I've been watching too many CBT Nuggets videos...