Category Archives: Quick Learn

PSMonday #45: March 6, 2017

Topic: Reusable Code III

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

Back again.

We’ve made two new changes to the below code. One, we replaced powershell_ise, inside of our Get-Process command, with $Service. Two, we added a new, first line to our code. This command will prompt the user to enter a value. Read-Host will then assign that value to the $Service variable. That value will be used in the remainder of the PowerShell code every place $Service is found (there’s only the one). Now, the code is no longer only good for checking for the powershell_ise process; it’ll check for whatever process is entered.

$Service = Read-Host -Prompt 'Enter a Process Name'
    Get-Process -Name $Service |
        Select-Object -Property @{N='Process Name';E={$_.Name}},
        Description,
        Company,
        @{N='Shared Memory';E={"$([Math]::Round($_.WorkingSet / 1MB)) MB"}},
        @{N='Private Memory Size';
            E={"$([Math]::Round($_.PrivateMemorySize / 1MB)) MB"}}

If you were to open notepad, you could use this code to return its process information. So let’s do that; let’s say we have notepad open and we run our code.

Enter a Process Name:

Enter a Process Name: notepad

Process Name        : notepad
Description         : Notepad
Company             : Microsoft Corporation
Shared Memory       : 9 MB
Private Memory Size : 1 MB

Our few lines of code just became useable against any running process on the computer. To be as thorough as possible, we need to consider what happens when we enter a process that isn’t actually running — what’s it going to do? Let’s close notepad and find out.

Enter a Process Name: notepad

Get-Process : Cannot find a process with the name "notepad". Verify the process name and call the cmdlet again.
At line:3 char:1
+ Get-Process -Name $Service |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (notepad:String) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand

Get-Process doesn’t handle this gracefully, so let’s make a few more changes. We’ll first add a try-catch. The try portion will wrap the Get-Process and Select-Object commands. While it isn’t always necessary, we need to add the ErrorAction parameter name, with the Stop parameter value to Get-Process. This is required in order to prevent this error from being displayed. Forcing a terminating error isn’t always required, so don’t add it to commands when it’s not necessary. You try it without -ErrorAction. If it doesn’t work, you try -ErrorAction SilentlyContinue, and if that doesn’t work, you use -ErrorAction Stop, as we’ve done here.

The catch portion of our try-catch wraps a newly added, Write-Warning command that will gracefully indicate when a process isn’t running, or perhaps, just wasn’t spelled correctly.

$Service = Read-Host -Prompt 'Enter a Process Name'

try {
    Get-Process -Name $Service -ErrorAction Stop |
        Select-Object -Property @{N='Process Name';E={$_.Name}},
            Description,
            Company,
            @{N='Shared Memory';E={"$([Math]::Round($_.WorkingSet / 1MB)) MB"}},
            @{N='Private Memory Size';
                E={"$([Math]::Round($_.PrivateMemorySize / 1MB)) MB"}}
} catch {
    Write-Warning -Message "Cannot locate the $Service process."
}


Here’s what happens now, when you enter a process that isn’t running, or doesn’t even exist.


Enter a Process Name: notepad
WARNING: Cannot locate the notepad process.

Enter a Process Name: asdf
WARNING: Cannot locate the asdf process.

That’s it for this Monday. Keep paying attention; it’s about to get good.

PSMonday #44: February 27, 2017

Topic: Reusable Code II

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

In today’s PSMonday, we’ll jump right back into calculated properties, as we continue to create some reusable code. Last week our final addition was to use a calculated property to modify the name of a property, as can be seen below. We took its default value of “Name,” and changed it to “Process Name.”

Get-Process -Name powershell_ise |
    Select-Object -Property @{Name='Process Name';Expression={$_.Name}},Description,Company,WorkingSet,PrivateMemorySize

Today we’ll add two more calculated properties, but before we do, let’s reformat this code a bit, so it fits better. First, we’re going to use “N” instead of Name and “E” instead of Expression, for the keys in our calculated property hash table. Second, we’ll take advantage of the commas between our  properties which allows each of them to sit on their own line. We could have put a space after each comma and left them on the same line as the first property, and it would have wrapped them for us; however, we’re about to add those other calculated properties.

Get-Process -Name powershell_ise |
    Select-Object -Property @{N='Process Name';E={$_.Name}},
        Description,
        Company,
        WorkingSet,
        PrivateMemorySize

The WorkingSet and PrivateMemorySize values are being reported in bytes. Let’s modify that, beginning with the WorkingSet property. Take a look at the newest changes, and we’ll discuss them after the example and its results.

