FTP / SFTP monitor for SCOM

In this post we’ll make a script for a FTP / SFTP monitor that can monitor the FTP/SFTP status, by doing the following operations:
-Log in
-Upload a file
-Download a file
-Delete the file

Since Powershell doesn’t have any built-in ftp support I was looking for some alternatives, and since I use WinSCP normally for ftp/sftp I found that they also support Powershell scripting, so why not take advantage of this? This guide was written with great help from WinSCP’s own page: https://winscp.net/eng/docs/library_powershell

Get the SSH fingerprint (This part is only nessary for the SFTP solution). Since we need the SSH fingerprint for logging into the SFTP we can obtain this by connecting to the SFTP from the normal WinSCP interface and doing the following steps:

-Download WINSCP: https://winscp.net/eng/download.php

-Make a SFTP session to the server you wish to monitor and connect to this session

-When the session is open, navigate to: Session->Server/Protocol information

-SSH fingerprintet is written under Server host key fingerprint

-Copy the fingerprint and paste it in the below script where there’s a lot of xx:xx:xx:

 

Obtaining the WinSCP .NET Assembly

Now we need to download WinSCP in a Powershell friendly edition, called the WinSCP .NET Assembly, get it by following this link: https://winscp.net/eng/docs/library_install

The files you need to succesfully run the script from a SCOM server is the .exe file and the .dll file.

 

Preparing the script for SCOM

Since we want to make the script work for SCOM, we need a way to communicate back to SCOM, this can be done by creating a propertybag. But don’t worry, we can also just for test communicate to the command-line, or if you want the final script just to write the result to the commandline, or send an exitcode.

The lines we need to implement a propertybag is the following lines:

Now we want to write either a succes or a failure to the propertybag, this is optained in either finally or the catch of the exception, first the succes:

And the failure:

Ok, so what is happening here? We just set the same parameters in SCOM to some specific string. We set the State to Healthy or Error, and write the exception message to the Description field.

Notice that we can just uncomment the lines if we want them written to the console/commandline on the following lines:

And if we also want exit-codes:

 

Setting the parameters

We also want to define which files to upload and which to delete, we set them in the start by the following lines:

Also I want to make a new file everytime, so I can just login and see when the file was created, and not depending on the test-file always being available, this is done by this line:

 

Please notice that we’re downloading everything in the remote folder, so please make a seperate folder for the test files, so you don’t download everything!

 

Changes if we want to monitor FTP

There’s only two things we need to change, the following two lines:

And we want to change them into:

We just change the connection type to FTP instead of SFTP, and we just uncomment the SSH fingerprint.

 

Below is the full script. Now you just need to set it up as a monitor in SCOM.

 

About the Author:

Casper Lillegård Madsen

