Tuesday, June 11, 2019

Monitor Changes in a Folder

I receive a daily email from Powershell.com (www.powershell.com).  When I saw this script I said to myself, "There were times I really could have used this.".  Particularly when I wanted to know what files were affected when running an update.  I saved this file as Run-fileWatcher.ps1:


In this example, I created a folder called Test_Folder


# Change the $folder variable to the folder you want to monitor
$folder = "C:\Test_Folder"
$filter = '*'  # looks for all changes


 try
{
   $fsw = New-Object System.IO.FileSystemWatcher $folder , $filter -ErrorAction Stop # the FileSystemWatcher component watches for changes in the LastWrite and LastAccess properties of a file.
}


catch [System.ArgumentException] # checks for errors
{
   Write-Warning "Oops: $_"
   return
}

$fsw.IncludeSubdirectories = $true
$fsw.NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'

do
{
   $result = $fsw.WaitForChanged([ System.IO.WatcherChangeTypes]::All, 1000)
   if ($result.TimedOut) { continue }
 
   $result


   Write-Host "Change in $($result.Name) - $( $result.ChangeType)"

} while ($true)


While the script was running, I created a file called Test.txt.  Inside the file, I entered "This is a test."  I saved the file to c:\test_folder, and the output was:



When I deleted the file, the output was:



It should be possible to write the output to a file, but I haven't tested that yet.  I will put that on my list, as well as adding modification time and date.


I hope you found this helpful.  Again, go to www.powershell.com and sign up for their email.  Some of their scripts I use regularly.


Later,
Mike E



Tuesday, June 4, 2019

Finding Items in Group Policies

I have had occasion to look for specific information within a client's group policies.  In particular, what drives are being mapped for a group of users.  This client a quite a few organizational units (OUs) and, thus, quite a few groups policies (GPOs).  To simplify this process, I looked for a Powershell solution to help me.  This is what I found:

Param
    (
        # This section is the -StringToFind parameter
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [String]
        $StringToFind

    )

    Begin
    {
    $GPOsToCheck = get-gpo -all |Sort-Object -property displayname # gather all GPO information and sort by displayname
    Write-Host " Checking through" $GPOsToCheck.count "GPO's"
    }
    Process
    {
   

$ListOFAffectedGPOs = @()
$count = 1
$GPOsToCheckCount = $GPOsToCheck.count
foreach ($item in $GPOsToCheck) # go through the list of group policies
{
$Result = Get-GPOReport -name $item.DisplayName -ReportType XML   

if ($Result -match $StringToFind) # check if Result variable information matches the string
{
$ListOFAffectedGPOs += $item.DisplayName
}
else
{

}
Write-Host "$count of $GPOsToCheckCount"
$count++ 
}
Write-Host "List of GPO's that contain $StringToFind"  -ForegroundColor Green
$ListOFAffectedGPOs


    }
    End
    {
    }




The syntax of the script is:
Get-GPOThatContains.ps1 -StringToFind password

Here is the output based on my home AD domain:
PS C:\scripts> .\Get-GPOsThatContain.ps1 -StringToFind password
 Checking through 9 GPO's
1 of 9
2 of 9
3 of 9
4 of 9
5 of 9
6 of 9
7 of 9
8 of 9
9 of 9
List of GPO's that contain password
Default Domain Policy
testdest
TestSource
Windows 7 Screen Lock

The script finds all GPOs, evaluates each policy for the string to find (in this case 'password'), and lists the policies that have that string.


I have found this script very useful when we need to quickly find a GPO entry or when onboarding a new client.


I hope you have found this helpful.  Please let me know if you have any questions.


Mike


Sunday, May 26, 2019

Add Entry to Windows Event Logs

When I have created scripts, that are to used in Production, I put in code that will write events to a specific Event Log so, in the event of a failure, I can use my events as a starting point for resolving the issue.

Writing your own events is relatively easy.  As an example, let's say I have a process that should be running 24x7.  The first thing to do is to look for the process:
$process = get-process MyProcess -ErrorAction SilentlyContinue
The '-ErrorAction SilentlyContinue' is needed because the get-process code would fail if the process isn't running.


The next step is to make the appropriate event log available to add events:
New-EventLog -LogName Application -Source "My Process Code"
The 'Log-Name' parameter needs to have the correct event log.  In this example, we want to add the events to the Application log
The '-Source' parameter is used to give name/label to the event.


At this point, we have the following code for the script:
$process = get-process MyProcess -ErrorAction SilentlyContinue
New-EventLog - LogName Application -Source "My Process Code"


Now that we have information about the process, and have the event log ready, we need to evaluate the $process variable. First, let's check to see if the $process was running:
if($process) {

     Write-EventLog -LogName Application -Source "My Process Code" -EntryType                             information -EventID 3000 -Message "My Process is running"
}
This line of code states that IF $process is true (because there is information in the variable), then write a event into the Application log, the source is My Process Code, the entry type is Information, give the event ID as 3000, and the message is the process is running.


Now, what if $process is empty, meaning the process was not running?  We add and Else statement to handle this situation:
Else {
      Write-EventLog -LogName Application -Source "My Process Code" -EntryType Error -EventID 3001 Message "My Process is not running"
}
As you can see, only change we made were the entry type, the event ID, and the message.


In my scripts, I will add a line, in the Else code, that restarts the process.  So, it would look like this:

Else {
      Write-EventLog -LogName Application -Source "My Process Code" -EntryType Error -EventID 3001 Message "My Process is not running"
      Invoke-expression "C:\Applications\MyProcess.exe"
}