Get-Process -Name powershell_ise |
    Select-Object -Property @{N='Process Name';E={$_.Name}},
        Description,
         Company,
        @{N='Shared Memory';E={"$([Math]::Round($_.WorkingSet / 1MB)) MB"}},
        PrivateMemorySize

Process Name      : powershell_ise
Description       : Windows PowerShell ISE
Company           : Microsoft Corporation
Shared Memory     : 420 MB
PrivateMemorySize : 405536768

As you can see in the above example, we first changed the term “WorkingSet” to “Shared Memory.” This time, however, we also modify the value it displays using the Expression key-value pair. We won’t go into this too deep, other than to say we first, divided our Working Set value by 1 MB, as we wanted to report our values in megabytes. Second, we directly accessed .NET to round our value using a Round method, and finally, we added the string “MB” to the end of the value to make it clear what measurement we’re using.

Next, we’ll add a third calculated property, but this time to the PrivateMemorySize property. This will allow it to function just like the modified WorkingSet property. As you can likely tell, I’ve used more lines than is necessary, in order that this best fits.

Get-Process -Name powershell_ise |
    Select-Object -Property @{N='Process Name';E={$_.Name}},
        Description,
        Company,
        @{N='Shared Memory';E={"$([Math]::Round($_.WorkingSet / 1MB)) MB"}},
        @{N='Private Memory Size';
            E={"$([Math]::Round($_.PrivateMemorySize / 1MB)) MB"}}

Process Name        : powershell_ise
Description         : Windows PowerShell ISE
Company             : Microsoft Corporation
Shared Memory       : 420 MB
Private Memory Size : 387 MB

Okay, let’s stop here and pick up from this point next Monday.

PSMonday #43: February 20, 2017

Topic: Reusable Code I

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

After I type the same thing a few times, and suspect I’ll need, or want, to continue to do that, I’ll typically write a function for it, so that I never have to type it again. That’s the purpose of a function. It’s a container for a series of commands that I can run, or invoke, simply by entering its name. Today we’ll start a four-part PSMonday on how to reuse code from beginning to end. As we close up PSMonday, these last several are going to be the ones in which to really pay attention.

Let’s start by entering a simple, Get-Process command, in order to determine whether or not the PowerShell ISE — the PowerShell Integrated Scripting Environment — is currently running.

Get-Process -Name powershell_ise | Select-Object -Property ProcessName,Id

ProcessName      Id
-----------      --
powershell_ise 5732

In the above example, we used Select-Object to filter the properties that are returned by Get-Process. The ISE is running, otherwise we would’ve received an error, indicating that the process could not be found.

Next, let’s pipe the results of an unfiltered, Get-Process command to a mildly modified Get-Member command, and take a look at all the properties of the returned object.

Get-Process -Name powershell_ise | Get-Member -MemberType Property

Here’s another filtered Get-Process command, that only includes the properties that hold information in which I am interested.

Get-Process -Name powershell_ise |
    Select-Object -Property Name,Description,Company,WorkingSet,PrivateMemorySize

Name              : powershell_ise
Description       : Windows PowerShell ISE
Company           : Microsoft Corporation
WorkingSet        : 162627584
PrivateMemorySize : 153522176

Let’s make a change to the Name property using what’s called a calculated property. Among other things, a calculated property allows us to rename a property’s default name to something else.

Get-Process -Name powershell_ise |
    Select-Object -Property @{Name='Process Name';Expression={$_.Name}},Description,Company,WorkingSet,PrivateMemorySize

Process Name      : powershell_ise
Description       : Windows PowerShell ISE
Company           : Microsoft Corporation
WorkingSet        : 162324480
PrivateMemorySize : 153812992

The calculated property looks like this, when it’s all by itself.

@{Name='Process Name';Expression={$_.Name}}

Notice in the above results that Name property is now named, Process Name. The calculated property consists of a hash table, as signified by @{}, that contains two, key-value pairs. One pair includes the Name key, and the second key-value pair includes an Expression key. We’ll continue next week with some additional examples of calculated properties, as we continue to learn about creating reusable code.

PSMonday #42: February 13, 2017

Topic: While

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

The last language construct we’re going to discuss is the While loop — yes, another looping construct. Let’s start with the help file in the first below command, followed by two ways to write the conceptual structure of the While loop.

Get-Help -Name about_While -ShowWindow

While (<condition>){<statement list>}

While (<condition>) {
     <statement list>
}

