Tag Archives: ForEach-Object

Remove and Add Users to an Active Directory Group

The more people that recognize that I’ve made an investment in Windows PowerShell, the more these posts write themselves. I was recently asked by a previous colleague to help them make some modifications in Active Directory (AD). They needed to strip out all the users from a single AD group, and then add a bunch of them back that were, conveniently, stored in a text file. While you can compare the current users in the group with the ones you want to add, this post will assume it’s suitable to momentarily remove all the users from a specific AD group.

When I completed this, out of the kindness of my heart, and because I want to promote and teach PowerShell whenever possible, I sent the previous colleague an email with the five steps I had completed. They might be useful for others as well, so here we are. I think I’m getting a lunch out of this, too. While it might not be Chipotle, I like food, so I’m sure it’ll be suitable.

1. Export Users in the AD Group: This command will return the current members of the VPN-Users group and store them in a Csv file. While this will allow us to compare our final results, its original purpose was as my safety net in case I made a mistake and wanted to make the previous members, members again.

PS> $GroupName = 'VPN-Users'
PS> Get-ADGroupMember -Identity $GroupName | Export-Csv -Path C:\VPN-Users01Pre.csv -NoTypeInformation

2. Remove Users from Group: This next command removes all the members of the group (which I had checked were only users). It did this by piping each member of the group to a ForEach-Object loop that ran Remove-ADGroupMember against the current user. Had there been other object types, you’d have to use a Where-Object command in between these two commands (command 1 | Where-Object… | command 2), filtering on the ObjectClass. The Remove-ADGroupMember cmdlet is using the -Confirm parameter with the $false value, so there’s no need for manual confirmation of each removal.

PS> Get-ADGroupMember -Identity $GroupName | Foreach-Object {Remove-ADGroupMember -Identity $GroupName -Members $_ -Confirm:$false}

3. Add Users to Group: After I allowed a few moments for replication, I went ahead and added all the users in the text file I had been supplied by using the Get-Content command and piping each entry to the ForEach-Object and Add-ADGroupMember cmdlets. It threw two errors when it wasn’t able to find a couple users in AD. While I didn’t, we could’ve written in precautions in this command to avoid this error, or programmatically fixed the file prior to this command.

PS> Get-Content -Path C:\UserNames.txt | Foreach-Object {Add-ADGroupMember -Identity $GroupName  -Members $_}

4. Export Users in the AD Group: Now that we have all the old group members removed, and the new ones added, we can create a new export of the group members for comparison. This isn’t necessary, but helps to see who was removed and added, if we desire that kind of information and comparison.

PS> Get-ADGroupMember -Identity $GroupName | Export-Csv -Path C:\VPN-Users02Post.csv -NoTypeInformation

5Compare Before and After Group Memberships: The next command will reach into the SamAccountName column inside both Csv files and compare them. Results that indicate <= mean they are in the file on the left (the file as the value to -ReferenceObject parameter), and results that indicate => mean they are only in the file on the right (-DifferenceObject parameter). Don’t forget, you can always use the -IncludeEqual parameter if you want to see which users where there before and after.

Compare-Object -ReferenceObject ((Import-Csv -Path C:\VPN-Users01Pre.csv).SamAccountName) -DifferenceObject ((Import-Csv -Path C:\VPN-Users02Post.csv).SamAccountName)

There ya go. The previous colleague said they had a problem finding some good examples of doing this online, so here’s to hoping that this will help someone when it they need it. Just maybe, I’ll be back with the right way to do this, where the users that are going to be added back, never get removed in the fist place.

Automate App Server (Visual) Website Test

To visually determine if a website is loading slowly, or not, you might open the browser, enter the URL, and note the speed at which the page loads. Well, since I write tools to speed things up, writing one for this need, seemed to make sense. Part of the work was already done, thanks to the little function below that I had already written. It’s a function called Open-InternetExplorer and you can likely guess what it does. If you feed it a URL as the value for the -Url parameter, it’ll open up that webpage, otherwise, it’ll just open to the default homepage.

Set-Alias -Name iexplore -Value Open-InternetExplorer
Function Open-InternetExplorer {
    Param ([string]$Url)
 
    If ($Url) {
        Start-Process -FilePath iexplore $Url
    } Else {
        Start-Process -FilePath iexplore
    }
}

