Fun With KQL – Extend

Introduction

When dealing with data, it’s not at all uncommon to want to create a new column of data by performing a calculation with two other columns. A common example is taking two stored columns, the purchase price of an item, and its shipping cost, then adding them together to get a column which wasn’t stored in your dataset, the total amount of the sale.

The Kusto Query Language lets you accomplish this through the extend operator. This operator allows you to manifest new columns in your output data, based on calculations.

The samples in this post will be run inside the LogAnalytics demo site found at https://aka.ms/LADemo. This demo site has been provided by Microsoft and can be used to learn the Kusto Query Language at no cost to you.

If you’ve not read my introductory post in this series, I’d advise you to do so now. It describes the user interface in detail. You’ll find it at https://arcanecode.com/2022/04/11/fun-with-kql-the-kusto-query-language/.

Note that my output may not look exactly like yours when you run the sample queries for several reasons. First, Microsoft only keeps a few days of demo data, which are constantly updated, so the dates and sample data won’t match the screen shots.

Second, I’ll be using the column tool (discussed in the introductory post) to limit the output to just the columns needed to demonstrate the query. Finally, Microsoft may make changes to both the user interface and the data structures between the time I write this and when you read it.

Extend Basics

Using extend is pretty simple. Let’s take a look.

We started by doing something that should be familiar if you’ve been reading the other posts in this Fun With KQL series. We piped the Perf table through a where operator to reduce the dataset to only those rows whose CounterName column holds the value of Free Megabytes.

In the Perf table, the amount of free space is stored in the CounterValue column in Megabytes. These days Megabytes are pretty small quantities, most drives these days hold Gigabytes, if not Terabytes of data.

To make the output easier to read, we’d like to have the amount of free space expressed in terms of Gigabytes. The extend operator can be used to accomplish this goal.

In the example we use the extend operator, followed by the name for our new column, FreeGB. Then comes an equal sign, and our calculation. To convert Megabytes to Gigabytes we’ll simply divide the CounterValue by 1000.

Before anyone begins a "well actually" debate in the comments, I’m aware that some argue the mega to giga conversion value should be 1024, others say 1000. To make the examples in this article simple and easy to understand, I’m going to use 1000.

In the output, which I have rearranged using the column pop out on the right, you’ll see I have the original column, CounterValue, as well as the new calculated column, FreeGB. Since it’s a bit hard to read in the screen hot I’ll point out the CounterValue column reads 75,032 megabytes, while the FreeGB column has 75.032 gigabytes.

One last item. You can actually omit the name for a new calculated columns. We could have just used extend CounterValue / 1000 if we wanted. However this will generate the new column with meaningless names like Column1, Column2 and so on. I highly advise giving all your calculated columns a name, even on simple queries.

Creating Multiple Columns with Extend

When using the extend operator, you can create multiple calculated columns at one time, as you can see in the example below.

After entering the first calculated column for FreeGB, I simply use a comma, then enter the calculation to get the amount of free space in terms of Kilobytes and named it FreeKB.

In the output (again rearranged with the column tool) you’ll see the original CounterValue column and both of my new, calculated columns FreeGB and FreeMB.

If you read my article Fun With KQL – Summarize, specifically the section titled Code Formatting Note, you’ll know the formatting of KQL code is extremely flexible. I placed the new calculation on the next line, and put the comma at the start of the line. Kusto would have been just as happy though if I’d put the comma at the end of the FreeGB line, or put everything on a single line.

Repeating a Column with Extend

In our output, we still retained the CounterValue column. It would be nice to have this column appear with the name of FreeMB so it is consistent with the other two column names. We can do this with extend by simply setting a new column name equal to the existing column, CounterValue.

Creating Calculated Values with Text Data

So far all of our calculations using the extend operator have been done with numeric data. It is also possible to perform calculations that concatenate text data.

Text based data is often stored in separate columns, which as humans (or other intelligent species) we consider a single value. A common example is someone’s name. Frequently stored in first name and last name columns, but often users prefer to see this in a single column as full name.

In our Perf data, the ObjectName and CounterName columns are closely associated. In order to reduce the number of columns in our output, it would be nice to combine these into a single column. To do that, Kusto provides the strcat function.

The strcat function accepts two or more parameters. You pass in a list of column names that were passed into the pipe (in this case from the Perf table) as well as static text enclosed in quotation marks, separated by commas.

In this example we pass in three values to strcat. First is the ObjectName column. Next is a static string of a dash, with a space on either side. Finally we pass in the CounterName column.

These are combined using the strcat function, and in the output named ObjectCounter. In the sample output I’ve included the original columns, as well as the new ObjectCounter column.

Conclusion

The extend operator is a powerful tool in your Kusto toolbox. With it you can create new columns for display purposes or as output to the next set of operators in the pipeline.

One question I get asked, with the example of FreeMB you have the new column FreeMB, as well as the original column of CounterValue. How do we remove the CounterValue column from the output since it wouldn’t be needed?

That question will be answered in my next post, Fun With KQL – Project, so stay tuned!

The demos in this series of blog posts were inspired by my Pluralsight courses Kusto Query Language (KQL) from Scratch and Introduction to the Azure Data Migration Service, two of the many courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With KQL – Summarize

Introduction

When data is analyzed, it is seldom done on a row by row basis. Instead, data analysts look at the big picture, looking at total values. For example, the total number of times the disk transfer counter is recorded for a time period may give an indication of disk utilization.

To aggregate these values with KQL, we’ll use the summarize operator.

The samples in this post will be run inside the LogAnalytics demo site found at https://aka.ms/LADemo. This demo site has been provided by Microsoft and can be used to learn the Kusto Query Language at no cost to you.

If you’ve not read my introductory post in this series, I’d advise you to do so now. It describes the user interface in detail. You’ll find it at https://arcanecode.com/2022/04/11/fun-with-kql-the-kusto-query-language/.

Note that my output may not look exactly like yours when you run the sample queries for several reasons. First, Microsoft only keeps a few days of demo data, which are constantly updated, so the dates and sample data won’t match the screen shots.

Second, I’ll be using the column tool (discussed in the introductory post) to limit the output to just the columns needed to demonstrate the query. Finally, Microsoft may make changes to both the user interface and the data structures between the time I write this and when you read it.

Summarize Basics

Let’s first see our query and its results, then we can start breaking it down.

We begin by creating a dataset, taking the Perf table and piping it into our summarize operator.

Next we need to tell what we want to summarize, and what column (or columns) we want to summarize for. Here, we want to create summaries for each CounterName.

And what do we want to summarize? That will be the number of times each CounterName appears in our data, for the default time range of last 24 hours (note the Time range just above the query pane).

In my previous post, Fun With KQL – Count, we covered the count operator. What you see here is different, we are using the count() function. When used in the query, the count function essentially creates a brand new column for the output. The number represents the number of times each value in the by column occurs in the dataset passed into summarize.

The output dataset has two columns. It contains the column name indicated after the by, in this case the CounterName.

The second column is count_, which is the number of rows for each counter. For example, you can see Disk Writes/sec occurred 111,043 times. The Avg. Disk sec/Transfer counter had 105,267 rows in the input dataset.

You can distinguish between the count operator and the count function by the parenthesis. All functions in KQL have parenthesis at the end. Most of the time these won’t contain anything, but on occasion a function will require one or more parameters, extra data the function needs to do its job. The parameters are placed inside the parenthesis.

Summarizing on Multiple Columns

Should you want to create summarizes for the combination of multiple columns it is easy to do so. Just list each column after the by keyword.

In this example, after the by we listed the ObjectName and CounterName columns. In the first row of output, the combination of LogicalDisk and Disk Write Bytes/sec occurred 105,461 times.

The combination of Memory and Available MBytes had 23,823 rows in the incoming dataset (here, the Perf table).

A Count By Any Other Name…

I think we can all agree, the default column name of count_ is pretty darn ugly. It doesn’t give us any clue as to what was being counted.

Ideally we should rename the column to something more meaningful, so let’s do just that.

To rename our count function when it gets to the output, simply create the new column name you want to use, and set it equal to the count function. In this example we chose the name PerfCount. In the output, you can see the column name now reflects our choice.

Code Formatting Note

In the previous example we moved the by down to the next line. I feel this improves readability, in addition to showing the flexibility of formatting in KQL.

To further enhance readability, we could also chose to put each column name on its own line. The list of columns needs to be separated by a comma, but KQL is not picky about where the comma goes. Both of the examples below are valid Kusto queries.

Personally I’m a "commas at the front" kind of person. I think it’s easier to read, lines up nicely, and it is much easier to rearrange the lines.

Whichever form you take though, be consistent. If you work in a company with other KQL developers, then agree on a standard for formatting your KQL queries, document it for everyone to refer to, and stick with it.

This, by the way, should apply to all languages you use: SQL, PowerShell, C#, and so on.

Summarize With Other Aggregations

So far, our examples have used the count() function to perform our summaries. Kusto allows us to summarize with a variety of aggregation functions. For this example, lets use summarize to get the average percentage of free disk space.

First, we take our Perf table and pipe it to the where operator to limit the data to only rows where the CounterName is % Free Space.

Next we pipe into a summarize, where we will aggregate two values. First, we want to get a count of rows which we rename to NumberOfEntries. Next, we want an average free space amount.

To do so we will use the avg function. The avg function requires one parameter, the value (usually a column name) we want to average. Here we want to average the CounterValue column. We’ll give this a meaningful column name of AverageFreeSpace.

In the output you can see our counter name, % Free Space, and that it had 105,657 entries in our dataset. The average amount of free space was 83.45 percent.

I also want to point out that the summarize operator allows us to perform aggregations on multiple columns at the same time. Here, we got a count aggregation, as well as an average.

Bin Basics

Often, it is useful to aggregate our data based on a column, but rather than returning a single value we want to break the results into groups. An extremely common example is dates. We may wish to see the total number of rows entered into our Perf table for each day.

To accomplish this we need to use KQL’s bin function.

As we’ve done in previous examples, we’ll use the count function and rename it to NumberOfEntries in the output. In our examples so far, after the by we have used one or more column names to summarize on.

To bin our data, more formally called bucketization, we use the bin function after the by. It requires two parameters. The first is the column with the data to bin on, the second is how to group the data within that column.

Here, we will bin on the datetime column TimeGenerated. We will then group our data into one day bins, as indicated by 1d. In the output you can see for April 9, we had 3,613,342 rows. For April 10, there wre 306,425 rows of data.

We could have chosen other ways to bin, for example using 1h would have binned the results into hours. 10m would group into 10 minute increments. For quick reference, here’s the most common list of time abbreviations.

Abbreviation Time Unit
d days
h hours
m minutes
s seconds
ms milliseconds
microsecond microseconds

Other Ways to Bin

While binning by date is by far the most common way the bin function is used, you can break your data into bins using other values.

In this example, lets create bins for our % Free Space values.

Our CounterValue column holds the percent of free space on a disk when the CounterName is % Free Space. We want to break the counts into buckets for each ten percent of free space.

On the first row of the results, you see the value 50. This represents the range 50 to 59 percent. At that level there were 4,328 rows.

The bottom row reads 60. From 60 to 69 percent there were 1,440 rows of data.

We didn’t have to pick 10, we could have instead used 5, or 25, or any other value which makes sense for this query.

Conclusion

With this post we covered the summarize operator, which is used to aggregate values. Along the way we learned about two new functions, count and bin.

The demos in this series of blog posts were inspired by my Pluralsight courses Kusto Query Language (KQL) from Scratch and Introduction to the Azure Data Migration Service, two of the many courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With KQL – Count

Introduction

The previous post in the series covered the take operator. In that post I mentioned that take was one of the simplest operators in KQL. But it is not the simplest, that honor goes to the count operator.

The count operator does nothing more than takes the piped in dataset and returns the number of rows in it. We’ll see more in a moment.

For now, I should mention samples in this post will be run inside the LogAnalytics demo site found at https://aka.ms/LADemo. This demo site has been provided by Microsoft and can be used to learn the Kusto Query Language at no cost to you.