Our first below example of the While loop, well, it does absolutely nothing. We begin by setting our $Value variable to the numeric value of 1, and then we (attempt) to enter the While loop. The biggest difference between the While loop and the Do loop is that our conditions come before the statement lists. This should be reminiscent of the If, and Switch statement variations, where the evaluations come first.

$Value = 1

While ($Value -eq 5) {
    $Value
    $Value = $Value + 1
}

In the next example, we’ll make one simple change and the code will run. Our condition will evaluate the $Value variable such that it’s $true when $Value is less than or equal to 5. Remember from the For loop, that $Value++ is the same as writing $Value = $Value + 1.

$Value = 1

While ($Value -le 5) {
    $Value
    $Value++
}

1
2
3
4
5

Our final example for the While loop, gives the user an opportunity to try and guess a number between one and five. Before we potentially, enter the While construct, we’ll set the $MagicNumber variable to the random number and the $Continue variable to $true. We enter the While providing that $Continue is equal to $true, and we know it will be, as it was just assigned that value.

Inside the statement list, we (1) request the user enter a number between one and five, and (2) use and If-Else to see if the $MagicNumber matches the guess entered by the user, as stored in $Guess. If it matches, we indicate that, and then set $Continue to $false. This means that the next time the While begins, the condition won’t match and we won’t enter the While. The loop will be over. If $Guess and $MagicNumber don’t match, the While will execute again, and we’ll ask for another number.

$MagicNumber = Get-Random -Minimum 1 -Maximum 5
$Continue = $true

While ($Continue -eq $true) {
    $Guess = Read-Host "Enter the magic number"
    If ($Guess -eq $MagicNumber) {
        "You guessed it: $Guess <--> $MagicNumber"
        $Continue = $false

    } Else {
        Write-Output -InputObject 'Try again.'
    }
}

Enter the magic number: 5
Try again.
Enter the magic number: 4
Try again.
Enter the magic number: 2
Try again.
Enter the magic number: 3
You guessed it: 3 <--> 3

And, that’s it. We’ve seen and learned everything from If to Switch and For to While. As it was stated previously, knowing which language construct to use should be left to your intuition, and a full understanding of a language’s conditional options is the only way that’s going to happen. Next week we’ll start discussing reusable code.

Sync Profile Script from Work to Home

After a few years of this whole I-do-PowerShell-everyday thing, my profile script has become quite the necessity at both work, and home. In fact, there’s much in my work profile script that I want at home, such as my prompt function, among others. With this ever present need, I set off to create a profile script that self-updates between my work and home computer, without any continuing need for me to do it myself.

So, about the profile script, just in case you ended up here and are clueless to that in which I’m referring. There’s a potential .ps1 file that can be created and edited so that every time you open the PowerShell ConsoleHost things run in the background, as the session begins. I use it to initialize variables, to create aliases, to declare functions, and more. In regard to Profiles, here’s an old Scripting Guy article that may be of help if you need it. I may have started there myself a few years ago.

I’ve broken the structure of my profile script template into three logical parts. Have a look at the first section of the template below, and then we’ll discuss it.

$WorkComputer = 'WorkComputer'
$HomeComputer = 'HomeComputer'

Switch ($env:COMPUTERNAME) {

    # Work computer only.
    {$_ -eq $WorkComputer} {

    } # End work computer only.

    # Home computer only.
    {$_ -eq $HomeComputer} {

    } # End home computer only.

    # Work and home computer.
    {$_ -eq $WorkComputer -or $_ -eq $HomeComputer} {
 
    } # End Work and home computer.
} # End Switch.

In lines 1 and 2, we create two variables, $WorkComputer and $HomeComputer. As you might expect, these variables store the computer names of my work, and my home computers. If you choose to use this template, then these variable assignments will require a small bit of manual editing on your own; however, it’s a one time thing, and in my mind, a small price to pay to have your profile synced between two computers.

Following these two variable assignments, is the above Switch statement. This defines what will be available on the two different computers. The first section in the Switch are things I only want available on my work computer, the second section are things I only want available on my home computer only, and the last section is for things that I want available on both my work and home computer. This last section is where I placed my prompt function, for instance, in order that it’s available regardless of where I’m working. I really don’t want to be without it.

At the end of this post, I’ll included the full, uninterrupted code, but for now, let’s have a look at the next section. This section defines the Sync-ProfileScript function.

