vCheck for Exchange 2010

imageOne of the main areas I redesigned in vCheck 6 was the new plugin concept, In my mind this was a nice HTML output which could be used for more than just vSphere checks, the plugins could potentially be any product which has a PowerShell snap-in or module, and even some which don’t 🙂

Shortly after the release I was contacted by Phil Randal who had done just this, he has taken the vCheck framework and written some Exchange 2010 plugins, this now turns the vCheck report into a Exchange monitoring report too.  Awesome stuff !

Now you can have a daily email with your Exchange 2010 details and issues.

Phil has added 6 initial Exchange 2010 plugins which add some great details, these include:

  • Basic Server Information
  • Database Statistics
  • Database Status
  • Public Folder Statistics
  • Mailboxes larger than x amount of MB
  • Mailboxes with deleted items above x amount of MB

So if you have Exchange 2010 then be sure to download this version of vCheck and give it a go, after all it doesn’t cost you a thing and could save you work in the future. Make sure you thank Phil for his hard work on Twitter, his account is @philrandal

Example Page

An example of the Exchange 2010 report can be viewed by clicking here.

Download

To download this version of vCheck you can download it here which includes the base script and all exchange plugins.

For more information on the base vCheck script and its framework including a demo of how to use it visit this page.

Plugins

All Exchange Plugins are accessible via the Exchange 2010 plugins page located here.