If you didn’t already notice, I’ve also included an alias be set for this function. With that in place, I can also enter iexplore to invoke the Open-InternetExplorer function. Notice that the Start-Process cmdlets inside the function include iexplore as the value for the -FilePath parameter. This is the Internet Explorer executable, and not the function alias.

Okay, so now we know how we can quickly open a new IE browser and determine what webpage to load. Let’s assume the page I want to check is http://appsrv01.subdomain.mydomain.com. This means, I can enter Open-InternetExplorer http://appsrv01.subdomain.mydomain.com and IE will open to that webpage (on that server). I may use a load balanced, front end URL for users to access the application, such as http://myapp.subdomain.mydomain.com. While the app is important (as it is one of the servers), knowing that all the app servers are responding and loading the page quickly is helpful.

For the remaining examples, let’s assume I have a variable called $MyAppServers that contains the default properties for five servers, returned from the Get-ADComputer cmdlet.

PS> $MyAppServers = Get-ADComputer -Filter * -SearchBase 'OU=MyApp,DC=subdomain,DC=mydomain,DC=com'
PS> $MyAppServers.Name
appsrv01
appsrv02
appsrv03
appsrv04
appsrv05

Now, let’s combine the $MyAppServers variable, with the function (and the ForEach-Object cmdlet, as the function wasn’t written to accept multiple URLs), so that we can open each app server’s webpage. This will allow us to determine if any of the pages load slower than any others, or more importantly, to determine if a page doesn’t load at all. Here’s how we do that.

PS> $MyAppServers.Name | ForEach-Object {Open-InternetExplorer -Url "$_.subdomain.mydomain.com"}

Once this command is run, it’ll open an Internet Explorer browser window for each app URL. In my case (Windows 8.1), I can hover over the IE taskbar items and view a small image of each IE window. Notice in the image below that one of the application servers didn’t load. I’ll need to look into that server!

automate-app-server-visual-website-test01

While I haven’t yet, the command above could be added to its own function, so I don’t have to type it out each time, and instead can simply enter the function’s name, or alias. In closing, I’ll mention another small function I added to my profile. This one will dump all running instances of Internet Explorer, allowing me to avoid manually closing each IE window, or typing out the Get-Process–Stop-Process command that the function runs.

Set-Alias -Name diexplore -Value Stop-InternetExplorer
Function Stop-InternetExplorer {
    Get-Process -Name iexplore | Stop-Process
}

Proving PowerShell’s Usefulness to Newbies, Part I

Months ago, I started writing a list of ways to potentially impress PowerShell newbies for a presentation (I’ve yet to ever give). Since I’m not sure if I’ll be up in front of a crowd of PSNewbies any time soon, I figured I would share them here. Consider these when you need to impress the PowerShell non-believers.

Part I
The power in PowerShell has been said to be a number of different things, by a number of different people. In today’s topic, the power is speed and accuracy. We’re going to automate the creation of 10,000 directories (a.k.a. folders). That’s no small task if you’re doing it manually.

Beginning in the example below, we’ll start by creating a single directory to hold the other 10,000 directories, by running a New-Item command. Once that’s finished, which is practically instantaneous, we’ll run second command to create the 10,000 new folders. We’ll do this by using a range operator (..) and the numbers 1 and 10000, piping each of those numbers and the ones in between, to that second New-Item command.

Notice the $_ variable, as this has the tendency to cause some confusion. This variable holds the value of the current object that’s entered, or crossed, the pipeline — you pick your visual. In this case, it represents the current number from within our range of numbers. The first time this command runs, $_ will be set to 1, the second time this runs it will be set to 2, and the last time it runs, it will be set to 10000. I should note, that in PowerShell 3.0, the $PSItem variable was introduced. It can be used in place of $_ in version 3.0 and above.

PS> New-Item -Path C:\Testing -ItemType Directory

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/6/2016   9:52 PM            Testing

PS> 1..10000 | ForEach-Object {New-Item -Path "C:\Testing\$_" -ItemType Directory}

    Directory: C:\Testing

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/6/2016   9:52 PM            1
d----          1/6/2016   9:52 PM            2
d----          1/6/2016   9:52 PM            3
d----          1/6/2016   9:52 PM            4
d----          1/6/2016   9:52 PM            5
d----          1/6/2016   9:52 PM            6
d----          1/6/2016   9:52 PM            7
d----          1/6/2016   9:52 PM            8
d----          1/6/2016   9:52 PM            9
d----          1/6/2016   9:52 PM            10
...

