Advanced Windows PowerShell Scripting Video Training

Advanced Windows PowerShell Scripting Video Training
Advanced Windows PowerShell Scripting Video Training

Friday, September 11, 2015

Changing a Computers Description in Active Directory to Match the currently Logged On User

This is one that I picked up off of PowerShell.com.  The problem is that that the answer is a bit long so I’m posting it here.  The IT Pro in question wants to change the Computers Description in Active Directory to match the login name of the currently logged in user.  A few issues come to mind.

1 – Does all of the clients have RSAT installed?  I’m going to assume no.  That means that we cannot use the Active Directory Modules cmdlets.

2 - Does all of the users have the rights to modify the description field of a client in Active Directory.  By default, they do not.  We will set this up at the attribute specific level.

3 – How will this script run?  We will implement it as a login script.

Modifying User Rights

First off, let’s tackle the user rights issue.  All users in your domain are able to read the contents of Active Directory.  Only a hand full should be allowed to modify it.  We are going to modify AD to allow for our users to modify just the client Description attribute.  This follows the Principal of Least Privilege.   Remember, once you do this, you will allow them to make changes on their own, outside of this script, to all clients Description fields.

You need to access Active Directory Users and Computers (ADUC).  You can find this on a Domain Controller or by installing RSAT from Microsoft.

In ADUC, right click the OU or Container that holds your client objects and select Delegate Control…

Click Next.

Click Add.

Here you need to add a security group containing all the users that you are going to allow to modify the Description property of computer objects.  In this demonstration, I am using Authenticated Users.  Click Check Names to verify that the group has been found and then click OK.

Click Next.

In the Tasks to Delegate window, select Create a custom task to delegate and click Next.

In the Active Directory Object Type window, select Only the following objects in the folder.

Scroll through the option and check Computer objects.

Click Next.

image

Uncheck General and check Property-specific.

Scroll down the list and check Write Description.  Click Next.

Click Finish.

Close ADUC.

 

Our Login Script

OK, now that security has been set up we need to write a PowerShell script.  We will be using Active Directory Services Integration (ADSI) to connect to Active Directory since we will not have the Active Directory PowerShell cmdlets to call upon.

Here is our login script:

1

2

3

4

5

6

7

8

9

10

11

# Get the user name.

$UserName = $env:USERNAME

 

# Get the client object from AD

$filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$env:COMPUTERNAME))"

$DN = ([adsisearcher]$filter).FindOne().Properties.distinguishedname

 

# Modify the object.

$ObjClient = [ADSI]"LDAP://$DN"

$ObjClient.Description = $UserName

$ObjClient.SetInfo()

We store the username in the variable $Username.  Next, we use ADSI to get the computer object from Active Directory.  Finally, we set the description property of the object to the user name and commit it to AD. I saved this script into \\lon-dc1\SYSVOL\Adatum.com\scripts as Login1.ps1.

 

Setting up the GPOs

Our next step is to configure a GPO to run our script. I call this GPO User Login Scripts. First we configure the login script in the GPO.

User Configuration / Windows Settings / Scripts / Logon.

Click the PowerShell Scripts  tab.

Click Add and browse to and select Login.ps1.

In the dropdown box labeled For this GPO, run scripts in the following order: select Run Windows PowerShell scripts first 

Next click OK.

image

Scope the GPO User Login Scripts to an OU containing the user accounts or at the Domain level.

 

Starting with Windows 8, the running of logon scripts is delayed by 5 minutes.  We are going to override this.

Create a second GPO called Client Login Scripts.

Computer Configuration / Administrative Templates / System / Group Policy

Modify the policy for Configure Logon Script Delay.

Enable the Policy and then set the minute value to 0.

Scope this policy to an OU that contains your clients, or at the domain level.

 

Testing the Script

Here is an image from ADUC showing the current description of client LON-CL1. 

image

Here is the same client after User1 logged in.

image

This opens up a few new possibilities.  Take a look at the modified code below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

# Get the user name.

$UserName = $env:USERNAME

 

# Get the client object from AD

$filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$env:COMPUTERNAME))"

$DN = ([adsisearcher]$filter).FindOne().Properties.distinguishedname

 

#Get the date/time stamp.

$Date = Get-Date

 

# Modify the object.

$ObjClient = [ADSI]"LDAP://$DN"

$ObjClient.Description = "Logon : $UserName at $Date"

$ObjClient.SetInfo()

Here is the result in ADUC:

image

Now you can see if someone is logged in at a quick glace.  If you set up a Logoff script for the users, ADUC can also report a log off.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

 

# Logoff script.

 

# Get the user name.

$UserName = $env:USERNAME

 

# Get the client object from AD

$filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$env:COMPUTERNAME))"

$DN = ([adsisearcher]$filter).FindOne().Properties.distinguishedname

 

#Get the date/time stamp.

$Date = Get-Date

 

# Modify the object.

$ObjClient = [ADSI]"LDAP://$DN"

$ObjClient.Description = "Logoff : $UserName at $Date"

$ObjClient.SetInfo()

 

 

image

 

Conclusion

Using this technique, you can get a quick look of who is where, and the last time you had a logon on a client. If the user does not perform a normal logoff or shutdown while connected to the network, you will not see a logoff message from the second script.  Also, if the user logs in using cached credentials while offline and then connects to the network, the Description property will not be modified.

Thursday, September 3, 2015

When to use Single and Double quotes with PowerShell

Here is another one of those enduring questions from my PowerShell classes. When to use single and double quotes.

