Advanced Windows PowerShell Scripting Video Training

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

Wednesday, May 29, 2013

Great example of Using PowerShell to modify AD properties

Today in class I took a question from the Active Directory Forums on PowerShell.com where I am a moderator.  The user had an Excel spreadsheet containing the first and last names of his users, and a field for their pager numbers.  He needed a way to test to see if the pager value in the spreadsheet matched the one in active directory.  If not, he needed the number changed.  Here is what I sent him:

Import-Module ActiveDirectory

 

ForEach ($User in (Import-CSV -Path C:\PS\Users.CSV))

{

    $Last = $User.Last

    $First = $User.first

    Get-ADUser -Filter '(Surname -eq $Last) -and (GivenName -eq $First)' |

    ForEach {If ($User.OtherPager -ne $_.Pager)

    {

        $_ |Set-ADObject -Replace @{OtherPager="$($User.OtherPager)"}

    }

    }

}

 

Assuming that he was using PowerShell V2, I had him import the PowerShell module for Active Directory.

 

I then had him load the excel spreadsheet (which was saved as a CSV file) and then cycle through each record one at a time.

 

Using the Get-ADUser cmdlet, I utilized its filtering capability to find a user in AD that match the first and last name of the record being searched.

 

If a match is found, I then compared the OtherPager property in AD to the one in the CSV.  If the matched, then nothing was done.  If they did not, the value from the CSV was applied to the object in AD.

 

The user said that he was supporting 1000 users.  My class estimated that without breaks, interruptions or mistakes, this short script would save this administrator 3.3 hours every time it was used. Not back for a little script.

Monday, May 20, 2013

Get a US Navy Date Time Group (DTG) with PowerShell

This past week I’ve been at Naval Station Great Lakes training for a mission later this year.  As I’m training up for this mission, I’m seeing some procedures that may benefit from PowerShell.  Many of the procedures that I will be executing involves utilizing a Navy Date Time Group (DTG).  The problem with DTGs is that they are not exactly logical.  Here is the format.
2 digit day
2 digit hour
2 digit minute
Z for ZUL (GMT for the civilians out there)
3 Character month
2 digit year.
So, the procedure that I will be utilizing this code with will require that I either figure this out on my own under a high tempo/stress situation, or I simply ask PowerShell to do this.  Since we do not utilize PowerShell for any of our tasks, I also needed a way to copy and paste this information into an application.  The code below will generate the DTG and also place it on the clipboard.  Relying on a manual copy and paste or simply getting the DTG from PowerShell and manually typing it would have added up to a lot of wasted time and potential errors.  To send any PowerShell output to the clipboard, simply pipe it to Clip. For Example:
Get-Service | Clip
Since the default need for this code is to place it on the clip board and not the in the pipeline, A –PassThru parameter is provided that will suppress all screen output and send a System.DateTime object with the DTG property to the pipeline.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
Function Get-NAVYDTG
{
[CmdletBinding()]
Param([Switch]$NoClip,
      [Switch]$PassThru
)
    $Date = (Get-Date).ToUniversalTime()
 
    # Day Component
    Switch ($Date.day.ToString().length)
    {
        1 {$Day = "0$($Date.Day)"; BREAK}
        Default {$Day = "$($Date.Day)"}
    }

    # Hour Component
    Switch ($Date.Hour.ToString().length)
    {
        1 {$Hour = "0$($Date.Hour)"; BREAK}
        Default {$Hour = "$($Date.Hour)"}
    }

    # Minute Component
    Switch ($Date.Minute.ToString().length)
    {
        1 {$Minute = "0$($Date.Minute)"; BREAK}
        Default {$Minute = "$($Date.Minute)"}
    }

    # Month Component
    $Month = ((((get-date).
          GetDateTimeFormats())[6]).
                   remove("0","3")).
                          Remove(3).
                          ToUpper()

    # Year Component
    $Year = ((get-date).Year.toString().Remove("0","2"))
 
    If ((!$NoClip) -and (!$PassThru)
)
    {
        Write-Output "$($Day)$($Hour)$($Minute)Z$($Month)$($Year)" |
        Clip
        Write-Host "$($Day)$($Hour)$($Minute)Z$($Month)$($Year)"
        Write-Host "Sent to Clipboard"
    }
    ELSEIF ($PassThru)
    {
        $Date |
        Select-Object -Property *,
        @{Name="DTG";Expression={"$($Day)$($Hour)$($Minute)Z$($Month)$($Year)"}} |
        Write-Output
    }
    Else
    {
        Write-Output "$($Day)$($Hour)$($Minute)Z$($Month)$($Year)"
    }

<#
.SYNOPSIS
Returns a Navy Date Time Group (DTG)

.DESCRIPTION
Returns a Navy Date Group Group (DTG) and copies it to the clipboard.

.PARAMETER NoClip
Prevents the DTG value from being sent to the clipboard and only
displayed on the screen.

.PARAMETER PassThru
Suppresses all screen output and passes a full Systsem.DateTime object
to the pipeline with the DTG property added.

.EXAMPLE
Get-NAVYDTG
040219ZAUG13
Sent to Clipboard

Displays the DTG and sends it to the clipboard

.EXAMPLE
Get-NAVYDTG -NoClip
040220ZAUG13

Displays the DTG, but dose not copy it to the clipboard.

.EXAMPLE
Get-NAVYDTG -PassThru
 
DateTime    : Sunday, May 05, 2013 7:42:17 PM
Date        : 5/5/2013 12:00:00 AM
Day         : 5
DayOfWeek   : Sunday
DayOfYear   : 125
Hour        : 19
Kind        : Utc
Millisecond : 446
Minute      : 42
Month       : 5
Second      : 17
Ticks       : 635033797374465212
TimeOfDay   : 19:42:17.4465212
Year        : 2013
DTG         : 051942ZAUG13

Passes a System.DateTime object to the pipeline with the DTG
property added.
#>
} # End Function Get-NAVYDTG