# Create Sync profile function.
Function Sync-ProfileScript {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [ValidateSet('Source','Destination')]
        [string]$ComputerType,

        [Parameter()]
        [string]$LocalProfileScriptPath = "$env:USERPROFILE\Dropbox\PowerShell\Profile\Microsoft.PowerShell_profile.ps1"
    )

    Begin {
        # Exit if NOT the ConsoleHost.
        If (-Not($Host.Name -eq 'ConsoleHost')) {
            break
        }
    } # End Begin.

    Process {
        If (Test-Path -Path $PROFILE) {
            $CompareFiles = Compare-Object -ReferenceObject (Get-Item $PROFILE).LastWriteTime -DifferenceObject (Get-Item $LocalProfileScriptPath).LastWriteTime -IncludeEqual |
                Select-Object -First 1

            If ([System.Boolean]($CompareFiles.SideIndicator -ne '==')) {

                Switch ($ComputerType) {
                    'Source' {Copy-Item -Path $PROFILE -Destination $LocalProfileScriptPath}

                    'Destination' {
                        Copy-Item -Path $LocalProfileScriptPath -Destination $PROFILE

                        'Profile Script has been updated. Restart ConsoleHost?'
                        Do {
                            $Prompt = Read-Host -Prompt 'Enter r to Restart or c to Cancel'
                        } Until ($Prompt -eq 'r' -or $Prompt -eq 'c')

                        If ($Prompt -eq 'r') {
                            Start-Process -FilePath powershell.exe
                            Stop-Process -Id $PID
                        }
                    }
                } # End Switch.
            } # End If.
        } # End If.
    } # End Process.

    End {
    } # End End.
} # End Function: Sync-ProfileScript.

The purpose of the Sync-ProfileScript function is to copy the profile script to a Dropbox folder, when the ConsoleHost is opened on the work computer. Additionally, it serves to copy the profile script from that same Dropbox folder, when the ConsoleHost is opened on the home computer. Keep in mind that I’ve purposely written this function to always go from the work computer, to the home computer. I don’t plan to make changes in the profile script on the home computer, that I’ll then expect or want, on the work computer.

Let’s cover what’s happening in this function. In lines 1 – 11 we define out Sync-ProfileScript function with two parameters: ComputerType and LocalProfileScriptPath. We use the ComputerType parameter to indicate whether this is the source or destination computer. Source is work and destination is home. The LocalProfileScriptPath parameter points to the profile script (.ps1 file), located in Dropbox. As it has a default value, I don’t have to send in a parameter value when the function is invoked.

Our Begin block serves one quick purpose and that’s to exit the function immediately, if we’re not in the ConsoleHost, and instead inside a host such as the ISE. The magic happens in the Process block. Here’s what we do, in order: (1) Test to see that there’s an actual profile script being used, (2) if there is, compare the LastWriteTime on the profile script file used by the ConsoleHost ($PROFILE), and the file in Dropbox, (3) if they are different, either copy the profile script used by the ConsoleHost to Dropbox, or copy the profile script in Dropbox to the location used by the ConsoleHost. Again, this is dependent on which computer we’re using: work or home.

Let’s stop and consider something from the perspective of the home computer. If I open the PowerShell ConsoleHost there, it’ll potentially download the newest version of the profile script from Dropbox to its place on the filesystem. Great. The problem is that any changes to the profile script won’t be usable until the next time that ConsoleHost is started. I think I could’ve run & $PROFILE, but I skipped that option as I vaguely remember that it didn’t always work for me.

Therefore, I added a bit more code. If the home computer notices a change to the profile script, it’ll indicate that to the user by writing “Profile Script has been updated. Restart ConsoleHost?” and “Enter r to Restart or c to Cancel.” If the user enters “r,” it’ll restart the ConsoleHost loading the newest version of the profile script by default. If the user enters “c,” it’ll cancel the ConsoleHost restart and the newest version of the profile script will not be updated on the home computer. It will, however, be updated the next time a ConsoleHost is opened.

Now, on to the final portion of my profile script. Don’t worry, this part is less involved than the Sync-ProfileScript function.

# Determine if sync counter variable exists.
If (-Not($env:SyncProfileCounter)) {
    $env:SyncProfileCounter = 0

    # Copy profile script to/from Dropbox by calling Sync-ProfileScript.
    If ($env:COMPUTERNAME -eq $WorkComputer) {
        Sync-ProfileScript -ComputerType Source

    } ElseIf ($env:COMPUTERNAME -eq $HomeComputer) {
        Sync-ProfileScript -ComputerType Destination
    } # End If-ElseIf.
} # End If.

