Advanced Windows PowerShell Scripting Video Training

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

Monday, November 6, 2017

How to Compress an NTFS Folder with PowerShell

Yes, I am actually taking a few minutes to write a blog entry.  I was taking a year off but I'm having to much fun teaching my 90th PowerShell class right now.  Here is a quick and easy question.  "How do I compress a folder with PowerShell?"

We have a target of E:\Mod01.  Here is the command.

Get-CimInstance -Query "SELECT * FROM CIM_Directory WHERE Name = 'E:\\Mod01'" |
    Invoke-CimMethod -MethodName Compress

The CIM_Directory class has all that we need.  The filter of 'E:\\Mod01' is not a typo.  We need to escape the backslash with another backslash.  This isolate the target so we do not compress other folders.  This object is passed to Invoke-CimMethod  where we call the Compress method. 

Nice and easy.  Remember to always verify that you are executing methods only against your intended target or really bad things can happen.

Tuesday, August 1, 2017

Adding Header Data to CSV Files

Don’t you just hate it when you get partial information?  With information systems, you would think that you would get everything you need.  It is like when I ask one of my Sailors why they are late and I have to ask a half dozen more questions before I get the full story. (Insert rolling eyes).

A neat thing that I do to demonstrate to my advance PowerShell classes is how to make a GUI using SAPIEN PowerShell Studio.  Yes, I know. PowerShell was not supposed to be ran in a GUI.  Try asking a non-technical user to execute something at the command line.  I would rather visit the dentist.  Placing a pretty GUI on top of the code fools them into wanting to use it.

For our stock application, We download information from Yahoo Finance.  Go ahead and execute the code below.

$Sym = "Msft"
$URL = "$Sym&f=snl1ghjkvw4P2"
Invoke-RestMethod -Uri $URL

"Msft","Microsoft Corporation",72.58,72.49,73.42,55.61,74.42,22184949,N/A,"-0.17%"

As you can see from the output, this is comma delimited information. You also may notice is that we are missing information.  Do you see what is missing? We are missing the header information that tells us the property names.  It is not part of the data received.  No problem.  We are free to create our own property names.  To do this, we are going to use ConvertFrom-CSV and use its parameter –Header.  Reading the help file for ConvertFrom-CSV, we see that we need to provide a comma separated list of property names.  The order of the names need to match the order that the data is received. Just remember that these names are the values of the -Header parameter.  Do not place the entire string in quotes. Provide a normal comma separated.

$Sym = "Msft"
$URL = "$Sym&f=snl1ghjkvw4P2"
$Data = Invoke-RestMethod -Uri $URL
$Data | ConvertFrom-csv -Header Symbol,Name,Ask,Low,High,Low52,High52,Volume,DayChange,ChangePercent

Symbol        : Msft
Name          : Microsoft Corporation
Ask           : 72.58
Low           : 72.49
High          : 73.42
Low52         : 55.61
High52        : 74.42
Volume        : 22185420
DayChange     : N/A

ChangePercent : -0.17% 

Friday, July 28, 2017

New Syntax for Hash Tables with Select-Object

Wow!  I am actually getting around to blogging this year.  This has been an epic year in terms of how busy I have been.  Between speaking at both the PowerShell Conferences in Europe and Asia, but also expanding my client base in Asia and the Pacific islands, I have been busy.  Oh, let’s not forget adding Windows Server 2016 classes, Security+ and my work on the Azure platform. 

OK, yes I have been busy.  At some point, what I have been learning does get spilled over into my classes and this blog.

One of the sticky points with learning PowerShell has been the creation of calculated properties with Select-Object.  To do so you need to use a hash table, associative table, or dictionary.  What you call it depends on your background.   Just remember that most IT pros who take my PowerShell classes are not programmers and have never been trained as such.  Makes for an interesting week for me.  
Below is an example of how we have been teaching calculated properties.
Get-Process |
    Select-Object -Property Name,
            Name = 'VirtMemory'
            Expression = {$PSItem.VM}

You could short hand it as such.

Get-Process |
    Select-Object -Property Name, @{N = 'VirtMemory'; E = {$PSItem.VM}}

Well, that hash table is the problem.  I have not found a way to eliminate it, but I have found a way to write it a different way.