If you’ve not read my introductory post in this series, I’d advise you to do so now. It describes the user interface in detail. You’ll find it at https://arcanecode.com/2022/04/11/fun-with-kql-the-kusto-query-language/.

Note that my output may not look exactly like yours when you run the sample queries for several reasons. First, Microsoft only keeps a few days of demo data, which are constantly updated, so the dates and sample data won’t match the screen shots.

Second, I’ll be using the column tool (discussed in the introductory post) to limit the output to just the columns needed to demonstrate the query. Finally, Microsoft may make changes to both the user interface and the data structures between the time I write this and when you read it.

Count Basics

To use count, simply take a dataset and pipe it into the count operator.

Here, we took the Perf table and piped it into count. In the output at the bottom, you can see it has 3,913,054 rows as of the time I ran this query. You will likely get a different value as the Demo database is updated constantly.

Use Count Like Take

You can use the count operator like take (covered in the post Fun With KQL – Take), to spot check your query as you develop it.

Here you can see the where operator was added to the query, along with several conditions. It resulted in 1,714 rows being returned.

The take operator lets you get a sample of the data. The count operator tells you how many rows will be returned by your query.

This is another great way to spot check your work. If you know, for example, the query should bring back 1,714 rows, and the count returns that value, you can have some assurance your query is working a you designed it.

Pairing Down Your Data

In a an earlier post I stated the best way to write a query is to start with the largest dataset and keep trimming until you get to the smallest. But if you are developing your query in stages, how do you know which dataset is the biggest?

Using count at each stage will help you to determine this. You start with your core dataset, and measure it. Then add your first filter, perhaps a where operator, and check its count. Next, comment this filter out and try with the next filter you wish to use and get its count. This will let you know which of the two filters removes the biggest number of rows.

Let’s see an example of this. We want to author a query to return data for the last hour, where the CounterName is Bytes Received/sec and the CounterValue is greater than zero.

In example 1, I apply the time filter and pipe it to the count operator.

In example 2, I remove the time filter and use the counter based filters instead.

Between the two of them, we’ll say (for example purposes) the time filter removed more rows, so in the final version of the query (shown in example 3) the time filter is placed first, followed by the counter filter.

Conclusion

That’s it! That’s all there is to the count operator. Sorry if you were expecting more, but there’s not a lot to say about such a simple KQL operator.

The demos in this series of blog posts were inspired by my Pluralsight courses Kusto Query Language (KQL) from Scratch and Introduction to the Azure Data Migration Service, two of the many courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With KQL – Take

Introduction

The take operator can be a useful debugging tool, or a tool to use as you develop your Kusto queries. It will grab a random number of rows from the incoming dataset and return them.

The samples in this post will be run inside the LogAnalytics demo site found at https://aka.ms/LADemo. This demo site has been provided by Microsoft and can be used to learn the Kusto Query Language at no cost to you.

If you’ve not read my introductory post in this series, I’d advise you to do so now. It describes the user interface in detail. You’ll find it at https://arcanecode.com/2022/04/11/fun-with-kql-the-kusto-query-language/.

Note that my output may not look exactly like yours when you run the sample queries for several reasons. First, Microsoft only keeps a few days of demo data, which are constantly updated, so the dates and sample data won’t match the screen shots.

Second, I’ll be using the column tool (discussed in the introductory post) to limit the output to just the columns needed to demonstrate the query. Finally, Microsoft may make changes to both the user interface and the data structures between the time I write this and when you read it.

Take Basics

The take operator is incredibly simple to use. Just take a dataset, then pipe it into the take operator and indicate how many rows you want.

In this example we took the Perf table, and piped the dataset it generated into the take operator. We indicated we wanted to get 10 rows, which it did as you can see.

It is important to understand that take grabs these rows at random. Further, there is no consistency between each execution of take. You are likely to get a different set of rows with each execution. Let me run the exact same query again, so you can see the different data being returned.

As you can see, the resulting data is completely different, even though I ran the exact same query within seconds after the first execution.

Why Take?

So why use the take operator? Well it is an incredibly useful tool for developing your query. As you add each new operator to your pipe, you can add a take at the end to spot check your results. Because take executes very quickly, you can rapidly find out if your query is working, or maybe you did something that returned zero rows.

Building Queries Step by Step

Lets look at an example of using take when building a query. We’ll reuse a query from our post on the where operator.

We took our Perf table and piped it into the where operator to limit the data to the last hour. We then pipe it into take to verify the rows are indeed from the last hour. Since they are, we can now continue our query development.

For the next step in our query, we want to limit the result set to only include rows where the CounterName column contains the text Bytes Received/sec.

As you can see, I added an and clause to our where operator to limit based on the CounterName. That was piped into our take so we could verify the results.

So far so good. Now lets add another condition! We’ll pipe the existing query into a new where clause to only return data when the CounterValue is greater than zero. We’ll then pipe that into our take so we can spot check the results.

There we go, the output shows no rows with a zero CounterValue, giving us some assurance things are working correctly.

From here we can keep adding more and more steps to our query, using take each time to spot check the results. The take operator winds up being an extremely useful tool, which I use frequently when developing queries.

Note too that for this example I changed the number of rows passed into the take operator to 33. I just wanted to illustrate this can be any value. Early in my development I set it to a low number, perhaps 100, but toward the end will bump it up to 1000, or even higher, so I can better verify my results.

Limit

You may see references to the limit operator. Be aware that limit is nothing more than a synonym for take.

There is no difference between limit and take. They behave identically. In some query languages the keyword limit is used to perform the same task, so to make learning KQL easier the creators included the ability to use limit and take interchangeably.

Conclusion

In this post we covered one of the easiest to use, but extremely useful operators in KQL: take. As you begin your life as a Kusto query developer, I’m sure you’ll find take as useful as I do.

The demos in this series of blog posts were inspired by my Pluralsight courses Kusto Query Language (KQL) from Scratch and Introduction to the Azure Data Migration Service, two of the many courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With KQL – Where

Introduction

In my previous post, we saw how the search operator was used to limit the results of a query. This post will focus on the where operator, which performs a similar function.

Whereas search is used to limit based on matching of a string, the where operator is used to match based on a condition. In this post we’ll see some of the conditions that can be used with a where operator to narrow down a dataset.

The samples in this post will be run inside the LogAnalytics demo site found at https://aka.ms/LADemo. This demo site has been provided by Microsoft and can be used to learn the Kusto Query Language at no cost to you.

If you’ve not read my introductory post in this series, I’d advise you to do so now. It describes the user interface in detail. You’ll find it at https://arcanecode.com/2022/04/11/fun-with-kql-the-kusto-query-language/.

Note that my output may not look exactly like yours when you run the sample queries for several reasons. First, Microsoft only keeps a few days of demo data, which are constantly updated, so the dates and sample data won’t match the screen shots.

Second, I’ll be using the column tool (discussed in the introductory post) to limit the output to just the columns needed to demonstrate the query. Finally, Microsoft may make changes to both the user interface and the data structures between the time I write this and when you read it.

Basic Where with Time

One of the most common uses of the where operator is to limit a result set based on time. Let’s look at a common example.

Here we take the Perf table, and pipe it into the where operator. We are then going to use the TimeGenerated column as the column to limit our results on.

Next up is our comparison. Here we use >= (greater than or equal), but == (equals) and <= (less than or equal to) are also valid, as is just less than and greater than (< and >).

Next up is ago, which in KQL is classified as a scalar function. Scalar functions return a specific value, in this case the ago scalar function returns a date, going back in time from right now, for a certain amount of time.

In this case ago(1h) will return a value of one hour in the past based on the current time.

If you could look over the entire 30,000 rows returned by the query, you would see all of them ocurred within the last hour (from when you ran the query).

Ago Time Ranges

In the previous example, we used 1h for one hour with ago. But we could have used any number, for example 12h would be 12 hours.

In addition we can use more time slices beyond just hours. Here is a table with the basic set of time slicers.

Abbreviation Time Unit
d days
h hours
m minutes
s seconds
ms milliseconds
microsecond microseconds

Restrictions on Ago

There is one restriction when using ago. The column you compare to must be a datetime column.

In the side bar, expand the LogAnalytics solution then expand the Perf table. Look beside the TimeGenerated column.

Beside the column name you’ll see the text (datetime). This indicates the data type of the column. In order for ago to work, the data type of the column used for the where operator must be a datetime.

In the list, you’ll note there are two other datetime columns, BucketEndTime and BucketStartTime. These columns could also have been used with the ago operator.

Using Where with Logical Joins

Just like the search operator, the where operator can be used with the logical joins of and and or.

Here you can see we used where with ago. Then on the next line we used and to add a second condition, looking for an exact match in the CounterName column for the text Bytes Received/sec.

Note that it was not necessary to use a pipe symbol on the third line of our query. Kusto knew to combine the where with the and as part of the same query.

We are not limited to a single and either, we can use multiple and clauses in our query.

As mentioned, we can also use the or in queries.

We can create a more complex condition for the where operator by combining and and or in a chain.

Stacking Where Operators

It is also possible to stack several where operators in sequence.

It is important to understand what happens here is a little different than previous examples. In the previous example, the Perf table is rendered as a dataset. That entire dataset then has the where clause applied to it.

This example is a bit different. First, the call to the Perf table creates a dataset. That dataset is then piped into the first where operator, which reduces the passed in dataset to only rows for the last hour, using the ago scalar function.

That dataset is then passed into the next where operator, which will only return rows for one of the two matches on CounterName. This then generates another dataset.

This dataset is then piped into the third where clause, which only includes rows where the CounterValue is greater than zero.

This is a very common technique with Kusto queries. You take the biggest dataset, then pipe it into an operator that will remove a large number of rows. That resultant dataset is then (optionally) piped into another operator, which further reduces the rows.

With each pipe operation you want to remove as many rows as possible. Each time the dataset should be reduced in size further and further until it has the smallest number of rows to give the results you are looking for.

Again, this is an important concept so make sure you understand it clearly, as it is used over and over again with Kusto.

Simulating Search with Where

It is possible to simulate the search operator using a where.

Here we take the output of the Perf and pipe it into where. We can then indicate a specific column name to look in, or use * to look across all columns in the passed in dataset.

The has clause is needed to indicate we want to look for the text anywhere inside the column, and finally we pass in the string to look for, in this case Bytes. Unlike search, we do not need to include any asterisks within the search string.

In my previous post you saw the search operator has startswith and endswith parameters to look for a word at the beginning or end of the text in a column. The where operator has similar functionality using the hasprefix and hassuffix parameter names.

In addition, search as the ability to look for text anywhere in a column using wildcards, for example *Bytes* looks for the text Bytes anywhere in a columns text. The where operator simulates this by using the contains parameter, and ommiting the asterisk, as in where * contains "Bytes".

Here are examples of all three.

As stated at the beginning, with all of these, you can use the * to indicate look in all columns, or you can use a specific column name.

Regular Expressions

I’m sure you will be overjoyed to find that the where operator also supports regular expressions. The syntax is almost identical to the search.

For this example I did specify a column name, but could have used * as well. I then use matches regex followed by the regular expression. If you want to know a bit more about this regular expression, see the previous post I did on the search operator.

Conclusion

In this post we took a look at the where operator. It is similar to search, except where looks for conditions rather than text strings, although it can do that as well.

The chief difference between the two is scope. The search operator can be used across multiple tables, while where can only be used with the passed in dataset, typically a single table.

The demos in this series of blog posts were inspired by my Pluralsight courses Kusto Query Language (KQL) from Scratch and Introduction to the Azure Data Migration Service, two of the many courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With KQL – Search

Introduction

In this post we will examine the KQL (Kusto Query Language) search operator. Search allows us to look across all columns in one or more tables for a specific text string.

The samples in this post will be run inside the LogAnalytics demo site found at https://aka.ms/LADemo. This demo site has been provided by Microsoft and can be used to learn the Kusto Query Language at no cost to you.

If you’ve not read my introductory post in this series, I’d advise you to do so now. It describes the user interface in detail. You’ll find it at https://arcanecode.com/2022/04/11/fun-with-kql-the-kusto-query-language/.