This section of the profile script invokes the Sync-ProfileScript function we discussed in the last section. If it’s run on the work computer, it indicates to the Sync-ProfileScript function to copy the profile script to Dropbox. If it’s run on the home computer, it indicates to the function to copy the profile script from Dropbox. We know this already, however. It uses a counter variable stored inside an environmental variable to ensure this If statement doesn’t perpetually run, and therefore perpetually call the Sync-ProfileScript function. I won’t a separate post about that here.

I realize that not everyone is using Dropbox. If you’re using another service, then I highly suspect you can use what you’ve learned here, with them as well. You’ll just need to determine the local path to use, and adjust your profile script accordingly. If someone wants to let me know about OneDrive, that would be great. I’d be more than happy to include that information in this post!

Here’s the complete profile script template. Thanks for your time.

$WorkComputer = 'WorkComputer'
$HomeComputer = 'HomeComputer'

Switch ($env:COMPUTERNAME) {

    # Work computer only.
    {$_ -eq $WorkComputer} {

    } # End work computer only.

    # Home computer only.
    {$_ -eq $HomeComputer} {

    } # End home computer only.

    # Work and home computer.
    {$_ -eq $WorkComputer -or $_ -eq $HomeComputer} {
 
    } # End Work and home computer.
} # End Switch.

# Create Sync profile function.
Function Sync-ProfileScript {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [ValidateSet('Source','Destination')]
        [string]$ComputerType,

        [Parameter()]
        [string]$LocalProfileScriptPath = "$env:USERPROFILE\Dropbox\PowerShell\Profile\Microsoft.PowerShell_profile.ps1"
    )

    Begin {
        # Exit if NOT the ConsoleHost.
        If (-Not($Host.Name -eq 'ConsoleHost')) {
            break
        }
    } # End Begin.

    Process {
        If (Test-Path -Path $PROFILE) {
            $CompareFiles = Compare-Object -ReferenceObject (Get-Item $PROFILE).LastWriteTime -DifferenceObject (Get-Item $LocalProfileScriptPath).LastWriteTime -IncludeEqual |
                Select-Object -First 1

            If ([System.Boolean]($CompareFiles.SideIndicator -ne '==')) {

                Switch ($ComputerType) {
                    'Source' {Copy-Item -Path $PROFILE -Destination $LocalProfileScriptPath}

                    'Destination' {
                        Copy-Item -Path $LocalProfileScriptPath -Destination $PROFILE

                        'Profile Script has been updated. Restart ConsoleHost?'
                        Do {
                            $Prompt = Read-Host -Prompt 'Enter r to Restart or c to Cancel'
                        } Until ($Prompt -eq 'r' -or $Prompt -eq 'c')

                        If ($Prompt -eq 'r') {
                            Start-Process -FilePath powershell.exe
                            Stop-Process -Id $PID
                        }
                    }
                } # End Switch.
            } # End If.
        } # End If.
    } # End Process.

    End {
    } # End End.
} # End Function: Sync-ProfileScript.

# Determine if sync counter variable exists.
If (-Not($env:SyncProfileCounter)) {
    $env:SyncProfileCounter = 0

    # Copy profile script to/from Dropbox by calling Sync-ProfileScript.
    If ($env:COMPUTERNAME -eq $WorkComputer) {
        Sync-ProfileScript -ComputerType Source

    } ElseIf ($env:COMPUTERNAME -eq $HomeComputer) {
        Sync-ProfileScript -ComputerType Destination
    } # End If-ElseIf.
} # End If.

TechNet Wiki Link:
https://social.technet.microsoft.com/wiki/contents/articles/37104.powershell-sync-profile-script-from-work-to-home.aspx

February 2017 Guru Link:
https://social.technet.microsoft.com/wiki/contents/articles/36936.technet-guru-competitions-february-2017.aspx

Three Ways to Set $PSDefaultParameterValues

Although we’ve discussed the $PSDefaultParameterValues before, I wanted to a do a quick recap. I need one place that shows the various ways to set this variable. That’s what this post will do for me, and perhaps you too.

First, however, let’s remind everyone what the $PSDefaultParameterValues variable does for us. It allows us to set a custom, default value for a function, or cmdlet’s parameter. One of the examples I mentioned before, in one of the three posts I’ve written about $PSDefaultParameterValues (1 | 2 | 3), used Get-Help.

This cmdlet includes a ShowWindow switch parameter that will open the full help inside its own GUI window. I tend to use this option a great deal to keep my ConsoleHost clean. In order to keep this post short, I’m just going to write the three ways in which I’m aware that we can set this variable.