I haven’t included all 10,000 results, for good reason, but as you can see, the moment the command was entered into the PowerShell host, it began to create the new folders. This would be the time at which you retie your shoes. The example further below indicates the command takes about 10 seconds to complete. Depending on your sneakers, if you’re wearing those, you might actually take longer. If you don’t have laces, take a drink of something, or simply sit there in amazement at all the work you’re not really doing. It might actually be too fast, once you’ve realized how much time you just freed up.

A little note about Measure-Command used below: Typically this cmdlet will internally measure things and not really do much that you can actually see. In this case, it’s actually going to create the folders as part of the measurement. To me, it makes sense that it wouldn’t be able to measure this unless it really creates the folders. On that note, if you ran the command above, you’ll want to delete the existing folders, if you want to measure how long it takes to create the folders. The New-Item cmdlet can’t create folders that already exist. To remove the directories, run:  Remove-Item -Path ‘C:\Testing\*’ from your PowerShell console.

PS> Measure-Command -Expression {1..10000 | ForEach-Object {New-Item -Path "C:\Testing\$_" -ItemType Directory}}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 10
Milliseconds      : 293
Ticks             : 102939103
TotalDays         : 0.000119142480324074
TotalHours        : 0.00285941952777778
TotalMinutes      : 0.171565171666667
TotalSeconds      : 10.2939103
TotalMilliseconds : 10293.9103

PS> Remove-Item -Path C:\Testing\*

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 9
Milliseconds      : 793
Ticks             : 97934962
TotalDays         : 0.000113350650462963
TotalHours        : 0.00272041561111111
TotalMinutes      : 0.163224936666667
TotalSeconds      : 9.7934962
TotalMilliseconds : 9793.4962

In the second command above, we actually measured the time it took to remove the directories we created — you saw that command a moment ago. It came in at a faster time than it took to create them, although this won’t always be the case. It’s still quite quick for either operation.

This is a great example of the power in PowerShell, but before I wrap up, let’s see how much time I just saved. Let’s assume it takes me 4 seconds to manually create a folder. To create 10,000 folders, I would need a free 40,000 seconds. To put that into hours, I would need over 11 of them to do this task by hand. We only get one person’s salary, but we should probably get a few.

PS> $SecondsToCreateFolder = 4
PS> $NumberOfFolderToCreate = 10000
PS> $NumberOfFolderToCreate * $SecondsToCreateFolder
40000
PS> $SecondsInMinutes = $MinutesInHours = 60
PS> $SecondsInMinutes
60
PS> $MinutesInHours
60
PS> ($NumberOfFolderToCreate * $SecondsToCreateFolder) / $SecondsInMinutes / $MinutesInHours
11.1111111111111

Thanks for reading this post.

Run Background Commands after Every Command (Part II)

The first part of this post began on PowerShell.org. Start there, and it’ll get you to Part I: http://powershell.org/wp/2015/10/12/run-background-commands-after-every-command.

Recently I shared a small function I wrote to replace my prompt function. In addition to creating the prompt (PS: C\>) each time one command ended, it would run some background commands. In my attempt to take advantage of this function, I added code that would modify the WindowTitle, the text at the top of the console host or ISE, to indicate if there were any background jobs. Today, I’ve improved the function.

The additions I made include all of the following:
– Uses the singular word “job” when there is only one background job.
– Uses the plural word “jobs” when there is more than one background job.
– Adds an asterisk (*) when any job is actively running.
– Adds a plus sign (+) when any job has more data.

Here’s the full, updated function, and some sequential images beneath that. I’ve dumped the nested If statement for a switch statement, improving the function’s readability. Before you get into this, there’s something to consider. The indicators are only as accurate as the last time the prompt function was invoked. They won’t update without the user pressing the Enter key. That said, it’s quite convenient that you can determine if a background job is still running by pressing Enter. You don’t even have to type anything.

Function Prompt {
    If (-Not($OriginalTitle)) {
        $Global:OriginalTitle = $Host.UI.RawUI.WindowTitle
    }

    If (Get-Job) {
        $Job = Get-Job
        Switch ($Job) {
            {$Job.State -eq 'Running'} {$State = '*'}
            {$Job.HasMoreData -eq $true} {$MoreData = '+'}
            {$Job.Count -eq 1} {$Host.UI.RawUI.WindowTitle = "[$($State)Job$($MoreData)] $OriginalTitle"; break}
            {$Job.Count -gt 1} {$Host.UI.RawUI.WindowTitle = "[$($State)Jobs$($MoreData)] $OriginalTitle"}
        }
    } Else {
        $Host.UI.RawUI.WindowTitle = $OriginalTitle
    }

    "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
}