Note that my output may not look exactly like yours when you run the sample queries for several reasons. First, Microsoft only keeps a few days of demo data, which are constantly updated, so the dates and sample data won’t match the screen shots.

Second, I’ll be using the column tool (discussed in the introductory post) to limit the output to just the columns needed to demonstrate the query. Finally, Microsoft may make changes to both the user interface and the data structures between the time I write this and when you read it.

Comments

Before I talk about search, I just wanted to mention how comments in KQL work. Here you can see my search query, with a comment above it.

Comments in KQL begin with two slashes //. If you want a multi-line comment you’ll have to begin each line with the two slashes.

Basic Search

Let’s take a look at a basic query that uses the search operator.

The query begins with our Perf table. On the next line is a pipe symbol (the vertical bar), following this is the search operator. Then, in quotation marks is the term we are looking for, Memory. So what’s happening here?

First, KQL accesses the entire Perf table. Next, it takes those results, and pipes them into the search operator. The job of search is to look across all columns in the data that was passed in, for a specific string of text. In this case the table Perf was passed into the search operator, but in a later post we’ll see ways to pass other types of data into search.

Once the search operator completes its work, it then pipes the data out to the next step in the query. Since there are no more operators after it, the next step is displaying the results in the pane at the bottom.

KQL knew the query was done when it encountered the blank line at the end. At that point it knows to go ahead and process the query. You can place more queries in the file after, and they won’t execute.

Optionally, you can place a semicolon to end the query. This is only needed though if you want to run multiple queries at the same time.

If you notice, the query is highlighted with a light gray background. This indicates the current query which will be executed when you click Run, or press Shift+Enter on your keyboard. If you need to run multiple queries you can highlight them, then run.

As you can see in the output the ObjectName column contains the word Memory. Likewise, the InstanceName column also as a row with the word Memory. Finally if you look in the CounterPath column the word Memory is embedded in the path.

The search term only has to occur in a single column to be included in the results.

Formatting the Query

In the previous example we used two lines for our query. This is a common practice when authoring Kusto queries, it improves readability and makes it easier to update.

This is not a requirement though, we could put the entire query on a single line, like so:

If you were to use the Format query button in the toolbar above the query pane, it will reformat your query breaking it into multiple lines as shown in the previous example.

Case Sensitivity with Search

By default, the search operator is not case sensitive. The following query will return the same results as the first one.

We can make a search case sensitive by adding the kind argument, indicating the search kind should be case sensitive.

Between the search operator and the term we are looking for, we add kind=case_sensitive. As you can see from the output, looking for memory with a lower case m finds no results.

Searching Everything

It is possible to search all the tables in the database. If you don’t pipe anything into search, it assumes you want to go over the whole database.

Be warned though, this is considered poor practice. It is slow on a large database, will take a long time, and will usually time out unless your database is very small.

I tried running this once on the whole sample Demo database. After half an hour it timed out.

There is a way to search across several tables though if you need to.

Searching Multiple Tables

If you have a handful of tables you want to search, you can append in, followed by the list of tables to search in parenthesis, then the term to search for.

This query searched only the tables Perf, Event, and Alert for the term Memory and displayed the output at the bottom.

Searching A Specific Column

So far we’ve seen search used to go over all columns in a table to look for a string. It is possible to search only a single column.

To search, you use the column name, followed by two equal signs, then the term to look for. This will look for an exact match. It will not return partial matches. In other words the entire contents of that column must match the search phrase exactly.

Searching For Text Anywhere In The Column

It is also possible to search for a phrase anywhere in the column text. You simply replace the double equal sign with a colon.

If you look at the results in the CounterName column, searching for MBytes returned Available MBytes, but it also returned Available MBytes Memory.

Searching Across Columns for Partial Text

In the previous example we saw how to search a single column for a partial match. In other words the text could appear anywhere in that specific column.

It is also possible to search across all the columns for a partial match using wildcards.

Doing so is simple, just place an asterisk * at the beginning and end of the search term. As you can see in the results, searching for *Bytes* found both Disk Read Bytes/sec and Bytes Sent/sec.

I slipped in another example on comments, the line with search demonstrates that a comment can come at the end of a line as well.

It is also possible to search for text which occurs only at the start, or at the end of a column’s text. To do so, you use the startswith or endswith parameters.

Finally, you can search for text with a specific word at the start and at the end, but any letters in between. Again, we use the wildcard asterisk.

Combining Searches Logically

Search will allow you to combine multiple searches in one expression, using the logical operators and and or.

I reused the search from the previous example, then used the and to add another condition. In the second condition I use an or to look for either the text C: or D: as an exact match in any column.

In the output you can see the CounterName column has matches for Free*Bytes. In the InstanceName column it found matches for both C: and D:. It also found matches inside the CounterPath column.

Regular Expressions

Finally, I’m sure fans of regular expressions will be thrilled to find out that the search operator does indeed support regular expressions (often abbreviated RegEx).

Here we used the search operator, followed by the name of the column to search, here InstanceName.

Then, to use a regular expression, add the keywords matches regex to the end, followed by the regular expression in quotes.

In this example the expression [A-Z] means any single character in the range A to Z. The colon is simply static text, it will look for any character from A to Z followed by a colon.

If you examine the InstanceName column you’ll see matches for C: and D:.

Conclusion

In this post we learned about the search operator, and the many ways to use it. In the next post we’ll examine the where operator.

The demos in this series of blog posts were inspired by my Pluralsight courses Kusto Query Language (KQL) from Scratch and Introduction to the Azure Data Migration Service, two of the many courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With KQL – The Kusto Query Language

Introduction

This post begins a new series on KQL – The Kusto Query Language. KQL is used to query large datasets on the Azure platform. In this new series you’ll learn many aspects of the Kusto language. There are a variety of places KQL is used on the Azure platform, including Azure Log Analytics, Azure Application Insights, Windows Defender Advanced Threat Protection, and the Azure Security Center to name a few.

Best of all, you can experiment with KQL without the need for your own dedicated Azure account. Microsoft has provided a user interface and sample dataset at https://aka.ms/LADemo you can use to learn Kusto at no cost to you.

In this introductory post, we’ll explore the user interface and write our first, very simple, KQL query. You might want to bookmark this post so you can easily refer back to it.

The Log Analytics User Interface

As mentioned, there are many places in Azure where you can use the KQL language to perform data analysis. For this series of blog posts, we will use the Azure Log Analytics demo site provided by Microsoft.

Across the top is the standard Azure toolbar. In the upper left is the Home &gt; navigator, and under it is the Logs logo letting you know where you are. As these are standard Azure interface elements we won’t dig deeper into them.

The Query Area

The main portion of the interface is taken up by the query area.

The area in the upper half is where you enter the query you want to run. The lower half is where the results are displayed. We’ll see an example of this in action later in this post.

Just above the query area is a toolbar. The Run button will execute the query you’ve entered. Note too, you can use the keyboard command SHIFT+ENTER to run a query. I’m a keyboard guy, so this is what I use most often to run queries, which you’ll see if you take either of my KQL courses on Pluralsight (I’ve linked to them in the Conclusion of this post).

To the right of the Run button is a Time range. You can use this to limit the time range for your query. We’ll learn more about this in a future post.

The Save button is pretty obvious, it will allow you to save your query. Share will let you share your query, or the results of the query.

New alert rule will let you add alerts for your queries. Export will let you export the results of your query to a CSV file or other formats.

Pin to lets you pin the query to your Azure dashboard, useful for frequently run queries.

The final menu option, Format query, does just that. Reformats you query to make it look nice and more readable.

Be aware, within the demo environment some of these features will be disabled. For example, you cannot save you query to the demo environment. In your own Azure environment however these will work fine.

The Side Bar

Over on the left you’ll see the side bar area. This holds a lot of items, so let’s take a look.

At the very top you see a tab shape holding the text New Query 1. It is possible to create multiple tabs, each with its own query and result pane. You can use the + (Plus button) beside the tab to open a new tab, and the x just within the tab to close it.

Under the tab is the word Demo. This is the name of the dataset you are working with. This would be similar to a database name in SQL Server, although be aware this is most definitely not a relational database.

Next up you see Tables, Queries, and Functions. These control what is displayed in the lower side bar area. Right now it is set to Tables, which we’ll dive into in just a moment.

Below this is a search bar, which you can use to search for a table name, or a query or function when that data is displayed. Under the search bar is a filter button, as well as ways you can group the information. We’ll go over these more in a future post.

Below this is the Favorites. If you have tables that you reference frequently, you can add them to the favorites for quick reference.

The Side Bar – Tables

Under the favorites are your tables, with the tables grouped into solutions. Let’s expand the LogManagement solution by clicking the triangle beside it.

What you can see is a partial list of tables in the Log Management solution, there are too many to fit in a single screen capture. If you scroll down you’ll see there are a lot of tables!

Scroll down to find the Perf table. We will be using this table a lot in this Fun With KQL blog series.

If you hover over the table name (and you don’t have to expand the table for this to work) a popup will appear. It has the name of the table, and a description of the data in it. Notice the star to the right of the table name. If you hover over it, a helpful hint Add to favorites appears. You can just click on the start to add it to your favorites area.

Under the table name you’ll see Use in editor. Clicking on it will insert the table name into your query window. For the Perf table this isn’t a big deal, but there are some long winded table names such as ContainerRegistryRepositoryEvents. Being able to click and insert will make authoring a query go a bit faster as well as reduce issues from typing mistakes.

At the bottom of this informational pop out is a section Useful links. Clicking on the link will open a new page to the Microsoft documentation for this table.

With the table expanded, you can now see all of the columns in this table. To the right of the column name is its data type. These are pretty standard data types, string, int, real (also known as a float, double, etc. in other languages), datetime, and more.

If you double click on the column name, it will insert the name of the column into the query editor.

The Side Bar – Queries

Let’s change the contents of the side bar by clicking on the Queries at the top of the side bar. Here you will see queries which Microsoft has built for you. You can use these as examples for building your own queries.

The queries are grouped into categories, although you can use the Group by to change this to a variety of other groupings.

Go down and expand the Other grouping, then scroll down a bit. Look for the queries that begin with m1_.

These are the queries I used in my Kusto Query Language (KQL) from Scratch course. Microsoft sponsored this course, and wanted to include them on the demo site. The m followed by a number indicates which module in the course the demos are associated with. The majority of the samples I will be using in this Fun With KQL series of blog posts will be derived from the m1-demo-intro and m2-demo-80-percent queries.

If you hover over the query name a pop out appears. It has a brief description, and the ability to either Run the query, or Load to editor. Unless it is a query you authored and run frequently, I suggest always loading it into the editor to review first.

The Side Bar – Functions

Let’s now click on the Functions at the top of the side bar.

This is a list of built in functions you can use in your queries. We won’t be going into functions as part of this Fun With KQL introductory series, but know this is where you can find them.

Additional Tools

As the final step in this introduction, lets look at the small toolbar just above the query editor, to the very right side of the user interface.

The Feedback is pretty obvious. A pop out will appear from the right side where you can file issues, ask for help, give product suggestions, or just tell the team at Microsoft how much you love KQL and the user interface.

The Queries button causes a window to appear, which displayes all of the built in queries in a card format. You can scroll down with all of the queries loaded, or click on one of the categories on the left to narrow the view.

If you hover over one of the query boxes it will provide the option to run the query or load the query into the editor. This is an easy way to browse through all of the queries and get more information about them.

The very right most item in this toolbar is a drop down menu with several options.

At the bottom are links to the Community pages where you can find help at the forums or jump to the GitHub repo for the Azure Monitor Community.

Just above it are four links to Microsoft documentation pages around the user interface as well as the Kusto Query Language.

The very top most item is a link to an Online course you can take. This will take you to Pluralsight and specifically the Kusto Query Language (KQL) from Scratch course.

And look at that, the author is little old me! Yes, this is the course I created for Pluralsight, sponsored by Microsoft. If you have a Pluralsight subscription already you can just sign in and take the course. If not, you can go to this link Kusto Query Language (KQL) from Scratch and use the Try for free link. You’ll get a free 10 day pass to Pluralsight with which you can watch my KQL courses, or any of the courses on Pluralsight including the many courses I have done for Pluralsight over the years.