$PSDefaultParameterValues.Add('Get-Help:ShowWindow',$true)

$PSDefaultParameterValues = @{'Get-Help:ShowWindow' = $true}

$PSDefaultParameterValues['Get-Help:ShowWindow'] = $true

Oh, Lee Holmes posted a welcome PSDefaultParameterValues addition on Twitter recently. I’ve included that addition below using the three above options, as well. Unlike his example, I moved from three underscores, to two. You’ll see what I mean below, if you haven’t already read the Tweet. With this example in place, the results of all the commands entered will end up in the $__ variable. Again, that’s two underscores. Run a Get-ADUser command, for instance, and you’ll get the results both on the screen, and in the $__ variable up until you run another command that can make use of the OutVariable common parameter.

$PSDefaultParameterValues.Add('Out-Default:OutVariable','__')

$PSDefaultParameterValues = @{'Out-Default:OutVariable' = '__'}

$PSDefaultParameterValues['Out-Default:OutVariable'] = '__'

ConsoleHost to ConsoleHost Variable

I often do things, or learn things, in PowerShell that lead me in the direction of writing a new post. Sometimes it’s just experimenting, and sometimes it’s a part of a bigger project. Well, the latter is why we are here today. A current profile script project of mine — that I’ll post as soon as possible — requires that a new ConsoleHost (that’s the standard blue console screen; not the ISE), open and then the current one close. That part is easy.

The problem is that my new ConsoleHost needs to know if a variable has been set, or not, in the previous ConsoleHost. Here, follow along. Start by opening a new ConsoleHost. Inside that host program, create a variable and assign it some sort of value, as I’ve done in the below example.

PS > $Variable = 'This is my standard variable.'
PS > $Variable
This is my standard variable.

In my above example, I tested that my variable had been properly assigned by returning its value. So far, so good. Now, we’ll run a command to (1) open a ConsoleHost from this ConsoleHost, and (2) exit the original ConsoleHost. The semi-colon is a command separator. It allows me to put two commands on the same line and have them run in succession with a single press of the Enter key. One ConsoleHost closes and a new one opens.

PS > Start-Process -FilePath powershell.exe; exit

Now that we have a new ConsoleHost and our old one has exited, let’s see if our variable is waiting for us.

PS > $Variable
PS > 

Nope. That variable was created in a different PowerShell session, and so there’s no getting it back. It simply doesn’t exist any longer. Without fully thinking it through, I briefly thought to use a globally scoped variable instead. It wasn’t long at all, before I realized what a foolish idea that had been. My ConsoleHost was the global scope. With another ConsoleHost, all I’d have is another, separate global scope. Not helpful. I considered writing the variable to disk, but luckily, I didn’t have to consider that option for long either. I had a better idea; I was going to use an environmental variable. Follow along.

PS > $env:Variable = 'This is my standard, environmental variable.'
PS > $env:Variable
This is my standard, environmental variable.
PS > Start-Process -FilePath powershell.exe; exit
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.

PS > # This is a new PowerShell ConsoleHost.
PS > $env:Variable
This is my standard, environmental variable.
PS > ise
PS > # This is in the PowerShell ISE Host.
PS > $env:Variable
This is my standard, environmental variable.

So, as we’ve seen, the environmental variable will follow us along from ConsoleHost to ConsoleHost to the ISE too, providing we open each of them from an existing host that includes the environment variable. Oh, and if you were wondering, the variable is still with us if we launch a ConsoleHost from the ISE (there’s a button for that). The environmental variable option works beautifully for my upcoming profile script project. I’ll link that from here when it’s up, and you’ll already understand that piece of it!

So it’s been included, the environmental variables are presented as a drive by PowerShell. The following command will show all of these variables. If you see some in there that you can use, then do so. It’s always better to use $env:COMPUTERNAME, than it is to hard code a computer name inside your functions.

Get-ChildItem -Path env:

Name                           Value
----                           -----
ALLUSERSPROFILE                C:\ProgramData
APPDATA                        C:\Users\tommymaynard\AppData\Roaming
CommonProgramFiles             C:\Program Files\Common Files
CommonProgramFiles(x86)        C:\Program Files (x86)\Common Files
CommonProgramW6432             C:\Program Files\Common Files
...
USERNAME                       tommymaynard
USERPROFILE                    C:\Users\tommymaynard
Variable                       This is my standard, environmental variable.
windir                         C:\Windows