Run-background-commands-after-every-command-PartII-01

Run-background-commands-after-every-command-PartII-02

Run-background-commands-after-every-command-PartII-03

Run-background-commands-after-every-command-PartII-04

Update: After looking at this a few more times, I decided to move the asterisk indicator (running jobs) to the back of the word Job, or Jobs, next the plus sign indicator (has more data). I thought it looked better. The code changes I made, are seen below, and a comparison image is down there, too. Cheers!

Function Prompt {
...
            {$Job.Count -eq 1} {$Host.UI.RawUI.WindowTitle = "[Job$($State)$($MoreData)] $OriginalTitle"; break}
            {$Job.Count -gt 1} {$Host.UI.RawUI.WindowTitle = "[Jobs$($State)$($MoreData)] $OriginalTitle"}
...
}

Run-background-commands-after-every-command-PartII-05

Report on Active Directory Objects in Abandoned Organizational Unit

Before we really start this post, I should mentioned that there’s no reason that the script discussed in this post can’t be run against an Organizational Unit (OU) that hasn’t been abandoned. It just worked out that I wrote the script in order to determine if an OU had be abandoned.

I threw a small script together in the last couple days and thought I’d share it. The reason for the script was because I may have had an Active Directory (AD) OU that was no longer being used. In order to determine if this was really the case, I wanted to check various properties on the user and computer objects in the OU, to include any nested OUs. These properties included the last logon time stamp, the last date the objects changed, and a few others.

The first couple of lines in the script set two different variables. The first one stores the Domain’s Distinguished Name, and the second one is assigned the location of the abandoned OU. The second variable is based partly on the first. This script requires the ActiveDirectory module and assumes it’s being run on PowerShell 3.0 or greater, as the AD module isn’t explicitly imported.

$DomainDN = ((Get-ADDomain).DistinguishedName)
$AbandonedOU = Get-ADObject -Filter * -SearchBase "OU=Finance,OU=Departments,$DomainDN"

In the next part of the script, we start to send the $AbandonedOU variable’s objects across the pipeline, to the Foreach-Object cmdlet. As each object passes across, we determine what type of AD object we’re dealing with. If it’s a user object, we set the $Command variable to the string, Get-ADUser. If it’s a computer object we set the $Command variable to the string, Get-ADComputer. If it’s neither, such as a nested OU, we’ll return to the $AbandonedOU variable and send the next object without assigning anything to the $Command variable (or running any of the upcoming code).

$AbandonedOU | ForEach-Object {
    If ($_.ObjectClass -eq 'user') {
        $Command = 'Get-ADUser'
    } ElseIf ($_.ObjectClass -eq 'computer') {
        $Command = 'Get-ADComputer'
    } Else {
        return
    }

Providing we have a user or computer AD object, we’ll run the code in the next example. This will execute the cmdlet, whether it be Get-ADUser or Get-ADComputer, returning the requested properties that we then calculate (think, customize).

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
}

Finally, we sort the collection of objects we’ve returned and customized, and in my case, pump the data out to a CSV file at the root of my C:\ drive. As you’ll see below, I’ve included both the code in the previous example and the additional code.

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
} | Sort-Object 'Last Logon TimeStamp' -Descending | Export-Csv -Path C:\AbandonedOU.csv -NoTypeInformation

I want to mention something about the line above that calculates the “Added To Domain By” property. In many environments this is going to only be <DOMAIN>\Domain Admins. The reason I added this, is because in the AD environment in which this ran, users, other than the Domain Admins, can join computers. I know this is a default; however, in many environments it is not allowed. This may or may not be a helpful property in your environment.

Cheers, and thanks for reading! I’ve included the complete script below.

$DomainDN = ((Get-ADDomain).DistinguishedName)
$AbandonedOU = Get-ADObject -Filter * -SearchBase "OU=Finance,OU=Departments,$DomainDN"

$AbandonedOU | ForEach-Object {
    If ($_.ObjectClass -eq 'user') {
        $Command = 'Get-ADUser'
    } ElseIf ($_.ObjectClass -eq 'computer') {
        $Command = 'Get-ADComputer'
    } Else {
        return
    }

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
} | Sort-Object 'Last Logon TimeStamp' -Descending | Export-Csv -Path C:\AbandonedOU.csv -NoTypeInformation