Your First Query

Whew, that was a lot of info, but now you have a good understanding of the user interface. In this case it is for examining Log Analytics, but the UI is similar for most of the places you can use Kusto with.

It’s time to write our first Kusto query. Remember the Perf table we looked at earlier? Lets write a query to return the contents of the Perf table.

In the query editor, type in Perf. Note that when it comes to table and column names KQL is case senstive. Perf is not the same as perf or PERF. The latter two will cause an error if you attempt to use them.

That’s it, no having to use a Select, no requirement to enter column names, just enter the name of the table, then click the Run button, or use SHIFT+ENTER.

To prevent a run away query, the interface limits the maximum number of rows that are returned, here 30,000. This is shown to you in the blue informational box just above the query resutls. You can dismiss the message by clicking the x toward the right side.

At the bottom it shows the run time for the query, here just a little over three seconds. That’s one of the huge benefits to Kusto, it is very fast!

If you click on any column header it will provide a few options.

The up and down arrows can be used to change the sorting between ascending and descending order. Faintly visible are the three dots which brings up a menu. In this menu you can filter the results.

If you look to the right side you’ll see the word Columns displayed vertically. Clicking it causes the column filter tool to pop out.

You can use this to unselect (or reselect) columns from the output. You can also transform the output to a pivot table. Note the pivot table is a new feature since the KQL from Scratch course was created.

The last thing to note is the Query details link on the very bottom right. This causes a pop out with more information about the execution of the query.

This gives a brief overview that can assist you with query tuning. I won’t take the time to go over each statistic, you can use the little i in a circle button to the right of each one for more details.

A Note on Result Filtering

Before I leave, I just wanted to point out an important aspect of filtering the results. These filters apply after the query has been run. Kusto has already brought back the (in this case) 30,000 rows of data.

There are techniques you can use when writing your queries that will filter down the results before they are returned. This will make the queries run faster, and make it easier to work with the data. Stay tuned, as we’ll be learning many of these techniques in this blog series, Fun With KQL.

Conclusion

Congratulations, and a big thank you for reading this far. It was a long blog post, but there were a lot of aspects of the user interface to cover.

You may want to bookmark this post for future reference. From here on we’ll be focused on the Kusto language itself, and only mention elements of the user interface when needed.

The demos in this series of blog posts were inspired by my Pluralsight courses Kusto Query Language (KQL) from Scratch and Introduction to the Azure Data Migration Service, two of the many courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

The Fun With PowerShell Roundup

PSCustomObject Sample

Introduction

Since September 2020 I’ve been posting extensively about PowerShell in a series I’ve titled Fun With PowerShell. In my next post I will begin a series on a new topic, but before I do I wanted to leave with a Fun With PowerShell Roundup" post.

Below you will find a listing of all posts in my Fun With PowerShell series. Note there are other posts I’ve done in this time frame, but since they did not focus on PowerShell I’ve omitted them from this listing.

The Posts

Date Title
20-09-29 VSCode User Snippets for PowerShell and MarkDown
20-12-05 Two New PowerShell Courses for Developers on Pluralsight
20-12-14 Iterate Over A Hashtable in PowerShell
20-12-21 Fixing the Failed To Acquire Token Error When Logging Into Azure from PowerShell
21-01-04 Suppress Write-Verbose When Calling A function in PowerShell
21-07-05 Fun with PowerShell Get-Random
21-07-12 Fun With PowerShell Strings
21-07-26 Fun With PowerShell Arrays
21-08-02 Fun With PowerShell Hash Tables
21-08-09 Fun With PowerShell Logic Branching
21-08-16 Fun With PowerShell Code Formatting
21-08-23 Fun with PowerShell Loops
21-07-19 Fun With PowerShell String Formatting
21-08-30 Fun With PowerShell Basic Functions
21-09-06 Fun With PowerShell Advanced Functions
21-09-13 Fun With PowerShell Pipelined Functions
21-09-20 Fun With The PowerShell Switch Parameter
21-09-20 Fun With the PowerShell Switch Parameter
21-09-27 Fun With PowerShell Write-Verbose
21-10-04 Fun With PowerShell Write-Debug
21-10-11 Fun With VSCode Snippets for Markdown and PowerShell
21-10-18 Fun With PowerShell Providers
21-11-15 Fun with PowerShell Enums
21-11-29 More Fun with PowerShell Enums
21-12-06 Fun with PowerShell Enum Flags
21-12-14 Fun With PowerShell Classes – The Basics
22-01-10 Fun With PowerShell Objects – PSCustomObject
22-01-17 Fun With PowerShell Objects – Adding Methods to PSCustomObject
22-01-24 Fun With PowerShell Objects – Creating Objects from C#
22-01-31 Fun With PowerShell Objects – Modifying Existing Objects
22-02-07 Fun With PowerShell Classes – Static Properties and Methods
22-02-14 Fun With PowerShell Classes – Overloading
22-02-21 Fun With PowerShell Classes – Constructors
22-03-24 Fun With PowerShell – Extracting Blog Titles and Links from a WordPress Blog with PowerShell
22-03-28           Fun With PowerShell – Extracting Blog Titles and Links from a WordPress Blog with PowerShell – Generating Markdown

Conclusion

This is the wrap up post for my Fun With PowerShell series. Use it as an index to quickly refer back to posts of interest to you. These will make a good quick reference to the many aspects of PowerShell programming.

In my next blog post I’ll begin a new "Fun With…" series, so stay tuned for more fun.

All of the posts in my Fun With PowerShell series were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With PowerShell – Extracting Blog Titles and Links from a WordPress Blog with PowerShell – Generating Markdown

Introduction

In my previous blogpost, Fun With PowerShell – Extracting Blog Titles and Links from a WordPress Blog with PowerShell, I described how I extracted the title, link, and publication date for posts in my WordPress blog using PowerShell. I then went on to use PowerShell to generate HTML code that I could insert into a post, or create a basic webpage.

It would also be useful to generate Markdown, instead of HTML, in case I want to use it somewhere such as my GitHub page. In this post we’ll see how to do just that, and create Markdown from the output array of PSCustomObjects.

For all of the examples we’ll display the code, then (when applicable) under it the result of our code. In this article I’ll be using PowerShell Core, 7.2.2, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

Additionally, be on the lookout for the backtick ` , PowerShell’s line continuation character, at the end of many lines in the code samples. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. My post Fun With PowerShell Pipelined Functions dedicates a section to the line continuation character if you want to learn more.

To run a snippet of code highlight the lines you want to execute, then in VSCode press F8 or in the IDE F5. You can display the contents of any variable by highlighting it and using F8/F5.

Where to Start

Much of the work has already been done in the previous post. Review it, stopping at the Creating HTML section. The array we created will now be used in generating Markdown.

This is the reason I created an array of custom objects holding the title, link, and publication date. Just as I used it to create HTML, I can now use it to generate Markdown.

Generating Markdown

Like I did with the HTML, I created a function to create Markdown. This is and advanced function that I’ll pipeline the array of PSCustomObjects into.

function Get-WPMarkdown()
{
  [CmdletBinding()]
  param (
          [Parameter (ValuefromPipeline)] $wpObjects
        , [switch] $FormatAsTable
        )

  process
  {
    # Create a formatted output line
    if (!$FormatAsTable.IsPresent)
    {
      # Create each line as a paragraph
      $outLine = @"
$($wpObjects.PubDate) - [$($wpObjects.Title)]($($wpObjects.Link))
"@
    }
    else
    {
      # Create each line as a row in a table
      $outLine = @"
|$($wpObjects.PubDate)|[$($wpObjects.Title)]($($wpObjects.Link))|
"@
    }

    # Return the formatted line
    $outLine
  }

}

The first parameter accepts the custom objects we generated from the pipeline. The second is a switch that will format the output as a row in a Markdown table, as opposed to just a line of Markdown text.

I then check to see if the switch was passed in, and format the line to return accordingly. Finally I send the generated line out of the function.

Using the Get-WPMarkdown Function

Now all we have to do is call the function. As a reminder, the data in the $outData variable is the array of custom objects we generated in the previous posts.

$outMd = $outData | Get-WPMarkdown

$wpOutputMd = 'D:\OneDrive\BlogPosts\Markdown\arcanecode.wordpress2.md'
Out-File -FilePath $wpOutputMd -InputObject $outMd -Force

This will generate our data as rows in a Markdown file. Below is a small example.

2020-09-29 - [VSCode User Snippets for PowerShell and MarkDown](https://arcanecode.com/2020/09/29/vscode-user-snippets-for-powershell-and-markdown/)
2020-12-05 - [Two New PowerShell Courses for Developers on Pluralsight](https://arcanecode.com/2020/12/05/two-new-powershell-courses-for-developers-on-pluralsight/)
2020-12-14 - [Iterate Over A Hashtable in PowerShell](https://arcanecode.com/2020/12/14/iterate-over-a-hashtable-in-powershell/)

Outputting a Markdown Table

In the code there was a switch to format the output Markdown as a table.

$outMd = $outData | Get-WPMarkdown -FormatAsTable

As I did with the HTML example, I wanted to wrap the generated data in the appropriate Markdown code to make this a complete Markdown table. I created another function to handle this.

function Add-WPMarkdownHeader()
{
  [CmdletBinding()]
  param (
          [Parameter (Mandatory = $true)]
          $markdownData
        )

  # Create a new array
  $outTable = @()

  # Add the html to create a left aligned table header
  $outTable += '|Date|Post|'
  $outTable += '|:-----|:-----|'

  # Add the existing table row data
  foreach ($row in $markdownData) { $outTable += $row }

  # Return the output
  return $outTable
}

As you can see, it creates a new array, adding the Markdown code for a table header, one specific for our data. It then cycles through the array that was passed in and adds it to the new array. Once done this new array is returned by the function.

To call it we simply use the following sample to write it to a file.

$outTable = Add-WPMarkdownHeader $outMd
Out-File -FilePath $wpOutputMd -InputObject $outTable -Force

Here is a sample of the output.

|Date|Post|
|:-----|:-----|
2020-09-29 - [VSCode User Snippets for PowerShell and MarkDown](https://arcanecode.com/2020/09/29/vscode-user-snippets-for-powershell-and-markdown/)
2020-12-05 - [Two New PowerShell Courses for Developers on Pluralsight](https://arcanecode.com/2020/12/05/two-new-powershell-courses-for-developers-on-pluralsight/)
2020-12-14 - [Iterate Over A Hashtable in PowerShell](https://arcanecode.com/2020/12/14/iterate-over-a-hashtable-in-powershell/)

Conclusion

In this post we saw how to generate Markdown code from a WordPress blog extract. Combined with the code in my previous post I now have a handy script I can use to generate HTML and Markdown code from my blog posts. This will be handy for both now, and when I want to create wrap up posts for future series.

These techniques can be easily adapted for any XML file that you wish to create a summary listing for, in HTML or Markdown or both.

The demos in this series of blog posts were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With PowerShell – Extracting Blog Titles and Links from a WordPress Blog with PowerShell

Introduction

Since September of 2020 I have been blogging heavily on PowerShell. In a few posts I’m going to start a new series on a different subject, but first I wanted to provide a wrap up post with links to all my recent PowerShell posts.

Extracting all of those titles and links by hand seemed like a labor intensive task, so of course I wanted to automate it. In addition, I’ll be able to reuse the code when I’m ready to wrap up my next, or a future, series.

My blog is hosted on WordPress.com, which provides an export function. In this post I’ll cover the code I created to extract all my links, and how I generated HTML from it. In my next post I’ll show the same methodology for generating Markdown, and in the next post will do the PowerShell roundup.

For all of the examples we’ll display the code, then (when applicable) under it the result of our code. In this article I’ll be using PowerShell Core, 7.2.2, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

Additionally, be on the lookout for the backtick ` , PowerShell’s line continuation character, at the end of many lines in the code samples. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. My post Fun With PowerShell Pipelined Functions dedicates a section to the line continuation character if you want to learn more.

