Hello tech world this is TechThoughts
and welcome to this special tech thoughts video series focused on
learning PowerShell. This is an operationally designed series that aims
to get you ramped up and using PowerShell quickly. This is episode seven in the
series: PowerShell Input and Output. As always, if you prefer written
documentation, the corresponding article for this topic can be found on the techthoughts.info blog which i've linked in the description below. Let's go ahead and
get started. Up to this point in the PowerShell series we've been primarily
statically assigning variables different values to use as the input for various
actions. As you start to progress in your PowerShell capabilities though you're
going to want to start taking some more dynamic input. We're going to cover a
couple of different ways to go ahead and do that. So one of the easiest ways to do that of course is to utilize native cmdlets. So we can take the $process variable, load that up with Get-Process,
and depending on what kind of return is loaded in here you could take various
actions. If you haven't checked out the PowerShell logic video, I highly
recommend that you do so. So you can use the output of one cmdlet as the
input for whatever type of action that you're looking to take. So let's see a
more in-depth example of this. So I'll go ahead and load up some code real fast,
and we'll be using the Invoke-WebRequest cmdlet to dynamically
evaluate a webpage, and we can pull some information off that web page to use as
the input for various different activities. So we've got a $webResults
variable here we're running Invoke-WebRequest which is going to allow us to go
ahead and visit a website that we specify. If you're coming from a more
Linux background this is very similar to curl. We're gonna be visiting reddit.com/r/powershell which is the PowerShell subreddit, which is a great PowerShell
resource, I highly recommend that you check it out. Note that I'm visiting the
json version of this website. Because we're dealing with PowerShell, HTML
doesn't do a whole lot for us. We want to work with objects and so i'm visiting
the json version of this because json is structured data, and we can use this to
get it into native PowerShell object format. I'll
be showing you how to do that in just a moment. So I'll go ahead and run this
first line with f8 in our console. That will reach out visit the website and
pull down the raw data information from that. You'll notice that if I pound out
$webResults down here in the console then we've got a couple of different
pieces of information like the status code of visiting the website, we got a
200, and then we've got the content of that JSON that we see here. Content is
what we're gonna be wanting to work with so we're going to drill down to this
particular sub property. So you'll notice in the next line that I have the $rawJSON variable being loaded with the $webResults we just collected dot content. So
we'll actually be dealing with this information that's right here and
ignoring these other sub properties. We're really after the content of that
JSON web page. Now, when dealing with collecting certain types of input
remember that there are a wide variety of different conversion cmdlets. So
we've collected some JSON from this web page, but we do have that ConvertFrom-JSON cmdlet that's going to allow us to take that JSON and convert it to
native PowerShell objects which are really easy and nice to work with. There
are several of these different conversion cmdlets and I highly
suggest that you get familiar with all of them. They can really make collecting
different types of datasets very easy to work with. Sso I'll go ahead and load up the $rawJSON variable with that content, and I'll
go ahead and convert that to normal PowerShell object information. And you'll
notice that now that I've run that line that if I pound out $objData down
here that I'm dealing now with native PowerShell objects. I've got a kind sub
property and a data object and if I drill down into that data object you'll
notice that we have further sub properties going down until children. I'm
getting that children from this sub property right here, which is actually
containing the data that we're after from that website. And we can start
seeing that we have some different posts that are inside of these children and
again we have that data sub property so I'm going to drill down even further
into data. And now you can start to see that we actually have all the
information associated with each of the posts that we retrieved from that website. So it's no surprise here that you see that
I've drilled down and loaded the object data data children data inside of this
post's variable. Making it nice and easy to work with. So once we have that post
information that we've retrieved and kind of processed into normal PowerShell
input, what I can start to do now is work with that like I would normal PowerShell
objects. So I can select different things you'll notice we have a variety of
different properties to choose from off of this website. And so we're gonna be
primarily dealing with a title of the post, and the score or the number of up
votes that it's received, and we'll go ahead and sort those posts using Sort-Object by the most popular post. So again using that pipeline to massage
that PowerShell object data to get the information that we're after. So I'm
gonna go ahead and run F8 on this line. Notice now that we have a somewhat more
human readable information set and that's going to be our title, sorted by
our number of up votes. So this is a great way to kind of pull in the top
information from /r/PowerShell on a day to day basis. So, what could you use this
for as far as input? Well, you could start writing some PowerShell logic to email
you the top three posts of the day. You could write PowerShell code to text you
the top post of the day. The sky's the limit but now that you've been shown how
you can dynamically collect input information off the web, you can start
coding a variety of very powerful capabilities. Okay so let's expand on
this example using another great way to get dynamic input which is going to be
the Read-Host cmdlet. So let's load up a quick line, go ahead and separate
this out, and what you can see here is that I am loading up the $numPosts variable and making sure that's an integer type. So we're gonna be collecting a number
from the user that's prompted. And using Read-Host I can prompt the user or
yourself to enter the number of posts to read. If I run this you'll notice that we
get that output in the console here asking us for please enter the number of
posts read. So I can of course type in five or three or
whatever number I wanted, and if we pound out $numPosts down here now, we of course have that integer number. So now that we've used Read-Host to collect that
dynamic input I can use the $posts variable that's still loaded with our
post information that we collected from the reddit website. We'll still select
the object, title and URL this time, and we'll still sort by the most popular
posts. But this time we'll only select those objects that are in the first
number of posts. So first as a parameter of Select-Object which allows you to
select the number of objects that you specify. So this is saying select the
first five because $numPosts is currently loaded with the number five. So
you'll notice if I run this line then we get five outputs because we told it in
using the $numPosts variable to select the first five most popular posts on /r/PowerShell today. Now that you have these URLs you could of course start visiting
these, and you would literally be using PowerShell to make yourself smarter at
PowerShell! Another great way to get dynamic input is from the actual file
system itself. So a lot of times in our operational roles we'll be wanting to
evaluate some type of log file. Let's take a quick look at an example of a log
file. So here I have something that you might see at work, or in your environment
where you have some logging going on for some type of information. You might have
some date information and then a status report. And then sometimes you'll have
some normal operation information then occasionally you might have an error, and
you might want to use PowerShell to identify when these particular types of
errors are starting to happen. So you want to utilize the contents of this
file as the input for your PowerShell functionality. The cmdlet that we'll
be using to accomplish this is going to be Get-Content. So this sample log that I
just showed you is located in C:\Test\sample.log and what I'll do is I'll load
that into a variable using the Get-Content cmdlet. So here we have a
variable, we're getting the content of that particular log. And so you'll notice that if I F8 this, we pound out $logContent down here that it has the exact same
information that I showed you in the actual log. Now we've got that inside
PowerShell and we'll be able to start working with that dynamic input. So if
you were after just the info type things we could of course pipe the contents of
$logContent to Select-String, and we could be looking for the pattern, info.
And if we run this you'll notice that now we ignore the errors and only return
back the normal operation logging. We could of course change that to error, run
it again, and this time we only see the errors. There are a variety of ways to
parse this type of string data. We could for example, if you're more comfortable
with regular expression or regex, we could load up a regex
expression. Regex is something that's a little bit beyond this PowerShell series, but you'll offer encounter it in a variety of different programming
examples. I just want to demonstrate real quick that we can of course take $logContent, pipe it to Select-String again but this time we'll provide it that
regex pattern, and find all matches. And what it's going to do this time, based on those regex is this is the regular expression for an IP address. So this
time we'll load this into memory and this will find any IP address that's
located inside of that log file. If having an IP pop inside the log file is
a critical issue to you, you can very quickly identify that dynamic input, and
take the appropriate action. Again, if you're not familiar with regular
expression, there are a variety of ways to do this. We of course showed this one
up here where we're actually searching for that error pattern, and of course if
you were still needed to go after that IP address we could do something like
use Where-Object, where the contents of $logContent, so the current objects in
the pipeline, is like asterisk dot asterisk dot asterisk dot asterisk. Which is a wild-card
for an IP address. So you'll notice that if I F8 this particular line, it also
discovers those two errors that contain an IP address inside of that particular
string. A word of caution when engaging Get-Content is to become familiar with
the different parameters that are available to Get-Content. One of
those is the raw parameter. So notice here that if I are load in this line, and we'll
change this variable to $raw. That this looks very similar to the one above
but I've specified the - raw parameter. And if I load this in, I want to point
out that this is now a very different set of information. So notice that if I
take in $logContent and pipe that to Get-Member, that there are a variety of note
properties and properties here and that this is of type system.string. If I
take $raw and pipe that to Get-Member. Oops, error there with the GN! That $raw looks very similar in nature, and also appears to be of system type string. But if we
start doing some of those string evaluations that we did a moment ago. So
for example I'll take just the errors. I'll replace $logContent with the $raw
variable. So we'll look in $raw and try to find the errors. I'll go ahead and click
f8. You'll notice that it does not. It returns the entire results of the log
file. And that's because $raw is not the same as $logContent. When we did Get-Content against that log, $logContent actually has those lines separated, so
you'll notice if I do $logContent[0] it contains one line, the first line. And if
I do $logContent[1] that contains the second line. But if I do $raw content 0
notice that it contains the individual character. So this is the raw string
information. You can't parse it or evaluate it in the
same way that you do when the Get-Content does not have the raw parameter
specified. So maybe you want to deal with some files that aren't necessarily just
log txt files. You might have some CSV information on disk, that you need to
evaluate, as your dynamic input. Get-Content can handle a wide
variety of file formats. So here I'm taking a $rawCSV input variable and loading it up with Get-Content C:\Test\tt.csv. If we
take a look at this for a moment this is some TechThoughts information that's
loaded into CSV format. We have the episode, title, link, and video length. So
this just kind of represents you know some CSV information that you might have
inside of your environment. And we can of course load that inside using Get-Content. So we now have that CSV information inside of our variable. Again,
don't forget about those conversion cmdlets. They work for a variety of
different things and we do have a ConvertFrom-CSV cmdlet. So we can take
that $rawCSV input, convert it into normal PowerShell objects. So I'll go
ahead and run this line here where I'll load that up into $objData. Notice now
that $objData is in normal PowerShell object format, and I can start working
with a dynamic input in any way that I see fit. Okay so let's switch over to a
couple of output examples. So far in this series we've been primarily using Write-Host as kind of our example of showing you how to write things out to the
console. And, if you've missed a couple of the earlier videos Write-Host is just
the cmdlet that writes a string out to the console. So if I f8 this here
you'll notice that the text output to console string got written out to the
console. So Write-Host is really easy to implement. Keep in mind that Write-Host
only deals with strings. So if I run a cmdlet and load that into a
variable. This now contains a PowerShell object. So we have the host info object
information loaded up inside of that variable. And if we try to Write-Host
that particular variable you'll notice that it's unable to do so. The best it
could do is kind of convert the object type to a string name, but it didn't
provide us the information that was contained within the object. So make sure
that when you're using Write-Host cmdlet that you're giving it an
actual string. Notice that one of these sub properties is version, and if I come
over here and I take Write-Host and drill down into host info dot version,
that is going to be a string and Write-Host has no problem giving us that
string output. One of the things that's really attractive for a lot of people
about Write-Host is that it has some color capabilities. So we have the
foreground color, where we can change the actual text color, and we have the
background color where we can change the actual background of the text. This is
really great for kind of providing your user or yourself some colored
information for easy reading on the console. You'll notice if I click f8 we
get warnings in yellow, errors are in red, critical errors have that background
color so on and so forth. So Write-Host is really easy to use and those colors
make it really attractive, but beyond just kind of tinkering around and making
sure that your variables have the information you think they do, you really
should not be using Write-Host in any type of production PowerShell. Jeffrey
Snover has a really great blog article where he talks about how Write-Host is actually considered harmful and he advocates for the use of Write-Verbose.
No seriously, don't use Write-Host. We'll be covering Write-Verbose in
later sections of the PowerShell course as we start getting into PowerShell functions, so stay tuned. So again feel free to use Write-Host and kind of like
tinkering around but while it can output to the console, you should
primarily avoid it. The next one up is going to be Write-Output. And if you've
been following along in the series you've already used this quite a bit. And
that's because Write-Output is the default method that PowerShell uses to
get stuff out to the console. So when I write the cmdlet Get-Process and I
f8 that... This output that's being displayed here to the console is
actually using Write-Output. So you've been using that the entire time, you just
may not have realized it. And so what I want to hammer home here is that all
three of these things are accomplishing the exact same thing. So if I run the Get-Process cmdlet that uses Write-Output if I load Get-Process into the
$processes variable and then use Write-Output to write that variable that does the exact same thing. Or, if I just take the $processes variable and just run it
all by itself without specifying Write-Output, it will
also use Write-Output and put that out to the console. Because of the way that
this kind of operates you will very rarely see Write-Output actually written
inside PowerShell code. It's kind of assumed because when you just put the
variable there, just like that, it's accomplishing the exact same thing.
The last output that we're going to be covering is going to be Out-File. So while
it's nice to be able to put things out to the console, occasionally you'll want
to generate some type of logging information, or put the results of
whatever you're collecting into some type of actual file that you can send on
to your security team or whoever desires that particular information. So we can
take our processes, load them up into processes variable and then simply take
that variable and pipe it to Out-File and specify a particular path. You can
leave it just like here and it will assume that path, or you can actually
give it the path parameter, just depends on how verbose you would like to be. So
you'll notice that if I load this up, it's gone ahead and created that
particular file for me in the processesInfo.txt file. So I'll show you a
quick trick where you can go to change directory, C:\Tests and if I type in code, and specify the
process file, it'll actually open up that txt file inside of Visual Studio code!
It's not a surprise here that we see the output that we would normally see in the
console, but now instead represented in a text file. So Out-File is just
basically taking the normal output and you can create a variety of different
file types. Again, don't forget about those conversion cmdlets. They're
super handy when dealing with input and output. So here I'll take that same
process information, but this time I'm going to convert it to CSV type. I'm
gonna put no type information so that it doesn't add any extra header information
to my CSV file, and then I'm going to out that to C:\Test\processes.csv. I'll
go ahead and f8 that now I'll take just a second to convert that
information over to CSV and if I run code processes.csv you can now see
that that output for those processes is now stored in CSV format on my file
system. Those conversion cmdlets again are extremely handy, you could
generate XML information or a variety of different file types and provide this
information in an output file, with the format that the desired team is
requesting. Remember that all of this information is available in written
format on the techthoughts.info blog. If this video helped you out go ahead and click
that like button and subscribe for more TechThoughts videos!