Get Multiple Percentages of Multiple Values

Before my parents decided to trade in their truck at the dealership this week, we began to discuss the possibility of me selling it for them. Back when we started the discussion, I was thinking about possible prices for the truck, and the payout for me. Go figure, but, much like other things, I was able to incorporate Windows PowerShell into this, too.

Had this actually happened, we were going to need to determine how much I was going make in this deal. Here’s how I saw the options:

– They indicate an amount they want for the truck, and everything over that amount is mine.
– They give me a set dollar amount to sell the truck, regardless of the purchase price.
– They agree to a percentage of the final purchase price.

This last option was the one that had me flip over to my PowerShell console. You see, I often use the console as my calculator. I’m faster at typing out my math problems there, than I am using the built-in calculator in Windows.

Let’s use the possible purchase prices of $12,500, $13,000, $13,500, $14,000, and $14,500. In case you’re interested, it was a 2005 F150 SuperCrew Cab with under 70,000 miles — you read that right, less than 7,000 miles per year. What I wanted to determine was how much my parents and I would each make if I received 1%, 2.5%, 5%, 7.5%, 10%, and 12% of each of the possible purchase prices. Enter PowerShell.

The first thing I did was pipe each of the possible purchase prices to the ForEach-Object cmdlet and write their values to the screen. The results indicated that each value was properly crossing the pipeline.

12500,13000,13500,14000,14500 | ForEach-Object {
    Write-Output -Verbose $_
}
12500
13000
13500
14000
14500

Next, I created a variable for each percentage, based on a calculation. On the way into the loop, it would multiply the possible purchase price, of that iteration, by the percentage amount and store it in the proper variable. Notice that I have variables with dots (or periods, or decimals) in them, such as ${7.5Percent}. In order to use something other than a letter, number, or underscore, we need to wrap the variable in curly brackets. It’s not recommend to use variable with anything other than letters and numbers, but I thought it made sense in this case.

12500,13000,13500,14000,14500 | ForEach-Object {
    $12Percent = $_ * .12
    $10Percent = $_ * .1
    ${7.5Percent} = $_ * .075
    $5Percent = $_ * .05
    ${2.5Percent} = $_ * .025
    $1Percent = $_ * .01
}

After these variables were assigned, I created a header and footer that helped to separate the results from one another. Remember, each iteration through the loop prints these two lines of asterisks. Notice that the top row will include the current possible purchase price in the middle. The colors will also help separate the different purchase prices and the percentages.

12500,13000,13500,14000,14500 | ForEach-Object {
    $12Percent = $_ * .12
    $10Percent = $_ * .1
    ${7.5Percent} = $_ * .075
    $5Percent = $_ * .05
    ${2.5Percent} = $_ * .025
    $1Percent = $_ * .01

    Write-Host -Object "*****$_*****" -ForegroundColor Green

    Write-Host -Object '***************' -ForegroundColor Green
}

Here’s how this displayed at this point.

Get-Multiple-Percentages-of-Multiple-Values00

In this next example, you can see where included the 90% value and 10% value of each possible purchase price. To determine these two values, we subtracted our 10% amount from the full purchase price (for 90%). The second value was already determined as part of setting the variable at the beginning of the loop, so we echoed what was already in the variable.

12500,13000,13500,14000,14500 | ForEach-Object {
    $12Percent = $_ * .12
    $10Percent = $_ * .1
    ${7.5Percent} = $_ * .075
    $5Percent = $_ * .05
    ${2.5Percent} = $_ * .025
    $1Percent = $_ * .01

    Write-Host -Object "*****$_*****" -ForegroundColor Green

    Write-Host -Object "90%: $($_ - $10Percent)" -ForegroundColor Yellow
    Write-Host -Object "10%: $10Percent" -ForegroundColor Gray

    Write-Host -Object '***************' -ForegroundColor Green
}

Here’s what it looked like when the code above was run.

Get-Multiple-Percentages-of-Multiple-Values01

Here’s the final code and results.