To run a snippet of code highlight the lines you want to execute, then in VSCode press F8 or in the IDE F5. You can display the contents of any variable by highlighting it and using F8/F5.

Extracting Data from WordPress

One of the administrator tools in the WordPress.com site is the ability to extract your blog. You can generate an XML file with the entire contents of your blog. This includes all of the data including the post itself, comments, and associated metadata. They do provide the ability to limit the extract by by date range and subjects.

As you can guess this extract file is large, far too much to sift through by hand. That’s where PowerShell came to my rescue!

For each post, the exported XML file has three lines we are interested in, the tags with <title>, <pubDate> and <link>. I tackled this in stages.

As the first stage, I simply loop over the data in the file, looking for the XML tags I need. When I’ve found all three, I have a small function that creates a PowerShell custom object. After each object is created, it is added into an array. I needed to do a little filtering, as over the last year I’ve added a few more blog posts on other topics. I did not want these to be included in my future "Fun With PowerShell Roundup" post.

Once I have an array of custom objects, I can easily use them in multiple scenarios. For generating HTML I created a function that takes each object and generates a line of HTML code. It also has a way to generate the line as an HTML row instead of a series of paragraphs.

For my purposes, this was all I needed. However there may be times when you wish to generate a complete, but basic, web page. There is one more function I created that will take the output of the HTML rows and add the lines needed to make it a valid HTML page.

Generating a Custom WordPress Object

I mentioned a function to create a custom object, so let’s start with that.