Okay, now we’re really done with today’s post, and oh, hey look, there’s our $env:Variable variable again.

PSMonday #41: February 6, 2017

Topic: Do-Until

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

I said I would say it this week too, so let’s get it out of the way: The Do-While and Do-Until loops ensure that the statement list is run at least once. Your code is going to execute at least one time using a Do construct. This is because the condition, that determines whether or not we loop again, isn’t evaluated until the statement list has run once. If you need to ensure your code executes at least one time, then the Do-While and Do-Until variations of the Do loop, may be the language construct you need.

Here’s the conceptual examples of the Do-Until.

Do {<statement list>} Until (<condition>)

Do {
    <statement list>

} Until (<condition>)

Our first below example should look somewhat familiar. We saw it first last week in the Do-While examples. We assign 1 to the $Number variable and then enter the Do loop. We output a string statement and then increment the $Number variable. Then, we check the condition. The condition indicates that we’ll continue to loop until $Number is less than 10. Well, 1 is always less than 10, so we exit the construct. This is a clear example that we always run the statement list at least once in a Do loop, before we check the condition.

$Number = 1

Do {
    Write-Output -InputObject "In the Do loop ($Number)."
    $Number++

} Until ($Number -lt 10)

In the Do loop (1).

Let’s modify this example. We’ll reinitialize the $Number variable back to 1 and start again. This time our condition states that we’ll continue to loop until $Number is greater than 10. As $Number is lower, you can likely guess that we’re going to run through this loop several times. Ten times, to be exact.

$Number = 1

Do {
    Write-Output -InputObject "In the Do loop ($Number)."
    $Number++

} Until ($Number -gt 10)

In the Do loop (1).
In the Do loop (2).
In the Do loop (3).
In the Do loop (4).
In the Do loop (5).
In the Do loop (6).
In the Do loop (7).
In the Do loop (8).
In the Do loop (9).
In the Do loop (10).

In closing out the Do variations, we’ll add a final example. Much like last week, we’ll transition from using numeric values to date times. In this example, we’ll add 10 seconds to the current time. Then, we’ll output a string value and sleep (pause) for a second. Eventually, the current time will be greater than the time when we began; therefore, the condition will be $true and we’ll exit the looping construct.

$Time = (Get-Date).AddSeconds(10)

Do {
    Write-Output -InputObject 'Waiting for time to pass...'
    Start-Sleep -Seconds 1

} Until ((Get-Date) -ge $Time)

Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...
Waiting for time to pass...

Next week is our final language construct — the While loop. By then, you’ll have a full and complete understanding of the various control structures we can use to respond to conditions in PowerShell. In case you hadn’t considered it, learning these concepts — even if it’s with PowerShell — is going to make learning other scripting, and even programming languages, easier to do.

PSMonday #40: January 30, 2017

Topic: Do-While

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

Next up on our language construct journey, is part one of the Do loop: the Do-While. Once you’ve read this week’s, and next week’s PSMonday, be sure to read the full about help topic using the below command.

Get-Help -Name about_Do -ShowWindow

Here’s a couple conceptual examples of the Do-While loop. Of the two, you’ll see me use the second example, in order that my code is easier to read.

Do {<statement list>} While (<condition>)

Do {
    <statement list>

} While (<condition>)

For our first example, we’ll start looping based on the value of a variable. We begin by assigning the numeric value of 1 to the $Number variable. This assignment is done outside of the looping construct. Once set, we begin the loop. On the first pass, we indicate that we’re in the loop, as well as the current value of the $Number variable, we then increase the value of the $Number, and then we check if it is less than (-lt) the numeric value of 10. While this condition is true, we’ll continue to loop, incrementing the value in $Number each time, until the value in $Number variable is no longer less than 10.

$Number = 1

Do {
    Write-Output -InputObject "In the Do loop ($Number)."
    $Number++

} While ($Number -lt 10)

In the Do loop (1).
In the Do loop (2).
In the Do loop (3).
In the Do loop (4).
In the Do loop (5).
In the Do loop (6).
In the Do loop (7).
In the Do loop (8).
In the Do loop (9).

Instead of using a numeric value condition, this time, we’ll use a datetime object instead. Take a look at this next example, and then follow the further below description of each task within the example.

"Current Time: $((Get-Date).ToString())"

$Time = (Get-Date).AddSeconds(10)

"Future Time : $($Time.ToString())"

Do {
    Write-Output -InputObject '--> Waiting for time to pass.'
    (Get-Date).ToString()
    Start-Sleep -Seconds 1

} While ((Get-Date) -lt $Time)