12500,13000,13500,14000,14500 | ForEach-Object {
    $12Percent = $_ * .12
    $10Percent = $_ * .1
    ${7.5Percent} = $_ * .075
    $5Percent = $_ * .05
    ${2.5Percent} = $_ * .025
    $1Percent = $_ * .01

    Write-Host -Object "*****$_*****" -ForegroundColor Green

    Write-Host -Object "87.5%: $($_ - $12Percent)" -ForegroundColor Yellow
    Write-Host -Object "12.5%: $12Percent" -ForegroundColor Gray

    Write-Host -Object "90%: $($_ - $10Percent)" -ForegroundColor Yellow
    Write-Host -Object "10%: $10Percent" -ForegroundColor Gray

    Write-Host -Object "92.5%: $($_ - ${7.5Percent})" -ForegroundColor Yellow
    Write-Host -Object "7.5%: ${7.5Percent}" -ForegroundColor Gray

    Write-Host -Object "95%: $($_ - $5Percent)" -ForegroundColor Yellow
    Write-Host -Object "5%: $5Percent" -ForegroundColor Gray

    Write-Host -Object "97.5%: $($_ - ${2.5Percent})" -ForegroundColor Yellow
    Write-Host -Object "2.5%: ${2.5Percent}" -ForegroundColor Gray

    Write-Host -Object "99%: $($_ - $1Percent)" -ForegroundColor Yellow
    Write-Host -Object "1%: $1Percent" -ForegroundColor Gray

    Write-Host -Object '***************' -ForegroundColor Green
}

Get-Multiple-Percentages-of-Multiple-Values02

In the results, above, I’ve only included the first three possible purchase prices, to save space. While all of this could have been figured out with a calculator, there’s no possible way it could have been done as quickly — at least for someone that knows PowerShell. I didn’t bother to create an object in this instance, although it would have made sense. In my mind this wasn’t going to be reused any more than it was this one time, and so I didn’t add the additional effort.

I hope this is helpful for someone. Cheers.

Using the Range Operator for Calculating Total Push-Ups

In December of 2014, I decided that my life in 2015 was in need of some push-ups. Instead of just starting with 10 a day, or some other arbitrary number, I thought I would do as many push-ups a day as it was the day in the year. This meant that on day one (January 1, 2015), I would do one push-up and on day two, I would do two push-ups, and so on. Today is the 20th day of the new year, and so I’ll have to do 20 tonight. I wanted to know how many push-ups I will have done by January 31st. Being the Windows PowerShell hobbyist that I am, I enlisted PowerShell to do my calculations for me.

I started with a variable, $EndDay, and the range operator (..). The combination of the two provides me an integer array of the days in January, such as 1..$EndDay (or, 1..31). Using this, I can calculate how many total push-ups I will have done by the end of the day on January 31st. The example below sets up the integer array, as well as the ForEach-Object loop where we’ll do our calculations. Note: I’m using the ForEach-Object alias, foreach.

$EndDay = 31
1..$EndDay | foreach {

}

The first thing we do, below, is include a second variable, $PushUps, that will collect the total number of push-ups for the month. We’ll use the += assignment operator. This operator takes whatever is already in $PushUps, and adds to it. If the current value stored in $PushUps was 1, and we used the += assignment operator like so, $PushUps += 2, then the value in $PushUps would be 3 (1 + 2 is equal to 3). If we used the standard assignment operator (=), then $PushUps would be 2, as 1 would be overwritten.

On the next line, below, we write some information on the screen. We write the current day: that’s the current number from the integer array represented by $_ (as of PowerShell 3.0, $_ can be represented as $PSItem). Then we write out the total number of push-ups completed by that day: $PushUps.

$EndDay = 31
1..$EndDay | foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
}

I noticed that when I reran the code in the ISE, that the value of $PushUps was incorrect on the second run. This is because the variable already exists, and by the end of the first run already contains 496—the number of push-ups I’ll have done by the end of January! Therefore, I added an If statement that removed the $PushUps variable when $_ was equal to $EndDay. This happens on the final run through the foreach.

$EndDay = 31
1..$EndDay| foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
    If ($_ -eq $EndDay) {
        Remove-Variable PushUps
    }
}

If you change the value for $EndDay to 365, you’ll be able to determine that after December 31st (if I can somehow keep this up) I will have done 66,299 total push-ups for the year. It’s hard to imagine that I could do 365 push-ups at once, but then again, it’s hard to imagine I’ll get though the rest of the month. Here’s an image that shows the the full results when we run the function above.

Using the Range Operator for Push-Up Calculations

Thanks for reading, and wish me good luck—I’m going to need it.