function Get-WPObject()
{
  [CmdletBinding()]
  param (
          [Parameter( Mandatory = $true ) ]
          [string] $Title
        , [Parameter( Mandatory = $true ) ]
          [string] $Link
        , [Parameter( Mandatory = $true ) ]
          [string] $PubDate
        )

  # Build a hash table with the properties
  $properties = [ordered]@{ Title = $Title
                            Link = $Link
                            PubDate = $PubDate
                          }

  # Start by creating an object of type PSObject
  $object = New-Object –TypeName PSObject `
                       -Property $properties

  # Return the newly created object
  return $object
}

The function is straightforward, it takes the three passed in parameters and creates a custom object from it. This is a common technique, it allows you to easily generate a custom object. It also leverages code reuse.

If you want to get a more detailed explanation on creating and using custom PowerShell objects, see my post Fun With PowerShell Objects – PSCustomObject.

Creating The Array

Before we create the array, we need to read in the data from the WordPress XML extract file. I create a variable to hold the location, then read it in.

$wpInput = 'D:\OneDrive\BlogPosts\Markdown\arcanecode.wordpress.2022-03-08.000.xml'

# Read the data from input file
$inData = Get-Content $wpInput

Now it’s time to read in the data from the XML file, one line at a time.

# Setup an empty array to hold the output
$outData = @()

foreach ($line in $inData)
{

  # Extract the title. Replace the XML tags with the Markdown for a link title
  if ($line.Trim().StartsWith('<title>'))
  {
    $title = $line.Trim().Replace('<title>', '').Replace('</title>', '')
  }

  # Extract the link, replacing the XML tags with the Markdown link characters
  if ($line.Trim().StartsWith('<link>'))
  {
    $link = $line.Trim().Replace('<link>', '').Replace('</link>', '')

    # For some reason the WordPress export uses http instead of https. Since the
    # blog supports https, lets fix that.
    $link = $link.Replace('http:', 'https:')
  }

  if ($line.Trim().StartsWith('<pubDate>'))
  {
    # Extract just the date, then covert it to a DateTime datatype
    $pubDateTemp = [DateTime]($line.Trim().Replace('<pubDate>', '').Replace('</pubDate>', ''))

    # Now use the ToString feature of a DataTime datatype to format the date
    $pubDate = $pubDateTemp.ToString('yyyy-MM-dd')

    # In addition to links to the blog posts themselves, the exported XML file also
    # has links to images. To weed these out, we will search for posts that have PowerShell
    # in the title. The Contains method is case sensitive so it will omit the links
    # to the images.
    #
    # When a match is found, it passes the Title/Link/PubDate to our function, which will
    # generate a custom object. This object will be added to our output array.
    if ($title.Contains('PowerShell'))
    {
      $outData += Get-WPObject -Title $title -Link $link -PubDate $pubDate
    }

  }

} # End the foreach ($line in $inData) loop

First I create an empty array that will hold the output. To learn more about arrays, see my post Fun With PowerShell Arrays.

Now I enter a foreach loop, to go over each line in the array. If you don’t know, when you use Get-Content it returns each line in the file as a row in an array. That’s what I want here, but be aware if you add the -Raw switch to the Get-Content it returns the entire file as one big text string.

The data in the XML occurs in the order of Title, Link, then PubDate. PubDate is the Publication Date for the blog post.

As I find the title and link, I remove the XML tags then copy the data into a local variable. For some reason the extract uses http for the links, so I wanted to correct it to use https.

When I find the PubDate, I wanted to reformat it as a string in YYYY-MM-DD format. I extract just the date portion of the line by removing the XML tags. I then cast it to a [DateTime] and store it in a temporary variable.

I can then call the ToString method of the DataTime datatype to format it in a format I want, namely YYYY-MM-DD (Year, Month, Day).

Next I check to see if the title contains the word PowerShell. If so, I now have the three pieces of info I need, and call my function to generate the PSCustomObject and add it to the output array.

Creating HTML

To create the HTML I wrote a function, Get-WPHtml. Like the other functions I created this as an Advanced function. To read up on Advanced Functions, see my article Fun With PowerShell Advanced Functions.

I needed this so I could pipe the data from the array containing my WordPress PSCustomObjects into it. By doing it this way, I could reuse the Get-WPHtml with any array that has objects with three properties of Title, Link, and PubDate.

Let’s look at the function.

function Get-WPHtml()
{
  [CmdletBinding()]
  param (
          [Parameter (ValuefromPipeline)] $wpObjects
        , [Parameter (Mandatory = $false)] $Indent = 0
        , [switch] $FormatAsTable
        )

  process
  {
    # Create a string with spaces to indent the code. If not used no indent is created.
    $space = ' ' * $Indent

    # Create a formatted output line
    if (!$FormatAsTable.IsPresent)
    {
      # Create each line as a paragraph
      $outLine = @"
$space<p>$($wpObjects.PubDate) - <a href="$($wpObjects.Link)" target=blank>$($wpObjects.Title)</a></p>
"@
    }
    else
    {
      # Create each line as a row in a table
      $outLine = @"
$space<tr> <td>$($wpObjects.PubDate)</td> <td><a href="$($wpObjects.Link)" target=blank>$($wpObjects.Title)</a></td> </tr>
"@
    }

    # Return the formatted line
    $outLine
  }

}

The first parameter will accept the data from our pipeline, as I explain in my article Fun With PowerShell Pipelined Functions. Next is an optional parameter that allows the user to indent each row a certain number of spaces. The final parameter toggles between formatting each row as a standard paragraph or as a table row.

The process block will run once for each piece of data passed in from the pipeline. It creates a variable with the number of spaces the user indicated. If the user didn’t pass a value in, this will wind up being an empty string.

Next we check to see if the switch FormatAsTable was passed in, then create an output string based on the users choice. For more on switches, refer to my article Fun With the PowerShell Switch Parameter.

As a final step we return the newly formatted line, which puts it out to the pipeline.

Using the New Function

Using these functions is easy. We take the array of custom objects, then pipe it into the new Get-WPHtml function using an indent of 2. The result is copied into the $outHtml variable which will be an array.

Finally we set the path for our output file, then use the Out-File cmdlet to write to disk.

$outHtml = $outData | Get-WPHtml -Indent 2

# Save the new array to a file. Use Force to overwrite the file if it exists
$wpOutputHtml = 'D:\OneDrive\BlogPosts\Markdown\arcanecode.wordpress2.html'
Out-File -FilePath $wpOutputHtml -InputObject $outHtml -Force

Creating a Full HTML Page

For my purposes, I am going to take the data in the file and copy and paste it into the WordPress post editor when I create my roundup blog post. For testing purposes, however, it was convenient to have a full webpage. With a full webpage I can open it in a web browser, see the result, and test it out. Further, in other projects I may actually need a full webpage and not the part of one that I’ll be using for my blog.

The version of the webpage with just paragraph tags will open OK in a browser, but the version of the table will not. So let’s fix that.

Here is the function I created to wrap the output of the previous function, when called using the -FormatAsTable flag, in the necessary HTML to make it a functioning webpage.

function Add-WPHtmlHeader()
{
  [CmdletBinding()]
  param (
          [Parameter (Mandatory = $true)]
          $htmlData
        )

  # Create a new array
  $outTable = @()

  # Add the html to create a left aligned table header
  $outTable += '<style>th { text-align: left; } </style>'
  $outTable += '<table>'
  $outTable += '<tr>'
  $outTable += '<th>Date</th> <th>Post</th>'
  $outTable += '</th>'

  # Add the existing table row data
  foreach ($row in $htmlData) { $outTable += $row }

  # Add the closing table tag
  $outTable += '</table>'

  # Return the output
  return $outTable
}

The one parameter is the array that was output from our Get-WPHtml function. While you can add rows to an array, or change values at a specific position, you can’t insert new rows at specific positions. As such we have to create a new empty array, which was done with $outTable.

We then add the lines needed to create the table header. For this article I’m assuming you are familiar with basic HTML tags.

Once the header rows have been added we cycle through the input array, adding each row to the new output array.

Finally we add the closing tag to finish off the table element, then return the output.

Generating the Complete Webpage

Now that the hard part is done, all we have to do is call the function, passing in the output of the previous function, stored in $outHtml. This will then be written to a file using the Out-File cmdlet.

$outTable = Add-WPHtmlHeader $outHtml

# Save the new array to a file. Use Force to overwrite the file if it exists
Out-File -FilePath $wpOutputHtml -InputObject $outTable -Force

The Output

Here is a sample of the output of our hard work. Note I’ve only included a few rows of blog posts to keep it brief.

<style>th { text-align: left; } </style>
<table>
<tr>
<th>Date</th> <th>Post</th>
</th>
  <tr> <td>2020-09-29</td> <td><a href="https://arcanecode.com/2020/09/29/vscode-user-snippets-for-powershell-and-markdown/" target=blank>VSCode User Snippets for PowerShell and MarkDown</a></td> </tr>
  <tr> <td>2020-12-05</td> <td><a href="https://arcanecode.com/2020/12/05/two-new-powershell-courses-for-developers-on-pluralsight/" target=blank>Two New PowerShell Courses for Developers on Pluralsight</a></td> </tr>
  <tr> <td>2020-12-14</td> <td><a href="https://arcanecode.com/2020/12/14/iterate-over-a-hashtable-in-powershell/" target=blank>Iterate Over A Hashtable in PowerShell</a></td> </tr>
</table>

Conclusion

In this post we tackled a project to create an HTML page based on the export of a WordPress blog. In the process we used many of the techniques I’ve blogged about over the last year and a half.

For the next post we’ll use these same techniques to create an output file in Markdown format.

The demos in this series of blog posts were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Getting Healthy With Tech

Introduction

Over the last few months, I’ve been working hard to get healthier. I’m diabetic, with high blood pressure, and overweight (like a lot of people in IT). So, I’ve been working hard to change that.

I’m also a tech nerd and a data guy. I’ve been using tech to track my progress, examine many critical metrics, and trend these over time.

I thought others may benefit from what I’ve done in this area over the last few months, and thought I’d share the tech and apps I’ve been using here.

Apple Watch

I use multiple platforms for my daily work, including Windows and Linux, but in my opinion it’s Apple who has health tech really nailed down. My Apple Watch is my first tool for tracking my health.

With it, I can track my exercises. Every day, no excuses, I do a minimum of thirty minutes of exercise although as I’ve progressed it’s turned into forty or more. I have an indoor exercise bike for rainy days, or on a nice day head to the great outdoors for a swift walk.

When I walk, I use walking / hiking poles to give my arms a bit of a workout. On days where I do my indoor bike, I do some weightlifting with some handheld barbells to work my arms.

My watch tracks the length of my workout, how many calories I burned, what the weather was like, and if I walk what path did I take, and what elevation change was included in my route.

In addition, it also tracks my blood oxygen level, heart rate, and has an ECG function. It also tracks my sleep to see how much rest I’m getting at night. The watch also has a cool little mindfulness app, which helps me calm down and focus.

Finally, it provides reminders to stand up every hour. Like a lot of IT people, when I get seated in front of my PC I get really focused and lose track of time. Suddenly 3 hours can go by. Then when I stand up my bad back has gone stiff, and my arthritic hips let me know they aren’t happy.

The reminder to stand every hour has really made a difference. Getting up and moving around hourly really helps eliminate those stiff and sore times.

Apple iPhone / iPad

The information in my watch feeds back to the Fitness and Health apps on my iPhone. Here I can review my information from the watch, combined with data from my other apps and tools, more of which I’ll cover momentarily.

As you can see, the fitness app gives me a nice dashboard of my progress. Today I did a 2.03-mile walk. My total exercise time for today was 50 minutes. Note that if I had done multiple exercise activities, for example 30 minutes on my bike and 10 minutes of weights, it would combine that time.

It also shows my total calories burned, along with my target for today. So far, I’ve done 440 of 600 calories for my day today. The fitness app helps you calculate your target, but you can override it. In addition, it will prompt you to increase it over time based on your past workouts.

For example, when I first started my goal was 520 calories. It then increased to 560, and now to 600. I’m sure in the near future I’ll be prompted to increase it further, although as I mentioned I can change this whenever I feel I’m ready.

Finally, it also shows how many times I’ve stood today, with a goal of standing up at least once during an hour, for 12 hours in the day.

The circles show your progress in a quick graphical format. Once the circle is closed you’ve completed your goal for the day, although it will continue the circle beyond the target. This makes it easy to challenge yourself to meet your goal.

Some refer to this as the “gameification” of exercise. Turning exercise into a game, much like trying to get a high score in a video game. While intellectually I understand what is going on, it’s still a fun challenge to meet these goals and very satisfying to see those circles close.

Beyond the software built into my Apple devices, there are some other devices and applications I use.

Basic App Requirements

Before I list the apps and devices, I wanted to list a few of my basic requirements in selecting health apps. You may not have these same requirements, so you may find other apps that work better for you.

First, the app must work on both iPad and iPhone, with bonus points if there is an associated app for the Apple Watch. I like to do my monitoring of data on the iPad (which has a nice big screen my old eyes can see) but be able to register things like taking my meds on my iPhone, which is generally handy.

Next, the app needed to sync between the iPad / iPhone. I found many good apps, but very few that had the ability to synchronize their information between devices.

Finally, it needs to be easy to use. My wife is also tracking her health, but while smart she’s not a technical person so it has to be good for an average user, and not just tech nerds like me.

Omron Blood Pressure Monitor

To keep track of my blood pressure I use a monitor from a company called Omron. They have multiple devices, mine has Bluetooth and allows for two users, which is nice as you can share with your spouse / significant other / pesky relative that won’t leave.

My model is the BP7350, but there are a range of models that support this functionality. In the Apple Appstore they have a corresponding Omron Connect app. It’s pretty simple, you take your BP on the machine, then open the app and it syncs that reading to the iPhone.

As you can see it displays my readings for today. I can tap the History button on the bottom to see my readings over time. Should my meter and phone not automatically synchronize, I can tap the Sync button in the upper right corner to have the two sync.

If you tap the + button, it brings up an additional menu. Tap on Profile, then App Settings and it will let you copy your readings into the Apple Health App.

Blood Glucose

Being diabetic it’s important to monitor my blood glucose (aka blood sugar) every day. For that, I use the OneTouch Verio Flex meter. While this meter is not in my insurance company’s “approved” list, it was only $26 (US). The test strips run about $22 (US) for thirty, about a month’s supply.

Both of these I happily pay out of pocket for the convenience of easily tracking my readings. There is a corresponding OneTouch app for the iPhone, it pairs with the meter over Bluetooth. I just take my reading and it automatically syncs to my phone.

As you can see it forms a two-way link to the Apple Health and Fitness apps. It copies my daily reading into Apple Health and reads in my workouts from Fitness. It also looks for and warns about negative trends.

And yes, before someone points it out, I know my sugars are way too high. Late last year we found out my previous meds had quit working. The doctor’s office says it happens sometimes. So, my doctor and I are working with different medications to see what is most effective for me. It’s still a work in progress.

I mentioned it is a two-way link, the app also writes my glucose numbers into my Apple Health app.

Weight Tracking

As you might expect as part of getting healthier, I wanted to lose weight. Thus, I needed an effective way to track it. Ideally, I wanted to be able to just step on a scale, and it be recorded in an app.

I already used some Wyze cameras, since I had the app already it was an easy choice to select the Wyze scale.

In addition to weight, it also has other measurements such as BMI. One tip, don’t step off the scale too soon. Let it read your weight in, then wait a second. It will then calculate its other measurements. At that point you can step off, and the readings will show up in the Wyze app.

On the screen that displays your weight, you can go into the settings (gear icon in the upper right) and turn on data sharing with third party apps, like Apple Health. At $33 (US) this was a no brainer purchase.

Hydration

Staying hydrated is important for good health. Especially for diabetics, as it helps keep the sugars flushed from your system.

Note, don’t take anything here as medical advice, I’m not a doctor, I’m just sharing what mine told me. Your situation may vary, so be sure to consult your own physician.

To track my hydration, and to get reminders that it is time to drink, I selected an app called WaterMinder. It has iPhone and iPad apps as well as an app for the Apple Watch. You enter basic data like your age, height, weight, and it calculates how much water you should take in.

To be honest this is the one failing I found with the app, the number it creates is about half of what every other site I found said I should be getting. For me it said 80 ounces a day, so I just doubled that, and overrode the goal to 160 ounces a day. Again, be sure to do your own research and discuss the proper fluid intake goals with your own doctor.

As you can see, the app provides a cute little graphic showing your intake for the day. So far, I’ve taken in 100.8 oz, or 63% of my goal. To add data, you can tap the + button and quick pick a water cup amount. Alternatively, you can tap the icon to the right of it, and it brings up a menu with various kinds of liquids and lets you type in how many ounces.

That’s one of the things I really like about this app. If you, for example, drink milk, it calculates how much water is in the milk then adds just that amount of water to your hydration total.

The app will sync between devices, but it’s not always automatic. You can force it though, although I didn’t see it documented. Just tap the + button, then tap outside it, and it will force a sync.

You can also share your readings with another user of the WaterMinder. I share mine with my wife, and she shares hers with me. We can see how much water the other one has ingested and encourage each other.

The app also has a nice history feature. It will show your trend over time, but for a given day it will also show you each individual entry. That way you can track exactly what you’d consumed that day.

Medication

Many people, as they get older, take one or more medications. Even younger, healthier folks tend to take one or more vitamins. As such, it’s important to have a way to track not just what medications you take but to remind you when it is time to take them.

I spent a lot of time trying a multitude of apps to find one that met my requirements. I finally found one called EveryDose.

EveryDose is simple to use. You enter in all your medications. Built in there is a list of valid medications, so as you begin to type you can then pick your medication from the list. Should your medication not be in the list, no problem you can elect to add it anyway.

You also enter the strength of the pill, for example 50 mg, then the dose, 1 pill, 2, 1.5 pills, etc. Like the medications, you can enter a custom value as well.

As part of the data entry, you can select a time of day to take the medication, as well as a frequency (as needed, once a day, once a week, etc.).

Note I’ve blurred out my prescription meds, but you can also enter your vitamins into the app to track them. When the reminder alert goes off, you can tell the app you took the meds on time, just now, or enter a specific date/time.

If you need to, you can go down the list and pick the meds you took individually. This is handy should you run out of a particular pill that day.

You can export your list of meds so you can easily share with your doctor or pharmacist, plus great logging so you can see which meds you have taken on what days.

The one thing it lacked was integration into Apple Health. I’d love to see it enter my vitamins and such into those areas in Apple Health. But its other features were enough to make me go with it.

Exporting Health Data

You can take your health data to the next level by exporting it. Once exported, you can add it to your own database, or most importantly share it with your health care provider.

To do that well, I found an excellent app called Health Auto Export. This is a multicomponent application. There is one app that runs on the iPhone itself. This app provides some simple reporting, but its main purpose is to run in the background and update your personal database. This data can then be used by the Health Auto Export iPad app, as well as their app that runs on MacOS.

The primary purpose is to export your health data to a variety of formats such as CSV. You can bring this into Excel or Numbers, then slice and dice to provide your doctor with just the information they need.

It also has a nice dashboard which you can customize. Here’s an example of mine:

This gives me an easy-to-use dashboard I can view on my Mac or my iPads. I can drill down, using the menu on the left, to get more details, along with trends displayed over various charts and graphs.

As I mentioned, the most important functionality for me is the ability to export data. This has allowed me to share with my doctor, which helps him adjust my medication and track my health.

Conclusion

As I work to improve my health, I’ve explored a variety of apps and tools to track my progress. I wanted to share what I’ve found so far in case you, too, are seeing to improve your health.

I said this earlier in the post but want to reiterate: I am not a medical professional, and I am not offering medical advice. Please consult your physician before embarking on any healthcare, such as exercise, hydration, medication, and the like.

For me this is a journey, a work in progress. It’s possible, even likely, that as time goes by, I will find other apps and devices to improve my health.

If you have suggestions, perhaps you’ve found a better app or device, then by all means share them in the comments so we can all get healthier. My slogan has become:

Exercise, hydrate, medicate, every day! No excuses. Be a monster!

ArcaneCode

Fun With PowerShell Classes – Constructors

Introduction

In our previous two posts, we covered the use of static properties and methods in classes, then saw how to use method overloading.

This is our final post of the series, and will cover the use of constructors. Constructors are functions that execute automatically when a class is instantiated.

For all of the examples we’ll display the code, then (when applicable) under it the result of our code. In this article I’ll be using PowerShell Core, 7.2.1, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

You may notice a backtick ` character at the end of many lines in the code samples. This is PowerShell’s line continuation character. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. I have a section dedicated to the line continuation character in my post Fun With PowerShell Pipelined Functions if you want to learn more.

To run a snippet of code highlight the lines (or parts of a line) you want to execute, then in VSCode press F8 or in the IDE F5. You can display the contents of any variable by highlighting it and using F8/F5.

Starting Point

As a reminder, this is our demo class TwittererRedux as we left it at the end of the last post. We’ll start from it in this post.

class TwittererRedux
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2022.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Overloaded Function that returns a string
  [string] TwitterURL($twitterHandle)
  {
    $this.TwitterHandle = $twitterHandle
    $url = $this.TwitterURL()
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

  # Can launch a twitter page without instantiating the class
  static [void] OpenTwitterPage([string] $TwitterHandle)
  {
    # Note here we cannot call the $this.TwitterUrl function
    # because no object exists (hence no $this)
    $url = "http://twitter.com/$($TwitterHandle)"
    Start-Process $url
  }

}

# Create a new instance and update the handle
$twit = [TwittererRedux]::new()
$twit.TwitterHandle = 'ArcaneCode'

Constructors

In the original version of our class, we created a new instance of the class by calling the ::new() static method. Then, we assigned a value to our TwitterHandle property. Wouldn’t it have been nice to do it all in one step?

Well that is possible through the use of a constructor. A constructor is a function that gets run automatically when the object is instantiated, as part of the ::new() static method. Every class gets a constructor automatically, it’s just empty.

Below is an example of our class with a constructor. For brevity I’m omitting most of the class code, and will reproduce the full class sample at the end of this post.

class TwittererRedux
{
  # Default Constructor
  TwittererRedux ()
  {
  }

  # Create a property
  [string]$TwitterHandle

  # ... rest of class goes here

A constructor is a function, with the exact same name as the class. As you can see in the sample above, TwitterRedux is both the name of the class, as well as of the function.

In this case the function is empty, it doesn’t do anything, which is what the behavior of the default constructor should be.

Having constructors allows us to create more compact code. For example, in previous examples we create a new instance of our class, then assign a handle to the TwitterHandle property on the next line. Using a constructor we can provide the ability to compress our code, creating a new instance and assigning the TwitterHandle value all in one step.

To do so, we need to create another function with the same name as our class, TwittererRedux.

class TwittererRedux
{
  # Default Constructor
  TwittererRedux ()
  {
  }

  # Constructor passing in Twitter Handle
  TwittererRedux ([string]$TwitterHandle)
  {
    $this.TwitterHandle = $TwitterHandle
  }

  # Create a property
  [string]$TwitterHandle

  # ... rest of class goes here

This technique uses overloading, as discussed in a previous post. Here we have added a single parameter, $TwitterHandle. Within the function we take the value passed in and assign it to the TwitterHandle property for the current instance, represented by $this.

Before I go on, I need to mention an important rule. When you override a constructor, you must manually add the default constructor! You can see that was done in the above sample.

So how do we use it? Pretty simple actually, when we instantiate our object by using new, we pass in the value.

# Create a new instance using an overloaded constructor
$twit = [TwittererRedux]::new('ArcaneCode')

# Display the result
$twit.TwitterHandle

Result:

ArcaneCode

Here when we called new, instead of leaving the parameter area empty we passed in a single string value. PowerShell then followed the rules of overloading as seen in the previous post. It looked over the set of constructors and found one that had a single parameter of type string and executed the code associated with it.

Let’s further expand by adding another constructor so we can assign both the handle and the name when we instantiate a new object from our class.

class TwittererRedux
{
  # Default Constructor
  TwittererRedux ()
  {
  }

  # Constructor passing in Twitter Handle
  TwittererRedux ([string]$TwitterHandle)
  {
    $this.TwitterHandle = $TwitterHandle
  }

  # Constructor passing in Twitter Handle and Name
  TwittererRedux ([string]$TwitterHandle, [string]$Name)
  {
    $this.TwitterHandle = $TwitterHandle
    $this.Name = $Name
  }

  # Create a property
  [string]$TwitterHandle

  # ... rest of class goes here

In our second constructor we have two string parameters, which will be assigned to the handle and name properties of the current object.

$twit = [TwittererRedux]::new('ArcaneCode', 'Mr. Code')
$twit.TwitterHandle
$twit.Name

Result:

ArcaneCode
Mr. Code

The Final Version

As promised, here is the final version of our TwitterRedux class.

class TwittererRedux
{
  # Default Constructor
  TwittererRedux ()
  {
  }

  # Constructor passing in Twitter Handle
  TwittererRedux ([string]$TwitterHandle)
  {
    $this.TwitterHandle = $TwitterHandle
  }

  # Constructor passing in Twitter Handle and Name
  TwittererRedux ([string]$TwitterHandle, [string]$Name)
  {
    $this.TwitterHandle = $TwitterHandle
    $this.Name = $Name
  }

  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2022.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Overloaded Function that returns a string
  [string] TwitterURL($twitterHandle)
  {
    $this.TwitterHandle = $twitterHandle
    $url = $this.TwitterURL()
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

  # Can launch a twitter page without instantiating the class
  static [void] OpenTwitterPage([string] $TwitterHandle)
  {
    # Note here we cannot call the $this.TwitterUrl function
    # because no object exists (hence no $this)
    $url = "http://twitter.com/$($TwitterHandle)"
    Start-Process $url
  }

}

Constructors vs Overloading

Constructors are very similar to overloading a function, but not exactly the same.

Just like overloading, each constructor declaration must be different in terms of the number of parameters and their data types.

Unlike overloads, you have access to the properties and methods of the object. This is what let us assign values to our properties when we created the new instance of the object.

Conclusion

Constructors can aid in making our code more compact, and allowing us to assign values when we instantiate new objects. Be cautious though, it can be tempting to make far more constructors than you need. Ensure that your constructors cover only the most common situations, otherwise you’ll have a confusing mess that you have to document and maintain.

The demos in this series of blog posts were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With PowerShell Classes – Overloading

Introduction

This is the next installment in our series of advanced techniques for PowerShell classes. In the previous installment we saw how to implement static properties and methods. In this one, we’ll see how to overload your methods.

We’ll take a deeper look at overloading in a moment, but first let me mention that for all of the examples we’ll display the code, then under it the result of our code when applicable. In this article I’ll be using PowerShell Core, 7.2.1, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

Additionally, many of the code samples have lines which end in a backtick `, PowerShell’s line continuation character. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. My post Fun With PowerShell Pipelined Functions dedicates a section to the line continuation character if you want to learn more.

In VSCode you can highlight snippet of code then press F8 to execute it. In the IDE, use F5. You can display the contents of any variable by highlighting it and using F8/F5.

Starting Point

For easy reference, here is our demo class as we left it at the end of the previous post.

class TwittererRedux
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2022.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

  # Can launch a twitter page without instantiating the class
  static [void] OpenTwitterPage([string] $TwitterHandle)
  {
    # Note here we cannot call the $this.TwitterUrl function
    # because no object exists (hence no $this)
    $url = "http://twitter.com/$($TwitterHandle)"
    Start-Process $url
  }

}

The Need to Overload a Method

About half way into our class definition we have a method named TwitterURL. This function is pretty simple, it takes the value in the TwitterHandle property, composes the URL to the Twitter site for it, then returns it.

Here is what it would look like in action.

$twit = [TwittererRedux]::new()
$twit.TwitterHandle = 'ArcaneCode'
$twit.TwitterURL()

Result:

http://twitter.com/ArcaneCode

What if we had a lot of handles we wanted to get URLs for. It would be a two step process for each one, first assigning the handle to the TwitterHandle property, then calling the TwitterURL method.

$twit.TwitterHandle = 'ArcaneCode'
$twit.TwitterURL()
$twit.TwitterHandle = 'N4IXT'
$twit.TwitterURL()
$twit.TwitterHandle = 'ArcaneTC'
$twit.TwitterURL()
$twit.TwitterHandle = 'BuckWoodyMSFT'
$twit.TwitterURL()
$twit.TwitterHandle = 'tradney'
$twit.TwitterURL()
$twit.TwitterHandle = 'VKCsh'
$twit.TwitterURL()
$twit.TwitterHandle = 'TechTrainerTime'
$twit.TwitterURL()

Of course these could all be put into an array, or read in from a file and run through a foreach loop, even so it still takes two calls to our $twit object. Wouldn’t it be nice to set the TwitterHandle property and return the URL all in one method call?

But what to call the method? We could go with SetHandleAndGetURL, or perhaps UpdateAndGetTwitter, or even HeyHeresAMethodToSetTheTwitterHandleAndGetTheURL.

But in fact we already have a great name in TwitterURL. Its short and explains just what it does. It’s too bad we can’t use it more than once.

Oh wait, we can!

Implementing an Overloaded Method

To create an overload, create a new function within your class and use the same name. To differentiate, you need to have it accept a different number of parameters. Let’s make this clearer by the example below.

  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  [string] TwitterURL($twitterHandle)
  {
    $this.TwitterHandle = $twitterHandle
    $url = $this.TwitterURL()
    return $url
  }

At the top is our original function. Below it is the overloaded version. In it I pass in a single parameter, the $TwitterHandle. PowerShell can use this to determine which version of the function to call.

If you execute the TwitterURL method with no parameters, the version of the function at the top is executed.

When you call TwitterURL and pass in a parameter, the version of the function at the bottom gets run. In here I first access the TwitterHandle property of the current object (represented by $this) and update it from the parameter $twitterHandle.

Next, I called the original function to get the properly formatted URL from the (newly updated) TwitterHandle property.

I chose to do it this way to demonstrate it is possible to call the original version of a function from its overloaded version. It’s certainly not required though, as the code to format the URL could have been copied from the original function. The downside to this however is if I make a change to one area, I have to make it to both.

Also be aware that setting the TwitterHandle property may be considered a side effect by some. It’s not intuitively obvious this will happen, and some users may think this to be a bug. It’s important then to make sure you document this thoroughly in your documentation.

Let’s see it in action. First, here is the complete class with the new overloaded function added.

class TwittererRedux
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2022.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Overloaded Function that returns a string
  [string] TwitterURL($twitterHandle)
  {
    $this.TwitterHandle = $twitterHandle
    $url = $this.TwitterURL()
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

  # Can launch a twitter page without instantiating the class
  static [void] OpenTwitterPage([string] $TwitterHandle)
  {
    # Note here we cannot call the $this.TwitterUrl function
    # because no object exists (hence no $this)
    $url = "http://twitter.com/$($TwitterHandle)"
    Start-Process $url
  }

}

After running this to get the updated class definition in memory let’s call it both ways.

# Create a new instance
$twit = [TwittererRedux]::new()

# Assign the handle, then call TwitterURL
$twit.TwitterHandle = 'ArcaneCode'
$twit.TwitterURL()

Result:

https://twitter.com/ArcaneCode

Now let’s call the overloaded version.

# Now call the overloaded version
$twit.TwitterURL('N4IXT')

Result:

https://twitter.com/N4IXT

We can also check the TwitterHandle property to ensure it has been updated.

$twit.TwitterHandle

Result:

N4IXT

We could have added additional overloads with more parameters. We could have defined [string] TwitterURL($twitterHandle, $anotherParameter), for example.

Other Ways to Differentiate an Overload

In the previous section I stated PowerShell differentiates overloads by the number of parameters passed into our function. There is one other way PowerShell can differentiate, and that is by the data type of each parameter. Let’s look at this simple example.

class over
{
  [string] hello()
    { return 'hello world' }

  [string] hello([string] $name)
    { return "hello string of $name"}

  [string] hello([int] $number)
    { return "hello integer of $number"}
}

As you can see, my class has three overloads. In the second two, we pass in a single parameter. What makes them different though is the data type for the parameter. In the first we use a string, the second an integer. To be accurate then, we need to say PowerShell can tell which overload to call by the number of parameters and the data type of each.

As should be obvious, you will need to strongly type all of your parameters for this to work, but when it comes to classes that’s not a bad idea anyway.

Just as proof, let’s see these working.

$o = [over]::new()
$o.hello()
$o.hello('mom')
$o.hello(33)

Result:

hello world
hello string of mom
hello integer of 33

You can also mix and match, with multiple data types and parameters. For example…

hello([string] $name, [int] $number)
hello([int] $number,  [string] $name)

Both of these are both valid. Just to reiterate, what you could not do is:

hello([string] $nameA, [int] $number)
hello([string] $nameB, [int] $anotherNumber)

PowerShell can’t make the distinction between the two, as they have the same number of parameters and data types in the same order.

Conclusion

Overloads can be very useful when working with classes. They allow you to continue to use method names you are familiar with yet alter them to meet your changing needs.

In our next installment we’ll cover the topic of class constructors, a way to populate properties when you instantiate your new object from the class definition.

The demos in this series of blog posts were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With PowerShell Classes – Static Properties and Methods

Introduction

Over the last few articles we’ve been covering PowerShell objects. The first article in the series, Fun With PowerShell Classes – The Basics, covered the basics of using of the PowerShell class construct introduced in PowerShell version 5.

If you’ve not read it I’d suggest you do so now as we’ll be building on it in this article, as well as in the next two, as we explore the advanced capabilities of PowerShell classes. In this post we’ll cover the concept of static properties and methods.

Let me mention that for all of the examples we’ll display the code, then under it the result of our code if applicable. In this article I’ll be using PowerShell Core, 7.2, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

At the end of the line in many code samples you may notice a backtick ` character. This is PowerShell’s line continuation character. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. My post Fun With PowerShell Pipelined Functions dedicates a section to the line continuation character if you want to learn more.

To run a snippet of code highlight the lines you want to execute, then in VSCode press F8 or in the IDE F5. You can display the contents of any variable by highlighting it and using F8/F5.

Starting Point

Just as a refresher, here is the class we originally developed.

class Twitterer
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

}

We can create a new instance of the class by calling the static new method built into all PowerShell defined classes. Once created we can start assigning values to its properties.

$twit = [Twitterer]::new()
$twit.TwitterHandle = 'ArcaneCode'
$twit.TwitterHandle

With that reminder, let’s start diving in to some advanced features offered by classes.

Static Properties

In a previous post on creating PowerShell objects from C# code, I introduced the concept of static properties and methods. A static property or method is one that can be called without having to create a new instance of an object from the class.

To call a static method or property, use the name of the class in brackets, then two colons, then the name of the property or method you want to call. In fact, you’ve already done this in creating a new instance of a class when you used [Twitterer]::new().

It’s easy to create your own static properties and methods. Let’s create a new version of our class, and name it TwittererRedux. (Redux is a fancy word meaning ‘bring back’.) Then, we’ll add a new static property to it.

class TwittererRedux
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2022.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

}

In the properties area we add one new property, Version. At the front we added the keyword static. This, of course, is the indicator this property is available even without creating a new instance of the class.

Having a version number is a fairly common and useful thing to include in a class. Let’s see it in use. First though, make sure you’ve executed the above class definition for TwitterRedux to make sure it is memory.

[TwittererRedux]::Version

Result:

2022.01.07.002

Other examples of things you might wish to manifest as static properties include the author name, contact information, copyright, or perhaps a link to documentation or github where your class is stored.

Let’s turn now to the creation of a static method.

Static Methods

In our original version of the class we have a method called OpenTwitter. This accessed the objects function TwitterURL, which returned the formatted Twitter URL for the users handle, stored in the objects TwitterHandle property. Finally it opened up the Twitter page in the default browser.

It could be useful to have a function that would do something similar, only without having to go to the effort of creating a class, then populating the TwitterHandle property just so we could call this function.

We’ll do so by adding a new, static function to the class.

class TwittererRedux
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2021.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

  # Can launch a twitter page without instantiating the class
  static [void] OpenTwitterPage([string] $TwitterHandle)
  {
    # Note here we cannot call the $this.TwitterUrl function
    # because no object exists (hence no $this)
    $url = "http://twitter.com/$($TwitterHandle)"
    Start-Process $url
  }

}

We call the new method similar to the way we accessed our property, when we called the new method to create the class.

[TwittererRedux]::OpenTwitterPage('ArcaneCode')

If your coding went well, your default browser should open to my Twitter page (or whosever you passed in).

Restrictions on Static Properties and Methods

There is one restriction when working with static properties and methods, but it is one you need to know about.

> Static properties and methods must be self contained, and cannot reference other properties and methods of the class.

This makes sense if you think about it a moment. You are only accessing the class definition, i.e. the blue print. Normal properties and methods only exist once an object has been created from a class. While it makes sense, it isn’t intuitively obvious so I wanted to point it out.

A Note on Defining Classes

I just wanted to point out a little "gotcha" when working with classes. Within a single PowerShell script (i.e. PS1 file) you can only define a class once. Normally this is not a big issue.

However, if you are developing a class it’s possible you might want multiple versions of it. For example, at the top of the script you might want your original version.

Under it you might want basically the same code, but you’ve added a new method, or perhaps changed the code to an existing method. You want to do this for testing, so you can compare one version versus the other.

PowerShell, or more precisely VSCode or the PowerShell IDE, will generate an error for the second, third, or more versions in your code warning you it has already been defined.

The fix is pretty simple, you could just comment out all but one of the class definitions, leaving the one you are working with uncommented. Alternatively, you could just create multiple PS1 files and work with different versions in each. VSCode is especially nice for this as it lets you have your PS1 files side by side.

Conclusion

Static properties and methods are not difficult to implement, and can be quite useful. As you create your classes, think about situations where they may benefit from a static property or method.

In the next installment of this series we’ll continue our examination of the PowerShell class type by diving into overloads.

The demos in this series of blog posts were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Fun With PowerShell Objects – Modifying Existing Objects

Introduction

So far we’ve covered a lot in this series on PowerShell Objects. We began by creating a class using the class keyword introduced in PowerShell 5. We then looked at using PSCustomObject to create new objects, then add methods to those objects. In the previous post, we used C# code to create our PowerShell objects.

In this installment, we’ll see how to add properties and methods to existing objects created by someone else. For this example we’ll use objects returned by the Get-ChildItem cmdlet, but we could use any objects.

For all of the examples we’ll display the code, then (when applicable) under it the result of our code. In this article I’ll be using PowerShell Core, 7.2.1, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

In the code samples be on the lookout for the backtick ` , PowerShell’s line continuation character, at the end of many lines. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. My post Fun With PowerShell Pipelined Functions dedicates a section to the line continuation character if you want to learn more.

To run a snippet of code highlight the lines you want to execute, then in VSCode press F8 or in the IDE F5. You can display the contents of any variable by highlighting it and using F8/F5.

Getting The Objects to Work With

As stated in the Introduction, we will use the objects returned by the Get-ChildItem cmdlet to add a new property and method to. We’ll set our location (in my case to the folder where these samples are stored), call the Get-ChildItem cmdlet and store the result in a variable, $items.

Set-Location C:\Users\arcan\OneDrive\BlogPosts\Markdown
$items = Get-ChildItem

Let’s look a the first item in the $items collection to see what its datatype is.

$items[0].GetType()

Result:

IsPublic IsSerial Name     BaseType
-------- -------- ----     --------
True     False    FileInfo System.IO.FileSystemInfo

As you can see, each item is of type FileInfo. Each object contains information about the corresponding file it represents. You can find a full list of properties and methods for the FileInfo class in the online Microsoft Documentation for FileInfo.

Coding Our New Method

For this example, we are going to iterate over our collection of FileInfo objects. To each one, we’ll be adding one property and one method. The property will just be a number that can be used to uniquely identify each object. We’ll look more at that momentarily.

The method we’ll add will analyze the existing Extension property of the FileInfo object. As I’m sure you know, the extension is the data that occurs after the period in the file name, and is used to identify the type of file it is.

Here is the script block for this method.

# Define the custom script property
$script = {

  switch ($this.Extension)
  {
    '.cs'   {$retValue = 'C#'}
    '.md'   {$retValue = 'Markdown'}
    '.ps1'  {$retValue = 'Script'}
    '.psd1' {$retValue = 'Module Definition'}
    '.psm1' {$retValue = 'Module'}
    '.xml'  {$retValue = 'XML File'}
    '.pptx' {$retValue = 'PowerPoint'}
    '.csv'  {$retValue = 'Comma Separated Values file'}
    '.json' {$retValue = 'JavaScript Object Notation data'}
    default {$retValue = 'Sorry dude, no clue.'}
  }

  return $retValue
}

Within the switch statement, I use $this to access the current object. It then access the Extension property of the current object.

It then goes down the list, comparing the extension value to the text to the left of the squiggly brace. If it finds a match, it will run the code inside the script block. This simply sets a return value variable to a more human friendly text representation of the file type.

If no match is found, it runs the code by the default value. For more in the switch statement, see my post Fun With PowerShell Logic Branching.

Adding The Property and Method to the FileInfo Object

Now that our script is defined, it’s time to add it as well as a property to our FileInfo objects. Let’s see the code, then we’ll break it down.

# Create an item count variable
$itemCount = 0

# Iterate over each DirectoryInfo object in the $items collection
foreach($item in $items)
{
  # Add a note property, setting it to the current item counter
  $itemCount++
  $item | Add-Member –MemberType NoteProperty `
                     –Name ItemNumber `
                     –Value $itemCount

  # Add script property to the individual file object
  Add-Member -InputObject $item `
             -MemberType ScriptMethod `
             -Name 'ScriptType' `
             -Value $script

  # Now display the already existing Name property along with the
  # property and method we just added.
  "$($item.ItemNumber): $($item.Name) = $($item.ScriptType())"
}

We begin by creating a variable, $itemCount. Within the foreach loop we’ll increment it, then use it as our index for the ItemNumber property.

The foreach loop is entered, where we iterate over the collection of FileInfo objects stored in $items. Each time through the loop, the current item is copied into the $item variable. For more info on foreach, see my post Fun With PowerShell Loops.

The next line is straightforward, we simply increment the $itemCount by one.

You’ve seen the Add-Member cmdlet used in recent posts on adding properties and methods to a PSCustomObject, so I won’t delve deep into it here.

The first call to Add-Member takes the current FileInfo object, stored in $item, and adds a new property we’ll name ItemNumber. When we add it, we’ll go ahead and assign the value in $itemCount. Note that this property could be updated at a future time, although we won’t need to for this example.

We then call Add-Member a second time, adding in the script you saw earlier and naming it ScriptType.

Finally, we use string interpolation to build a nicely formatted string with the item number, the name of the file (a native property of the FileInfo object), and finally we call the method we just added ScriptType.

Here is the final output of our effort.

Result:

1: blog-template.md = Markdown
2: fun-with-powershell-classes-the-basics.md = Markdown
3: fun-with-powershell-classes-the-basics.ps1 = Script
4: fun-with-powershell-enum-flags-header.png = Sorry dude, no clue.
5: Fun-With-PowerShell-Enum-Flags.md = Markdown
6: Fun-With-PowerShell-Enums.md = Markdown
7: Fun-With-PowerShell-Objects-Part 1.md = Markdown
8: Fun-With-PowerShell-Objects-Part 1.ps1 = Script
9: Fun-With-PowerShell-Objects-Part 2.md = Markdown
10: Fun-With-PowerShell-Objects-Part 2.ps1 = Script
11: Fun-With-PowerShell-Objects-Part 3.cs = C#
12: Fun-With-PowerShell-Objects-Part 3.md = Markdown
13: Fun-With-PowerShell-Objects-Part 3.ps1 = Script
14: Fun-With-PowerShell-Objects-Part 4.md = Markdown
15: Fun-With-PowerShell-Objects-Part 4.ps1 = Script
16: fun-with-powershell-objects-part-1.png = Sorry dude, no clue.
17: Fun-With-PowerShell-Providers.md = Markdown
18: Fun-With-PowerShell-Write-Debug.md = Markdown
19: Fun-With-PowerShell-Write-Verbose-and-Write-Debug.md = Markdown
20: Fun-With-VSCode-Code-Snippets.md = Markdown
21: IMG_0965.JPG = Sorry dude, no clue.
22: more-fun-with-powershell-enums-header.png = Sorry dude, no clue.
23: More-Fun-With-PowerShell-Enums.md = Markdown
24: More-Fun-With-PowerShell-Enums.ps1 = Script
25: security-for-apartment-dwellers-01.png = Sorry dude, no clue.
26: Security-for-Apartment-Dwellers-header.png = Sorry dude, no clue.
27: Security-for-Apartment-Dwellers.md = Markdown

I author these blog posts in markdown within VSCode first, prior to copying them into my WordPress based blog. This folder contains a markdown file for each post, most of which have one or more accompanying code files associated with it.

I deliberately omitted the image type extensions (.jpg, .png) from the switch statement so you could see the default option being taken.

Persistance

There is an important fact you need to recall when using this technique. The new property and method only exist for the set of FileInfo objects contained in our $items collection. If I were to get another collection, perhaps using $moreItems = Get-ChildItem, the FileInfo objects stored in $moreItems will NOT have our ItemNumber and ScriptType in them.

You will have to explicitly add custom properties and methods to objects each time you need them.

Conclusion

As you can see, this technique offers many possibilities. You could define a script block at the top of your script that does a complex calculation, or perhaps formats data for easier reading by the end user. Then you simply add this script block to the objects you generate.

This makes your code much more readable. The complex part is at the top where we define the script block, out of the way of your loops.

It can also promote code reuse. You can create a separate script with many script blocks that you commonly use. You call that script from the one you are currently developing, and you instantly have a set of new properties and methods that can be added to the set of objects you are working with.

This method provides for extensibility with objects you did not author, or have the source code for. Perhaps you have a compiled PowerShell module that came from a vendor, but it lacks that one method that would, for your unique situation, make it much easier to work with. You can now code that method for yourself and use it.

As time goes on I’m sure you’ll find many situations you can use these techniques to solve problems.

In the next three installments of this series we’ll return to the PowerShell class type. We’ll look at some of the advanced abilities that it offers.

The demos in this series of blog posts were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.