st4evr st4evr - 3 months ago 5
PowerShell Question

PowerShell Script Loop - Alert New Files Created

I am hoping someone with PowerShell experience can ever so kindly advice (if it's possible) how we can loop this script. Unfortunately we are not too familiar with PowerShell script looping just yet and looking for some advice on which would be the best method/way of doing this. We use the script below at work to send an email alert to employees when a new file is created within the noted folder and/or folders beginning on the specified date and it includes a .csv file listing the file which is then removed. We don't need to keep the .csv files generated after the alert is sent. Right now we are copying and pasting the code over and over again and modifying parts of it as new folders are added. However, our concern is that eventually this file would be pages long as folders are added as the weeks/months/years go by and new folders have to be added.

Ideally the only changes we would be making to the script would be the folder paths to look in the network drive, the creation time, and the recipients it will be going to.

Any advise on the best way possible to achieve all or any of this is immensely appreciated.

Thank you very much!!!

$arr = @()
$file = "\\DesignatedNetworkDrive\NewFileFinderReport.csv"
$mesg = "Please see the attached file for a list of files that were created after July 1, 2016."


# Folder: Corporateshare

gci \\DesignatedNetworkDrive\,\\DesignatedNetworkDrive\Corporateshare\ | Where-Object { $_.CreationTime -ge "07/02/2016" } | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty FullName $_.FullName
$obj | Add-Member NoteProperty Length $_.Length
$obj | Add-Member NoteProperty Owner ((Get-ACL $_.FullName).Owner)
$obj | Add-Member NoteProperty LastAccessTime $_.LastAccessTime
$obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
$obj | Add-Member NoteProperty CreationTime $_.CreationTime

$arr += $obj
}
$arr | Export-CSV -notypeinformation "$file"



if ($(get-item -path "$file").length -gt 0) {send-mailmessage -from "sysadmin@something.com" -to "jendoe@something.com","johndoe@something.com" -subject "New File Found in Corporateshare" -body "$mesg" -Attachments "$file" -smtpServer mail.somecompany.com}

Remove-Item $file



# Folder: Administration
$arr = @()
$file = "\\DesignatedNetworkDrive\NewFileFinderReport.csv"
$mesg = "Please see the attached file for a list of files that were created after May 3, 2016."


gci \\DesignatedNetworkDrive\Administration\,\\DesignatedNetworkDrive\Administration\_Private\ | Where-Object { $_.CreationTime -ge "05/04/2016" } | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty FullName $_.FullName
$obj | Add-Member NoteProperty Length $_.Length
$obj | Add-Member NoteProperty Owner ((Get-ACL $_.FullName).Owner)
$obj | Add-Member NoteProperty LastAccessTime $_.LastAccessTime
$obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
$obj | Add-Member NoteProperty CreationTime $_.CreationTime

$arr += $obj
}
$arr | Export-CSV -notypeinformation "$file"



if ($(get-item -path "$file").length -gt 0) {send-mailmessage -from "jendoe@something.com","johndoe@something.com" -body "$mesg" -Attachments "$file" -smtpServer mail.somecompany.com}

Remove-Item $file


# Folder: Procurement
$arr = @()
$file = "\\DesignatedNetworkDrive\NewFileFinderReport.csv"
$mesg = "Please see the attached file for a list of files that were created after May 24, 2016."


gci \\DesignatedNetworkDrive\Procurement\ | Where-Object { $_.CreationTime -ge "05/25/2016" } | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty FullName $_.FullName
$obj | Add-Member NoteProperty Length $_.Length
$obj | Add-Member NoteProperty Owner ((Get-ACL $_.FullName).Owner)
$obj | Add-Member NoteProperty LastAccessTime $_.LastAccessTime
$obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
$obj | Add-Member NoteProperty CreationTime $_.CreationTime

$arr += $obj
}
$arr | Export-CSV -notypeinformation "$file"



if ($(get-item -path "$file").length -gt 0) {send-mailmessage -from "jendoe@something.com","johndoe@something.com" -subject "New File Found in Procurement" -body "$mesg" -Attachments "$file" -smtpServer mail.somecompany.com}

Remove-Item $file

Answer

You'll want to abstract the procedure away into a function.

The key to turning a snippet of PowerShell code into a useful function is to identify the variable parts of it - these should be turned into parameters that you can then pass to the function when calling it.

Looking at your code, very few parts a variable, namely:

  • Creation date
  • Folders paths

Additionally, you may want to allow optional changing of the email recipients, mail subject and SMTP server - these may change in the future, or deviate for specific purposes.

With that in mind, we end up with something like:

function Check-NewFiles 
{
    param(
        [Parameter(Mandatory)]
        [string[]]$Path,
        [Parameter(Mandatory)]
        [datetime]$CreatedAfter,
        [string]$Subject = "New files found",
        [string[]]$Recipients = @("jendoe@something.com","johndoe@something.com"),
        [string]$MailFrom = "noreply@something.com",
        [string]$SmtpServer
    )

    # Folder: Administration
    $arr = @()
    $file = "\\DesignatedNetworkDrive\NewFileFinderReport.csv"
    $mesg = "Please see the attached file for a list of files that were created after {0:MMM dd, yyyy}" -f $CreatedAfter

    Get-ChildItem $Path  | Where-Object { $_.CreationTime -ge $CreatedAfter } | ForEach-Object {
        $obj = New-Object PSObject
        $obj | Add-Member NoteProperty FullName $_.FullName
        $obj | Add-Member NoteProperty Length $_.Length
        $obj | Add-Member NoteProperty Owner ((Get-ACL $_.FullName).Owner)
        $obj | Add-Member NoteProperty LastAccessTime $_.LastAccessTime
        $obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
        $obj | Add-Member NoteProperty CreationTime $_.CreationTime

        $arr += $obj
    }
    $arr | Export-CSV -notypeinformation "$file"



    if ($(get-item -path "$file").length -gt 0) {
        Send-MailMessage -From $MailFrom -body "$mesg" -Attachments "$file" -smtpServer $SmtpServer -To $Recipients
    }

    Remove-Item $file
}

Now you can call it with a single line of code each time:

Check-NewFiles -Path \\DesignatedNetworkDrive\,\\DesignatedNetworkDrive\Corporateshare\ -CreatedAfter '07/02/2016'
Check-NewFiles -Path \\DesignatedNetworkDrive\Administration\,\\DesignatedNetworkDrive\Administration\_Private\ -CreatedAfter '05/04/2016'

For many instances of the same call with a large set of parameters, I like to set up a collection of hashtables with the parameter arguments - that way I can easily loop over the entire collection and call the function while splatting the parameters:

$FoldersToWatch = @(
    # Folder: Corporateshare
    @{
        Path = '\\DesignatedNetworkDrive\','\\DesignatedNetworkDrive\Corporateshare\'
        CreatedAfter = "07/02/2016"
        MailFrom = "sysadmin@something.com" 
        Recipients = "jendoe@something.com","johndoe@something.com"
    },

    # Folder: Administration
    @{
        Path = '\\DesignatedNetworkDrive\Administration\','\\DesignatedNetworkDrive\Administration\_Private\'
        CreatedAfter = "05/04/2016"
        Recipients = "jendoe@something.com","johndoe@something.com"
    },

    # Folder: Procurement
    @{
        Path = '\\DesignatedNetworkDrive\Procurement\'
        CreatedAfter = "05/25/2016"
        Subject = "Procurement, IMPORTANT!"
    }
)

Now, put the above in its own file, call it FoldersToWatch.ps1.

Then, in the script where the Check-NewFiles function is defined, dot-source the FoldersToWatch.ps1 file and then loop through the hashtables and call the function:

# function Check-NewFiles goes up here

. .\FoldersToWatch.ps1

$FoldersToWatch |Foreach-Object {
    Check-NewFiles @_
}

And there you have it. Now anybody can update the parameters in FoldersToWatch.ps1 without messing with script itself and without losing sight of what's going on.


I also want to point out that the way you create a new object with New-Object and Add-Member and then adding it to an array is entirely unnecessary. Use Select-Object and pipe it straight to Export-Csv:

Get-ChildItem $Path |Where-Object { $_.CreationTime -ge $CreatedAfter } |Select-Object FullName,Length,@{Label='Owner';Expression={(Get-Acl $_.FullName).Owner}},LastAccessTime,LastWriteTime,CreationTime |Export-CSV -notypeinformation "$file"

resulting in the final function looking more like:

function Check-NewFiles 
{
    param(
        [Parameter(Mandatory)]
        [string[]]$Path,
        [Parameter(Mandatory)]
        [datetime]$CreatedAfter,
        [string]$Subject = "New files found",
        [string[]]$Recipients = @("jendoe@something.com","johndoe@something.com"),
        [string]$MailFrom = "noreply@something.com",
        [string]$SmtpServer
    )

    $file = "\\DesignatedNetworkDrive\NewFileFinderReport.csv"
    $mesg = "Please see the attached file for a list of files that were created after {0:MMM dd, yyyy}" -f $CreatedAfter

    Get-ChildItem $Path |Where-Object { $_.CreationTime -ge $CreatedAfter } |Select-Object FullName,Length,@{Label='Owner';Expression={(Get-Acl $_.FullName).Owner}},LastAccessTime,LastWriteTime,CreationTime |Export-CSV -notypeinformation "$file"

    if ($(get-item -path "$file").length -gt 0) {
        Send-MailMessage -From $MailFrom -body "$mesg" -Attachments "$file" -smtpServer $SmtpServer -To $Recipients
    }

    Remove-Item $file
}
Comments