First off we need to establish the object type that is created by both single and double quotes. Take a look at the code below. Both single and double quotes produce a System.String object type. At this point there is no difference between the two.

PS C:\> $Single = ''

 

PS C:\> $Double = ""

 

PS C:\> $Single.GetType()

 

IsPublic IsSerial Name                                     BaseType                                         

-------- -------- ----                                     --------                                         

True     True     String                                   System.Object                                    

 

 

 

PS C:\> $Double.GetType()

 

IsPublic IsSerial Name                                     BaseType                                         

-------- -------- ----                                     --------                                         

True     True     String                                   System.Object 

The difference comes when you create strings that contain variables.

PS C:\> $Var1 = "Hello"

 

PS C:\> $Var2 = 'World'

 

PS C:\> Write-Output "$Var1 $Var2"

Hello World

 

PS C:\> Write-Output '$Var1 $Var2'

$Var1 $Var2

Here two strings are produce from variables. The first utilizes double quotes while the second utilizes single quotes. In the first, the value of the variables are placed in the string. In the second string, utilizing single quotes, the characters are treated as literal. In other words, they are no longer treated as variable names.

You may be thinking “Big deal”. Well, here is where it can become a big deal. Consider the following.

PS C:\> Write-Output 'The price of gas is $4.35 a gallon.'

The price of gas is $4.35 a gallon.

 

PS C:\> Write-Output "The price of gas is $4.35 a gallon."

The price of gas is .35 a gallon.

Do you see the difference? In the string created with single quotes, you get accurate output. In the other string, PowerShell is looking for the value of the variable $4. Well, $4 has a value of NULL. Now you just set the price of gas at $.35 a gallon. We call this a RGE. Otherwise known as a Resume Generating Event.

You could write your string this way.

PS C:\> Write-Output "The price of gas is `$4.35 a gallon."

The price of gas is $4.35 a gallon.

What you may not see is in front of the “$” is a “`” back tick. This is the PowerShell Escape Character. Be careful if you use it. It is hard to see. The Escape Character makes the dollar sign be treated as a character instead of the name of a variable. This will give us the correct output. Remember, this is not the single quote, but the back tick.  The back tick is on the left of a US keyboard where the single quote is on the right.

Wednesday, September 2, 2015

When to use [] or {} or ()

Yesterday I posted a question that comes up often in my PowerShell classes on when to use special variables. Today I am going to post another one. When to open and close code and what characters to use.

Indexes of Arrays [ ]

The Square braces are used when we want to access an element of an array, also known as a collection.

# Accessing Array Elements

 

# Create an array.

$Fruit = "Apple", "Grape", "Orange", "Pear"

 

# Access the first element of the array using index 0.

$Fruit[0]

 

# Access the second element of the array using index 1.

$Fruit[1]

 

# Access the last element in an array using -1.

$Fruit[-1]

 

# Access the last element in an array using -2.

$Fruit[-2]

OK, that was easy. Not so for the next two.

 

Using Script Blocks { }

The curly braces are used with script blocks. A script block allows you to take multiple lines of code and use them as a single unit. Many commands can utilize script blocks. Script Blocks are used when declaring functions, classes, workflows, enums, hash tables, and DSC. They are also used in cmdlets with parameters such as ScriptBlock, Begin, Process, and End. Also IF and SWITCH statements use them.  The help files of cmdlets will guide you in how to use them. Here are a few examples.

# ScriptBlock used in a functions

Function MyFunction

{

    Write-Host "Hello"

}

 

# ScriptBlock used in a remote command

Invoke-Command -ComputerName DC2 -ScriptBlock {Get-Process}

 

# ScriptBlock used in the pipeline with ForeEach-Object

Get-Process |

    ForEach-Object -Process {$_ | Select-Object -ExpandProperty Name}

 

Using Parenthesis ( )

Parenthesis can be used in a few different ways. We can use them to control the order of operation of math.

# Parenthesis used to control the order of a math operation

 

4 + 5 * 2

(4 + 5) * 2

We also use them to execute a cmdlet first in order to gain direct access to the object that the cmdlet produces.

# Parenthesis used get immediate access to the object a cmdlet produces

 

Get-Date | Select-Object -ExpandProperty DayOfWeek

(Get-Date).DayOfWeek

When we are going to do a comparison operation, such as for an IF or SWITCH statement, we use parenthesis to denote the comparison operation.

# Parenthesis used in an IF statement

 

If (1 -lt 5) {Write-Host "It Works"}

 

# Parenthesis used in an SWITCH statment

Switch ("PowerShell")

{

    "PowerShell" {Write-Host "PowerShell"}

    "ShellPower" {Write-Host "ShellPower"}

 

}

We also use the parenthesis with the PARAM keyword.

# Parenthesis used with the PARAM keyword

Function MyCode

{

Param ($Num1, $Num2)

    Write-Output ($Num1 + $Num2)

 

}

 

MyCode -Num1 10 -Num2 5

We also use parenthesis with sub expressions.

# Parenthesis in sub expressions

$Procs = Get-Process | Select-Object -First 1

Write-Output "The process name is $Procs.Name"

 

Write-Output "The process name is $($Procs.Name)"

If you are a bit frustrated when all the different ways to use parenthesis, I do not blame you. They are also used in other loop constructs to. It will come down to your knowledge of PowerShell. That will come with time. Remember the help files are there to guide you. Use them. The examples will show you which set of characters to use with each cmdlet or parameter if required.