diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57a1574 --- /dev/null +++ b/.gitignore @@ -0,0 +1,196 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studo 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +*.[Cc]ache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt diff --git a/Solution/README.md b/Solution/README.md new file mode 100644 index 0000000..e4ccbdb --- /dev/null +++ b/Solution/README.md @@ -0,0 +1,476 @@ +# check_iis +Internet Information Server check plugin for Icinga2, Icinga, Centreon, Shinken, Naemon and other nagios like systems. + +## General Requirements + +* Windows OS with .NET Framework 3.5 SP1 or newer installed, this plugin comes in flavours for both 3.5 and 4.0+ +* IIS 7.0 or higher, this means Windows Server 2008 or newer. +* This plugin can only check Sites and AppPools locally on the same machine it runs from. Expects Icinga2 agent, NSCP or similar to execute it. +* To run this the agent/executor service must run as admin, user rights is not enough due to requiring admin with elevated rights to read the configuration store from IIS. +* Case sensitivity in matches for Sites and Application Pools. +* Use only the named switches, the single character shortcuts might change in the future or disappear completely. + +## Assumptions +This executable assumes the following: +* That the plugin is able to run as SYSTEM or with a user that has Administrative privileges. +* When you run inventory with the switch to only return running services, this assumes that the Sites and AppPools are in their correct states. The output from this can be used to correctly configure the arguments. + +## Usage: + check_iis.exe - Windows Service Status plugin for Icinga2, Icinga, Centreon, Shinken, Naemon and other nagios like systems. + Version: 0.8 + + a:inventory-apppools Parameter to use to provide inventory instead of checking for the health. + A:inventory-websites Parameter to use to provide inventory instead of checking for the health. + B:check-websites Parameter to use to check the health status of the local Sites + C:check-apppools Parameter to use to check the health status of the local AppPools + E:inv-level Optional: Parameter to change the level of output. Default is 'normal', available options are 'normal','full' + f:inv-format Optional: Parameter to provide output of the inventory in other formats, valid options are 'readable', 'i2conf' and 'json' + F:excluded-sites Optional: Excludes sites from checks and inventory. Provide multiple with spaces between + G:included-sites Optional: Includes sites to check while all other sites are excluded, affects both checks and inventory. Provide multiple with spaces between + h:warn-sites Optional: These sites will return Warning if they are not in the expected state. Provide multiple with spaces between + H:stopped-sites Optional: These sites are checked that they are stopped. Provide multiple with spaces between + I:excluded-apppools Optional: Excludes apppools from checks and inventory. Provide multiple with spaces between + J:included-apppools Optional: Includes apppools to check while all other apppools are excluded, affects both checks and inventory. Provide multiple with spaces between + k:warn-apppools Optional: These apppools will return Warning if they are not in the expected state. Provide multiple with spaces between + K:stopped-apppools Optional: These apppools are checked that they are stopped. Provide multiple with spaces between + L:perfcounter-sites Parameter to use to get perfcounters from local Sites + M:perfcounter-apppools Parameter to use to get perfcounters from local AppPools + v:verbose Parameter to use when trying to figure out why a service is not included, excluded or similarly when the returned output is not as expected + V:single-check Used together with the Icinga2 Auto Apply rules, this is set when there is a single Site or AppPool to check. Do take great care if you use this outside of the auto apply rules. + w:expected-state Optional: Parameter to provide the expected State of the AppPool or Site, used together with --single-check + W:icinga2 Used in the Icinga2 CommandDefinition, returns output and perfcounter to the correct class. Do not use via command line. + x:hide-empty-vars Only print returned data that has values in them, skip empty arrays and vars. + X:inv-running-only Only inventory running Sites and/or AppPools, depending on what has been selected for inventory + +## Examples + +### Monitoring + +#### Application Pools +Monitor Application Pools + + check_iis.exe --check-apppools + +Monitor Application Pools, only monitor Application Pool ".NET v4.5" + + check_iis.exe --check-apppools --included-apppools ".NET v4.5" + +Monitor Application Pools, all should be started but for ".NET v4.5", which is expected to be stopped + + check_iis.exe --check-apppools --stopped-apppools ".NET v4.5" + +Monitor Application Pools, all should be started, if AppPool ".NET v2.0" is in a different state it should only warn + + check_iis.exe --check-apppools --warn-apppools ".NET v2.0" + +Monitor Application Pools, monitor all AppPools but for ".NET v2.0" + + check_iis.exe --check-apppools --excluded-apppools ".NET v2.0" + +Monitor Application Pools, monitor all but for ".NET 2.0" and".NET v4.5", in addition AppPool ".NET v4.5 Classic" should be stopped instead of running + + check_iis.exe --check-apppools --excluded_apppools ".NET v2.0" ".NET v4.5" --stopped-apppools ".NET v4.5 Classic" + +Monitor Application Pools, all AppPools should be running, with performance data + + check_iis.exe --check-apppools --perfcounter-apppools + +Monitor Application Pools, all AppPools should be running, excluded AppPool ".NET v2.0", with performance data + + check_iis.exe --check-apppools --perfcounter-apppools --excluded-apppools ".NET v2.0" + + +#### Sites +Monitor Sites + + check_iis.exe --check-websites + +Monitor Sites, only monitor Site ".NET v4.5" + + check_iis.exe --check-websites --included-sites ".NET v4.5" + +Monitor Sites, all should be started but for ".NET v4.5", which is expected to be stopped + + check_iis.exe --check-websites --stopped-sites ".NET v4.5" + +Monitor Sites, all should be started, if AppPool ".NET v2.0" is in a different state it should only warn + + check_iis.exe --check-websites --warn-sites ".NET v2.0" + +Monitor Sites, monitor all Sites but for ".NET v2.0" + + check_iis.exe --check-websites --excluded-sites ".NET v2.0" + +Monitor Sites, monitor all but for ".NET 2.0" and".NET v4.5", in addition AppPool ".NET v4.5 Classic" should be stopped instead of running + + check_iis.exe --check-websites --excluded-sites ".NET v2.0" ".NET v4.5" --stopped-sites ".NET v4.5 Classic" + +Monitor Sites, all Sites should be running, with performance data + + check_iis.exe --check-websites --perfcounter-sites + +Monitor Sites, all Sites should be running, excluded AppPool ".NET v2.0", with performance data + + check_iis.exe --check-websites --perfcounter-sites --excluded-sites ".NET v2.0" + +#### AppPools and Sites +Monitor Application Pools and Sites (all expected running) + + check_iis.exe --check-websites --apppools + +Monitor Application Pools and Sites with perfcounter + + check_iis.exe --apppools --perfcounter-apppools --check-websites --perfcounter-sites + +Monitor Application Pools (excluded ".NET v2.0") and Sites (excluded "WsusPool") + + check_iis.exe --apppools --perfcounter-apppools --check-websites --perfcounter-sites --excluded-apppools ".NET v2.0" --excluded-sites "WsusPool" + +### Inventory +#### AppPools +Inventory AppPools + + check_iis.exe --inventory-apppools + +Inventory AppPools, only inventory "DefaultAppPool" + + check_iis.exe --inventory-apppools --included-apppools "DefaultAppPool" + +Inventory AppPools, only return the running AppPools + + check_iis.exe --inventory-apppools --inv-running-only + +Inventory AppPools, only return the running AppPools, do not print empty arrays or keys with empty values + + check_iis.exe --inventory-apppools --inv-running-only --hide-empty-vars + +Inventory AppPools, all but ".NET v4.5" + + check_iis.exe --inventory-apppools --excluded-apppools ".NET v4.5" + +#### Sites +Inventory Sites + + check_iis.exe --inventory-websites + +Inventory Sites, only return the running Sites + + check_iis.exe --inventory-websites --inv-running-only + +Inventory Sites, all but "WsusPool" + + check_iis.exe --inventory-sites --excluded-sites "WsusPool" + +Inventory Sites, only inventory "Default Web Site", full inventory + + check_iis.exe --inventory-websites --included-sites "Default Web Site" --inv-level full + +Inventory Sites, only return the running Sites, do not print empty arrays or keys with empty values + + check_iis.exe --inventory-websites --inv-running-only --hide-empty-vars + +Inventory Sites, do not print empty arrays or keys with empty values, return it as icinga2 format configuration. + + check_iis.exe --inventory-websites --hide-empty-vars --inv-format i2conf + + +#### AppPools and Sites + +Inventory AppPools and Sites + + check_iis.exe --inventory-apppools --inventory-websites + +Inventory AppPools and Sites, only running + + check_iis.exe --inventory-apppools --inventory-websites --inv-running-only + +Inventory AppPools and Sites with all recognized details, as json + + check_iis.exe --inventory-apppools --inventory-websites --inv-level full --inv-format json + +Inventory AppPools and Sites with all recognized details, as json, only running + + check_iis.exe --inventory-apppools --inventory-websites --inv-level full --inv-format json --inv-running-only + +Inventory AppPools and Sites with all recognized details, as i2conf output, only running + + check_iis.exe --inventory-apppools --inventory-websites --inv-level full --inv-format i2conf --inv-running-only + +Inventory AppPools and Sites with all recognized details, as readable output, only running, hide empty vars + + check_iis.exe --inventory-apppools --inventory-websites --inv-level full --inv-format readable --inv-running-only --hide-empty-vars + + +## Configuration + +To make this plugin work with Icinga2, NSCP or others, it needs to be configured. +Below are some configuration examples on how to configure this plugin with Icinga2 (agent) and NSCP/NSClient++. + +### Icinga2 agent + +All configuration samples below assumes local configuration on agent, configuration for central use to come. + +#### Command Definition + + object CheckCommand "check_iis" { + import "plugin-check-command" + + command = [ PluginDir + "/check_iis.exe" ] + + arguments = { + "--check-apppools" = { + set_if = "$iis_check_apppools$" + description = "Parameter to use to check the health status of the local Sites" + } + "--check-websites" = { + set_if = "$iis_check_sites$" + description = "Parameter to use to check the health status of the local Sites" + } + "--inventory-apppools" = { + set_if = "$iis_inventory_apppools$" + description = "Parameter to use to provide inventory of apppools instead of checking for the health." + } + "--inventory-websites" = { + set_if = "$iis_inventory_sites$" + description = "Parameter to use to provide inventory of sites instead of checking for the health." + } + "--inv-level" = { + value = "$iis_inventory_level$" + description = "Optional parameter to change the level of output. Default is 'normal', available options are 'normal','full'" + } + "--inv-format" = { + value = "$iis_inventory_format$" + description = "Optional parameter to provide output of the inventory in other formats, valid options are 'readable', 'i2conf' and 'json'" + } + "--excluded-sites" = { + value = "$iis_excluded_sites$" + description = "Optional: Excludes sites from checks and inventory. Provide multiple with spaces between" + } + "--included-sites" = { + value = "$iis_included_sites$" + description = "Optional: Includes sites to check while all other sites are excluded, affects both checks and inventory. Provide multiple with spaces between" + } + "--warn-sites" = { + value = "$iis_warn_sites$" + description = "Optional: These sites will return Warning if they are not in the expected state. Provide multiple with spaces between" + } + "--stopped-sites" = { + value = "$iis_stopped_sites$" + description = "Optional: These sites are checked that they are stopped. Provide multiple with spaces between" + } + "--excluded-apppools" = { + value = "$iis_excluded_apppools$" + description = "Optional: Excludes apppools from checks and inventory. Provide multiple with spaces between" + } + "--included-apppools" = { + value = "$iis_included_apppools$" + description = "Optional: Includes apppools to check while all other apppools are excluded, affects both checks and inventory. Provide multiple with spaces between" + } + "--warn-apppools" = { + value = "$iis_warn_apppools$" + description = "Optional: These apppools will return Warning if they are not in the expected state. Provide multiple with spaces between" + } + "--stopped-apppools" = { + value = "$iis_stopped_apppools$" + description = "Optional: These apppools are checked that they are stopped. Provide multiple with spaces between" + } + "--perfcounter-sites" = { + set_if = "$iis_perfcounter_sites$" + description = "Parameter to use to get basic performance counters for the Sites" + } + "--perfcounter-apppools" = { + set_if = "$iis_perfcounter_apppools$" + description = "Parameter to use to get basic performance counters for the AppPools" + } + "--inv-running-only" = { + set_if = "$iis_inv_running_only$" + description = "Only inventory running Sites and/or AppPools, depending on what has been selected for inventory" + } + "--hide-empty-vars" = { + set_if = "$inv_hide_empty_vars$" + description = "Optional parameter for inventory: Only print returned data that has values in them, skip empty arrays and vars." + } + "--icinga2" = { + set_if = "$iis_icinga2$" + description = "Used in the Icinga2 CommandDefinition, returns output and perfdata to the correct class. Do not use via command line." + } + "--single-check" = { + set_if = "$iis_single_check$" + description = "Used together with the Icinga2 Auto Apply rules, this is set when there is a single Site or AppPool to check. Do take great care if you use this outside of the auto apply rules." + } + "--expected-state" = { + set_if = "$iis_single_check$" + value = "$iis_expected_state$" + description = "Optional: Parameter to provide the expected State of the AppPool or Site, used together with --single-check" + } + "--verbose" = { + set_if = "$iis_verbose$" + description = "Only inventory running Sites and/or AppPools, depending on what has been selected for inventory" + } + } + vars.iis_check_apppools = false + vars.iis_check_sites = false + vars.iis_perfcounter_apppools = false + vars.iis_perfcounter_sites = false + vars.iis_inventory_apppools = false + vars.iis_inventory_sites = false + vars.iis_inv_running_only = false + vars.iis_inv_hide_empty_vars = false + vars.iis_icinga2 = false + vars.iis_verbose = false + vars.iis_expected_state = "Started" + vars.iis_single_check = false + + } + +#### AppPool Monitoring +Monitor all AppPools, does not need any data from inventory. + + apply Service "IIS AppPools"{ + import "generic-service" + + check_command = "check_iis" + + assign where host.name == NodeName + + vars.iis_check_apppools = true + vars.iis_stopped_apppools = "" + } + +Monitor all AppPools, same as above, just added with perfcounter option. + + apply Service "IIS AppPools"{ + import "generic-service" + + check_command = "check_iis" + + assign where host.name == NodeName + + vars.iis_check_apppools = true + vars.iis_perfcounter_apppools = true + vars.iis_stopped_apppools = "" + } + +Monitors AppPools based on inventory data (stored in the host object), this specific apply rule only adds running apppools to monitoring, with perfcounters + + apply Service "IIS AppPoolR " for (name => config in host.vars.inv.iis.apppool) { + import "generic-service" + + check_command = "check_iis" + + vars += config + vars.iis_check_apppools = true + vars.iis_included_apppools = vars.Name + vars.iis_perfcounter_apppools = true + assign where config.State == "Started" + } + +Monitors AppPools based on inventory data (stored in the host object), this specific apply rule only adds stopped apppools to monitoring, with perfcounters + + apply Service "IIS AppPoolS " for (name => config in host.vars.inv.iis.apppool) { + import "generic-service" + + check_command = "check_iis" + + vars += config + vars.iis_check_apppools = true + vars.iis_included_apppools = vars.Name + vars.iis_stopped_apppools = vars.Name + vars.iis_perfcounter_apppools = true + assign where config.State == "Stopped" + } + +Recommended method: Monitors AppPools based on inventory data (stored in the host object), this specific apply rule adds uses the state from inventory, with perfcounters + + apply Service "IIS AppPool " for (name => config in host.vars.inv.iis.apppool) { + import "generic-service" + + check_command = "check_iis" + + vars += config + vars.iis_check_apppools = true + vars.iis_included_apppools = vars.Name + vars.iis_perfcounter_apppools = true + vars.iis_expected_state = vars.State + vars.iis_single_check = true + } + + +#### Monitor Sites +Monitors all Sites, does not use inventory data. + + apply Service "IIS Sites"{ + import "generic-service" + + check_command = "check_iis" + + vars.iis_check_sites = true + vars.iis_perfcounter_sites = true + + assign where host.name == NodeName + + } + +Monitors all Sites, does not use inventory data, the same as above, just added perfcounters. + + apply Service "IIS Sites"{ + import "generic-service" + + check_command = "check_iis" + + vars.iis_check_sites = true + vars.iis_perfcounter_sites = true + assign where host.name == NodeName + + } + +Monitors Sites based on inventory data (stored in the host object), this specific apply rule only adds running sites to monitoring, with perfcounters + + apply Service "IIS SiteR " for (name => config in host.vars.inv.iis.site) { + import "generic-service" + + check_command = "check_iis" + + vars += config + vars.iis_check_sites = true + vars.iis_included_sites = vars.Name + vars.iis_perfcounter_sites = true + assign where config.State == "Started" + } + +Monitors Sites based on inventory data (stored in the host object), this specific apply rule only adds stopped Sites to monitoring, with perfcounters + + apply Service "IIS SiteS " for (name => config in host.vars.inv.iis.site) { + import "generic-service" + + check_command = "check_iis" + + vars += config + vars.iis_check_sites = true + vars.iis_included_sites = vars.Name + vars.iis_stopped_sites = vars.Name + vars.iis_perfcounter_sites = true + assign where config.State == "Stopped" + } + +Recommended method: Monitors Sites based on inventory data (stored in the host object), this specific apply rule adds uses the state from inventory, with perfcounters + + apply Service "IIS Site " for (name => config in host.vars.inv.iis.site) { + import "generic-service" + + check_command = "check_iis" + + vars += config + vars.iis_check_sites = true + vars.iis_included_sites = vars.Name + vars.iis_perfcounter_sites = true + vars.iis_expected_state = vars.State + vars.iis_single_check = true + } + +### NSCP / NSClient++ +ToDo + +Add in nsclient.ini + +Test via check_nrpe or similar to verify. \ No newline at end of file diff --git a/Solution/check_iis.sln b/Solution/check_iis.sln new file mode 100644 index 0000000..4e0d5aa --- /dev/null +++ b/Solution/check_iis.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.22823.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "check_iis", "check_iis\check_iis.csproj", "{DDDAFB7C-C277-45C9-8126-18871A0636CB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Debug|x64.ActiveCfg = Debug|x64 + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Debug|x64.Build.0 = Debug|x64 + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Debug|x86.ActiveCfg = Debug|x86 + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Debug|x86.Build.0 = Debug|x86 + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Release|Any CPU.Build.0 = Release|Any CPU + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Release|x64.ActiveCfg = Release|x64 + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Release|x64.Build.0 = Release|x64 + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Release|x86.ActiveCfg = Release|x86 + {DDDAFB7C-C277-45C9-8126-18871A0636CB}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Solution/check_iis/App.config b/Solution/check_iis/App.config new file mode 100644 index 0000000..40ea42a --- /dev/null +++ b/Solution/check_iis/App.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Solution/check_iis/AppPool.cs b/Solution/check_iis/AppPool.cs new file mode 100644 index 0000000..320d6f2 --- /dev/null +++ b/Solution/check_iis/AppPool.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MonitoringPluginsForWindows +{ + class AppPool + { + public String Name { get; private set; } + public Boolean AutoStart { get; private set; } + public Boolean Enable32bitAppOnWin64 { get; private set; } + public Boolean IsLocallyStored { get; private set; } + public String ManagedPipelineMode { get; private set; } + public String ManagedRuntimeVersion { get; private set; } + public long QueueLength { get; private set; } + public String State { get; private set; } + public String ProcessModelIdentityType { get; private set; } + public TimeSpan ProcessModelIdleTimeout { get; private set; } + public Boolean ProcessModelLoadUserProfile { get; private set; } + public long ProcessModelMaxProcesses { get; private set; } + public Boolean ProcessModelPingingEnabled { get; private set; } + public Int32 ProcessModelPingInterval { get; private set; } + public long ProcessModelPingResponseTime { get; private set; } + public String ProcessModelUserName { get; private set; } + public Boolean CpuSmpAffinitized { get; private set; } + public String FailureLoadBalancerCapabilities { get; private set; } + public Array WorkerProcesses { get; private set; } + + public AppPool(String name, Boolean autoStart, Boolean enable32bitAppOnWin64, Boolean isLocallyStored, + String managedPipelineMode, String managedRuntimeVersion, long queueLength, String state, + String processModelIdentityType, TimeSpan processModelIdleTimeout, + Boolean processModelLoadUserProfile, long processModelMaxProcesses, Boolean processModelPingingEnabled, + String processModelUserName, Boolean cpuSmpAffinitized, String failureLoadBalancerCapabilities, + Array workerProcesses) + { + Name = name; + AutoStart = autoStart; + Enable32bitAppOnWin64 = enable32bitAppOnWin64; + IsLocallyStored = isLocallyStored; + ManagedPipelineMode = managedPipelineMode; + ManagedRuntimeVersion = managedRuntimeVersion; + QueueLength = queueLength; + State = state; + ProcessModelIdentityType = processModelIdentityType; + ProcessModelIdleTimeout = processModelIdleTimeout; + ProcessModelLoadUserProfile = processModelLoadUserProfile; + ProcessModelMaxProcesses = processModelMaxProcesses; + ProcessModelPingingEnabled = processModelPingingEnabled; + ProcessModelPingResponseTime = ProcessModelPingResponseTime; + ProcessModelUserName = processModelUserName; + CpuSmpAffinitized = cpuSmpAffinitized; + FailureLoadBalancerCapabilities = failureLoadBalancerCapabilities; + WorkerProcesses = workerProcesses; + } + } + + class AppPoolWorkerProcesses + { + public String AppPoolName { get; private set; } + public Boolean IsLocallyStored { get; private set; } + public String State { get; private set; } + public Array ApplicationDomains { get; private set; } + + public AppPoolWorkerProcesses(String appPoolName, Boolean isLocallyStored, String state, Array applicationDomains) + { + AppPoolName = appPoolName; + IsLocallyStored = isLocallyStored; + State = state; + ApplicationDomains = applicationDomains; + } + } + + class AppPoolWPAppDomains + { + public String Id { get; private set; } + public Int32 Idle { get; private set; } + public Boolean IsLocallyStored { get; private set; } + public String PhysicalPath { get; private set; } + public String VirtualPath { get; private set; } + public Array AppPoolWorkerProcesses { get; private set; } + + public AppPoolWPAppDomains(String id, Int32 idle, Boolean isLocallyStored, String physicalPath, String virtualPath, + Array appPoolWorkerProcesses) + { + Id = id; + Idle = idle; + IsLocallyStored = isLocallyStored; + PhysicalPath = physicalPath; + VirtualPath = virtualPath; + AppPoolWorkerProcesses = appPoolWorkerProcesses; + } + } + +} diff --git a/Solution/check_iis/FodyWeavers.xml b/Solution/check_iis/FodyWeavers.xml new file mode 100644 index 0000000..2e6d4a7 --- /dev/null +++ b/Solution/check_iis/FodyWeavers.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Solution/check_iis/IISInventory.cs b/Solution/check_iis/IISInventory.cs new file mode 100644 index 0000000..a55c2af --- /dev/null +++ b/Solution/check_iis/IISInventory.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MonitoringPluginsForWindows +{ + class IISInventory + { + public String IISVersion { get; private set; } + public Array AppPools { get; private set; } + public Array WebSites { get; private set; } + public Array StartedAppPools { get; private set; } + public Array StoppedAppPools { get; private set; } + public Array StartedSites { get; private set; } + public Array StoppedSites { get; private set; } + public IISInventory(String iisVersion, Array appPools, Array webSites, Array startedAppPools, Array stoppedAppPools, Array startedSites, Array stoppedSites) + { + IISVersion = iisVersion; + AppPools = appPools; + WebSites = webSites; + StartedAppPools = startedAppPools; + StoppedAppPools = stoppedAppPools; + StartedSites = startedSites; + StoppedSites = stoppedSites; + } + } + +} diff --git a/Solution/check_iis/IcingaSerializer.cs b/Solution/check_iis/IcingaSerializer.cs new file mode 100644 index 0000000..0579e9e --- /dev/null +++ b/Solution/check_iis/IcingaSerializer.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +class IcingaSerializer +{ + public static string Serialize(object o, bool bSkipEmpty = false, int indent = 0) + { + if (o == null) return "null"; + if (o.GetType() == typeof(String)) return "\"" + ((String)o).Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; + if (o.GetType() == typeof(int)) return ((int)o).ToString(); + if (o.GetType() == typeof(Int32)) return ((Int32)o).ToString(); + if (o.GetType() == typeof(Int64)) return ((Int64)o).ToString(); + if (o.GetType() == typeof(bool)) return ((bool)o).ToString().ToLower(); + if (o.GetType() == typeof(float)) return ((float)o).ToString(); + if (o.GetType() == typeof(TimeSpan)) return (("\"" + (TimeSpan)o).ToString() + "\""); + + string spacer = " "; + string indention = " "; + + for (int i =0; i 0) + { + listOutput += "\n" + spacer + "]"; + } + else + { + listOutput += "]"; + } + return listOutput; + } + + // It's an actual object, so we need to walk the object model for fields to output. + Type oClass = o.GetType(); + string output = ""; + if (indent == 1) + { + output = "{\n"; + } + else + { + output = "\n" + spacer + "{\n"; + } + bool first = true; + + foreach (MemberInfo mi in oClass.GetMembers()) + { + // Skip empty. + if (mi.MemberType == MemberTypes.Property && bSkipEmpty == true) + { + string amIempty = Serialize(oClass.InvokeMember(mi.Name, BindingFlags.GetProperty, null, o, null)); + if (amIempty == "\"\"" || amIempty == "null" || amIempty == "[]" || amIempty == "[\n]") + { + continue; + } + + } + if (mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property) + { + if (!first) + { + output += "\n"; + } + else + { + first = false; + } + + output += spacer + mi.Name + " = "; + + if (mi.MemberType == MemberTypes.Field) + { + output += Serialize(oClass.InvokeMember(mi.Name, BindingFlags.GetField, null, o, null), bSkipEmpty, indent); + } + else if (mi.MemberType == MemberTypes.Property) + { + output += Serialize(oClass.InvokeMember(mi.Name, BindingFlags.GetProperty, null, o, null), bSkipEmpty, indent); + } + } + + } + + output += "\n" + spacer + "}"; + return output; + + } + +} diff --git a/Solution/check_iis/Program.cs b/Solution/check_iis/Program.cs new file mode 100644 index 0000000..e8c8231 --- /dev/null +++ b/Solution/check_iis/Program.cs @@ -0,0 +1,1486 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; +using Microsoft.Win32; +using Microsoft.Web.Administration; +using Icinga; +using Fclp; +using Newtonsoft.Json; + +namespace MonitoringPluginsForWindows +{ + class check_iis + { + + static List listPerfData = new List(); + static List listSiteOutput = new List(); + static List listAppPoolOutput = new List(); + + static List listIISInventory = new List(); + static List listAppPoolsOnComputer = new List(); + static List listSitesOnComputer = new List(); + static List listStoppedSites = new List(); + static List listStartedSites = new List(); + static List listStoppedAppPools = new List(); + static List listStartedAppPools = new List(); + + static string outputAppPools = ""; + static string outputSites = ""; + static string iisVersion = "unspecified"; + + static bool errorAppPools = false; + static bool errorSites = false; + + static bool do_debug = false; + static bool do_verbose = false; + static bool do_i2 = false; + + static bool bDefaultSitesIncluded = false; + static bool bDefaultSitesExcluded = false; + static bool bDefaultSitesStopped = false; + static bool bDefaultSitesWarn = false; + + static bool bDefaultAppPoolsIncluded = false; + static bool bDefaultAppPoolsExcluded = false; + static bool bDefaultAppPoolsStopped = false; + static bool bDefaultAppPoolsWarn = false; + + static int iNumberOfAppPools = 0; + static int iNumberOfStartedAppPools = 0; + static int iNumberOfStoppedAppPools = 0; + static int iNumberOfStoppingAppPools = 0; + static int iNumberOfStartingAppPools = 0; + static int iNumberOfUnknownAppPools = 0; + static int iNumberOfCorrectAppPools = 0; + static int iNumberOfWrongAppPools = 0; + + static int iNumberOfSites = 0; + static int iNumberOfStartedSites = 0; + static int iNumberOfStoppedSites = 0; + static int iNumberOfStoppingSites = 0; + static int iNumberOfStartingSites = 0; + static int iNumberOfUnknownSites = 0; + static int iNumberOfCorrectSites = 0; + static int iNumberOfWrongSites = 0; + + static int Main(string[] args) + { + int returncode = 0; + int temp = 3; + + bool do_inventory_sites = false; + bool do_inventory_apppools = false; + bool do_sites = false; + bool do_apppools = false; + bool do_perfcounter_sites = false; + bool do_perfcounter_apppools = false; + bool do_all_running_only = false; + bool do_skip_empty_vars = false; + bool do_singluar_check = false; + bool do_skip_empty_apppools = false; + + string inventory_format = "readable"; + string inventory_level = "normal"; + string expected_state = "NotSet"; + + string[] excluded_sites = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + string[] included_sites = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + string[] stopped_sites = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + string[] warn_sites = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + + string[] excluded_apppools = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + string[] included_apppools = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + string[] stopped_apppools = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + string[] warn_apppools = new string[] { "thisshouldprobablyneverbeoverwrittenbysomething" }; + + List temp_excluded_sites = new List(); + List temp_included_sites = new List(); + List temp_stopped_sites = new List(); + List temp_warn_sites = new List(); + + List temp_excluded_apppools = new List(); + List temp_included_apppools = new List(); + List temp_stopped_apppools = new List(); + List temp_warn_apppools = new List(); + + var p = GetP(); + + p.Setup('Z', "debug") + .Callback(value => do_debug = value) + .WithDescription("\t\tParameter to to get maximum verbosity (for debugging)") + .SetDefault(false); + + p.Setup('v', "verbose") + .Callback(value => do_verbose = value) + .WithDescription("\tParameter to use when trying to figure out why a service is not included, excluded or similarly when the returned output is not as expected") + .SetDefault(false); + + p.Setup('A', "inventory-websites") + .WithDescription("Parameter to use to provide inventory instead of checking for the health.") + .Callback(value => do_inventory_sites = value); + + p.Setup('a', "inventory-apppools") + .WithDescription("Parameter to use to provide inventory instead of checking for the health.") + .Callback(value => do_inventory_apppools = value); + + p.Setup('B', "check-websites") + .WithDescription("Parameter to use to check the health status of the local Sites") + .Callback(value => do_sites = value); + + p.Setup('C', "check-apppools") + .WithDescription("Parameter to use to check the health status of the local AppPools") + .Callback(value => do_apppools = value); + + p.Setup('f', "inv-format") + .Callback(value => inventory_format = value) + .WithDescription("\tOptional: Parameter to provide output of the inventory in other formats, valid options are 'readable', 'i2conf' and 'json'") + .SetDefault("readable"); + + p.Setup('E', "inv-level") + .Callback(value => inventory_level = value) + .WithDescription("\tOptional: Parameter to change the level of output. Default is 'normal', available options are 'normal','full'") + .SetDefault("normal"); + + p.Setup('X', "inv-running-only") + .WithDescription("Only inventory running Sites and/or AppPools, depending on what has been selected for inventory") + .Callback(value => do_all_running_only = value); + + p.Setup('x', "hide-empty-vars") + .WithDescription("Only print returned data that has values in them, skip empty arrays and vars.") + .Callback(value => do_skip_empty_vars = value); + + p.Setup('W', "icinga2") + .WithDescription("\tUsed in the Icinga2 CommandDefinition, returns output and perfcounter to the correct class. Do not use via command line.") + .Callback(value => do_i2 = value); + + p.Setup('V', "single-check") + .WithDescription("\tUsed together with the Icinga2 Auto Apply rules, this is set when there is a single Site or AppPool to check. Do take great care if you use this outside of the auto apply rules.") + .Callback(value => do_singluar_check = value); + + p.Setup('U', "skip-empty-apppools") + .WithDescription("Optional: Do not inventory AppPools which are empty.") + .Callback(value => do_skip_empty_apppools = value); + + p.Setup('w', "expected-state") + .Callback(value => expected_state = value) + .WithDescription("Optional: Parameter to provide the expected State of the AppPool or Site, used together with --single-check"); + + p.Setup>('F', "excluded-sites") + .WithDescription("Optional: Excludes sites from checks and inventory. Provide multiple with spaces between") + .Callback(items => temp_excluded_sites = items); + + p.Setup>('G', "included-sites") + .WithDescription("Optional: Includes sites to check while all other sites are excluded, affects both checks and inventory. Provide multiple with spaces between") + .Callback(items => temp_included_sites = items); + + p.Setup>('H', "stopped-sites") + .WithDescription("\tOptional: These sites are checked that they are stopped. Provide multiple with spaces between") + .Callback(items => temp_stopped_sites = items); + + p.Setup>('h', "warn-sites") + .WithDescription("\tOptional: These sites will return Warning if they are not in the expected state. Provide multiple with spaces between") + .Callback(items => temp_warn_sites = items); + + p.Setup>('I', "excluded-apppools") + .WithDescription("Optional: Excludes apppools from checks and inventory. Provide multiple with spaces between") + .Callback(items => temp_excluded_apppools = items); + + p.Setup>('J', "included-apppools") + .WithDescription("Optional: Includes apppools to check while all other apppools are excluded, affects both checks and inventory. Provide multiple with spaces between") + .Callback(items => temp_included_apppools = items); + + p.Setup>('K', "stopped-apppools") + .WithDescription("Optional: These apppools are checked that they are stopped. Provide multiple with spaces between") + .Callback(items => temp_stopped_apppools = items); + + p.Setup>('k', "warn-apppools") + .WithDescription("\tOptional: These apppools will return Warning if they are not in the expected state. Provide multiple with spaces between") + .Callback(items => temp_warn_apppools = items); + + p.Setup('L', "perfcounter-sites") + .WithDescription("Parameter to use to get perfcounters from local Sites") + .Callback(value => do_perfcounter_sites = value); + + p.Setup('M', "perfcounter-apppools") + .WithDescription("Parameter to use to get perfcounters from local AppPools") + .Callback(value => do_perfcounter_apppools = value); + + p.SetupHelp("?", "help") + .Callback(text => Console.WriteLine(text)) + .UseForEmptyArgs() + .WithHeader("\t" + System.AppDomain.CurrentDomain.FriendlyName + " - Windows Service Status plugin for Icinga2, Icinga, Centreon, Shinken, Naemon and other nagios like systems.\n\tVersion: " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); + + var result = p.Parse(args); + + if ((do_apppools == false) && (do_inventory_apppools == false) && (do_inventory_sites == false) && (do_sites == false)) + { + // Not going to check Inventory or Checks, no parameters specified for it. + return (int)ServiceState.ServiceUnknown; + } + + if (temp_excluded_sites.Count > 0) + { + excluded_sites = temp_excluded_sites.Select(i => i.ToString()).ToArray(); + PrintArray("excluded_sites", excluded_sites); + } + if (temp_included_sites.Count > 0) + { + included_sites = temp_included_sites.Select(i => i.ToString()).ToArray(); + PrintArray("included_sites", included_sites); + } + if (temp_stopped_sites.Count > 0) + { + stopped_sites = temp_stopped_sites.Select(i => i.ToString()).ToArray(); + PrintArray("stopped_sites", stopped_sites); + } + if (temp_warn_sites.Count > 0) + { + warn_sites = temp_warn_sites.Select(i => i.ToString()).ToArray(); + PrintArray("warn_sites", warn_sites); + } + if (temp_excluded_apppools.Count > 0) + { + excluded_apppools = temp_excluded_apppools.Select(i => i.ToString()).ToArray(); + PrintArray("excluded_apppools", excluded_apppools); + } + if (temp_included_apppools.Count > 0) + { + included_apppools = temp_included_apppools.Select(i => i.ToString()).ToArray(); + PrintArray("included_apppools", included_apppools); + } + if (temp_stopped_apppools.Count > 0) + { + stopped_apppools = temp_stopped_apppools.Select(i => i.ToString()).ToArray(); + PrintArray("stopped_apppools", stopped_apppools); + } + if (temp_warn_apppools.Count > 0) + { + warn_apppools = temp_warn_apppools.Select(i => i.ToString()).ToArray(); + PrintArray("warn_apppools", warn_apppools); + } + + + // Lets see which of the supplied switches changed anything in the include/exclude/stopped context + CalculateDefaultValues(excluded_sites, included_sites, stopped_sites, warn_sites, excluded_apppools, included_apppools, stopped_apppools, warn_apppools); + + // Inventory is blocked from running at the same time as other checks, thus it is run first if specified. + if (do_inventory_apppools == true || do_inventory_sites == true) + { + temp = IisInventory(do_sites, do_apppools, do_inventory_sites, do_inventory_apppools, excluded_apppools, included_apppools, + excluded_sites, included_sites, inventory_format, inventory_level, do_all_running_only, do_skip_empty_vars, do_skip_empty_apppools); + return temp; + } + + if (do_apppools == true) + { + returncode = CheckAllAppPools(returncode, do_perfcounter_apppools, excluded_apppools, included_apppools, stopped_apppools, warn_apppools, do_singluar_check, expected_state); + } + if (do_sites == true) + { + returncode = CheckAllSites(returncode, do_perfcounter_sites, excluded_sites, included_sites, stopped_sites, warn_sites, do_singluar_check, expected_state); + } + + returncode = HandleExitText(returncode); + + return (int)returncode; + + } + + private static FluentCommandLineParser GetP() + { + return new FluentCommandLineParser(); + } + + private static void CalculateDefaultValues(string[] excluded_sites, string[] included_sites, string[] stopped_sites, string[] warn_sites, + string[] excluded_apppools, string[] included_apppools, string[] stopped_apppools, string[] warn_apppools) + { + if (excluded_sites.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default excluded_sites list."); + bDefaultSitesExcluded = true; + } + + if (included_sites.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default included_sites list."); + bDefaultSitesIncluded = true; + } + + if (stopped_sites.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default stopped_sites list."); + bDefaultSitesStopped = true; + } + + if (warn_sites.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default warn_sites list."); + bDefaultSitesWarn = true; + } + + if (excluded_apppools.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default excluded_apppools list."); + bDefaultAppPoolsExcluded = true; + } + + if (included_apppools.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default included_apppools list."); + bDefaultAppPoolsIncluded = true; + } + + if (stopped_apppools.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default stopped_apppools list."); + bDefaultAppPoolsStopped = true; + } + + if (warn_apppools.Contains("thisshouldprobablyneverbeoverwrittenbysomething")) + { + if (do_verbose == true) + Console.WriteLine("INFO: Default warn_apppools list."); + bDefaultAppPoolsWarn = true; + } + return; + } + + private static int HandleExitText(int returncode) + { + + // ORDER the output + string output = ""; + if ((errorAppPools == true) && (errorSites == false)) + { + output = outputAppPools + outputSites; + } + else if ((errorAppPools == false) && (errorSites == true)) + { + output = outputSites + outputAppPools; + } + else + { + output = outputSites + outputAppPools; + } + + // Handle returncode and exit with proper messages. + if (returncode == (int)ServiceState.ServiceOK) + { + output = "OK: " + output; + } + else if (returncode == (int)ServiceState.ServiceWarning) + { + output = "WARNING: " + output; + } + else if (returncode == (int)ServiceState.ServiceCritical) + { + output = "CRITICAL: " + output; + } + else if (returncode == (int)ServiceState.ServiceUnknown) + { + output = "UNKNOWN: " + output; + } + else + { + output = "UNHANDLED: " + output; + } + + string outputLong = ""; + + int x = 1; + if (iNumberOfSites > 1) + { + foreach (string outputW in listSiteOutput) + { + if (x < listSiteOutput.Count) + { + outputLong = outputLong + outputW + "\n"; + } + else + { + outputLong = outputLong + outputW; + } + x++; + } + } + + int y = 1; + if (iNumberOfAppPools > 1) + { + foreach (string outputA in listAppPoolOutput) + { + if (y < listAppPoolOutput.Count) + { + outputLong = outputLong + outputA + "\n"; + } + else + { + outputLong = outputLong + outputA; + } + y++; + } + } + string perfdata = ""; + foreach (string outputP in listPerfData) + { + perfdata = perfdata + outputP; + } + + if (do_i2 == true) + { + CheckResult IcingaOutput = new CheckResult(); + IcingaOutput.State = (ServiceState)returncode; + IcingaOutput.Output = output + outputLong; + IcingaOutput.PerformanceData = perfdata; + } + else + { + Console.WriteLine(output); + if (iNumberOfAppPools > 1 || iNumberOfSites > 1) + Console.WriteLine(outputLong); + Console.WriteLine(" | " + perfdata); + } + + return returncode; + } + + private static void PrintArray(string arrayname, Array array) + { + if (do_debug) + { + Console.WriteLine("DEBUG - Array: " + arrayname); + foreach (var row in array) + { + Console.WriteLine("DEBUG - row: " + row); + } + Console.WriteLine("DEBUG: End of Array: " + arrayname); + } + } + + private static int ReturnCodeMagic(int current_returncode, int suggested_returncode) + { + if (do_debug == true) + { + Console.WriteLine("DEBUG: Current returncode:\t" + current_returncode); + Console.WriteLine("DEBUG: Suggested returncode:\t" + suggested_returncode); + } + + if (current_returncode > suggested_returncode) + { + if (do_debug == true) + { + Console.WriteLine("DEBUG: Current returncode is higher"); + } + return current_returncode; + } + else + { + if (do_debug == true) + { + Console.WriteLine("DEBUG: Suggested returncode is higher"); + } + return suggested_returncode; + } + + } + + private static int IisInventory(bool do_sites, bool do_apppools, bool do_inventory_sites, bool do_inventory_apppools, string[] excluded_apppools, + string[] included_apppools, string[] excluded_sites, string[] included_sites, string inventory_format, string inventory_level, bool do_running_only, + bool do_skip_empty_vars, bool do_skip_empty_apppools) + { + var iisManager = new ServerManager(); + + Array AppPools = new string[] { }; + Array WebSites = new string[] { }; + + bool temp = true; + bool bVerboseInventory = false; + + // Detect if IIS is installed. + if (IisInstalled() == (int)ServiceState.ServiceUnknown) + return (int)ServiceState.ServiceUnknown; + + if (inventory_level == "full" && (do_inventory_apppools || do_inventory_sites)) + bVerboseInventory = true; + + if (do_sites) + { + if (do_verbose) + Console.WriteLine("INFO: Inventory of Sites to check later"); + + temp = InventorySites(iisManager, excluded_sites, included_sites, false, false, false); + + if (temp == false) + return (int)ServiceState.ServiceUnknown; + + } + + if (do_inventory_sites) + { + if (do_verbose) + Console.WriteLine("INFO: Inventory of Sites"); + + temp = InventorySites(iisManager, excluded_sites, included_sites, bVerboseInventory, do_running_only, true); + + if (temp == false) + return (int)ServiceState.ServiceUnknown; + + } + + if (do_apppools) + { + if (do_verbose) + Console.WriteLine("INFO: Inventory of AppPools to check later"); + + temp = InventoryAppPools(iisManager, excluded_apppools, included_apppools, false, false, false, false); + + if (temp == false) + return (int)ServiceState.ServiceUnknown; + + } + + if (do_inventory_apppools) + { + if (do_verbose) + Console.WriteLine("INFO: Inventory of AppPools"); + + temp = InventoryAppPools(iisManager, excluded_apppools, included_apppools, bVerboseInventory, do_running_only, true, do_skip_empty_apppools); + + if (temp == false) + return (int)ServiceState.ServiceUnknown; + + } + + if (do_inventory_apppools || do_inventory_sites) + { + if (do_inventory_apppools) + AppPools = listAppPoolsOnComputer.ToArray(); + + if (do_inventory_sites) + WebSites = listSitesOnComputer.ToArray(); + + listIISInventory.Add(new IISInventory(iisVersion, AppPools, WebSites, listStartedAppPools.ToArray(), listStoppedAppPools.ToArray(), listStartedSites.ToArray(), listStoppedSites.ToArray())); + + if (inventory_format == "json") + temp = InventoryOutputJSON(); + + if (inventory_format == "i2conf") + temp = InventoryOutputI2Conf(do_inventory_apppools, do_inventory_sites, do_skip_empty_vars, bVerboseInventory); + + if (inventory_format == "readable") + temp = InventoryOutputReadable(do_inventory_apppools, do_inventory_sites, do_skip_empty_vars, bVerboseInventory); + + if (temp == false) + return (int)ServiceState.ServiceUnknown; + } + + return (int)ServiceState.ServiceOK; + } + + private static bool InventoryOutputJSON() + { + + string json = JsonConvert.SerializeObject(listIISInventory, Formatting.Indented); + Console.WriteLine(json); + + return true; + } + + private static bool InventoryOutputI2Conf(bool do_inventory_apppools, bool do_inventory_sites, bool do_skip_empty_vars, bool bVerboseInventory) + { + Dictionary listIISInventoryLocal = listIISInventory.ToDictionary(o => o.IISVersion, o => o); + + foreach (var IISInventoryLocal in listIISInventory) + { + Console.WriteLine("vars.inv.iis.version = \"" + IISInventoryLocal.IISVersion + "\""); + } + string i2conf1 = ""; + if (do_inventory_sites == true) + { + + i2conf1 = IcingaSerializer.Serialize(listStoppedSites.ToArray(), do_skip_empty_vars); + Console.WriteLine("vars.inv.iis.sites.stopped = " + i2conf1); + + i2conf1 = IcingaSerializer.Serialize(listStartedSites.ToArray(), do_skip_empty_vars); + Console.WriteLine("vars.inv.iis.sites.started = " + i2conf1); + + foreach (WebSite Site in listSitesOnComputer.ToArray()) + { + string i2conf = IcingaSerializer.Serialize(Site, do_skip_empty_vars); + Console.WriteLine("vars.inv.iis.site[\"" + Site.Name + "\"] = " + i2conf); + Console.WriteLine(""); + } + } + + if (do_inventory_apppools == true) + { + + i2conf1 = IcingaSerializer.Serialize(listStoppedAppPools.ToArray(), do_skip_empty_vars); + Console.WriteLine("vars.inv.iis.apppools.stopped = " + i2conf1); + + i2conf1 = IcingaSerializer.Serialize(listStartedAppPools.ToArray(), do_skip_empty_vars); + Console.WriteLine("vars.inv.iis.apppools.started = " + i2conf1); + + foreach (AppPool AppPool in listAppPoolsOnComputer.ToArray()) + { + string i2conf = IcingaSerializer.Serialize(AppPool, do_skip_empty_vars); + Console.WriteLine("vars.inv.iis.apppool[\"" + AppPool.Name + "\"] = " + i2conf); + Console.WriteLine(""); + } + + } + + return true; + } + + private static bool InventoryOutputReadable(bool do_inventory_apppools, bool do_inventory_sites, bool do_skip_empty_vars, bool bVerboseInventory) + { + Dictionary listIISInventoryLocal = listIISInventory.ToDictionary(o => o.IISVersion, o => o); + foreach (var IISInventoryLocal in listIISInventory) + { + Console.WriteLine("IISVersion: " + IISInventoryLocal.IISVersion); + } + + if (do_inventory_sites == true) + { + Console.WriteLine("Sites:"); + foreach (WebSite Site in listSitesOnComputer.ToArray()) + { + string readable = ReadableSerializer.Serialize(Site, do_skip_empty_vars); + Console.WriteLine(readable); + } + } + + if (do_inventory_apppools == true) + { + Console.WriteLine("AppPools:"); + foreach (AppPool AppPool in listAppPoolsOnComputer.ToArray()) + { + string readable = ReadableSerializer.Serialize(AppPool, do_skip_empty_vars); + Console.WriteLine(readable); + + } + + } + + return true; + + } + + public static int IisInstalled() + { + try + { + using (RegistryKey iisKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\InetStp")) + { + iisVersion = (int)iisKey.GetValue("MajorVersion") + "." + (int)iisKey.GetValue("MinorVersion"); + + return (int)ServiceState.ServiceOK; + } + } + catch + { + Console.WriteLine("IIS is not installed."); + return (int)ServiceState.ServiceUnknown; + } + } + + private static bool InventorySites(ServerManager iisManager, string[] excluded_sites, string[] included_sites, bool bVerboseInventory, bool do_running_only, bool do_inventory) + { + + SiteCollection sites = iisManager.Sites; + foreach (Site Site in sites) + { + string sSiteName = Site.Name.ToString(); + + if (bDefaultSitesExcluded == true) + { + // No sites to exclude here. + } + else if (excluded_sites.Contains(sSiteName)) + { + if (do_verbose == true) + Console.WriteLine("INFO: Site in exclude list: " + sSiteName); + + continue; + } + if (bDefaultSitesIncluded == true) + { + // No sites to skip here. + } + else if (included_sites.Contains(sSiteName)) + { + if (do_verbose == true) + Console.WriteLine("INFO: Included Site: " + sSiteName); + } + else if (bDefaultSitesIncluded == false) + { + if (do_verbose == true) + Console.WriteLine("INFO: Site not in include list: " + sSiteName); + + continue; + } + + if (do_running_only == true) + { + // Skip any sites (for inventory purposes) that are not started. + if (Site.State != ObjectState.Started) + { + if (do_verbose == true) + Console.WriteLine("INFO: Skipping Site for inventory, it is not running: "); + + continue; + } + + } + + if (do_inventory == true && Site.State == ObjectState.Started) + { + listStartedSites.Add(sSiteName); + } + else if (do_inventory == true && Site.State == ObjectState.Stopped) + { + listStoppedSites.Add(sSiteName); + } + else if (do_inventory == true) + { + Console.WriteLine("AppPool in a bad state during inventory!"); + } + + + Array SiteBindings = InventorySiteBindings(Site, bVerboseInventory); + Array SiteApplications = InventorySiteApplications(Site, bVerboseInventory); + + listSitesOnComputer.Add(new WebSite(Site.Id, Site.Name, Site.ServerAutoStart, Site.IsLocallyStored, Site.State.ToString(), Site.LogFile.Directory, + Site.LogFile.Enabled, SiteBindings, SiteApplications)); + + } + return true; + } + + private static bool InventoryAppPools(ServerManager iisManager, string[] excluded_apppools, string[] included_apppools, bool bVerboseInventory, bool do_running_only, bool do_inventory, bool do_skip_empty_apppools) + { + ApplicationPoolCollection appPools = iisManager.ApplicationPools; + + foreach (ApplicationPool AppPool in appPools) + { + string sAppPoolName = AppPool.Name.ToString(); + + if (bDefaultAppPoolsExcluded == true) + { + // No apppools to exclude here. + } + else if (excluded_apppools.Contains(sAppPoolName)) + { + if (do_verbose == true) + Console.WriteLine("INFO: AppPool in exclude list: " + sAppPoolName); + + continue; + } + + if (bDefaultAppPoolsIncluded == true) + { + // No apppools to check for include, default include list. + } + else if (included_apppools.Contains(sAppPoolName)) + { + if (do_verbose == true) + Console.WriteLine("INFO: Included AppPool: " + sAppPoolName); + } + else if (bDefaultAppPoolsIncluded == false) + { + if (do_verbose == true) + Console.WriteLine("INFO: AppPool not in include list: " + sAppPoolName); + + continue; + } + + if (do_running_only == true) + { + // Skip any apppools (for inventory purposes) that are not started. + if (AppPool.State != ObjectState.Started) + { + if (do_verbose) + Console.WriteLine("INFO: Skipping AppPool for inventory, it is not running: " + sAppPoolName); + continue; + } + } + + if (do_skip_empty_apppools == true && do_inventory == true) + { + int NumberOfTimesUsed = GetNumberApplicationsInAppPool(iisManager, sAppPoolName); + if (NumberOfTimesUsed == 0) + { + if (do_verbose) + Console.WriteLine("INFO: AppPool '" + sAppPoolName + "' is not used by any Applications, this is being skipped due to skip-empty-apppools set"); + // Skip, this AppPool is not used anywhere + continue; + } + } + + Array ArrWorkerProcesses = InventoryWorkerProcesses(AppPool, bVerboseInventory); + + if (do_inventory == true && AppPool.State == ObjectState.Started) + { + listStartedAppPools.Add(sAppPoolName); + } + else if (do_inventory == true && AppPool.State == ObjectState.Stopped) + { + listStoppedAppPools.Add(sAppPoolName); + } + else if (do_inventory == true) + { + Console.WriteLine("AppPool in a bad state during inventory!"); + } + + listAppPoolsOnComputer.Add(new AppPool(AppPool.Name, AppPool.AutoStart, AppPool.Enable32BitAppOnWin64, AppPool.IsLocallyStored, + Enum.GetName(typeof(ManagedPipelineMode), AppPool.ManagedPipelineMode), AppPool.ManagedRuntimeVersion, AppPool.QueueLength, + Enum.GetName(typeof(ObjectState), AppPool.State), Enum.GetName(typeof(ProcessModelIdentityType), AppPool.ProcessModel.IdentityType), + AppPool.ProcessModel.IdleTimeout, AppPool.ProcessModel.LoadUserProfile, AppPool.ProcessModel.MaxProcesses, AppPool.ProcessModel.PingingEnabled, + AppPool.ProcessModel.UserName, AppPool.Cpu.SmpAffinitized, Enum.GetName(typeof(LoadBalancerCapabilities), AppPool.Failure.LoadBalancerCapabilities) + , ArrWorkerProcesses)); + + } + return true; + } + private static int GetNumberApplicationsInAppPool(ServerManager iisManager , string AppPoolName) + { + int NumberOfTimesUsed = 0; + foreach (Site site in iisManager.Sites) + { + foreach (Application app in site.Applications) + { + if (app.ApplicationPoolName == AppPoolName) + NumberOfTimesUsed++; + } + } + return NumberOfTimesUsed; + } + private static Array InventoryWorkerProcesses(ApplicationPool AppPool, bool bVerboseInventory) + { + List listAppPoolWorkerProcesses = new List(); + + // Skip, verbose inventory is not wanted. + if (bVerboseInventory == false) + return listAppPoolWorkerProcesses.ToArray(); + + foreach (var WorkerProcess in AppPool.WorkerProcesses) + { + + Array temp_AppPoolWorkerProcessAppDomains = InventoryWPAppDomains(WorkerProcess.ApplicationDomains, AppPool, bVerboseInventory); + + listAppPoolWorkerProcesses.Add(new AppPoolWorkerProcesses(WorkerProcess.AppPoolName, WorkerProcess.IsLocallyStored, + WorkerProcess.State.ToString(), temp_AppPoolWorkerProcessAppDomains)); + } + + Array ArrWorkerProcesses = listAppPoolWorkerProcesses.ToArray(); + return ArrWorkerProcesses; + } + + private static Array InventoryWPAppDomains(ApplicationDomainCollection WPAppDomains, ApplicationPool AppPool, bool bVerboseInventory) + { + List listAppPoolWPApplicationDomains = new List(); + + // Skip, verbose inventory is not wanted. + if (bVerboseInventory == false) + return listAppPoolWPApplicationDomains.ToArray(); + + foreach (var ApplicationDomain in WPAppDomains) + { + + //Array ArrWorkerProcesses = InventoryWorkerProcesses(AppPool, bVerboseInventory); + Array ArrWorkerProcesses = new Array[] { null }; + listAppPoolWPApplicationDomains.Add(new AppPoolWPAppDomains(ApplicationDomain.Id, ApplicationDomain.Idle, + ApplicationDomain.IsLocallyStored, ApplicationDomain.PhysicalPath, ApplicationDomain.VirtualPath, ArrWorkerProcesses)); + + } + + Array ArrWPAppDomains = listAppPoolWPApplicationDomains.ToArray(); + return ArrWPAppDomains; + } + + private static Array InventorySiteBindings(Site site, bool bVerboseInventory) + { + List listSiteBindings = new List(); + + foreach (var Binding in site.Bindings) + { + // If there is a returned certificate, this will not be null + string certificateHash = null; + if (Binding.CertificateHash != null) + { + // convert the returned bytestring to hex, so it is easy to check the thumbprint. + certificateHash = BitConverter.ToString(Binding.CertificateHash).Replace("-", string.Empty); + } + + string certificateStore = null; + if (Binding.CertificateStoreName != null) + certificateStore = Binding.CertificateStoreName.ToString(); + + string bindingInformation = null; + if (Binding.BindingInformation.ToString() != null) + bindingInformation = Binding.BindingInformation.ToString(); + + string host = null; + if (Binding.Host.ToString() != null) + host = Binding.Host.ToString(); + + string protocol = null; + if (Binding.Protocol.ToString() != null) + protocol = Binding.Protocol.ToString(); + + try + { + listSiteBindings.Add(new SiteBinding(protocol, bindingInformation, host, certificateHash, + certificateStore, Binding.UseDsMapper, Binding.IsIPPortHostBinding, Binding.IsLocallyStored)); + } + catch (Exception e) + { + Console.WriteLine("Error in reading sitebinding for site '" + site.Name.ToString() + "', protocol '" + protocol + "', error: " + e); + } + + } + + Array ArrSiteBindings = listSiteBindings.ToArray(); + return ArrSiteBindings; + } + + private static Array InventorySiteApplications(Site site, bool bVerboseInventory) + { + List listSiteApplications = new List(); + + // Return empty array, verbose inventory is not specified. + if (bVerboseInventory == false) + return listSiteApplications.ToArray(); + + foreach (var Application in site.Applications) + { + Array VirtualDirectories = InventorySiteAppVirtualDirectories(Application.VirtualDirectories, bVerboseInventory); + listSiteApplications.Add(new SiteApplications(Application.ApplicationPoolName, Application.EnabledProtocols, + Application.IsLocallyStored, Application.Path, VirtualDirectories)); + + } + + Array ArrSiteApplications = listSiteApplications.ToArray(); + return ArrSiteApplications; + } + + private static Array InventorySiteAppVirtualDirectories(VirtualDirectoryCollection Application, bool bVerboseInventory) + { + List listVirtualDirectories = new List(); + + // Return empty array, verbose inventory is not specified. + if (bVerboseInventory == false) + return listVirtualDirectories.ToArray(); + + foreach (var VirtualDirectory in Application) + { + listVirtualDirectories.Add(new SiteAppVirtualDirectories(VirtualDirectory.Path, VirtualDirectory.PhysicalPath, + VirtualDirectory.IsLocallyStored, VirtualDirectory.LogonMethod.ToString(), VirtualDirectory.UserName)); + } + + Array ArrSiteAppVirtDirs = listVirtualDirectories.ToArray(); + return ArrSiteAppVirtDirs; + } + + public static int CheckAllSites(int returncode, bool do_perfcounter_sites, string[] excluded_sites, + string[] included_sites, string[] stopped_sites, string[] warn_sites, bool do_singluar_check, + string expected_state) + { + var iisManager = new ServerManager(); + + bool temp = false; + temp = InventorySites(iisManager, excluded_sites, included_sites, false, false, false); + if (temp == false) + return (int)ServiceState.ServiceUnknown; + + // If Expected State is set, we use a different logic further down. + bool bExpectedStateSet = false; + if (expected_state != "NotSet") + bExpectedStateSet = true; + + string output; + + Dictionary listOverSites = listSitesOnComputer.ToDictionary(o => o.Name, o => o); + + // Loop for each site in sites + foreach (var site in listOverSites.Values) + { + bool bSiteShouldBeStopped = false; + bool bSiteShouldBeWarned = false; + + string sSiteName = site.Name; + + if (bDefaultSitesStopped == true) + { + // No need to evaluate any further + } + else if (stopped_sites.Contains(sSiteName)) + { + if (do_verbose == true) + { + Console.WriteLine("INFO: Site in stopped list: " + sSiteName); + } + bSiteShouldBeStopped = true; + } + + if (bDefaultSitesWarn == true) + { + // No need to evaluate any further + } + else if (warn_sites.Contains(sSiteName)) + { + if (do_verbose == true) + { + Console.WriteLine("INFO: Site in warn list: " + sSiteName); + } + bSiteShouldBeWarned = true; + } + + output = ""; + + if (site.ServerAutoStart == true) + { + output = "Site '" + sSiteName + "' set to AutoStart and site state is '" + site.State.ToString() + "'"; + + } + else + { + output = "Site '" + sSiteName + "' not set to AutoStart and site state is '" + site.State.ToString() + "'"; + } + // OK + if (do_singluar_check == true && bExpectedStateSet == true && site.State == expected_state) + { + output = output + " which is correct"; + + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceOK); + + CountSiteState(site.State.ToString()); + iNumberOfCorrectSites++; + } + else if (do_singluar_check == true && bExpectedStateSet == true) + { + if (bSiteShouldBeWarned == true) + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceWarning); + } + else + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceCritical); + } + + outputSites = outputSites + "Site '" + sSiteName + "' with an incorrect state: '" + site.State.ToString() + "'. "; + output = output + " when it is set to be '" + expected_state + "'."; + + CountSiteState(site.State.ToString()); + iNumberOfWrongSites++; + errorSites = true; + } + else if ((bSiteShouldBeStopped == true && site.State == ObjectState.Stopped.ToString()) || + (bSiteShouldBeStopped == false && site.State == ObjectState.Started.ToString())) + { + output = output + " which is correct"; + + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceOK); + + CountSiteState(site.State.ToString()); + iNumberOfCorrectSites++; + } + else if ((bSiteShouldBeStopped == true && site.State == ObjectState.Started.ToString()) || + (bSiteShouldBeStopped == true && site.State == ObjectState.Starting.ToString()) || + (bSiteShouldBeStopped == true && site.State == ObjectState.Stopping.ToString()) || + (bSiteShouldBeStopped == true && site.State == ObjectState.Unknown.ToString())) + { + if (bSiteShouldBeWarned == true) + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceWarning); + } + else + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceCritical); + } + + outputSites = outputSites + "Site '" + sSiteName + "' with an incorrect state: '" + site.State.ToString() + "'. "; + output = output + " when it is set to be '" + ObjectState.Stopped.ToString() + "'."; + + CountSiteState(site.State.ToString()); + iNumberOfWrongSites++; + errorSites = true; + } + else if ((bSiteShouldBeStopped == false && site.State == ObjectState.Stopped.ToString()) || + (bSiteShouldBeStopped == false && site.State == ObjectState.Starting.ToString()) || + (bSiteShouldBeStopped == false && site.State == ObjectState.Stopping.ToString()) || + (bSiteShouldBeStopped == false && site.State == ObjectState.Unknown.ToString())) + { + if (bSiteShouldBeWarned == true) + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceWarning); + } + else + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceCritical); + } + + outputSites = outputSites + "Site '" + sSiteName + "' with an incorrect state: '" + site.State.ToString() + "'. "; + output = output + " when it is set to be '" + ObjectState.Started.ToString() + "'. "; + + CountSiteState(site.State.ToString()); + iNumberOfWrongSites++; + errorSites = true; + } + + + if (do_perfcounter_sites) + output = PerfCounterSites(sSiteName, output, do_singluar_check); + + listSiteOutput.Add(output); + iNumberOfSites++; + + } + + if (errorSites == false) + { + if (iNumberOfSites == 0) + { + outputSites = "No Sites matched the filters given, or none exist on this server."; + } + if (iNumberOfSites == 1) + { + string tempOutput = string.Join(",", listSiteOutput.ToArray()); + outputSites = tempOutput; + } + else + { + outputSites = "All Sites are in their correct states. "; + } + } + + listPerfData.Add(" 'NumberOfSites'=" + iNumberOfSites); + listPerfData.Add(" 'NumberOfStartedSites'=" + iNumberOfStartedSites + ";;;0;" + iNumberOfSites); + listPerfData.Add(" 'NumberOfStoppedSites'=" + iNumberOfStoppedSites + ";;;0;" + iNumberOfSites); + listPerfData.Add(" 'NumberOfStoppingSites'=" + iNumberOfStoppingSites + ";;;0;" + iNumberOfSites); + listPerfData.Add(" 'NumberOfStartingSites'=" + iNumberOfStartingSites + ";;;0;" + iNumberOfSites); + listPerfData.Add(" 'NumberOfUnknownSites'=" + iNumberOfUnknownSites + ";;;0;" + iNumberOfSites); + listPerfData.Add(" 'NumberOfCorrectSites'=" + iNumberOfCorrectSites + ";;;0;" + iNumberOfSites); + listPerfData.Add(" 'NumberOfWrongSites'=" + iNumberOfWrongSites + ";;;0;" + iNumberOfSites); + + return returncode; + } + + public static int CheckAllAppPools(int returncode, bool do_perfcounter_apppools, string[] excluded_apppools, + string[] included_apppools, string[] stopped_apppools, string[] warn_apppools, bool do_singluar_check, + string expected_state) + { + var iisManager = new ServerManager(); + + bool temp = false; + temp = InventoryAppPools(iisManager, excluded_apppools, included_apppools, false, false, false, false); + if (temp == false) + return (int)ServiceState.ServiceUnknown; + + // If Expected State is set, we use a different logic further down. + bool bExpectedStateSet = false; + if (expected_state != "NotSet") + bExpectedStateSet = true; + + string output; + + Dictionary listOverAppPools = listAppPoolsOnComputer.ToDictionary(o => o.Name, o => o); + + // Loop for each AppPool in appPools list + foreach (var apppool in listOverAppPools.Values) + { + bool bAppPoolShouldBeStopped = false; + bool bAppPoolShouldBeWarned = false; + + string sAppPoolName = apppool.Name.ToString(); + + if (bDefaultAppPoolsStopped == true) + { + // No need to evaluate any further + } + else if (stopped_apppools.Contains(sAppPoolName)) + { + if (do_verbose == true) + { + Console.WriteLine("INFO: AppPool in stopped list: " + sAppPoolName); + } + bAppPoolShouldBeStopped = true; + } + + if (bDefaultAppPoolsWarn == true) + { + // No need to evaluate any further + } + else if (warn_apppools.Contains(sAppPoolName)) + { + if (do_verbose == true) + { + Console.WriteLine("INFO: AppPool in warn list: " + sAppPoolName); + } + bAppPoolShouldBeWarned = true; + } + + output = ""; + + if (apppool.AutoStart == true) + { + output = "AppPool '" + sAppPoolName + "' set to AutoStart and apppool state is '" + apppool.State.ToString() + "'"; + + } + else + { + output = "AppPool '" + sAppPoolName + "' not set to AutoStart and apppool state is '" + apppool.State.ToString() + "'"; + } + // OK + if (do_singluar_check == true && bExpectedStateSet == true && apppool.State == expected_state) + { + output = output + " which is correct"; + + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceOK); + + CountAppPoolState(apppool.State.ToString()); + iNumberOfCorrectAppPools++; + } + else if (do_singluar_check == true && bExpectedStateSet == true) + { + if (bAppPoolShouldBeWarned == true) + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceWarning); + } + else + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceCritical); + } + + outputAppPools = outputAppPools + "AppPool '" + sAppPoolName + "' with an incorrect state: '" + apppool.State.ToString() + "'. "; + output = output + " when it is set to be '" + expected_state + "'."; + + CountAppPoolState(apppool.State.ToString()); + iNumberOfWrongAppPools++; + errorAppPools = true; + } + else if ((bAppPoolShouldBeStopped == true && apppool.State == ObjectState.Stopped.ToString()) || + (bAppPoolShouldBeStopped == false && apppool.State == ObjectState.Started.ToString())) + { + output = output + " which is correct"; + + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceOK); + + CountAppPoolState(apppool.State.ToString()); + iNumberOfCorrectAppPools++; + } + else if ((bAppPoolShouldBeStopped == true && apppool.State == ObjectState.Started.ToString()) || + (bAppPoolShouldBeStopped == true && apppool.State == ObjectState.Starting.ToString()) || + (bAppPoolShouldBeStopped == true && apppool.State == ObjectState.Stopping.ToString()) || + (bAppPoolShouldBeStopped == true && apppool.State == ObjectState.Unknown.ToString())) + { + if (bAppPoolShouldBeWarned == true) + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceWarning); + } + else + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceCritical); + } + + outputAppPools = outputAppPools + "AppPool '" + sAppPoolName + "' with an incorrect state: '" + apppool.State.ToString() + "'. "; + output = output + " when it is set to be '" + ObjectState.Stopped.ToString() + "'."; + + CountAppPoolState(apppool.State.ToString()); + iNumberOfWrongAppPools++; + errorAppPools = true; + } + else if ((bAppPoolShouldBeStopped == false && apppool.State == ObjectState.Stopped.ToString()) || + (bAppPoolShouldBeStopped == false && apppool.State == ObjectState.Starting.ToString()) || + (bAppPoolShouldBeStopped == false && apppool.State == ObjectState.Stopping.ToString()) || + (bAppPoolShouldBeStopped == false && apppool.State == ObjectState.Unknown.ToString())) + { + if (bAppPoolShouldBeWarned == true) + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceWarning); + } + else + { + returncode = ReturnCodeMagic(returncode, (int)ServiceState.ServiceCritical); + } + + outputAppPools = outputAppPools + "AppPool '" + sAppPoolName + "' with an incorrect state: '" + apppool.State.ToString() + "'. "; + output = output + " when it is set to be '" + ObjectState.Started.ToString() + "'."; + + CountAppPoolState(apppool.State.ToString()); + iNumberOfWrongAppPools++; + errorAppPools = true; + } + + if (do_perfcounter_apppools) + output = PerfCounterAppPools(sAppPoolName, output, do_singluar_check); + + listAppPoolOutput.Add(output); + iNumberOfAppPools++; + + } + + if (errorAppPools == false) + { + if (iNumberOfAppPools == 0) + { + outputAppPools = "No AppPools matched the filters given, or none exist on this server."; + } + if (iNumberOfAppPools == 1) + { + string tempOutput = string.Join(",", listAppPoolOutput.ToArray()); + outputSites = tempOutput; + } + else + { + outputAppPools = "All AppPools are in their correct states. "; + } + } + + listPerfData.Add(" 'NumberOfAppPools'=" + iNumberOfAppPools); + listPerfData.Add(" 'NumberOfStartedAppPools'=" + iNumberOfStartedAppPools + ";;;0;" + iNumberOfAppPools); + listPerfData.Add(" 'NumberOfStoppedAppPools'=" + iNumberOfStoppedAppPools + ";;;0;" + iNumberOfAppPools); + listPerfData.Add(" 'NumberOfStoppingAppPools'=" + iNumberOfStoppingAppPools + ";;;0;" + iNumberOfAppPools); + listPerfData.Add(" 'NumberOfStartingAppPools'=" + iNumberOfStartingAppPools + ";;;0;" + iNumberOfAppPools); + listPerfData.Add(" 'NumberOfUnknownAppPools'=" + iNumberOfUnknownAppPools + ";;;0;" + iNumberOfAppPools); + listPerfData.Add(" 'NumberOfCorrectAppPools'=" + iNumberOfCorrectAppPools + ";;;0;" + iNumberOfAppPools); + listPerfData.Add(" 'NumberOfWrongAppPools'=" + iNumberOfWrongAppPools + ";;;0;" + iNumberOfAppPools); + + return returncode; + } + + public static void CountSiteState(string status) + { + + if (status == ObjectState.Started.ToString()) + { + iNumberOfStartedSites++; + } + else if (status == ObjectState.Stopped.ToString()) + { + iNumberOfStoppedSites++; + } + else if (status == ObjectState.Starting.ToString()) + { + iNumberOfStartingSites++; + } + else if (status == ObjectState.Stopping.ToString()) + { + iNumberOfStoppingSites++; + } + else + { + iNumberOfUnknownSites++; + } + } + + public static void CountAppPoolState(string status) + { + + if (status == ObjectState.Started.ToString()) + { + iNumberOfStartedAppPools++; + } + else if (status == ObjectState.Stopped.ToString()) + { + iNumberOfStoppedAppPools++; + } + else if (status == ObjectState.Starting.ToString()) + { + iNumberOfStartingAppPools++; + } + else if (status == ObjectState.Stopping.ToString()) + { + iNumberOfStoppingAppPools++; + } + else + { + iNumberOfUnknownAppPools++; + } + } + + + private static string PerfCounterSites(string sSiteName, string output, bool do_singluar_check) + { + try + { + string prefix = ""; + if (!do_singluar_check) + { + prefix = sSiteName + "_"; + } + + PerformanceCounterCategory cat = new PerformanceCounterCategory("Web Service", "."); + List counters = new List(); + + PerformanceCounter total_get_requests = new PerformanceCounter("Web Service", "Total Get Requests", sSiteName, "."); + listPerfData.Add(" '" + prefix + "Total Get Requests'=" + total_get_requests.NextValue() + "c;;;;"); + + PerformanceCounter total_post_requests = new PerformanceCounter("Web Service", "Total Post Requests", sSiteName, "."); + listPerfData.Add(" '" + prefix + "Total Post Requests'=" + total_post_requests.NextValue() + "c;;;;"); + + PerformanceCounter total_connection_attempts = new PerformanceCounter("Web Service", "Total Connection Attempts (all instances)", sSiteName, "."); + listPerfData.Add(" '" + prefix + "Total Connection Attemps'=" + total_connection_attempts.NextValue() + "c;;;;"); + + PerformanceCounter total_bytes_sent = new PerformanceCounter("Web Service", "Total Bytes Sent", sSiteName, "."); + listPerfData.Add(" '" + prefix + "Total Bytes Sent'=" + total_bytes_sent.NextValue() + "B;;;;"); + + PerformanceCounter total_bytes_received = new PerformanceCounter("Web Service", "Total Bytes Received", sSiteName, "."); + listPerfData.Add(" '" + prefix + "Total Bytes Received'=" + total_bytes_received.NextValue() + "B;;;;"); + + PerformanceCounter current_connections = new PerformanceCounter("Web Service", "Current Connections", sSiteName, "."); + listPerfData.Add(" '" + prefix + "Current Connections'=" + current_connections.NextValue() + "B;;;;"); + output = output + ", current connections: " + current_connections.NextValue(); + } + catch (Exception e) + { + Console.WriteLine("Error in getting performance data: " + e); + } + + return output; + } + + private static string PerfCounterAppPools(string sAppPoolName, string output, bool do_singluar_check) + { + try + { + string prefix = ""; + if (!do_singluar_check) + { + prefix = sAppPoolName + "_"; + } + PerformanceCounterCategory cat = new PerformanceCounterCategory("Web Service", "."); + List counters = new List(); + + PerformanceCounter total_worker_processes_created = new PerformanceCounter("APP_POOL_WAS", "Total Worker Processes Created", sAppPoolName, "."); + listPerfData.Add(" '" + prefix + "Total Worker Processes Created'=" + total_worker_processes_created.NextValue() + "c;;;;"); + + PerformanceCounter total_worker_processes_failures = new PerformanceCounter("APP_POOL_WAS", "Total Worker Process Failures", sAppPoolName, "."); + listPerfData.Add(" '" + prefix + "Total Worker Process Failures'=" + total_worker_processes_failures.NextValue() + "c;;;;"); + + PerformanceCounter apppool_uptime = new PerformanceCounter("APP_POOL_WAS", "Current Application Pool Uptime", sAppPoolName, "."); + listPerfData.Add(" '" + prefix + "Current Application Pool Uptime'=" + apppool_uptime.NextValue() + "c;;;;"); + output = output + ", application pool uptime: " + apppool_uptime.NextValue(); + } + catch (Exception e) + { + Console.WriteLine("Error in getting performance data: " + e); + } + + return output; + } + + + } + +} \ No newline at end of file diff --git a/Solution/check_iis/Properties/AssemblyInfo.cs b/Solution/check_iis/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9acb2b0 --- /dev/null +++ b/Solution/check_iis/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("check_iis")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("check_iis")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("dddafb7c-c277-45c9-8126-18871a0636cb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.8.*")] diff --git a/Solution/check_iis/ReadableSerializer.cs b/Solution/check_iis/ReadableSerializer.cs new file mode 100644 index 0000000..911a81d --- /dev/null +++ b/Solution/check_iis/ReadableSerializer.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +class ReadableSerializer +{ + + public static string Serialize(object o, bool bSkipEmpty = false, int indent = 0) + { + if (o == null) return "null"; + if (o.GetType() == typeof(string)) return "\"" + ((string)o).Replace("\"", "\\\"") + "\""; + if (o.GetType() == typeof(int)) return ((int)o).ToString(); + if (o.GetType() == typeof(Int32)) return ((Int32)o).ToString(); + if (o.GetType() == typeof(Int64)) return ((Int64)o).ToString(); + if (o.GetType() == typeof(bool)) return ((bool)o).ToString().ToLower(); + if (o.GetType() == typeof(float)) return ((float)o).ToString(); + if (o.GetType() == typeof(TimeSpan)) return (("\"" + (TimeSpan)o).ToString() + "\""); + + string spacer = " "; + string indention = " "; + indent++; + for (int i = 0; i < indent; i++) + { + spacer = spacer + indention; + } + + + if (o is IList) + { + string listOutput = ""; + bool listFirst = true; + IList l = (IList)o; + foreach (object o2 in l) + { + if (!listFirst) + { + listOutput += ""; + } + else + { + listFirst = false; + } + listOutput += Serialize(o2, bSkipEmpty, indent); + } + return listOutput; + } + + + // It's an actual object, so we need to walk the object model for fields to output. + Type oClass = o.GetType(); + string output = "\n"; + bool first = true; + + foreach (MemberInfo mi in oClass.GetMembers()) + { + // Skip empty. + if (mi.MemberType == MemberTypes.Property && bSkipEmpty == true) + { + string amIempty = Serialize(oClass.InvokeMember(mi.Name, BindingFlags.GetProperty, null, o, null)); + if (amIempty == "\"\"" || amIempty == "null" || amIempty == "[]") + { + continue; + } + + } + if (mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property) + { + + if (!first) + { + output += "\n"; + } + else + { + first = false; + } + + output += spacer + mi.Name + " = "; + + if (mi.MemberType == MemberTypes.Field) + { + output += Serialize(oClass.InvokeMember(mi.Name, BindingFlags.GetField, null, o, null), bSkipEmpty, indent); + } + else if (mi.MemberType == MemberTypes.Property) + { + output += Serialize(oClass.InvokeMember(mi.Name, BindingFlags.GetProperty, null, o, null), bSkipEmpty, indent); + } + } + + } + + //output += "\n"; + return output; + + } + +} \ No newline at end of file diff --git a/Solution/check_iis/WebSite.cs b/Solution/check_iis/WebSite.cs new file mode 100644 index 0000000..f9d866e --- /dev/null +++ b/Solution/check_iis/WebSite.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MonitoringPluginsForWindows +{ + class WebSite + { + public long Id { get; private set; } + public String Name { get; private set; } + public Boolean ServerAutoStart { get; private set; } + public Boolean IsLocallyStored { get; private set; } + public String State { get; private set; } + public String LogFileDirectory { get; private set; } + public Boolean LogFileEnabled { get; private set; } + public Array Bindings { get; private set; } + public Array Applications { get; private set; } + + public WebSite(long id, String name, Boolean serverAutoStart, Boolean isLocallyStored, String state, + String logFileDirectory, Boolean logFileEnabled, Array bindings, Array applications) + { + Id = id; + Name = name; + ServerAutoStart = serverAutoStart; + IsLocallyStored = isLocallyStored; + State = state; + LogFileDirectory = logFileDirectory; + LogFileEnabled = logFileEnabled; + Bindings = bindings; + Applications = applications; + } + } + + class SiteBinding + { + public String Protocol { get; private set; } + public String BindingInformation { get; private set; } + public String Host { get; private set; } + public String CertificateHash { get; private set; } + public String CertificateStoreName { get; private set; } + public Boolean UseDsMapper { get; private set; } + public Boolean IsIPPortHostBinding { get; private set; } + public Boolean IsLocallyStored { get; private set; } + + public SiteBinding(String protocol, String bindingInformation, String host, String certificateHash, + String certificateStoreName, Boolean useDsMapper, Boolean isIpPortHostBinding, Boolean isLocallyStored) + { + Protocol = protocol; + BindingInformation = bindingInformation; + Host = host; + CertificateHash = certificateHash; + CertificateStoreName = certificateStoreName; + UseDsMapper = useDsMapper; + IsIPPortHostBinding = isIpPortHostBinding; + IsLocallyStored = isLocallyStored; + } + } + + class SiteApplications + { + public String ApplicationPoolName { get; private set; } + public String EnabledProtocols { get; private set; } + public Boolean IsLocallyStored { get; private set; } + public String Path { get; private set; } + public Array VirtualDirectories { get; private set; } + + public SiteApplications(String applicationPoolName, String enabledProtocols, Boolean isLocallyStored, + String path, Array virtualDirectories) + { + ApplicationPoolName = applicationPoolName; + EnabledProtocols = enabledProtocols; + IsLocallyStored = isLocallyStored; + Path = path; + VirtualDirectories = virtualDirectories; + } + } + + class SiteAppVirtualDirectories + { + public String Path { get; private set; } + public String PhysicalPath { get; private set; } + public Boolean IsLocallyStored { get; private set; } + public String LogonMethod { get; private set; } + public String UserName { get; private set; } + + public SiteAppVirtualDirectories(String path, String physicalPath, Boolean isLocallyStored, + String logonMethod, String userName) + { + Path = path; + PhysicalPath = physicalPath; + IsLocallyStored = isLocallyStored; + LogonMethod = logonMethod; + UserName = userName; + } + } +} diff --git a/Solution/check_iis/check_iis.csproj b/Solution/check_iis/check_iis.csproj new file mode 100644 index 0000000..e07e3b2 --- /dev/null +++ b/Solution/check_iis/check_iis.csproj @@ -0,0 +1,130 @@ + + + + + Debug + AnyCPU + {DDDAFB7C-C277-45C9-8126-18871A0636CB} + Exe + Properties + MonitoringPluginsForWindows + check_iis + v3.5 + 512 + true + + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + MonitoringPluginsForWindows.check_iis + + + + ..\packages\FluentCommandLineParser.1.4.2\lib\net35\FluentCommandLineParser.dll + True + + + ..\packages\Microsoft.Web.Administration.7.0.0.0\lib\net20\Microsoft.Web.Administration.dll + False + False + + + ..\packages\Newtonsoft.Json.7.0.1\lib\net35\Newtonsoft.Json.dll + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/Solution/check_iis/icinga2clr.cs b/Solution/check_iis/icinga2clr.cs new file mode 100644 index 0000000..2dc321a --- /dev/null +++ b/Solution/check_iis/icinga2clr.cs @@ -0,0 +1,44 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +using System; +using System.Collections; + +namespace Icinga +{ + public enum ServiceState + { + ServiceOK, + ServiceWarning, + ServiceCritical, + ServiceUnknown + } + + public class CheckResult + { + public ServiceState State; + public String Output; + public String PerformanceData; + } + + public interface ICheckPlugin + { + CheckResult Check(Hashtable args); + } +} \ No newline at end of file diff --git a/Solution/check_iis/packages.config b/Solution/check_iis/packages.config new file mode 100644 index 0000000..079569f --- /dev/null +++ b/Solution/check_iis/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file