We represent the DTG as ZULU or GMT time.  Line 7 converts the local into ZULU time.
Since a proper DTG must have 2 digit representation of the day,hour, and minute components, I needed a way to add a zero to any one of these components should they return a single digit. Lines 9 through 28 provides this assurance.
The month component must be given with only 3 characters and must be represented in upper case.  Line 31 provides this.
Line 38 provides the last 2 digits of the year.
Lines 40-47 send the properly formatted DTG to the clipboard if the –NoClip and –PassThru parameters are omitted. 
If –PassThru is provided, lines 48-54 send the System.DateTime object with a DTG property added to the pipeline.
If –NoClip is provided, line 57 simply writes the DTG to the pipeline.








Wednesday, May 15, 2013

Data Deduplication Demo

Data Deduplication can save valuable amounts of hard drive space.  In today’s cost conscious environments, saving hard drive space can translate into budgets that can be utilized elsewhere.  The question that often pops up is “How much space will data dedup save me?”  Unfortunately, there is no way to make a accurate prediction.  Data dedup works best with static data.  That is because there is no reason to dedup data that changes often. 
The PowerShell code below will generate a few thousand text files that will share a lot of common bit patterns.  This will help to demonstrate some space savings with dedup.
$String
$NewLineIndex = 0

For ($X=0;$X -lt 10000;$X++)
{

    $C1 = [Char]((Get-Random(35)) + 65)
    $C2 = [Char]((Get-Random(35)) + 65)
    $C3 = [Char]((Get-Random(35)) + 65)
    $C4 = [Char]((Get-Random(35)) + 65)

    $String += "$($C1)$($C2)$($C3)$($C4) "

    If ($NewLineIndex -gt 15)
    {
        $Strgin += "`n"
        $NewLineIndex = 0
    }

    $Name = "E:\PS\Files$($X).txt"
    $NewLineIndex++
    $String | Out-File -LiteralPath $Name

}

Executing this code will generate a lot of files, but this will also help make more of a visual impact with this demonstration.  Once this code has executed, we need to install the data deduplcation
Install-WIndowsFeature FS-Data-Deduplication
Once this feature is installed, you need to enable this feature on the volume that is holding your data.  In this case, it is the E: drive.
Enable-DedupVolume –Volume E:
Data deduplication will only work when scheduled and on data that is at least 5 days old.   To make this demo work, we need to set the age requirment to 0 days.
Set-DedupVolume –Volume E: –MinimumFileAgeDays 0
Now we can start a deduplication.  To see our space savings:
Get-DedupStatus –Volume E:
image
Now we can start the deduplication.
image
You can check the status of the deduplication job:

image
And now to see what we got back:
image
This may not seem like a lot of savings for 10,000 files, but then again these were small files for the most part.  You results will vary.  In the end, this could be used as a tactic to free up space on hard drives that are critically short on space.  Also take a look at the File Server Resource Manager for more tools to help identify data that may be able to be moved to offline storage.

Wednesday, May 8, 2013

Retrieve a list of all Disabled Computer Accounts

I just helped a user with a big headache.  The user is new to PowerShell and was working way to hard.  First of all, I am impressed that this administrator was dipping into the .NET framework.  Take a look at his code.

$strfilter = "(&(objectClass=user)(objectCategory=person))"

 

$objDomain = New-Object System.DirectoryServices.DirectoryEntry

 

$objSearcher = New-Object System.DirectoryServices.DirectorySearcher

$objSearcher.SearchRoot = $objDomain

$objSearcher.PageSize = 1000

$objSearcher.Filter = $strFilter

$objSearcher.SearchScope = "Subtree"

$objSearcher.PropertiesToLoad.Add("cn") | Out-Null

$objSearcher.PropertiesToLoad.Add("member") | Out-Null

$objSearcher.PropertiesToLoad.Add("proxyAddresses") | Out-Null

$objSearcher.PropertiesToLoad.Add("displayName") | Out-Null

$objSearcher.PropertiesToLoad.Add("distinguishedname") | Out-Null

$objSearcher.PropertiesToLoad.Add("useraccountcontrol") | Out-Null

 

$users = $objSearcher.FindAll()

foreach ($user in $users)

{

"Testing $($user.properties.item(""distinguishedname""))"

"UAC: $($user.useraccountcontrol)"

if($user.useraccountcontrol -band 2) 

{ write-host -foregroundcolor red "`t account is disabled" } 

ELSE 

{ write-host -foregroundcolor green "`t account is not disabled" }

 

}

 

You have to admin, for a beginner, this is impressive.  The problem is that the Active Directory module would have greatly reduced his efforts.  Here is the code that he was looking for.

Get-ADComputer -Filter 'Enabled -eq $False' |


Select-Object -Property Name, Enabled

A little bit more refined and simpler to both use and understand.  This code also keeps the information as an object so the administrator can execute actions against these objects if needed.