"Current Time: $((Get-Date).ToString())"

Current Time: 1/29/2017 1:46:20 PM
Future Time : 1/29/2017 1:46:30 PM
--> Waiting for time to pass.
1/29/2017 1:46:20 PM
--> Waiting for time to pass.
1/29/2017 1:46:21 PM
--> Waiting for time to pass.
1/29/2017 1:46:22 PM
--> Waiting for time to pass.
1/29/2017 1:46:23 PM
--> Waiting for time to pass.
1/29/2017 1:46:24 PM
--> Waiting for time to pass.
1/29/2017 1:46:25 PM
--> Waiting for time to pass.
1/29/2017 1:46:26 PM
--> Waiting for time to pass.
1/29/2017 1:46:27 PM
--> Waiting for time to pass.
1/29/2017 1:46:28 PM
--> Waiting for time to pass.
1/29/2017 1:46:29 PM
Current Time: 1/29/2017 1:46:30 PM

We began the above example by echoing the current date and time. Then we created the $Time variable and made use of the AddSeconds() method to store the date and time 10 seconds into the future. So we know that time, we echoed it to the screen, as well.

Next, we entered the Do-While construct. Our statement list included writing a string to indicate that we we’re waiting for time to pass, writing the current date and time, and then sleeping for one second. Once these commands were completed, we checked the current date and time as a part of the Do-While’s condition. If the current time was still less than (-lt) ten seconds in the future from when we started ($Time), then we looped again. When that condition was eventually met — when the current time was no longer less than that future time — we exited the Do-While statement and echoed the current time.

We’ll cover the Do-Until next week — a language construct variation that you should probably already be able to figure out based on today’s PSMonday. I’ll say it today and next Monday, as well: The Do loops — both Do-While and Do-Until — ensure that we’ll go through the statement list at least one time. This is to say, that the condition is not evaluated until your code has been executed at least once.

PSMonday #39: January 23, 2017

Topic: For

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

This week we’ll discuss the For loop. The first of the two below commands can be used to open the For help file. Inside the file you’ll find the command layout, as we’ve seen with the other language constructs, and cmdlets. The way I write the For language construct, has been included below.

Get-Help -Name about_For -ShowWindow

For (<init>; <condition>; <repeat>) {
    <statement list>
}

The For loop begins with the For keyword and a set of parenthesis. Inside the parenthesis is an initialization (listed as init above), followed by a semi-colon. Following the initialization, we include a condition. By now, you undoubtedly realize there will always be some sort of required condition within our language constructs. Foll­owing the second semi-colon, we have a way to increment our initialization variable, as an assurance we’ll loop through the For loop as many times as is indicated by our condition. This may make more sense, very shortly.

We’re only going to look at a single example for the For loop, but we’ll be sure to highlight all the key features, as well as fully walk through the example. Take a look at the below example, and then we’ll discuss it thoroughly.

For ($i = 1; $i -le 10; $i++) {
    "The value of the `$i variable is $i."
    Start-Sleep -Seconds 1
}

We begin our For statement by entering the For keyword, followed by a set of parenthesis. To initialize the For construct, we’ll set a variable, $i, to the numeric value of 1, as $i = 1. We could have used a different number, however, it would require some changes in the areas we’ll discuss next. We could’ve use a different variable, too, had we opted to do so.

Our condition, $i -le 10, indicates to continue to loop through this For construct, so long as $i is less than, or equal to, 10. The final value in the parenthesis, $i++, increments the value in $i by 1, for each loop iteration. It’s shorthand for $i = $i + 1. Inside the curly brackets is where we enter our statement(s); this is where we do our work. In our example, we’ll echo a string that includes the value of the $i variable, and then sleep, or pause, for one second. Here’s the results of our example.

The value of the $i variable is 1.
The value of the $i variable is 2.
The value of the $i variable is 3.
The value of the $i variable is 4.
The value of the $i variable is 5.
The value of the $i variable is 6.
The value of the $i variable is 7.
The value of the $i variable is 8.
The value of the $i variable is 9.
The value of the $i variable is 10.

There’s some confusion about when to use a foreach variation (foreach and ForEach-Object), and the For construct. I use For when I know how many times I need to loop, as this is a requirement. If you know this piece of information, then you might consider the For loop over the other two looping constructs we’ve discussed thus far. Think back to foreach and ForEach-Object: the number of times we were going to loop, was never a consideration before we began.

We’ll be back next Monday where we’ll cover another looping construct: the Do-While loop.