72 thoughts on “vCheck for Exchange 2010

  1. virtualrol

    Alan, how are you? I wanted to inform you that the link to download the script vCheck for exchange 2010 is not working, a message that the file does not exist is shown

  2. Pingback: vCDAudit for vCloud Director | Virtu-Al.Net

  3. Rob

    We get a boat load of RPC errors when trying to run it. Will investigate, but if someone else has run into it, please advise.

  4. Gerardo

    Hi there. I specify the mailbox server, not the vcenter. Ok. The sript finishes showing the plugin status: (yes for all). That’s all i see. No exchange related status (db, mailboxes, etc). What im doing wrong?

  5. Jonas Rydström

    I installed latest PowerCLI and Exchange 2010 Management Tool pack two days ago and I am now having problems with the DAG Plugin.

    I have the problem with the Status and sort parameter for Get-DatabaseAvailabilityGroup.

    If I just take away the -Status parameter including sort inside the DAG ps1 file it works. And if I am running the Get-DatabaseAvailabilityGroup -Status | Sort Server in Exchange Managementshell it is working but not in normal powershell. Can someone help me. I am a newbie in all theese scripting things.

  6. Duane

    I’m getting this when running the script….

    Sending data to a remote command failed with the following error message: Deserialized objects exceed the memory quota.
    For more information, see the about_Remote_Troubleshooting Help topic.
    + CategoryInfo : OperationStopped: (System.Manageme…pressionSyncJob:PSInvokeExpressionSyncJob) [], PSRe
    motingTransportException
    + FullyQualifiedErrorId : JobFailure

  7. Phil

    The “server to connect” to bit is in vCheck’s GlobalVariables.ps1.

    When using the Exchange Plugins, just type some descriptive text

  8. Phil

    It requires the Exchange Management Shell on the box it is being run on. All my testing’s been done on a Windows 2008 R2 server which has the EMC installed on it.

  9. Ehmsen

    Hi

    I have a question related to VMware View, is there any project on implementing the view in this amazing work ?

    Best regads
    Kenneth Ehmsen

  10. Avram Woroch

    I have a couple of very basic questions:

    1) At the start, it asks for a server to connect to. With the vSphere vCheck this is obvious. With the Exchange one, I’m not clear on what server to point it at.
    2) I assume this needs the Exchange Management Shell or PowerShell components installed on the workstation or should be run on the server?

    Thanks!

  11. Phil

    Tony,

    I don’t have any non-DAG databases to test or develop with, alas.

    Not sure why the DB status is failing, but it should be simple enough to debug.

  12. Mark Hodges

    ok…while trying to figure out what could be happening I completely resetup the scheduled task, enabled logging in the script to event log and its now working.
    The only thing I did change was to change the emailto to an array vs a single account as our D-lists are locked down

  13. Mark Hodges

    I’ve been having a hell of a time getting this to run in a scheduled task on Windows 2008 R2.
    I logged in as scheduled task user, ran through the script the first time, validated that it emailed the report and then I setup the scheduled task with highest privleges, run if someone is logged in or not and when the task runs automatically it sits there in a running state.
    Is there any way to have this log what is happening as I never get any output with this.

  14. Tony

    Great script! Just two things:
    1) It doesn’t report the database status “Exchange 2010 DB Status”- report area has no rows. I have 15 DAG databases, 3 Public Folders and 1 non-DAG database. The DAG and public folder statistics report normally.

    2) Can the scheduled report be saved to a directory plus email?

    Thanks, Tony

  15. Phil

    Hi Greg,

    No idea at all what’s going on there.

    Does it work if you run vCheck from a Powershell prompt and not via PowerGUI?

  16. Greg

    I applied the updates for 2.1Beta4 and I corrected the sort statement however, I am still seeing the following error when the script in Plugin 13 is executed:

    Get-DatabaseAvailabilityGroup : The type initializer for ‘Microsoft.Exchange.Cluster.Replay.
    DiagCore’ threw an exception.
    At H:\WindowsPowerShell\Scripts\VCheckExchange\Plugins\13 Exchange 2010 Database Availablility Groups.ps1:18 char:40
    + $DAGs = Get-DatabaseAvailabilityGroup <<<< -Status |
    + CategoryInfo : NotSpecified: (:) [Get-DatabaseAvailabilityGroup], TypeInitializationException
    + FullyQualifiedErrorId : System.TypeInitializationException,Microsoft.Exchange.Managem
    ent.SystemConfigurationTasks.GetDatabaseAvailabilityGroup

    This appears to be a data type initialization error, which I have not been able to pinpoint. Any thoughts?

  17. Phil

    Greg,

    That one should have been fixed in the 2.1Beta4 download

    The Sort statement should say “Sort Name”, not “Sort Server, Name”

  18. Pingback: vCheck for Exchange 2010 « MidThought's

  19. Greg

    When I run the vCheck.ps1 script using Quest PowerGUI Script Editor, I am encountering the following error message from module 13 (Exchange Database Availability Groups) when stepping through the script:

    Get-DatabaseAvailabilityGroup : The type initializer for ‘Microsoft.Exchange.Cluster.Replay.DiagCore’ threw an exception.
    At H:\WindowsPowerShell\Scripts\VCheckExchange\Plugins\13 Exchange 2010 Database Availablility Groups.ps1:17 char:40
    + $DAGs = Get-DatabaseAvailabilityGroup <<<< -Status | Sort Server, Name + CategoryInfo : NotSpecified: (:) [Get-DatabaseAvailabilityGroup], TypeInitializationException + FullyQualifiedErrorId : System.TypeInitializationException,Microsoft.Exchange.Managem
    ent.SystemConfigurationTasks.GetDatabaseAvailabilityGroup

    I have the plug-ins stored on a mapped network share. Any ideas?

  20. Pingback: Segregating The vCheck Daily Report Scripts - Chris Colotti's Blog

  21. Steve

    @Phil
    So remote reg works.. tried to connect from the server running the script to several exchagne servers with no problems. Also verified that the key does exist as well. This is not that big of a deal..

  22. Phil

    @Steve,

    Try this code block instead where the registry is opened. If we’re unable to open the remote registry we’ll report rollup version as “Unknown” instead

    $Rollups = “Unknown”
    $InstDates = “”
    $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(‘LocalMachine’, $target)
    If ($registry) {
    $installedRollUps = $registry.OpenSubKey($rollUpKey).GetSubKeyNames()
    $Rollups = “”
    foreach ($rollUp in $installedRollUps) {
    $thisRollUp = “$rollUpKey\\$rollUp”
    $Rollups += ($thisRollUp | %{$registry.OpenSubKey($_).getvalue(‘DisplayName’)}) + “, ”
    $InstDates += ($thisRollUp | %{$registry.OpenSubKey($_).getvalue(‘Installed’)}) + “, ”
    }
    }

  23. Phil

    Hi Steve, I’m guessing your servers don’t have remote registry access enabled, or the rollup key doesn’t exist on the boxes.

    You can read the source code to find the key used and regedit on one of the boxes to see if that key exists on the server.

    And try attaching to remote server using regedit.

  24. Steve

    First – Thanks for such great scripts.

    I get the below errors when running (11) Exchange 20xx Basic Server…

    No data shows for either rollups or rollup install date
    Running on Exchange 2007

    3:47:40 PM …Collating Server Details for GHDxxxx
    You cannot call a method on a null-valued expression.
    At C:\Documents and Settings\xxxxxxxxx\Plugins\11 Exchange 2
    0xx Basic Server Information.ps1:44 char:74
    + $installedRollUps = $registry.OpenSubKey($rollUpKey).GetSubKeyNames <<<
    < ()
    + CategoryInfo : InvalidOperation: (GetSubKeyNames:String) [], Ru
    ntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Documents and Settings\xxxxxxxxxxxxx\Plugins\11 Exchange 2
    0xx Basic Server Information.ps1:49 char:71
    + $Rollups += ($thisRollUp | %{$registry.OpenSubKey($_).getvalue <<<< (
    'DisplayName')}) + ", "
    + CategoryInfo : InvalidOperation: (getvalue:String) [], RuntimeE
    xception
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Documents and Settings\xxxxxxxxxxxxxx\Plugins\11 Exchange 2
    0xx Basic Server Information.ps1:50 char:73
    + $InstDates += ($thisRollUp | %{$registry.OpenSubKey($_).getvalue <<<<
    ('Installed')}) + ", "
    + CategoryInfo : InvalidOperation: (getvalue:String) [], RuntimeE
    xception
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Any help on the above?

  25. Phil

    @Chris,

    Morning reports are what motivated me to write these plugins in the first place.

    Fixed in the version to be released later today.

  26. Chris

    Hey guys, fantastic plugin. I have a suggestion, can you add a section that views the databases preference order and if the database is not mounted on its first activation preference server then to flag that up in the report?

    This way you can view if any databases have been mounted overnight to another server ,although your monitoring software should alert on this but still great to get it into a morning report.

    eg. db01 in my dag preference is for EX0201 with copies on 202(pref order 2),203(pref order 3). If it was mounted on 202 then the report would say that the database db01 is not mounted on the correct server.

    but other then that looks like a fantastic plugin for a morning report 😀 , good work.

  27. BVGorp

    @Phil

    Nice new update. When using Ex2k7 I would only change one thing:

    Server column holds ServerName
    Database column holds ServerName\StorageGroupName\DatabaseName

    With this change it only holds the DatabaseName

    Select-Object DisplayName,
    ServerName,
    Database,
    ItemCount,

    to

    Select-Object DisplayName,
    ServerName,
    DatabaseName,
    ItemCount,

    This also works with Ex2k10

  28. BVGorp

    Nice update 🙂

    Did a little change in 16. we have Mailbox and Hub transport on the same server so ServerRole is “Mailbox, HubTransport” and does not equal Mailbox. and it seems Where-Object has some issues with -contains so i made it -like

    $exServers = Get-ExchangeServer -ErrorAction SilentlyContinue |
    Where-Object {$_.IsExchange2007OrLater -eq $True} |
    Where-Object { $_.ServerRole -like “*Mailbox*” } |
    Sort Name

  29. Phil

    Oh, I forgot to mention – the disk space report can now be configured to report on only those drives with <= x% free space.

  30. Phil

    OK, I’ve uploaded Exchange20xx plugins v2 beta 1

    http://www.rebee.clara.net/Exchange20xxPluginsv2Beta1.zip

    The plugins have been renamed, so save your existing ones somewhere else before unpacking.

    Not sure how well this will play with unmounted databases on 2007 or 2010, but I’ve tried to make the code more resilient.

    It tries to address the issues raised so far on here.

    Enjoy.

    Phil

  31. Phil

    @Rob,

    Works for me on Exchange 2010. Script could be hacked to conditionally report stuff which is only valid for mounted databases. I don’t have any unmounted DBs to hand, alas.

    DB Status Script is 2010-only, until someone with exchange 2007 comes up with the equivalent for that environment.

  32. Rob

    Made the changes, however, if you change the $Database.Created to anything else but that, the statistics dont appear in the report. I have also notices that the 94 Exchange 2010 DB Status does not show up in the report either.

  33. Phil

    @Rob

    BVGorp’s fix is probably the right one – change $Database.Name to $Database where it is used in that plugin

  34. Rob

    Phil, BVGorp,

    I made those changes that BVGorp suggested on lines 18,19,22 and i now get the following error instead of the ones i had previously

    Get-MailboxStatistics : The database ‘DREX01\Test SG-DR\Test SG-DR’ to be accessed on server ‘drex01.XXXXX.com’
    is not mounted or is not available.
    At C:\Scripts\vCheckExchange\Plugins\92 Exchange 2010 DB Statistics.ps1:18 char:41
    + $MBCount = @(Get-MailboxStatistics <<<< -Database $Database).Count
    + CategoryInfo : ResourceUnavailable: (DREX01\Test SG-DR\Test SG-DR:MailboxDatabase) [Get-MailboxStatisti
    cs], DatabaseUnavailableException
    + FullyQualifiedErrorId : DEE5E972,Microsoft.Exchange.Management.MapiTasks.GetMailboxStatistics

    Im guessing it can only get statistics off mounted Databases?
    I am running mine on the actual exchange server itself.
    Exchange 2007 Version 8.3 (Build 83.6)

  35. BVGorp

    Running EX2k7 RU6

    $Database.Name is only holding the DB name not the server name.

    Then errors are generated like
    Get-MailboxStatistics : The specified mailbox database “MGT-SRV.domain.local\Mailbox Database 1” does not exist.
    At C:\New Folder\Plugins\92 Exchange 2010 DB Statistics.ps1:19 char:37

    EX2k7 does not have DAG. That probably the reason it needs a server name to check against.

  36. Phil

    @Rob

    Looks like you’re missing a Header.jpg file in your vCheckExchange directory.

    I added the ViewinEntireForest thing because I found it in another script, you can just comment out the affected lines in 90…

    Mailbox name thing could be because of spaces in mailbox name – might be throwing things

    Instead of

    $MBCount = @(Get-MailboxStatistics -Database $Database.Name).Count

    try

    $MBCount = @(Get-MailboxStatistics -Database “$Database.Name”).Count

    or

    $MBCount = @(Get-MailboxStatistics -Database “$($Database.Name)”).Count

    and similarly elsewhere in plugin(s) and see if that helps.

  37. Phil

    @BVGorp

    In a 2010 DAG-based world, the databases are primary, which is why I sorted the way I did, but I do take your point about sort orders. I’ll update it in the weekend.

  38. Phil

    @BVGorp

    I’ve done all my testing on a management box, not on the actual exchange servers, and not seen your problem. Is your Exchange Management Shell up to date with current exchange SPs and hotfix rollups? 2007 or 2010?

  39. BVGorp

    And i adjusted the Sort function to sort by Server name first and by DB name second.

    92 93 94 (2nd to last line)
    Sort DatabaseName => Sort Server,DatabaseName

    95 96 (line 19)
    Sort Name => Sort Server,Name

  40. Rob

    Hi Phil,

    I changed 97 Exchange 2010 Drive Details.ps1 to 91a Exchange 2010 Drive Details and it seems to have done the trick with putting that information at the top.

    But i am guessing that will cause problems going forward should i download an updated version

    These are the only errors i have left that i am trying to figure out:

    ——————————————————————————————-

    Exception calling “ToBase64String” with “1” argument(s): “Value cannot be null.
    Parameter name: inArray”
    At C:\Scripts\vCheckExchange\vCheck.ps1:207 char:27
    + [Convert]::ToBase64String <<<< ($pic)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Property 'ViewEntireForest' cannot be found on this object; make sure it exists and is settable.
    At C:\Scripts\vCheckExchange\Plugins\90 Exchange 20xx Load Snapin.ps1:21 char:26
    + $AdminSessionADSettings. <<<< ViewEntireForest = $( If ($ViewEntireForest) { 1 } Else { 0 } )
    + CategoryInfo : InvalidOperation: (ViewEntireForest:String) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound
    ————————————————————————————————

    Get-MailboxStatistics : The specified mailbox database "ldnex01.XXXX.Com\DR – PLC Q to U Storage Group" does no
    t exist.
    At C:\Scripts\vCheckExchange\Plugins\92 Exchange 2010 DB Statistics.ps1:18 char:41
    + $MBCount = @(Get-MailboxStatistics <<<< -Database $Database.Name).Count
    + CategoryInfo : NotSpecified: (0:Int32) [Get-MailboxStatistics], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : B7A4F35E,Microsoft.Exchange.Management.MapiTasks.GetMailboxStatistics

    Get-MailboxStatistics : The specified mailbox database "ldnex01.XXXX.com\DR – PLC Q to U Storage Group" does no
    t exist.
    At C:\Scripts\vCheckExchange\Plugins\92 Exchange 2010 DB Statistics.ps1:19 char:37
    + $MBAvg = Get-MailboxStatistics <<<< -Database $Database.Name |
    + CategoryInfo : NotSpecified: (0:Int32) [Get-MailboxStatistics], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : B7A4F35E,Microsoft.Exchange.Management.MapiTasks.GetMailboxStatistics

    Get-MailboxStatistics : The specified mailbox database "ldnex01.XXXX.com\DR – PLC Q to U Storage Group" does no
    t exist.
    At C:\Scripts\vCheckExchange\Plugins\92 Exchange 2010 DB Statistics.ps1:22 char:38
    + $DelAvg = Get-MailboxStatistics <<<< -Database $Database.Name |
    + CategoryInfo : NotSpecified: (0:Int32) [Get-MailboxStatistics], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : B7A4F35E,Microsoft.Exchange.Management.MapiTasks.GetMailboxStatistics

    I promise to stop pestering you 🙂 hopefully other people with exchange 2007 will benefit from your great work.

    Thank you again.

  41. BVGorp

    I run this script not on the exchange server itself but on a management box and i had to change 92 a little on lines 18 19 and 22 change “$Database.Name” in “$Database”. Probably need this change also when having multiple DB servers. Don’t know if its EX2007 related but if the change is not made then it searches the DB on the management box.

  42. Phil

    Hi Rob,

    Glad it works now.

    I’ve made your suggested changes to both the DB stats and PF Stats plugins.

    The plugin list is sorted into filename order by vCheck.ps1 before the plugins are run, so that they are always run in a deterministic order.

    What order do you prefer?

  43. Rob

    Phil, what can I say besides thank you and you are my hero. works perfectly. I added the following to the Exchange 2010 DB Statistics:

    “Last Full Backup” = $Database.LastFullBackup
    “Last Incremental Backup”=$Database.LastIncrementalBackup
    Mounted = $Database.mounted

    Quick question:
    Is the plugin numbers the order in which things are displayed in the report? if i wanted to move some things that are at the bottom of the report to the top, do i just give it a lower number?

    Thank you again, really appreciate what you guys have done.

    Thank you again.

  44. Phil

    Hi Rob,

    I’ve put another test build of the plugins here:

    http://www.rebee.clara.net/Exchange2010PluginsTest2.zip

    Note, 91a has been renamed for consistency with the other names.

    I give no Public Folder whitespace info for Exchange 2007; I haven’t figured out how to get it.

    The Mailbox Database Whitespace estimatimation code for Exchange 2007 is a bit at variance with what Exchange 2010 reports, but is probably the best we can get.

    Cheers,

    Phil

  45. Phil

    Rob,

    On line 15 or so in those scripts, remove “-ErrorAction SilentlyContinue” and see what error, if any, occurs.

    Database copy status is probably 2010-only.

    I don’t have any 2007 to test against..

    If you run this in an exchange command shell, it should tell you enough to debug

    $Databases = Get-MailboxDatabase -Status
    foreach($Database in $Databases) { $Database | fl }

  46. Rob

    Phil

    thank you so much. I am getting most of the information, the only bit that is not populating is “Exchange DB Status”

    When I run the script there are a lot of errors that come up, im presuming because it is looking for Exchange 2010?

    Couple of examples are as follows:

    —————————————————
    11:34:49 ..finished calculating Exchange 2010 DB Statistics by Phil Randal v1.6
    You cannot call a method on a null-valued expression.
    At C:\Scripts\vCheckExchange\Plugins\93 Exchange 2010 PF Statistics.ps1:23 char:57
    + “DatabaseSize (GB)” = “{0:n3}” -f ($DBSize.ToBytes <<<< () / 1GB)
    + CategoryInfo : InvalidOperation: (ToBytes:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    —————————————————
    You cannot call a method on a null-valued expression.
    At C:\Scripts\vCheckExchange\Plugins\92 Exchange 2010 DB Statistics.ps1:32 char:43
    + "DatabaseSize (GB)" = $DBSize.ToGB <<<< ()
    + CategoryInfo : InvalidOperation: (ToGB:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
    —————————————————

    11:34:49 ..finished calculating Exchange 2010 PF Statistics by Phil Randal v1.6
    The term 'Get-MailboxDatabaseCopyStatus' is not recognized as the name of a cmdlet, function, scrip
    t file, or operable program. Check the spelling of the name, or if a path was included, verify that
    the path is correct and try again.
    At C:\Scripts\vCheckExchange\Plugins\94 Exchange 2010 DB Status.ps1:18 char:44
    + $Status = Get-MailboxDatabaseCopyStatus <<<< -server $Servername
    + CategoryInfo : ObjectNotFound: (Get-MailboxDatabaseCopyStatus:String) [], CommandNo
    tFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    You will have to forgive me, I am a bit of a n00b.

    Once again, thank you guys very much for the great work

  47. Phil

    Rob,

    Can you try this test version here:

    http://www.rebee.clara.net/Exchange2010PluginsTest.zip

    It should mostly work on Exchange2007. Fixes welcome for the bits which don’t.

    The DAG Information plugin checks for the exchange 2010 snapin first and will silently do nothing in an Exchange 2007 environment.

    I’ve added a separate plugin to load the appropriate exchange snapin as the first plugin.

    Cheers,

    Phil

  48. Phil

    Hi Rob,

    I don’t have an Exchange 2007 server to test against, alas.

    The Exchange 2010 (aka Exchange 14) specific stuff should be easily spotted in the source of the plugins. Checking for either Exchange 2007 or Exchange 2010 in those places will probably work for both Exchange server types.

    Phil

  49. Jeff Patton

    First, LOVE THIS! i’ve written a couple of plugins for what i do in my own job, and once i’m satisifed with them, i’ll email it to you. But i’m having a problem.

    I’m working on some plugins and one of the things I’ve noticed is that if my code returns objects it’s not getting outputted properly.

    $Details |gm -membertype properties

    Will list out the property, but if the object has multiple entries for that it just outputs the property name and a count of the objects inside.

    $Details |gm -MemberType Properties

    TypeName: Selected.System.Management.Automation.PSCustomObject

    Name MemberType Definition
    —- ———- ———-
    name NoteProperty System.String name=IT MessageStats

    outputs

    name 14

    as opposed to

    name: account1
    name: account2

    and so on…

    Am i doing something wrong? I’m about to adjust my code to create a “new” property for each account name, i’d rather not do that as it feels dirty.

    Thanks for your help!

  50. Rob

    Guys, thanks again for providing such great stuff.

    hate to be cheeky, but is there a way to get this to work with exchange 2007?

    Thank you

  51. kenny

    I love it!

    Thank you so much Alan/Phil combo! You guys are great. This came out awesome, and works really fast as well!

    What about querying multiple exchange boxes at once?

  52. Phil

    Due to some last minute changes to the script, the output isn’t exactly what you see in the sample above, but that’s cosmetic only.

    The two size-reporting scripts are controlled by two parameters size, and number to report on.

    This means that you can list all maiboxes over xMB in size or the top n of them. Selecting zero or a negative number for n selects all mailboxes. Use with care 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.