5 Comments

  1. Henrik March 4, 2016 at 18:52 - Reply

    Great writeup. I have myself used the sftp utility which don’t need to be installed. You can attach it to the MP as a resource. Then it will be available anywhere you need it.

  2. Amar March 5, 2016 at 4:14 - Reply

    SCOM doesn’t allow powershell scripts to run through the generic two state monitor available in the console.
    Could you please share more details on using this powershell script in SCOM monitor?

  3. ron October 3, 2016 at 10:46 - Reply

    Much easier – use Auto FTP Manager. Full SFTP support, automation, and much more in a easy to use package.
    http://www.deskshare.com/ftp-client.aspx

  4. Jared January 18, 2017 at 17:28 - Reply

    Hello,

    I changed a couple things and now the script is not working. I’m unable to see any errors logged in the Operations Manager or Windows PowerShell event logs. Can you see what I’m doing wrong? I’m also curious to know where the exception message is being derived from; maybe that’s why the alert stays open?

    #param (
    # $localPath = “c:ScriptsWinSCP”, #Path to folder containing the winscp and test files
    # $localFile = “c:ScriptsWinSCPSCOMTestFileSecureFTP.txt”, #File that is used to test writes/reads
    # $remotePath = “/HeartBeat/”, # Path to user folder on ftp server
    # $remoteFile = “/HeartBeat/SCOMTestFileSecureFTP.txt” #< edited. finish editing
    #)

    #Generate propertybag
    $api = New-Object -comObject "MOM.ScriptAPI"
    $PropertyBag = $api.CreatePropertyBag()

    #Generate content for our testfile
    #"Testing" | Out-File "c:ScriptsWinSCPSCOMTestFileSecureFTP.txt"

    # Load WinSCP .NET assembly
    # Use "winscp.dll" for the releases before the latest beta version.
    [Reflection.Assembly]::LoadFrom("\C:Program Files (x86)WinSCPWinSCPnet.dll") | Out-Null #This one works!

    # Session.FileTransferred event handler

    function FileTransferred
    {
    param($e)

    if ($e.Error -eq $Null)
    {
    Write-Host ("Upload of {0} succeeded" -f $e.FileName)
    }
    else
    {
    Write-Host ("Upload of {0} failed: {1}" -f $e.FileName, $e.Error)
    }

    if ($e.Chmod -ne $Null)
    {
    if ($e.Chmod.Error -eq $Null)
    {
    Write-Host ("Permisions of {0} set to {1}" -f $e.Chmod.FileName, $e.Chmod.FilePermissions)
    }
    else
    {
    Write-Host ("Setting permissions of {0} failed: {1}" -f $e.Chmod.FileName, $e.Chmod.Error)
    }

    }
    else
    {
    Write-Host ("Permissions of {0} kept with their defaults" -f $e.Destination)
    }

    if ($e.Touch -ne $Null)
    {
    if ($e.Touch.Error -eq $Null)
    {
    Write-Host ("Timestamp of {0} set to {1}" -f $e.Touch.FileName, $e.Touch.LastWriteTime)
    }
    else
    {
    Write-Host ("Setting timestamp of {0} failed: {1}" -f $e.Touch.FileName, $e.Touch.Error)
    }

    }
    else
    {
    # This should never happen during "local to remote" synchronization
    Write-Host ("Timestamp of {0} kept with its default (current time)" -f $e.Destination)
    }
    }

    # Main script

    try
    {
    $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
    Protocol = [WinSCP.Protocol]::Sftp #secure ftp
    HostName = "#######"
    UserName = "#######"
    Password = "#######"
    SshHostKeyFingerprint = "#######"
    }

    $session = New-Object WinSCP.Session
    try
    {
    # Will continuously report progress of synchronization
    $session.add_FileTransferred( { FileTransferred($_) } )

    # Connect
    $session.Open($sessionOptions)

    $LocalFileName = (Get-Date).tostring("dd-MM-yyyy-hh-mm-ss")
    New-Item -itemType File -Path $localPath -Name ($LocalFileName + ".txt")

    # $stamp = Get-Date -Format "yyyyMMdd"
    # $fileName = "export_$stamp.txt"
    # $remotePath = "/HeartBeat/$fileName"
    # $localPath = "C:Scripts$fileName"
    $localPath = "\#####################"
    $remotePath = "/#######/"
    $remoteFiles = "/#######/*.txt"

    # Synchronize files
    $synchronizationResult = $session.SynchronizeDirectories(
    [WinSCP.SynchronizationMode]::Remote, $localPath, $remotePath, $False)

    # Throw on any error
    $synchronizationResult.Check()

    }
    finally
    {

    #################################################
    #Delete File older than in local directory
    #################################################
    $limit = (Get-Date).AddSeconds(-5)

    Get-ChildItem $LocalPath -Recurse | ? {-not $_.PSIsContainer -and $_.CreationTime -lt $limit} | Remove-Item
    }

    #################################################
    #Delete the file in the remote directory
    #################################################
    try
    {
    # Delete all Files
    $session.RemoveFiles($remoteFiles).Check() #Comment if the *.txt file should not be deleted for testing.
    }
    finally
    {
    # Disconnect, clean up
    $session.Dispose()
    }
    #exit 0 #for testing only
    $PropertyBag.AddValue("State","Healthy")
    }
    catch [Exception]
    {
    #Write-Host $_.Exception.Message #for testing only.
    #exit 1 #for testing only
    $PropertyBag.AddValue("Description",$_.Exception.Message)
    $PropertyBag.AddValue("State","Error")
    }

    $PropertyBag

Leave A Comment