Showing posts with label Active Directory. Show all posts
Showing posts with label Active Directory. Show all posts

Monday, July 2, 2012

The shortest distance

Everybody likes shortcuts.  If you can save yourself some time and effort, why not take the opportunity?

Powershell offers several ways to save yourself some typing and shorten your command lines and scripts.  Plus you almost never blow yourself up.

One way is to string together all of the slicing and dicing you might need to do.  If a method for an object returns an array and I know I need a specific element in that array I can specify that element  right after the method invocation.  And I can call a method right from that array element.  Allow me to demonstrate.

In a previous example I had to parse the DistinguishedName for a user.  I model my test domain based on a former employer who managed users and computers by location.  Delegations were done so support staff at each site could manage their objects.  My OU structure looks like this:

In order to find the site name for an arbitrary user account I can assign a variable at each step to parse the DistinguishedName.  In this example I also show the value of the variables to confirm each step:

But I don't need to use all those variables.  This can be processed in a single line.  Below I build up each step.  The last line shows how I combine all of these steps into a single command:

Another handy shortcut is to enclose a command in parentheses and do the same slicing and dicing.  In the above example I don't even need to assign the Get-ADUser output to a variable.  I can use the command
 (Get-ADUser AlAlpha).DistinguishedName.split(',')[2].substring(3)

Letting PowerShell process things in parentheses has many uses and works for all cmdlets.  You can nest some really crazy crap and it will still work.  In the following example I know I have a user in Atlanta with the last name Alpha and I'm not sure what the account name is but I have to change his first name to Allen.  The first command get-aduser -searchbase "OU=Atlanta,OU=Locations,DC=toddp,DC=local" -filter * | where {$_.surname -eq 'Alpha'} shows how I can do this search.  In the second command I enclose that first command in parentheses and use that as the identity for my Set-ADUser command.  I call Get-ADUser one last time to show the change was made in the GivenName attribute.

Another PowerShell shortcut is to use aliases for the cmdlets. 
Here we see all of the aliases that start with the letter "g".  Two aliases I use frequently are gm for Get-Member and gwmi for Get-WmiObject.


In this example the Get-ADUser output is piped through the Get-Member cmdlet using the alias GM.

You can create your own aliases using the Set-Alias cmdlet.
After using Import-Module (or its alias, ipmo) to import the ActiveDirectory cmdlets I use Set-Alias to create the alias gadu for the Get-ADUser command.  Note that an alias can only be for a cmdlet, not a cmdlet and its parameters.  You can work around this limitation by using a function as shown in the last example for Set-Alias.


There are two other aliases that you may encounter.

There is a default alias for ForEach-Object which is the percent sign (%) and an alias for Where-Object which is the question mark (?).  This will come in handy.

How cool is this?  I pipe Get-Process through the ForEach-Object shortcut to pull out the ProcessName and use the Where-Object shortcut to show just the processes starting with the letter "c".  That's good enough for me.

In Powershell 3 there is an implied ForEach-Object for arrays.  Instead of piping the array through ForEach-Object cmdlet or shortcut you just enclose the object in parentheses and process the properties you need.
These are the processes running on my Windows 8 demo VM.  But if I need to grab just the process name I can use the syntax (Get-Process).ProcessName
Pretty sweet.  There are some other handy shortcuts coming in PowerShell 3.

So we have all these nice shortcuts.  Shouldn't we use them all the time?  The rule of thumb from our friend Don Jones is that if you are writing a quick and dirty command for yourself, then the shortcuts can save you time and effort.  But scripts with shortcuts look more cryptic and are harder to read.  So if you are creating a more invovled script that will be used by someone else you should be considerate and make it more accessible by avoiding shortcuts. 

Knowing these shortcuts and aliases (or at least how to find what is being aliased) helps you be more efficient.  And if you come across a script written by someone else who used them you won't be caught off guard

Tuesday, May 29, 2012

I, Object

As discussed in the post on the pipeline, part of the power behind PowerShell is that the output from most cmdlets is an object or an array of objects.  And cmdlets are designed to process objects received from the pipeline.  Since PowerShell is built on the .Net framework everything is an object.

An object has properties that contain information about the object and is operated on using methods.  For example, I am an object and have properties such as my name and phone number.  To contact me you might use the method of calling my phone number and addressing me by name. 

You can see the properties and methods of an object by piping it through the Get-Member cmdlet.  Even a scalar such as a simple string is still an object.
My string variable has an attribute named length, the number of characters in the string, and a bunch of methods for slicing and dicing the string.

An array is an object that is a collection of objects.  If you pipe an array through Get-Member you get the properties and methods of the objects contained in the array, not the array itself.  Arrays are very accommodating that way since the attributes of the members are much more interesting.