Here is what the complete script will look like:
$process = get-process MyProcess -ErrorAction SilentlyContinue
New-EventLog - LogName Application -Source "My Process Code"


if($process) {

     Write-EventLog -LogName Application -Source "My Process Code" -EntryType                             information -EventID 3000 -Message "My Process is running"

}

Else {
      Write-EventLog -LogName Application -Source "My Process Code" -EntryType Error -EventID 3001 Message "My Process is not running"
      Invoke-expression "C:\Applications\MyProcess.exe"
}
I hope you find this article helpful.  Please feel free to contact me if you have any questions.


Mike


Wednesday, May 22, 2019

I'm coming back

Greetings everyone!

After a lengthy hiatus, I will be coming back with some cool scripts I have been using over the last 14 months.

So, stay tuned!

Mike

Sunday, May 13, 2018

BREAKING NEWS: Powershell isn't always the answer

Greetings!


It is hard for me to say that Powershell isn't the end all, be all for automation.  But, at times, the simplest solution is the best.  In this case it was stopping and starting custom applications.


We have a server that needs to run two custom applications.  We will call these App1 and App2.  These applications have to be running in the foreground.  The first script stopped the applications by using a .bat file that contained the following code:
TASKKILL /IM App1.exe /F
TASKKILL /IM App2.exe /F


The script to start the applications contained the following code:
START C:\app1.exe
START C:\app2.exe


We were using Task Scheduler to run the scripts every morning at 4:00am.  When the tasks ran, the apps would stop but the apps wouldn't start in the foreground.


At first we thought we could create a Powershell script to get the applications to run in the foreground.  Unfortunately, we had the same problem when we created a scheduled task to run the Powershell script.


As we were trying to figure out why this was happening, our boss, Amos, came into the room and asked if he could take a look at the scheduled task.  After a minute he said, "I found the problem. You have to use Run only when user is logged on.  This setting will start the applications in the foreground.  Using Run whether user is logged on or not will force the applications to start in the background."  We made the changes and the applications started correctly.


So, we learned that it is necessary to know how to use the tools that will run your scripts.


I hope you find this useful.


Mike

Monday, April 30, 2018

Web Site Restart

Greetings,
We have had occasion to find a way to restart a web site when we get a notification that the site is unavailable.  The restart process includes restarting the associated application pool (app pool) and the site itself.  In PowerShell, this task is simple to accomplish.  The code is as follows:

$wstate = Get-Website -Name 'Default Web Site'

$pstate = Get-IISAppPool -Name DefaultAppPool

if ($wstate.state -ne "Started") {

Restart-WebAppPool -Name DefaultAppPool

Start-Website -Name 'Default Web Site'



}

We will use the Default Web Site in this example.  The first line ($wstate = Get-Website -Name 'Default Web Site'), gets all the information regarding the default web site, including its state (started, stopped).  The second line ($pstate = Get-IISAppPool -Name DefaultAppPool ) gets the information regarding the app pool for the default web site.

Within the IF statement, if the state of the website ($wstate) is not 'Started' then the app pool is restarted (Restart-WebAppPool) and the web site is started (Start-Website).

To verify the site has started you can add the following commands to start a browser and browse to the site:
$url = 'http://<your web site>'

$ie.Visible = $true

$ie = new-object -ComObject internetexplorer.application

$ie.Navigate($url)

I hope this information is helpful. 

Mike

Monday, April 23, 2018

When did these Pings run?

Greetings.

Took a little break from the blog.  I'm back and I have a script I created for my colleague, Tom.

Tom called me and wanted to know if there was a way to get time stamps when pinging a server.  To clarify, show a time stamp for each return value from the ping. In addition, the script should run like the Ping -t command and dump the output into a text file.

I researched how to ping within Powershell and found that the Test-Connection cmdlet will do what Tom has requested.  I started by creating a variable that will be used by the While loop

$i = 1

Then, we create the While loop
While ($i = 1){


}
This will run the commands within the loop until $i is something other than 1.

Now, we create the body of the script.  We will run the script against Google (www.google.com)

test-connection www.google.com | select @{N='Time';E={[dateTime}::Now}},@{N='Destination';E={$_.address}},replysize,@{N='Time(ms)';E={$_.Responsetime}} | export-csv -append c:\ping.txt

In the 'select' statement:
@{N='Time';E={[dateTime]::Now}}....This will create a column called 'Time' and this column will hold information from the [dateTime]::Now expression

@{N='Destination';E={$_.address}}...this will create a column called 'Destination' containing the information in the $_.address field

replysize...this is the size of the reply pack in Bytes

@{N='Time(ms)';E={$_.ResponseTime}}...this will create a column called 'Time(ms)' containing the information in the $_.ResponseTime field

The last part of the statement will send the output to a file called Ping.txt in a comma-separated value (CSV) format

In the end, the script will look like this:

$i = 1

While ($i = 1) {

test-connection www.google.com | select @{N='Time';E={[dateTime]::Now}},@{N='Destination';E={$_.address}},replysize,@{N='Time(ms)'; E={$_.ResponseTime}} | export-csv -append c:\ping.txt
 
} 

Some of you may be saying, "There isn't any code that changes $i to something other than 1."  And you would be correct.  This script will run until you manually stop the script, in most cases by using CTRL-C.

The output of the script will look like this:
 


 I hope you find this script helpful.  Be sure to comment if you have any questions.

Mike