$Hash1 = @{ 'Name' = 'VirtMemory'; Expression = {$PSItem.VM}}
Get-Process | Select-Object -Property Name, $Hash1

The idea is to break it up into multiple parts so the student can focus more on what is going on and less on the confusing syntax used with Select-Object. You can add additional calculated properties as shown below.

$Hash1 = @{ 'Name' = 'VirtMemory'; Expression = {$PSItem.VM}}
$Hash2 = @{ 'Name' = 'WorkMemory'; Expression = {$PSItem.WS}}
Get-Process | Select-Object -Property Name, $Hash1, $Hash2

This technique will not work for all.  If what you are using works for you, then continue to use your technique.  I will be introducing this syntax in my PowerShell class next week in Phoenix to gauge the class response to this technique as a step in their understanding of how to perform this very common task and understand it.

Thursday, June 15, 2017

“The system cannot find the file specified” when Creating a Server-to-Server Replica Group

Today while doing a demonstration on deploying a server-to-server replica group on Windows Server 2016, I had a new error message pop up.

New-SRPartnership : Unable to create replication group RG02, detailed reason: The system cannot find the file specified.

What threw me off was the “The system cannot find the file specified”.  Well, here is the command that I used:

New-SRPartnership `
    -SourceComputerName LON-SVR1 `
    -SourceRGName RG01 _`
    -SourceVolumeName M: `
    -SourceLogVolumeName N: `
    -DestinationComputerName LON-SVR4 `
    -DestinationRGName RG02 `
    -DestinationVolumeName M: `
    -DestinationLogVolumeName N: `

I did not specify any file. I logged into LON-SVR4 and tried to look for the StorgeReplica log.  It was not present.  When I executed Get-WindowsFeature –Name Storage-Replica, it returned a state of InstallPending.  Problem solved.

My code was executed from the other member of the replica.  It rebooted before it told the other server to do so.  My mistake.  I still do not know what file it was referring to, but the reboot needed to complete the installation fixed the problem.

Monday, May 15, 2017

Change in Azure Cmdlets

I know.  Surprise!  I am actually sitting down to do some blogging.  It has been a very busy 2017.  Last week I just returned home from delivering 4 sessions at the PowerShell Conference in Europe.  The organizers did an outstanding job. We all had a great time. 

I like to be prepared before speaking to an audience. A month before the conference, I had everything prepared.  Every line of code was thoroughly tested.  A week before the conference, I took a few days off in Venice, Italy and ran through everything one more time.  All the code worked.  24 hours prior to delivering one of my presentations, I ran through the code again and…. It broke.

I started to receive errors like these two when trying to work with Azure.

Save-AzureRmProfile : The term 'Save-AzureRmProfile' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or
if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ Save-AzureRmProfile -Path $AzureProfile -Force
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Save-AzureRmProfile:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Run Login-AzureRmAccount to login.
    + CategoryInfo          : InvalidOperation: (:) [New-AzureRmResourceGroup], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.NewAzureResourceGroupCmdlet
    + PSComputerName        : localhost

One of the frustrations that we Microsoft trainers have with Azure is how fast it changes.  At one level, this is really good.  It means that Microsoft is actively improving the product.  However, the reason for the code breaking is a bit frustrating.  Below are the two cmdlets have changed.


These cmdlets essentially do the same thing.  I was working with Azure using local background jobs to perform multiple tasks in Azure at the same time.  I fixed this code a few hours before going on stage.  As of this writing, no mention in the online documentation of the old cmdlets about the change.  On another note, the old cmdlets worked when I was not using background jobs. Interesting twist to the story.

Friday, March 24, 2017

Determine which Nodes have UAC Enabled with PowerShell

Wow!  It has been a while since I last blogged.  This has been a very busy year. Between my Navy activities and all the classes that I’m bringing online for clients, I am burning the candle at both ends.

As many of you know, my last day of a PowerShell class is “Project Day”.  One of the projects that I helped out with this week involved determining if UAC was enabled.  Utilizing the template we created in class and a check of the registry, we were able to:
  •         Remote to each client.
  •         Determine if the client was online.
  •         Report back the state of UAC.

I decided to advance the code just a little to make sure everything reports back as an object.  Below is that code.  Wish I had more time to explain it step by step, but I have to keep moving.  I only have 3 weeks to get everything done for PowerShell Conference Europe.

ForEach ($N in (Get-ADComputer -filter *).Name)
    $Obj = New-Object -TypeName PSObject -Property @{
        ComputerName = $Null
        UACEnabled = $Null
        Online = $False
        $Obj.ComputerName = $N
        $Data = Invoke-Command `
                -ComputerName $N `
                -ErrorAction Stop `
                -ScriptBlock {
                    (Get-ItemProperty `
                        -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System `
                        -Name EnableLUA).EnableLUA
        $Obj.Online = $True
        If ($Data = 1) {$Obj.UACEnabled = $True}
        Else {$Obj.UACEnabled = $False}
    Catch {$Online = $False}
    Write-Output $Obj

Wednesday, March 8, 2017

Demonstrating Hashing a File

I’m spending the week in San Diego delivering a Security+ class as part of my Annual Training for the United States Navy.  It has been a few years since my last Security+ class that I delivered to the US Air Force.  Since then, PowerShell has undergone a few version updates.  I decided to leverage one of the new cmdlets to help use demonstrate file hashing.

The group that we are working with this week is sharp and asking some really great questions.  There is a little confusion out there on what a hash is so I’m adding this to both my military and civilian Security+ classes. 


Demonstration of using a hashing algorithm to verify
file integrity.

ITC J. Yoder

Creation Date: March 7, 2017

Slide: Symmetric Algorithms

This code is designed to be ran inside of the PowerShell ISE.
Select the code in each step and press F8.

Tested on PowerShell 5.  Execute the command below to determine your
PowerShell version.

# Step 1 : Create a Folder Structure
New-Item -Path c:\ps\Original -ItemType Directory
New-Item -Path c:\ps\Copy -ItemType Directory

# Step 2 : Create a text file in the original directory.
"This is my text" | Out-File -FilePath C:\ps\Original\Data1.txt

# Step 3 : Create a hash for this file using SHA256
$HashOriginal = Get-FileHash -Path C:\ps\Original\Data1.txt

# Step 4 : View the hash.

# Step 5 : Copy the file.
Copy-Item -Path C:\ps\Original\Data1.txt -Destination C:\ps\Copy\Data1.txt

# Step 6 : Create a hash Of the copy using SHA256
$HashCopy = Get-FileHash -Path C:\ps\Copy\Data1.txt

# Step 7 : Test the hashes for equality.
$HashOriginal.Hash -eq $HashCopy.Hash

# Step 8 : Rename the copy.
Rename-item -Path C:\ps\Copy\Data1.txt -NewName C:\ps\Copy\Data2.txt

# Step 9 : Create a hash Of the renamed copy using SHA256
$HashRenamed = Get-FileHash -Path C:\ps\Copy\Data2.txt

# Step 10 : Test the hashes for equality.
$HashOriginal.Hash -eq $HashRenamed.Hash

# Step 11 : Modify the copy by adding a period to the end.
"This is my text." | Out-File -FilePath C:\ps\Copy\Data2.txt

# Step 12 : Create a hash Of the Modified copy using SHA256
$HashModified = Get-FileHash -Path C:\ps\Copy\Data2.txt

# Step 13 : Test the hashes for equality.
$HashOriginal.Hash -eq $HashModified.Hash

# Step 14 : View the hashes
Write-Host "Original : $($HashOriginal.Hash)" -BackgroundColor DarkGreen
Write-Host "Copy     : $($HashCopy.Hash)" -BackgroundColor DarkGreen
Write-Host "Renamed  : $($HashRenamed.Hash)" -BackgroundColor DarkGreen
Write-Host "Modified : $($HashModified.Hash)" -BackgroundColor DarkRed

# Step 15 : Clean up the disk.
Remove-Item -Path C:\ps\Original -Confirm:$false -Recurse
Remove-Item -Path C:\ps\Copy -Confirm:$false -Recurse

Thursday, February 2, 2017

__error__ in PowerShell ISE

Wow, I finally got around to doing a little blogging.  This has thus far been a very busy year.  I’m actually writing this in the air heading to teach a PowerShell class in North Carolina and prepping my sessions for PowerShell Summit Europe in a few months. 

When I start helping my students debug their issues in class, I often walk up to their monitors and see the error right away.  I am very cautious to point out at the beginning of class that the reason that I see their errors so fast is because I have made the same errors so often.  Here is one that left me scratching my head.

While adding a valid parameter to a function that I was developing, I had this error in the ISE.
I’ll be honest, this was a new one for me.  Looking at the message, I realized that there was a casting of string.  This pointed me to my param block. Sure enough, I placed a comma where I should not have.

You can see it right after [String].  My bad.  Hey, all of us make mistakes.

Wednesday, January 4, 2017

Using functions with PowerShell Background Jobs Part 2 of 2

Yesterday we learned how to use the InitializationScript parameter of Start-Job. Today we are going to allow for further modularization of our code and send multiple functions to Start-Job.  We could very easily send multiple functions in the same script block, but I need to pick and choose which functions I will be working with for each process so I need to be able to send them separately.  Here is our code.

$JobFunction1 = {
 Function Beep
    Param ($Tone)


$JobFunction2 = {
 Function Beep2
    Param ($Tone, $Time)

$InitializationScript = $executioncontext.invokecommand.NewScriptBlock("$JobFunction1 $JobFunction2")

$JobSplat = @{
    Name = "Test1"
    InitializationScript = $InitializationScript
    ArgumentList = 300, 400, 200

Start-Job @JobSplat -ScriptBlock {
            Param($Value, $Tone, $Time)
            Beep -Tone $Value
            Beep2 -Tone $Tone -Time $Time

A lot of the code is the same as yesterday with a few exceptions. Notice that we are creating $JobFunction1 and $JobFunction2.  This allows me to place a single function inside of a script block, but be able to send multiple script blocks to Start-Job.  I am also using the $ExecutionContext automatic variable. According to About_Automatic_Variables, this variable:
Contains an EngineIntrinsics object that represents the execution context of the Windows PowerShell host. You can use this variable to find the execution objects that are available to cmdlets.

OK, that was informative.  It is on object of type System.Management.Automation.EngineIntrinsics. In short, you can create new script blocks with it.  I use it to join the 2 variables containing the script blocks that I need to send to Start-Job. I save the output in a variable called $InitializationScript. This is now the value of –InitializationScript.

When you run this code, you will hear two beeps.

Tuesday, January 3, 2017

Using functions with PowerShell Background Jobs Part 1 of 2

Happy New Year everyone!  I am spending my New Years’ day working on some code that I’ve been putting off while the family has been in town.  Something about shoveling sunshine as opposed to shoveling snow.  I’m working on expanding some Azure code that I have developed and I’m looking at ways to further modularize the code.  Since I use Background jobs with script blocks to speed the processes that I am running, I’m looking at using the InitializationScript parameter of Start-Job.

The InitializationScript parameter allows you to run code before the ScriptBlock parameter runs.  When you include functions inside of it, you are able to place those functions in the same memory that the ScriptBlock will execute in.  Let’s take a look at our code to set this up.

First we create a variable that will hold our function that we will call from the background job.

$JobFunctions = {
 Function Beep
    Param ($Tone)


In this case, we are going to make the console beep.  We are also going to pass the value for the tone of the beep to the parameter $Tone.

Next we will set up a splat for a few of the parameters of Start-Job. Notice that we are setting the value for –InitializationScript to be the $JobFunctions that we created above.  Also, we need to pass a value to what will be our script block.  That value is 300.

$JobSplat = @{
    Name = "Test1"
    InitializationScript = $JobFunctions
    ArgumentList = 300

Now we can run our background job.

Start-Job @JobSplat -ScriptBlock {
            Beep –Tone $Value

First off we call our splat and then we call our script block.  We need to pass the value of 300 to the script block so we created a parameter called $Value. In other words $Value = 300.  Next we call the function that we sent to the initialization script.  The function name is Beep it has a parameter called –Tone.  We provide it the value of 300 that is contained in $Value.  Once this runs, you will hear a beep.

Tomorrow we will send multiple functions to our background job.