Some objects have many of properties.  Usually you want to keep just the few of those properties so you will use the Select-Object cmdlet to create the subset.

But what if I need to go in the opposite direction?  What if I need to add some properties to an object?  The Add-Member cmdlet is the answer to your question.
This is my string from the previous example.  I use Add-Member to append a property to the string that contains the last character in the string. 

Sometimes it is handy to create an array of custom objects.  I use this technique when my goal is to use Export-CSV to create a spreadsheet of just the information requested by my PHB

$CSV = 'C:\Users\Administrator\Documents\DisabledUsers.csv'
$All = Get-QADUser -Disabled -SearchRoot 'OU=Locations,dc=toddp,dc=local' `
          -SearchScope Subtree


$Disabled = @()
foreach ($U in $All) {
    $U.Name
    $New = New-Object Object
    $New | Add-Member NoteProperty Name $U.name
    $New | Add-Member NoteProperty First $U.FirstName
    $New | Add-Member NoteProperty Last $U.LastName
    $New | Add-Member NoteProperty Full $U.DisplayName
    $DN = $U.DN.Split(',')
    $Site = $DN[$DN.Length-4].Substring(3)
    $New | Add-Member NoteProperty Site $Site
    $Disabled += $New
}
$Disabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

In this example I'm using the Quest Active Directory cmdlet Get-QADUser to show some information on disabled user accounts.  First I create the empty array by assigning it the value @().  (In PowerShell the @ is called the splat operator.  Apparently the designers thought calling it "splunge" was a bit silly.)  I then use the New-PSObject cmdlet to create a custom object in my loop.  I use Add-Member to add properties for my report to the object, and then append the custom object to the array.  My test domain has an OU named Locations which contains several site OUs and within those site OUs there are OUs for users and computers in the site.  So I parse the DN (distinguished name) property of the user account to determine the site name.

That works but it looks kind of ugly and requires too much typing.  In PowerShell 2 the New-Object cmdlet has been revamped to allow a technique called "splatting".  So instead of a bunch of Add-Member statements you assign all of the properties in a script bock that is preceded by the splat operator.

$CSV = 'C:\Users\Administrator\Documents\DisabledUsers2.csv'
$All = Get-QADUser -Disabled -SearchRoot 'OU=Locations,dc=toddp,dc=local' `
         -SearchScope Subtree


$Disabled = @()
foreach ($U in $All) {
   $U.Name
   $DN = $U.DN.Split(',')
   $Site = $DN[$DN.Length-4].Substring(3)
   $New = New-Object PSObject -Property @{
       Name  = $U.name;
       First = $U.FirstName;
       Last  = $U.LastName;
       Full  = $U.DisplayName;
       Site  = $Site
   }
   $Disabled += $New
}

$Disabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

That saves me some typing and it does improve legibility.  But splatting doesn't respect the order of my properties:

So if I need the properties in a specific order I use the Select-Object cmdlet:

$Enabled = $Enabled | Select-Object name,First,Last,Full,Site
$Enabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

Which gives us the output like the first example:

This technique gets even easier in PowerShell 3 which includes the object type [PSCustomObject].  Shay Levy goes into detail on how this will work once PowerShell 3 is released.  For my example the script in PowerShell 3 would look like:

$CSV = 'C:\Users\Administrator\Documents\DisabledUsers2.csv'
$All = Get-QADUser -Disabled -SearchRoot 'OU=Locations,dc=toddp,dc=local' `
         -SearchScope Subtree


$Disabled = @()
foreach ($U in $All) {
   $U.Name
   $DN = $U.DN.Split(',')
   $Site = $DN[$DN.Length-4].Substring(3)
   $Disabled += [PSCustomObject]@{
       Name  = $U.name
       First = $U.FirstName
       Last  = $U.LastName
       Full  = $U.DisplayName
       Site  = $Site
   }
}

$Disabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

Not only does this save me even more typing, but the [PSCustomObject] will preserve the order of the properties and perform more efficiently than the New-Object cmdlet.

There is no need to object to PowerShell objects.  You can augment and condense the objects you get from cmdlets and even create your own objects.  Just another example of the awesome power of PowerShell.

Commentors: How else do you use custom objects to improve your efficiency? 

--updated 26 Jun 2012 to correct formatting--

Monday, April 30, 2012

Pipeline Conga Line

http://28.media.tumblr.com/tumblr_l2k058TExA1qzjpn4o1_500.jpg
Unix, DOS, and PowerShell all have the ability to combine commands so the output from one command becomes the input for the next command.  This is called a pipeline because the commands are connected like sections of a pipe.  All three platforms use the vertical bar as the connector which is why that character is sometimes referred to as the pipe.

Unix and DOS also use the less-than sign < to pipe input from a file to a command, the greater-than sign > to pipe output from a command to a text file, and two greater-than signs >> to append to a file.  All of these pipes are useful and you can sometimes do complex things, but because the Unix and DOS pipes are text based there are limitations on what can be accomplished.

PowerShell has powerful cmdlets for getting data to and from files so it only needs the vertical bar pipe.  Because PowerShell is object oriented and the cmdlets are designed to process objects passed from the pipeline some insanely powerful capabilities are available.  Often you can complete a task with a single command line.

So let's stop talking and start dancing.

Get-GPO -All | where {$_.displayname -match 'XenApp5'} | Set-GPPermissions -TargetName 'XAAdmins' -TargetType Group -PermissionLevel GpoEditDeleteModifySecurity

The administrators of our XenApp farm requested access to edit the group policies that manage the farm.  All of the GPOs include 'XenApp5' in the name.  The output from Get-GPO is piped into the Where-Object command to filter out any GPOs that don't have 'XenApp5' in the name.  The resulting objects are piped into the Set-GPPermissions cmdlet to assign the XAAdmins group the permission to edit the GPOs.  And I complete a complex task in a single line.

Most of your pipeline commands will follow this general pattern.  You will run a command or get some input, filter the pipeline to keep only the stuff you want to work on, then process those objects further.  The filters Where-Object and Select-Object are quite powerful but depending on your source you can speed up your scripts by filtering objects at the source.  Don Jones calls this Filter Left, Format Right.

That article also notes that the pipeline processor assumes that every pipeline ends with Out-Default to dump the results to the screen unless another Out-* command is at the end of the pipe.  PowerShell is using the Extended Type System to convert the objects to text and, unless directed otherwise, to make a best guess at how to format the results.  The Out-* commands produce no objects for any comdlets in the pipeline after them to process.  Therefore the Out-* command is always the last command, even if it is the implied Out-Default.

Where else can we send output?
If you are not using the implied Out-Default you will probably use Out-File, although Out-GridView has some handy capabilities.

What options do we have for formatting the data before we pipe it out somewhere?
Depending on the objects PowerShell will automatically Format-List or Format-Table before doing the implied Out-Default.  We have some handy ConvertTo cmdlets to format the data as HTML, CSV, or XML.  There are also the Export-CSV and Export-CliXML that combine the ConvertTo and Out-File operations into a single cmdlet.

So our pipeline conga line dance steps are:
  1. Get some data objects from a file or cmdlet
  2. Filter the data by limiting the number of objects and/or number of attributes
  3. Process the data by sorting or using the data in another cmdlet
  4. Repeat steps 2 and 3 as needed
  5. Format the data if needed
  6. Output the formatted processed data to the screen or file
Something like:

get-wmiobject win32_service | where {$_.StartMode -eq 'Auto'} | select-object name,displayName,PathName,StartName,Status,State | sort-object StartName,displayName

Use Get-WMIObject to find all of the services that are set to start automatically, select the properties of interest, and sort them so services running under the same account are grouped together.

Get-WMIObject does allow us to filter so we only get the WMI objects we want.  Doing so will make the command run faster because we return less data.  I can also format the output into a table so I get more information on the page.

get-wmiobject win32_service -filter "StartMode='Auto'" | select-object name,displayName,PathName,StartName,Status,State | sort-object StartName,displayName | format-table



If I replace Format-Table with Out-Gridview I get the output in an interactive window that lets me further sort and filter the data.


Or I can replace the Out-GridView with a Format-HTML | Out-File and create an .html web page that I can deliver to my PHB so he can open the report in his web browser.









So don't be a PowerShell wallflower.  Get out there and dance.


Commenters: Have you done any crazy cmdlet stacking on the pipeline?  How many cmdlets have you been able to use in a single pipeline?

Friday, April 6, 2012

PowerShell Prep

I like watching cooking shows because eating is my hobby.  I used to wonder how the host could complete a three course meal in 22 minutes when it takes me 30 minutes just to get started because I had to go back to the store to get that one ingredient I forgot to put on my shopping list.

http://www.glovegame.com/nutrition.html
The secret of the cooking shows is that the ingredients are prepped -- all of the vegies are chopped up and all of the spices are pre-measured.  The host tosses them in a pot, stirs them together, then pulls the pre-made masterpiece out of the second oven.  (I guess the real secret is that the cooking show hosts cheat.)

Let's get prepped and ready so we can do some cooking with PowerShell.

Ingredients

  1. Download the latest version of PowerShell.  PowerShell 2.0 is built into Windows 7 and Server 2008.  But there will be cool enhancements in PowerShell 3.0 so be sure to check Technet or Download Center before continuing. 
  2. Get any other modules or add-ins you might use.  This can include:
    1. The Remote Server Administration Tools (RSAT) which include modules for Active Directory and Group Policy management
    2. The PowerShellPack that has some extra modules for OS management
    3. The PowerShell Community Extensions which has some useful extras
    4. If you are working on a specific server application such as Exchange or SQL or SharePoint there are add-ins available from the install media
    5. VMWare's PowerCli if you will be managing VMWare vSphere environments
  3. Download the PowerGui which includes the best free IDE for PowerShell. Windows 7 and Server 2008 come with an IDE but it lacks many of the features of the PowerGui Script Editor.
  4. Download PowerGui PowerPacks for any applications you have to support. In particular the Active Directory PowerPack has many AD cmdlets that are often more powerful than those available in the Microsoft modules.


Recipe


Installing all this stuff is easy.  The downloads are all .exes or .msis.  A bunch of next-next-next and the installs are done.  One thing to note is that the RSAT modules are Windows features which are not available until you enable them. 

Control Panel > Programs > Turn Windows features on or off.

Expand Remote Server Administration Tools.  In the following examples I will use the Active Directory module so expand Role Administration Tools > AD DS and AD LDS Tools and select Active Directory Module for Windows PowerShell.  Select any other tools you will need.

While there are many useful extensions and add-ons for PowerShell, you probably don't want to install all of them, unless you plan to use them.  While it might be kind of handy to have everything available, at some point things get unwieldy.
http://www.wengerna.com/giant-knife-16999
There are two types of add-ons, modules and snap-ins.  Snap-ins are add-ons from version 1 of PowerShell while modules are add-ons available in version 2.  Because the Quest PowerGui ActiveDirectory PowerPack was available in version 1 it is still a snap-in.

How do you see what add-ons are available?

Get-Module -ListAvailable
Get-PSSnapin -Registered


Cmdlets in modules are not available until you import the module.  Cmdlets in snap-ins are not available until you add the snap in:

Import-Module <ModuleName>
Add-PSSnapin <SnapinName>



There are no cmdlets that contain the word "forest" until we import the ActiveDirectory Module


All PowerShell cmdlets have a verb-noun structure.  All of the Quest Active Directory cmdlets precede the noun with "QAD".  I don't have any QADComputer commands until I add the Quest.ActiveRoles.ADManagement snap-in.

But if I really want to save time in my PowerShell kitchen I should have the most common add-ons available every time I start PowerShell.  Frequently used modules or snap-ins can be included in my PowerShell profile.  There are four locations where you can store profiles, but the profile that is easiest to access is the one for the current user which is specified in the $profile variable.



Now when I launch PowerShell I have all of my Active Directory related cmdlets available.


Ummm.... OK.  Not quite.  When I install the Quest PowerPacks I get prompted to change the ExecutionPolicy to AllSigned.  The PowerPacks are signed so this setting is appropriate.  I will discuss signing in a future post.  But for now let's just bypass the execution policy for my test user.


Bypass is the least secure of the execution policies so I don't want to apply it everywhere.  Instead I apply it to just my test user by specifying -scope CurrentUser on the Set-ExecutionPolicy cmdlet.

Now when I launch PowerShell my add-ons are available


The default shell is good for running scripts behind the scenes such as logon scripts.  But for developing scripts the PowerGui Script Editor provides many advantages.  One of the advantages is that making add-ons available is a simple menu choice.

File > PowerShell Libraries...

ActiveDirectory and the Quest.ActiveRoles.ADManagement add-ons are already available because PowerGui still processes my profile script.  But cmdlets for other modules become available just by selecting them in this dialog and clicking OK.

Our PowerShell kitchen is prepped and ready.  Now we can start cooking so we can eat.

Commenters: What modules and snap-ins do you load in your profile?

Monday, February 27, 2012

Why we're here

This is to be a compendium of techniques for scripting repetitive tasks for those who work on or around Windows servers and workstations.  If you have to do it once, you will have to do it again... and again... and again...

The platforms covered include DOS batch (because there are times where it is necessary or more convenient use DOS), VBScript (because there is still alot of it about), and Powershell (because it is the undisputed heavyweight champion of scripted automation *ding ding*).

Targets include Active Directory (and associated tools like Group Policy and NTFS), VMWare vSphere and ESX (using PowerCli), and the Windows operating system in general (I'm looking at you, WMI).

This isn't a primer so you should already have an idea of the basics of the various technologies and the tasks you want to preform.  The goal is to put similar resources in a single location and share some techniques that will help you improve your efficiency in completing your tasks

I hope to share some things I've learned and I hope you can share with me.  Let's all work smarter, not harder. And stay thirsty, my friends.