├── assets ├── PoShPrtg_32x32.png ├── PoShPrtg_64x64.png ├── Powershell_256.png ├── PoShPrtg_128x128.png ├── PoShPrtg_256x256.png ├── PoShPrtg_500x500.png └── Prtg_500x300_rgb.png ├── library └── PoShPRTG │ ├── PoShPRTG │ ├── Class1.cs │ └── PoShPRTG.csproj │ └── PoShPRTG.sln ├── PoShPRTG ├── internal │ ├── tepp │ │ ├── assignment.ps1 │ │ ├── example.tepp.ps1 │ │ └── readme.md │ ├── functions │ │ ├── readme.md │ │ ├── _newfunction_dummy.ps1.txt │ │ ├── Set-TypesNamesToPRTGObject.ps1 │ │ ├── Compare-ObjectProperty.ps1 │ │ └── Write-Log.ps1 │ ├── scriptblocks │ │ └── scriptblocks.ps1 │ ├── scripts │ │ ├── preimport.ps1 │ │ ├── strings.ps1 │ │ ├── postimport.ps1 │ │ └── license.ps1 │ └── configurations │ │ ├── readme.md │ │ └── configuration.ps1 ├── en-us │ ├── strings.psd1 │ └── about_PoShPRTG.help.txt ├── functions │ ├── readme.md │ ├── core │ │ ├── Disconnect-PRTGServer.ps1 │ │ ├── Invoke-PRTGSensorTreeRefresh.ps1 │ │ ├── Get-PRTGSensorTree.ps1 │ │ └── Connect-PRTGServer.ps1 │ ├── object │ │ ├── Get-PRTGObjectTAG.ps1 │ │ ├── Invoke-PRTGObjectRefresh.ps1 │ │ ├── Test-PRTGObjectNotification.ps1 │ │ ├── Move-PRTGObjectPosition.ps1 │ │ ├── Set-PRTGObjectAlarmAcknowledgement.ps1 │ │ ├── Receive-PRTGObjectProperty.ps1 │ │ ├── Receive-PRTGObject.ps1 │ │ ├── Receive-PRTGObjectStatus.ps1 │ │ ├── Rename-PRTGObject.ps1 │ │ ├── Get-PRTGObjectProperty.ps1 │ │ ├── Set-PRTGObjectPriority.ps1 │ │ ├── Set-PRTGObjectProperty.ps1 │ │ ├── Receive-PRTGObjectDetail.ps1 │ │ ├── Remove-PRTGObject.ps1 │ │ ├── Enable-PRTGObject.ps1 │ │ ├── Remove-PRTGObjectTAG.ps1 │ │ ├── Disable-PRTGObject.ps1 │ │ ├── Add-PRTGObjectTAG.ps1 │ │ └── Copy-PRTGObject.ps1 │ ├── group │ │ └── Get-PRTGGroup.ps1 │ ├── sensor │ │ └── Get-PRTGSensor.ps1 │ ├── probe │ │ └── Get-PRTGProbe.ps1 │ ├── device │ │ └── Get-PRTGDevice.ps1 │ └── deployment │ │ ├── New-PRTGDefaultFolderStructureToProbe.ps1 │ │ ├── Show-PRTGTemplateSummaryFromObjectTAG.ps1 │ │ └── Compare-PRTGDeviceSensorsFromTemplateTAG.ps1 ├── tests │ ├── functions │ │ └── readme.md │ ├── general │ │ ├── strings.Exceptions.ps1 │ │ ├── Help.Exceptions.ps1 │ │ ├── strings.Tests.ps1 │ │ ├── FileIntegrity.Exceptions.ps1 │ │ ├── PSScriptAnalyzer.Tests.ps1 │ │ ├── Manifest.Tests.ps1 │ │ ├── FileIntegrity.Tests.ps1 │ │ └── Help.Tests.ps1 │ ├── readme.md │ └── pester.ps1 ├── bin │ └── readme.md ├── changelog.md ├── xml │ ├── PoShPRTG.Format.ps1xml │ ├── PoShPRTG.Types.ps1xml │ ├── readme.md │ └── TypeDefinition.ps1 ├── PoShPRTG.psm1 └── PoShPRTG.psd1 ├── .gitattributes ├── license ├── .gitignore └── README.md /assets/PoShPrtg_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndiBellstedt/PoShPRTG/HEAD/assets/PoShPrtg_32x32.png -------------------------------------------------------------------------------- /assets/PoShPrtg_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndiBellstedt/PoShPRTG/HEAD/assets/PoShPrtg_64x64.png -------------------------------------------------------------------------------- /assets/Powershell_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndiBellstedt/PoShPRTG/HEAD/assets/Powershell_256.png -------------------------------------------------------------------------------- /assets/PoShPrtg_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndiBellstedt/PoShPRTG/HEAD/assets/PoShPrtg_128x128.png -------------------------------------------------------------------------------- /assets/PoShPrtg_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndiBellstedt/PoShPRTG/HEAD/assets/PoShPrtg_256x256.png -------------------------------------------------------------------------------- /assets/PoShPrtg_500x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndiBellstedt/PoShPRTG/HEAD/assets/PoShPrtg_500x500.png -------------------------------------------------------------------------------- /assets/Prtg_500x300_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndiBellstedt/PoShPRTG/HEAD/assets/Prtg_500x300_rgb.png -------------------------------------------------------------------------------- /library/PoShPRTG/PoShPRTG/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoShPRTG 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /PoShPRTG/internal/tepp/assignment.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | # Example: 3 | Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name PoShPRTG.alcohol 4 | #> -------------------------------------------------------------------------------- /PoShPRTG/en-us/strings.psd1: -------------------------------------------------------------------------------- 1 | # This is where the strings go, that are written by 2 | # Write-PSFMessage, Stop-PSFFunction or the PSFramework validation scriptblocks 3 | @{ 4 | 'key' = 'Value' 5 | } -------------------------------------------------------------------------------- /PoShPRTG/internal/tepp/example.tepp.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | # Example: 3 | Register-PSFTeppScriptblock -Name "PoShPRTG.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' } 4 | #> -------------------------------------------------------------------------------- /PoShPRTG/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | This is the folder where the functions go. 4 | 5 | Depending on the complexity of the module, it is recommended to subdivide them into subfolders. 6 | 7 | The module will pick up all .ps1 files recursively -------------------------------------------------------------------------------- /PoShPRTG/tests/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is where the function tests go. 4 | 5 | Make sure to put them in folders reflecting the actual module structure. 6 | 7 | It is not necessary to differentiate between internal and public functions here. -------------------------------------------------------------------------------- /PoShPRTG/internal/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | This is the folder where the internal functions go. 4 | 5 | Depending on the complexity of the module, it is recommended to subdivide them into subfolders. 6 | 7 | The module will pick up all .ps1 files recursively -------------------------------------------------------------------------------- /PoShPRTG/bin/readme.md: -------------------------------------------------------------------------------- 1 | # bin folder 2 | 3 | The bin folder exists to store binary data. And scripts related to the type system. 4 | 5 | This may include your own C#-based library, third party libraries you want to include (watch the license!), or a script declaring type accelerators (effectively aliases for .NET types) 6 | 7 | For more information on Type Accelerators, see the help on Set-PSFTypeAlias -------------------------------------------------------------------------------- /PoShPRTG/internal/scriptblocks/scriptblocks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Stored scriptblocks are available in [PsfValidateScript()] attributes. 3 | This makes it easier to centrally provide the same scriptblock multiple times, 4 | without having to maintain it in separate locations. 5 | 6 | It also prevents lengthy validation scriptblocks from making your parameter block 7 | hard to read. 8 | 9 | Set-PSFScriptblock -Name 'NPSLogFile.ScriptBlockName' -Scriptblock { 10 | 11 | } 12 | #> -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | *.PSD1 diff=astextplain 19 | 20 | -------------------------------------------------------------------------------- /PoShPRTG/internal/scripts/preimport.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Add all things you want to run before importing the main function code. 3 | 4 | WARNING: ONLY provide paths to files! 5 | 6 | After building the module, this file will be completely ignored, adding anything but paths to files ... 7 | - Will not work after publishing 8 | - Could break the build process 9 | #> 10 | 11 | $moduleRoot = Split-Path (Split-Path $PSScriptRoot) 12 | 13 | # Load the strings used in messages 14 | "$moduleRoot\internal\scripts\strings.ps1" 15 | -------------------------------------------------------------------------------- /PoShPRTG/internal/configurations/readme.md: -------------------------------------------------------------------------------- 1 | # Configurations 2 | 3 | Through the `PSFramework` you have a simple method that allows you to ... 4 | 5 | - Publish settings 6 | - With onboard documentation 7 | - Input validation 8 | - Scripts that run on change of settings 9 | - That can be discovered and updated by the user 10 | - That can be administrated by policy & DSC 11 | 12 | The configuration system is a bit too complex to describe in a help file, you can however visit us at http://psframework.org for detailed guidance. 13 | 14 | An example can be seen in the attached ps1 file -------------------------------------------------------------------------------- /PoShPRTG/tests/general/strings.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | $exceptions = @{ } 2 | 3 | <# 4 | A list of entries that MAY be in the language files, without causing the tests to fail. 5 | This is commonly used in modules that generate localized messages straight from C#. 6 | Specify the full key as it is written in the language files, do not prepend the modulename, 7 | as you would have to in C# code. 8 | 9 | Example: 10 | $exceptions['LegalSurplus'] = @( 11 | 'Exception.Streams.FailedCreate' 12 | 'Exception.Streams.FailedDispose' 13 | ) 14 | #> 15 | $exceptions['LegalSurplus'] = @( 16 | 17 | ) 18 | 19 | $exceptions -------------------------------------------------------------------------------- /PoShPRTG/internal/scripts/strings.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This file loads the strings documents from the respective language folders. 3 | This allows localizing messages and errors. 4 | Load psd1 language files for each language you wish to support. 5 | Partial translations are acceptable - when missing a current language message, 6 | it will fallback to English or another available language. 7 | #> 8 | Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'PoShPRTG' -Language 'en-US' 9 | 10 | $script:PRTGSession = "" 11 | [String]$script:PRTGServer = "" 12 | [String]$script:PRTGUser = "" 13 | [String]$script:PRTGPass = "" 14 | -------------------------------------------------------------------------------- /PoShPRTG/tests/general/Help.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | # List of functions that should be ignored 2 | $global:FunctionHelpTestExceptions = @( 3 | 4 | ) 5 | 6 | <# 7 | List of arrayed enumerations. These need to be treated differently. Add full name. 8 | Example: 9 | 10 | "Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]" 11 | #> 12 | $global:HelpTestEnumeratedArrays = @( 13 | 14 | ) 15 | 16 | <# 17 | Some types on parameters just fail their validation no matter what. 18 | For those it becomes possible to skip them, by adding them to this hashtable. 19 | Add by following this convention: = @() 20 | Example: 21 | 22 | "Get-DbaCmObject" = @("DoNotUse") 23 | #> 24 | $global:HelpTestSkipParameterType = @{ 25 | 26 | } 27 | -------------------------------------------------------------------------------- /PoShPRTG/en-us/about_PoShPRTG.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | about_PoShPRTG 3 | 4 | SHORT DESCRIPTION 5 | PoShPRTG is a comprehensive module for administering PRTG NETWORK MONITOR. 6 | 7 | LONG DESCRIPTION 8 | PoShPRTG is a comprehensive module for administering PRTG NETWORK MONITOR (www.paessler.com/prtg). 9 | 10 | It eases the rollout-/deployment process for new machines and managment of existing machines with all there sensors. 11 | The shipped cmdlets are used to call the PRTG API (http://prtg.paessler.com/api.htm?username=demo&password=demodemo) 12 | 13 | All cmdlets are build with 14 | - powershell regular verbs 15 | - mostly with pipeling availabilties 16 | - comprehensive logging on verbose and debug channel 17 | 18 | KEYWORDS 19 | PoShPRTG -------------------------------------------------------------------------------- /library/PoShPRTG/PoShPRTG/PoShPRTG.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net4.5.2 5 | 6 | 7 | 8 | ..\..\..\PoShPRTG\bin 9 | ..\..\..\PoShPRTG\bin\PoShPRTG.xml 10 | 11 | 12 | 13 | ..\..\..\PoShPRTG\bin 14 | ..\..\..\PoShPRTG\bin\PoShPRTG.xml 15 | 16 | 17 | 18 | false 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /PoShPRTG/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | # 1.5.1.0 3 | - Upd: Refactoring module 4 | - Refactoring directory and file structure, to meet my current standard 5 | - Start to implement PSFramework module for logging purpose and maybe more 6 | - Upd: Change variable scopes from "global" to "script" 7 | - PRTGServer, PRTGUser, PRTGPass, PRTGSensorTree 8 | - Upd: Command Connect-PRTGServer 9 | - Implement new switch "DoNotQuerySensorTree" (alias 'QuickConnect', 'NoSensorTree') to allow faster connection to PRTG Server when only live queries are needed to do. 10 | - Fix: Issue #1 - Set-PRTGObjectAlamAcknowledgement is misspellt 11 | - Rename command to "Set-PRTGObjectAlarmAcknowledgement" and set an alias for the wrong name to avoid bracking change 12 | 13 | # 1.5.0.0 - Inital module release 14 | Published Version 1.5.0.0 as the initial online available release of PoShPRTG -------------------------------------------------------------------------------- /PoShPRTG/internal/scripts/postimport.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Add all things you want to run after importing the main function code 3 | 4 | WARNING: ONLY provide paths to files! 5 | 6 | After building the module, this file will be completely ignored, adding anything but paths to files ... 7 | - Will not work after publishing 8 | - Could break the build process 9 | #> 10 | 11 | $moduleRoot = Split-Path (Split-Path $PSScriptRoot) 12 | 13 | # Load Configurations 14 | (Get-ChildItem "$moduleRoot\internal\configurations\*.ps1" -ErrorAction Ignore).FullName 15 | 16 | # Load Scriptblocks 17 | (Get-ChildItem "$moduleRoot\internal\scriptblocks\*.ps1" -ErrorAction Ignore).FullName 18 | 19 | # Load Tab Expansion 20 | (Get-ChildItem "$moduleRoot\internal\tepp\*.tepp.ps1" -ErrorAction Ignore).FullName 21 | 22 | # Load Tab Expansion Assignment 23 | "$moduleRoot\internal\tepp\assignment.ps1" 24 | 25 | # Load License 26 | "$moduleRoot\internal\scripts\license.ps1" -------------------------------------------------------------------------------- /PoShPRTG/tests/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is the folder, where all the tests go. 4 | 5 | Those are subdivided in two categories: 6 | 7 | - General 8 | - Function 9 | 10 | ## General Tests 11 | 12 | General tests are function generic and test for general policies. 13 | 14 | These test scan answer questions such as: 15 | 16 | - Is my module following my style guides? 17 | - Does any of my scripts have a syntax error? 18 | - Do my scripts use commands I do not want them to use? 19 | - Do my commands follow best practices? 20 | - Do my commands have proper help? 21 | 22 | Basically, these allow a general module health check. 23 | 24 | These tests are already provided as part of the template. 25 | 26 | ## Function Tests 27 | 28 | A healthy module should provide unit and integration tests for the commands & components it ships. 29 | Only then can be guaranteed, that they will actually perform as promised. 30 | 31 | However, as each such test must be specific to the function it tests, there cannot be much in the way of templates. -------------------------------------------------------------------------------- /PoShPRTG/internal/tepp/readme.md: -------------------------------------------------------------------------------- 1 | # Tab Expansion 2 | 3 | ## Description 4 | 5 | Modern Tab Expansion was opened to users with the module `Tab Expansion Plus Plus` (TEPP). 6 | 7 | It allows you to define, what options a user is offered when tabbing through input options. This can save a lot of time for the user and is considered a key element in user experience. 8 | 9 | The `PSFramework` offers a simplified way of offering just this, as the two example files show. 10 | 11 | ## Concept 12 | 13 | Custom tab completion is defined in two steps: 14 | 15 | - Define a scriptblock that is run when the user hits `TAB` and provides the strings that are his options. 16 | - Assign that scriptblock to the parameter of a command. You can assign the same scriptblock multiple times. 17 | 18 | ## Structure 19 | 20 | Import order matters. In order to make things work with the default scaffold, follow those rules: 21 | 22 | - All scriptfiles _defining_ completion scriptblocks like this: `*.tepp.ps1` 23 | - Put all your completion assignments in `assignment.ps1` -------------------------------------------------------------------------------- /PoShPRTG/tests/general/strings.Tests.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | This test verifies, that all strings that have been used, 4 | are listed in the language files and thus have a message being displayed. 5 | 6 | It also checks, whether the language files have orphaned entries that need cleaning up. 7 | #> 8 | 9 | 10 | 11 | Describe "Testing localization strings" { 12 | $moduleRoot = (Get-Module PoShPRTG).ModuleBase 13 | $stringsResults = Export-PSMDString -ModuleRoot $moduleRoot 14 | $exceptions = & "$global:testroot\general\strings.Exceptions.ps1" 15 | 16 | foreach ($stringEntry in $stringsResults) { 17 | if ($stringEntry.String -eq "key") { continue } # Skipping the template default entry 18 | It "Should be used & have text: $($stringEntry.String)" -TestCases @{ stringEntry = $stringEntry } { 19 | if ($exceptions.LegalSurplus -notcontains $stringEntry.String) { 20 | $stringEntry.Surplus | Should -BeFalse 21 | } 22 | $stringEntry.Text | Should -Not -BeNullOrEmpty 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andreas Bellstedt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /PoShPRTG/tests/general/FileIntegrity.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | # List of forbidden commands 2 | $global:BannedCommands = @( 3 | 'Write-Host' 4 | 'Write-Verbose' 5 | 'Write-Warning' 6 | 'Write-Error' 7 | 'Write-Output' 8 | 'Write-Information' 9 | 'Write-Debug' 10 | 11 | # Use CIM instead where possible 12 | 'Get-WmiObject' 13 | 'Invoke-WmiMethod' 14 | 'Register-WmiEvent' 15 | 'Remove-WmiObject' 16 | 'Set-WmiInstance' 17 | 18 | # Use Get-WinEvent instead 19 | 'Get-EventLog' 20 | ) 21 | 22 | <# 23 | Contains list of exceptions for banned cmdlets. 24 | Insert the file names of files that may contain them. 25 | 26 | Example: 27 | "Write-Host" = @('Write-PSFHostColor.ps1','Write-PSFMessage.ps1') 28 | #> 29 | $global:MayContainCommand = @{ 30 | "Write-Host" = @( "Write-Log.ps1" ) 31 | "Write-Verbose" = @( "Write-Log.ps1" ) 32 | "Write-Warning" = @( "Write-Log.ps1" ) 33 | "Write-Error" = @( "Write-Log.ps1" ) 34 | "Write-Output" = @( "Write-Log.ps1" ) 35 | "Write-Information" = @( "Write-Log.ps1" ) 36 | "Write-Debug" = @( "Write-Log.ps1" ) 37 | } -------------------------------------------------------------------------------- /library/PoShPRTG/PoShPRTG.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2010 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{5054146A-5065-4E51-B7EB-B74EA57467DA}") = "PoShPRTG", "PoShPRTG\PoShPRTG.csproj", "{8F4F7CFB-50D1-4FCF-AEC4-CFC2BEA22ADE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {8F4F7CFB-50D1-4FCF-AEC4-CFC2BEA22ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {8F4F7CFB-50D1-4FCF-AEC4-CFC2BEA22ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {8F4F7CFB-50D1-4FCF-AEC4-CFC2BEA22ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {8F4F7CFB-50D1-4FCF-AEC4-CFC2BEA22ADE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {15884AB8-E6E5-4512-91A1-3C40AA2625AB} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /PoShPRTG/xml/PoShPRTG.Format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Foo.Bar 7 | 8 | Foo.Bar 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Foo 21 | 22 | 23 | Bar 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /PoShPRTG/internal/configurations/configuration.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This is an example configuration file 3 | 4 | By default, it is enough to have a single one of them, 5 | however if you have enough configuration settings to justify having multiple copies of it, 6 | feel totally free to split them into multiple files. 7 | #> 8 | 9 | <# 10 | # Example Configuration 11 | Set-PSFConfig -Module 'PoShPRTG' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'" 12 | #> 13 | 14 | Set-PSFConfig -Module 'PoShPRTG' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging." 15 | Set-PSFConfig -Module 'PoShPRTG' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments." -------------------------------------------------------------------------------- /PoShPRTG/functions/core/Disconnect-PRTGServer.ps1: -------------------------------------------------------------------------------- 1 | function Disconnect-PRTGServer { 2 | <# 3 | .Synopsis 4 | Disconnect-PRTGServer 5 | 6 | .DESCRIPTION 7 | Clears variables from memory: 8 | $script:PRTGServer 9 | $script:PRTGUser 10 | $script:PRTGPass 11 | 12 | .NOTES 13 | Author: Andreas Bellstedt 14 | 15 | .LINK 16 | https://github.com/AndiBellstedt/PoShPRTG 17 | 18 | .EXAMPLE 19 | Disconnect-PRTGServer 20 | 21 | Remove connection data, so it disconnects from PRTG Server. 22 | #> 23 | [CmdletBinding( 24 | SupportsShouldProcess = $false, 25 | ConfirmImpact = 'medium' 26 | )] 27 | Param( 28 | # Force to disconnect and suppress errors 29 | [Switch] 30 | $Force 31 | ) 32 | 33 | if ($Force) { $ErrorAction = "SilentlyContinue" } else { $ErrorAction = "Continue" } 34 | 35 | Write-Log -LogText "Removing PRTG variables from memory" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus 36 | "PRTGServer", "PRTGUser", "PRTGPass", "PRTGSensorTree" | ForEach-Object { 37 | Get-Variable $_ -Scope global -ErrorAction $ErrorAction | Remove-Variable -Scope global -Force -ErrorAction $ErrorAction -Verbose:$false -Debug:$false -WhatIf:$false 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PoShPRTG/internal/scripts/license.ps1: -------------------------------------------------------------------------------- 1 | New-PSFLicense -Product 'PoShPRTG' -Manufacturer 'Andreas Bellstedt' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2019-01-05") -Text @" 2 | Copyright (c) 2019 Andreas Bellstedt 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | "@ -------------------------------------------------------------------------------- /PoShPRTG/xml/PoShPRTG.Types.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Deserialized.Foo.Bar 6 | 7 | 8 | PSStandardMembers 9 | 10 | 11 | 12 | TargetTypeForDeserialization 13 | 14 | 15 | Foo.Bar 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Foo.Bar 25 | 26 | 27 | SerializationData 28 | 29 | PSFramework.Serialization.SerializationTypeConverter 30 | GetSerializationData 31 | 32 | 33 | 34 | 35 | PSFramework.Serialization.SerializationTypeConverter 36 | 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # ignore the settings folder and files for VSCode and PSS 3 | .vscode/* 4 | *.psproj 5 | *TempPoint* 6 | 7 | # Ignore staging info from Visual Studio 8 | library/PoShPRTG/.vs/* 9 | library/PoShPRTG/PoShPRTG/bin/* 10 | library/PoShPRTG/PoShPRTG/obj/* 11 | 12 | # ignore PowerShell Studio MetaData 13 | PoShPRTG/PoShPRTG.psproj 14 | PoShPRTG/PoShPRTG.psproj.bak 15 | PoShPRTG/PoShPRTG.psprojs 16 | PoShPRTG/PoShPRTG.psproj 17 | 18 | # ignore the TestResults 19 | TestResults/* 20 | 21 | # ignore the publishing Directory 22 | publish/* 23 | 24 | 25 | # Windows image file caches 26 | Thumbs.db 27 | ehthumbs.db 28 | 29 | # Folder config file 30 | Desktop.ini 31 | 32 | # Recycle Bin used on file shares 33 | $RECYCLE.BIN/ 34 | 35 | # Windows Installer files 36 | *.cab 37 | *.msi 38 | *.msm 39 | *.msp 40 | 41 | # Windows shortcuts 42 | *.lnk 43 | 44 | # ========================= 45 | # Operating System Files 46 | # ========================= 47 | 48 | # OSX 49 | # ========================= 50 | 51 | .DS_Store 52 | .AppleDouble 53 | .LSOverride 54 | 55 | # Thumbnails 56 | ._* 57 | 58 | # Files that might appear in the root of a volume 59 | .DocumentRevisions-V100 60 | .fseventsd 61 | .Spotlight-V100 62 | .TemporaryItems 63 | .Trashes 64 | .VolumeIcon.icns 65 | 66 | # Directories potentially created on remote AFP share 67 | .AppleDB 68 | .AppleDesktop 69 | Network Trash Folder 70 | Temporary Items 71 | .apdisk 72 | 73 | -------------------------------------------------------------------------------- /PoShPRTG/tests/general/PSScriptAnalyzer.Tests.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param ( 3 | [switch] 4 | $SkipTest, 5 | 6 | [string[]] 7 | $CommandPath 8 | ) 9 | 10 | if ($SkipTest) { return } 11 | 12 | if(-not $CommandPath) { 13 | [string[]]$CommandPath = @("$global:testroot\..\functions", "$global:testroot\..\internal\functions") 14 | } 15 | 16 | $global:__pester_data.ScriptAnalyzer = New-Object System.Collections.ArrayList 17 | 18 | Describe 'Invoking PSScriptAnalyzer against commandbase' { 19 | $commandFiles = Get-ChildItem -Path $CommandPath -Recurse | Where-Object Name -like "*.ps1" 20 | $scriptAnalyzerRules = Get-ScriptAnalyzerRule 21 | 22 | foreach ($file in $commandFiles) { 23 | Context "Analyzing $($file.BaseName)" { 24 | $analysis = Invoke-ScriptAnalyzer -Path $file.FullName -ExcludeRule PSAvoidTrailingWhitespace, PSShouldProcess 25 | 26 | forEach ($rule in $scriptAnalyzerRules) { 27 | It "Should pass $rule" -TestCases @{ analysis = $analysis; rule = $rule } { 28 | If ($analysis.RuleName -contains $rule) { 29 | $analysis | Where-Object RuleName -EQ $rule -outvariable failures | ForEach-Object { $null = $global:__pester_data.ScriptAnalyzer.Add($_) } 30 | 31 | 1 | Should -Be 0 32 | } else { 33 | 0 | Should -Be 0 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /PoShPRTG/xml/readme.md: -------------------------------------------------------------------------------- 1 | # XML 2 | 3 | This is the folder where project XML files go, notably: 4 | 5 | - Format XML 6 | - Type Extension XML 7 | 8 | External help files should _not_ be placed in this folder! 9 | 10 | ## Notes on Files and Naming 11 | 12 | There should be only one format file and one type extension file per project, as importing them has a notable impact on import times. 13 | 14 | - The Format XML should be named `PoShPRTG.Format.ps1xml` 15 | - The Type Extension XML should be named `PoShPRTG.Types.ps1xml` 16 | 17 | ## Tools 18 | 19 | ### New-PSMDFormatTableDefinition 20 | 21 | This function will take an input object and generate format xml for an auto-sized table. 22 | 23 | It provides a simple way to get started with formats. 24 | 25 | ### Get-PSFTypeSerializationData 26 | 27 | ``` 28 | C# Warning! 29 | This section is only interest if you're using C# together with PowerShell. 30 | ``` 31 | 32 | This function generates type extension XML that allows PowerShell to convert types written in C# to be written to file and restored from it without being 'Deserialized'. Also works for jobs or remoting, if both sides have the `PSFramework` module and type extension loaded. 33 | 34 | In order for a class to be eligible for this, it needs to conform to the following rules: 35 | 36 | - Have the `[Serializable]` attribute 37 | - Be public 38 | - Have an empty constructor 39 | - Allow all public properties/fields to be set (even if setting it doesn't do anything) without throwing an exception. 40 | 41 | ``` 42 | non-public properties and fields will be lost in this process! 43 | ``` -------------------------------------------------------------------------------- /PoShPRTG/functions/core/Invoke-PRTGSensorTreeRefresh.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PRTGSensorTreeRefresh { 2 | <# 3 | .Synopsis 4 | Invoke-PRTGSensorTreeRefresh 5 | 6 | .DESCRIPTION 7 | Refreshes sensortree information from prtg server 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Invoke-PRTGSensorTreeRefresh 17 | 18 | Refreshes the sensortree for caching current prtg current object configuration. 19 | 20 | .EXAMPLE 21 | Invoke-PRTGSensorTreeRefresh -Server "https://prtg.corp.customer.com" -User "prtgadmin" -Pass "111111" 22 | 23 | Refreshes the sensortree with custom credentials for caching current prtg current object configuration. 24 | #> 25 | [CmdletBinding( 26 | DefaultParameterSetName = 'Default', 27 | SupportsShouldProcess = $false, 28 | ConfirmImpact = 'medium' 29 | )] 30 | Param( 31 | # Url for PRTG Server 32 | [ValidateNotNullOrEmpty()] 33 | [ValidateScript({ if ( ($_.StartsWith("http")) ) { $true } else { $false } })] 34 | [String] 35 | $Server = $script:PRTGServer, 36 | 37 | # User for PRTG Authentication 38 | [ValidateNotNullOrEmpty()] 39 | [String] 40 | $User = $script:PRTGUser, 41 | 42 | # Password or PassHash for PRTG Authentication 43 | [ValidateNotNullOrEmpty()] 44 | [String] 45 | $Pass = $script:PRTGPass, 46 | 47 | # Output the sensor tree 48 | [Switch] 49 | $PassThru 50 | ) 51 | 52 | Write-Log -LogText "Refresh PRTG SensorTree in Memory" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 53 | $Result = Get-PRTGSensorTree -Server $Server -User $User -Pass $Pass -Verbose:$false 54 | $script:PRTGSensorTree = $Result 55 | 56 | if ($PassThru) { $Result } 57 | } 58 | -------------------------------------------------------------------------------- /PoShPRTG/internal/functions/_newfunction_dummy.ps1.txt: -------------------------------------------------------------------------------- 1 | function Use-NewFunction { 2 | <# 3 | .Synopsis 4 | %ToDo% 5 | 6 | .DESCRIPTION 7 | %ToDo% 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | %ToDo% 17 | 18 | .EXAMPLE 19 | %ToDo% -Server "https://prtg.corp.customer.com" -User "admin" -Pass "1111111" 20 | #> 21 | [CmdletBinding( 22 | DefaultParameterSetName = 'Default', 23 | SupportsShouldProcess = $true, 24 | ConfirmImpact = 'Low' 25 | )] 26 | Param( 27 | # Hilfebeschreibung zu Param1 28 | [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] 29 | [ValidateNotNullOrEmpty()] 30 | [ValidateSet('group', 'device', 'sensor', 'probenode')] 31 | [ValidateScript( { $true })] 32 | [Alias('objID', 'ID')] 33 | $Param1, 34 | 35 | # SensorTree from PRTG Server 36 | [ValidateNotNullOrEmpty()] 37 | [xml] 38 | $SensorTree = $script:PRTGSensorTree, 39 | 40 | # Url for PRTG Server 41 | [ValidateNotNullOrEmpty()] 42 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } })] 43 | [String] 44 | $Server = $script:PRTGServer, 45 | 46 | # User for PRTG Authentication 47 | [ValidateNotNullOrEmpty()] 48 | [String] 49 | $User = $script:PRTGUser, 50 | 51 | # Password or PassHash for PRTG Authentication 52 | [ValidateNotNullOrEmpty()] 53 | [String] 54 | $Pass = $script:PRTGPass 55 | ) 56 | 57 | Begin {} 58 | 59 | Process { 60 | if ($pscmdlet.ShouldProcess("Target", "Operation")) { 61 | Write-Log -LogText "Doing Use-Template..." -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 62 | 63 | } 64 | } 65 | 66 | End {} 67 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/core/Get-PRTGSensorTree.ps1: -------------------------------------------------------------------------------- 1 | function Get-PRTGSensorTree { 2 | <# 3 | .Synopsis 4 | Get-PRTGSensorTree 5 | 6 | .DESCRIPTION 7 | Return the current sensortree from PRTG Server 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Get-PRTGSensorTree 17 | 18 | Query the sensortree for caching prtg current object configuration. 19 | 20 | .EXAMPLE 21 | Get-PRTGSensorTree -Server "https://prtg.corp.customer.com" -User "prtgadmin" -Pass "1111111" 22 | 23 | Query the sensortree with custom credentials for caching prtg current object configuration. 24 | #> 25 | [CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $false, ConfirmImpact = 'Low')] 26 | Param( 27 | # Url for PRTG Server 28 | [ValidateNotNullOrEmpty()] 29 | [ValidateScript({ if ( ($_.StartsWith("http")) ) { $true } else { $false } })] 30 | [String] 31 | $Server = $script:PRTGServer, 32 | 33 | # User for PRTG Authentication 34 | [ValidateNotNullOrEmpty()] 35 | [String] 36 | $User = $script:PRTGUser, 37 | 38 | # Password or PassHash for PRTG Authentication 39 | [ValidateNotNullOrEmpty()] 40 | [String] 41 | $Pass = $script:PRTGPass 42 | ) 43 | 44 | $body = @{ 45 | username = $User 46 | passhash = $Pass 47 | } 48 | 49 | Write-Log -LogText "Getting PRTG SensorTree from PRTG Server $($Server)" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 50 | #[xml]$Result = Invoke-RestMethod -Uri "$Server/api/table.xml?content=sensortree" -Body $body -ErrorAction Stop -Verbose:$false 51 | 52 | $webresult = Invoke-WebRequest -Uri "$Server/api/table.xml?content=sensortree" -Body $body -UseBasicParsing 53 | $content = $webresult.Content 54 | [xml]$result = $content 55 | 56 | $result.pstypenames.Insert(0, "PRTG.SensorTree") 57 | $result.pstypenames.Insert(1, "PRTG") 58 | 59 | return $result 60 | } 61 | -------------------------------------------------------------------------------- /PoShPRTG/internal/functions/Set-TypesNamesToPRTGObject.ps1: -------------------------------------------------------------------------------- 1 | function Set-TypesNamesToPRTGObject { 2 | <# 3 | .Synopsis 4 | Set-TypesNamesToPRTGObject 5 | 6 | .DESCRIPTION 7 | Add module specific type names to result objects of a function. 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .EXAMPLE 13 | Set-TypesNamesToPRTGObject $PRTGObject 14 | Work on the specified object 15 | #> 16 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 17 | [CmdletBinding(ConfirmImpact="low", SupportsShouldProcess=$false)] 18 | param( 19 | # Object to work on 20 | $PRTGObject 21 | ) 22 | 23 | begin {} 24 | 25 | process { 26 | foreach ($item in $PRTGObject) { 27 | if ($item.pstypenames[0] -eq "PRTG.Object.Compare") { $null = $item.pstypenames.Remove("PRTG.Object.Compare") } 28 | 29 | switch ($item.LocalName) { 30 | 'probenode' { 31 | if ($item.pstypenames -notcontains "PRTG.Object.Probenode") { 32 | $item.pstypenames.Insert(0, "PRTG.Object.Probenode") 33 | } 34 | } 35 | 36 | 'group' { 37 | if ($item.pstypenames -notcontains "PRTG.Object.Group") { 38 | $item.pstypenames.Insert(0, "PRTG.Object.Group") 39 | } 40 | } 41 | 42 | 'device' { 43 | if ($item.pstypenames -notcontains "PRTG.Object.Device") { 44 | $item.pstypenames.Insert(0, "PRTG.Object.Device") 45 | } 46 | } 47 | 48 | 'sensor' { 49 | if ($item.pstypenames -notcontains "PRTG.Object.Sensor") { 50 | $item.pstypenames.Insert(0, "PRTG.Object.Sensor") 51 | } 52 | } 53 | } 54 | 55 | if ($item.pstypenames -notcontains "PRTG.Object") { $item.pstypenames.Insert(1, "PRTG.Object") } 56 | if ($item.pstypenames -notcontains "PRTG") { $item.pstypenames.Insert(2, "PRTG") } 57 | 58 | $item 59 | } 60 | } 61 | 62 | end {} 63 | } 64 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Get-PRTGObjectTAG.ps1: -------------------------------------------------------------------------------- 1 | function Get-PRTGObjectTAG { 2 | <# 3 | .Synopsis 4 | Get-PRTGObjectTAG 5 | 6 | .DESCRIPTION 7 | Get the tags property from an PRTG object out of the sensor tree and returns a string array 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Get-PRTGObjectTAG -ObjectId 1 17 | 18 | Get ObjectTags from object with ID 1 19 | #> 20 | [CmdletBinding( 21 | DefaultParameterSetName = 'ReturnAll', 22 | SupportsShouldProcess = $false, 23 | ConfirmImpact = 'Low' 24 | )] 25 | [OutputType( [String[]] )] 26 | Param( 27 | # ID of the object to pause/resume 28 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 29 | [ValidateNotNullOrEmpty()] 30 | [ValidateScript( { $_ -gt 0 } )] 31 | [Alias('objID', 'ID')] 32 | [int] 33 | $ObjectID, 34 | 35 | # SensorTree from PRTG Server 36 | [ValidateNotNullOrEmpty()] 37 | [xml] 38 | $SensorTree = $script:PRTGSensorTree 39 | ) 40 | 41 | begin {} 42 | 43 | process { 44 | #Get the object 45 | Write-Log -LogText "Get object tags from object ID $ObjectID." -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 46 | try { 47 | $Object = Get-PRTGObject -ID $ObjectID -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 48 | } catch { 49 | Write-Log -LogText $_.exception.message -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 50 | return 51 | } 52 | 53 | if ($Object.tags) { 54 | [Array]$result = $Object.tags.Split(' ') 55 | } else { 56 | Write-Log -LogText "No tags in object ""$($Object.name)"" (ID:$($ObjectID))" -LogType Warning -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Warning 57 | return 58 | } 59 | 60 | Write-Log -LogText "Found $($result.count) $(if($result.count -eq 1){"tag"}else{"tags"}) in object ID $ObjectID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 61 | $result 62 | } 63 | 64 | end {} 65 | } 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ![logo][] PowerShell for PRTG - PoShPRTG 3 | | Plattform | Information | 4 | | --------- | ----------- | 5 | | PowerShell gallery | [![PowerShell Gallery](https://img.shields.io/powershellgallery/v/PoShPRTG?label=psgallery)](https://www.powershellgallery.com/packages/PoShPRTG) [![PowerShell Gallery](https://img.shields.io/powershellgallery/p/PoShPRTG)](https://www.powershellgallery.com/packages/PoShPRTG) [![PowerShell Gallery](https://img.shields.io/powershellgallery/dt/PoShPRTG?style=plastic)](https://www.powershellgallery.com/packages/PoShPRTG) | 6 | | GitHub | [![GitHub release](https://img.shields.io/github/release/AndiBellstedt/PoShPRTG.svg)](https://github.com/AndiBellstedt/PoShPRTG/releases/latest) ![GitHub](https://img.shields.io/github/license/AndiBellstedt/PoShPRTG?style=plastic)
![GitHub issues](https://img.shields.io/github/issues-raw/AndiBellstedt/PoShPRTG?style=plastic)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/AndiBellstedt/PoShPRTG/master?label=last%20commit%3A%20master&style=plastic)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/AndiBellstedt/PoShPRTG/development?label=last%20commit%3A%20development&style=plastic) | 7 |
8 | 9 | # Description 10 | 11 | Welcome to the developing repository for PoShPRTG module. 12 | PoShPRTG is a comprehensive module for administering PRTG NETWORK MONITOR (www.paessler.com/prtg). 13 | 14 | It eases the rollout-/deployment process for new machines and managment of existing machines with all there sensors. 15 | The shipped cmdlets are used to call the PRTG API (http://prtg.paessler.com/api.htm?username=demo&password=demodemo) 16 | 17 | 18 | All cmdlets are build with 19 | - powershell regular verbs 20 | - mostly with pipeling availabilties 21 | - comprehensive logging on verbose and debug channel 22 | 23 | 24 | The idea for the module was born while maintaining quiete a large deployment of PRTG installation in my job at abtis GmbH (www.abtis.de) 25 | 26 | I startet to dig into the API documentation and write some powershell functions after work, just for fun and hopefully easy of work. 27 | After then the collection of functions was growing and growing so I decided to put them together in this module. 28 | 29 | 30 | Hopefully some other people may benefit from my work and investigation. 31 | 32 | 33 | [logo]: assets/PoShPrtg_256x256.png 34 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Invoke-PRTGObjectRefresh.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PRTGObjectRefresh { 2 | <# 3 | .Synopsis 4 | Invoke-PRTGObjectRefresh 5 | 6 | .DESCRIPTION 7 | Enables an (paused) PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | .LINK 19 | https://github.com/AndiBellstedt/PoShPRTG 20 | 21 | .EXAMPLE 22 | Invoke-PRTGObjectRefresh -ObjectId 1 23 | 24 | Refreshes objct with ID 1 25 | #> 26 | [CmdletBinding( 27 | DefaultParameterSetName = 'Default', 28 | SupportsShouldProcess = $true, 29 | ConfirmImpact = 'medium' 30 | )] 31 | Param( 32 | # ID of the object to resume 33 | [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 34 | [ValidateNotNullOrEmpty()] 35 | [ValidateScript( { $_ -gt 0 } )] 36 | [Alias('ObjID', 'ID')] 37 | [int[]] 38 | $ObjectId, 39 | 40 | # Url for PRTG Server 41 | [ValidateNotNullOrEmpty()] 42 | [ValidateScript( { if (($_.StartsWith("http"))) {$true} else {$false} } )] 43 | [String] 44 | $Server = $script:PRTGServer, 45 | 46 | # User for PRTG Authentication 47 | [ValidateNotNullOrEmpty()] 48 | [String] 49 | $User = $script:PRTGUser, 50 | 51 | # Password or PassHash for PRTG Authentication 52 | [ValidateNotNullOrEmpty()] 53 | [String] 54 | $Pass = $script:PRTGPass 55 | ) 56 | 57 | Begin { 58 | $body = @{ 59 | id = 0 60 | username = $User 61 | passhash = $Pass 62 | } 63 | } 64 | 65 | Process { 66 | foreach ($id in $ObjectId) { 67 | $body.id = $id 68 | if ($pscmdlet.ShouldProcess("objID $Id", "Call scan now procedure on object")) { 69 | try { 70 | Write-Log -LogText "Call scan now procedure on object ID $id ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 71 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/scannow.htm" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 72 | } catch { 73 | Write-Log -LogText "Failed to Call scan now procedure on object ID $id. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 74 | } 75 | } 76 | } 77 | } 78 | 79 | End {} 80 | } 81 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Test-PRTGObjectNotification.ps1: -------------------------------------------------------------------------------- 1 | function Test-PRTGObjectNotification { 2 | <# 3 | .Synopsis 4 | Test-PRTGObjectNotification 5 | 6 | .DESCRIPTION 7 | Test a notifcation action for a object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | .LINK 19 | https://github.com/AndiBellstedt/PoShPRTG 20 | 21 | .EXAMPLE 22 | Test-PRTGObjectNotification -ObjectId 1 23 | 24 | Test a notifcation action for object with ID 1 25 | #> 26 | [CmdletBinding( 27 | DefaultParameterSetName = 'Default', 28 | SupportsShouldProcess = $true, 29 | ConfirmImpact = 'medium' 30 | )] 31 | Param( 32 | # ID of the object to resume 33 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 34 | [ValidateNotNullOrEmpty()] 35 | [ValidateScript( { $_ -gt 0 } )] 36 | [Alias('ObjID', 'ID')] 37 | [int[]] 38 | $ObjectId, 39 | 40 | # Url for PRTG Server 41 | [ValidateNotNullOrEmpty()] 42 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 43 | [String] 44 | $Server = $script:PRTGServer, 45 | 46 | # User for PRTG Authentication 47 | [ValidateNotNullOrEmpty()] 48 | [String] 49 | $User = $script:PRTGUser, 50 | 51 | # Password or PassHash for PRTG Authentication 52 | [ValidateNotNullOrEmpty()] 53 | [String] 54 | $Pass = $script:PRTGPass 55 | ) 56 | 57 | Begin {} 58 | 59 | Process { 60 | foreach ($id in $ObjectId) { 61 | $body = @{ 62 | id = $id 63 | username = $User 64 | passhash = $Pass 65 | } 66 | 67 | if ($pscmdlet.ShouldProcess("objID $Id", "Test notification for object")) { 68 | try { 69 | Write-Log -LogText "Test notification for object ID $id ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 70 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/notificationtest.htm" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 71 | } catch { 72 | Write-Log -LogText "Failed to test notification for object ID $id. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 73 | } 74 | } 75 | } 76 | } 77 | 78 | End {} 79 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/group/Get-PRTGGroup.ps1: -------------------------------------------------------------------------------- 1 | function Get-PRTGGroup { 2 | <# 3 | .Synopsis 4 | Get-PRTGGroup 5 | 6 | .DESCRIPTION 7 | Returns one or more groups from sensortree 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Get-PRTGGroup 17 | 18 | Query all groups from the default sensortree (global variable after connect to PRTG server) 19 | 20 | .EXAMPLE 21 | Get-PRTGGroup -SensorTree $SensorTree 22 | 23 | Query groups by name from a non default sensortree 24 | 25 | .EXAMPLE 26 | Get-PRTGGroup -Name "Group01" 27 | 28 | Query groups by name 29 | 30 | .EXAMPLE 31 | Get-PRTGGroup -Name "Group01", "Group*" 32 | 33 | Multiple names are possible 34 | 35 | .EXAMPLE 36 | "Group01" | Get-PRTGGroup 37 | 38 | Piping is also possible 39 | 40 | .EXAMPLE 41 | Get-PRTGGroup -ObjectId 1 42 | 43 | Query groups by object ID 44 | 45 | .EXAMPLE 46 | 1 | Get-PRTGGroup 47 | 48 | Piping is also possible 49 | #> 50 | [CmdletBinding( 51 | DefaultParameterSetName = 'ReturnAll', 52 | SupportsShouldProcess = $false, 53 | ConfirmImpact = 'Low' 54 | )] 55 | Param( 56 | # ID of the PRTG object 57 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'ID', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 58 | [ValidateNotNullOrEmpty()] 59 | [ValidateScript( { $_ -gt 0 })] 60 | [Alias('ObjID', 'ID')] 61 | [int[]] 62 | $ObjectId, 63 | 64 | # Name of the group 65 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'Name', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 66 | [String[]] 67 | $Name, 68 | 69 | # sensortree from PRTG Server 70 | [ValidateNotNullOrEmpty()] 71 | [xml] 72 | $SensorTree = $script:PRTGSensorTree 73 | ) 74 | 75 | begin { 76 | $queryParam = @{ 77 | "Type" = "group" 78 | "SensorTree" = $SensorTree 79 | "Verbose" = $false 80 | } 81 | } 82 | 83 | process { 84 | $result = @() 85 | 86 | switch ($PsCmdlet.ParameterSetName) { 87 | 'ID' { 88 | foreach ($item in $ObjectId) { 89 | $result += Get-PRTGObject -ObjectID $item @queryParam 90 | } 91 | } 92 | 93 | 'Name' { 94 | foreach ($item in $Name) { 95 | $result += Get-PRTGObject -Name $item @queryParam 96 | } 97 | } 98 | 99 | Default { 100 | $result = Get-PRTGObject @queryParam 101 | } 102 | } 103 | 104 | $result 105 | } 106 | 107 | end {} 108 | } 109 | -------------------------------------------------------------------------------- /PoShPRTG/functions/sensor/Get-PRTGSensor.ps1: -------------------------------------------------------------------------------- 1 | function Get-PRTGSensor { 2 | <# 3 | .Synopsis 4 | Get-PRTGSensor 5 | 6 | .DESCRIPTION 7 | Returns one or more sensors from sensortree 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Get-PRTGSensor 17 | Query all sensors from the default sensortree (global variable after connect to PRTG server) 18 | 19 | .EXAMPLE 20 | Get-PRTGSensor -SensorTree $SensorTree 21 | Query sensors by name from a non default sensortree 22 | 23 | .EXAMPLE 24 | Get-PRTGSensor -Name "Sensor01" 25 | 26 | Query sensors by name 27 | 28 | .EXAMPLE 29 | Get-PRTGSensor -Name "Sensor01", "Sensor*" 30 | 31 | Multiple names are possible 32 | 33 | .EXAMPLE 34 | "Sensor01" | Get-PRTGSensor 35 | 36 | Piping is also possible 37 | 38 | .EXAMPLE 39 | Get-PRTGSensor -ObjectId 1 40 | 41 | Query sensors by object ID 42 | 43 | .EXAMPLE 44 | 1 | Get-PRTGSensor 45 | 46 | Piping is also possible 47 | #> 48 | [CmdletBinding( 49 | DefaultParameterSetName = 'ReturnAll', 50 | SupportsShouldProcess = $false, 51 | ConfirmImpact = 'Low' 52 | )] 53 | Param( 54 | # ID of the PRTG object 55 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'ID', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 56 | [ValidateNotNullOrEmpty()] 57 | [ValidateScript( { $_ -gt 0 })] 58 | [Alias('ObjID', 'ID')] 59 | [int[]] 60 | $ObjectId, 61 | 62 | # Name of the sensor 63 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'Name', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 64 | [String[]] 65 | $Name, 66 | 67 | # Sensortree from PRTG Server 68 | [ValidateNotNullOrEmpty()] 69 | [xml] 70 | $SensorTree = $script:PRTGSensorTree 71 | ) 72 | 73 | begin { 74 | $queryParam = @{ 75 | "Type" = "sensor" 76 | "SensorTree" = $SensorTree 77 | "Verbose" = $false 78 | } 79 | } 80 | 81 | process { 82 | $result = @() 83 | 84 | switch ($PsCmdlet.ParameterSetName) { 85 | 'ID' { 86 | foreach ($item in $ObjectId) { 87 | $result += Get-PRTGObject -ObjectID $item @queryParam 88 | } 89 | } 90 | 91 | 'Name' { 92 | foreach ($item in $Name) { 93 | $result += Get-PRTGObject -Name $item @queryParam 94 | } 95 | } 96 | 97 | Default { 98 | $result = Get-PRTGObject @queryParam 99 | } 100 | } 101 | 102 | $result 103 | } 104 | 105 | end {} 106 | } 107 | -------------------------------------------------------------------------------- /PoShPRTG/functions/probe/Get-PRTGProbe.ps1: -------------------------------------------------------------------------------- 1 | function Get-PRTGProbe { 2 | <# 3 | .Synopsis 4 | Get-PRTGProbe 5 | 6 | .DESCRIPTION 7 | Returns one or more probes from sensortree 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Get-PRTGProbe 17 | Query all probes from the default sensortree (global variable after connect to PRTG server) 18 | 19 | Get-PRTGProbe -SensorTree $SensorTree 20 | Query probes by name from a non default sensortree 21 | 22 | .EXAMPLE 23 | Get-PRTGProbe -Name "Probe01" 24 | Query probes by name 25 | 26 | Get-PRTGProbe -Name "Probe01", "Probe*" 27 | # Multiple names are possible 28 | 29 | "Probe01" | Get-PRTGProbe 30 | # Piping is also possible 31 | 32 | .EXAMPLE 33 | Get-PRTGProbe -ObjectId 1 34 | Query probes by object ID 35 | 36 | Get-PRTGProbe -ObjID 1, 100 37 | Get-PRTGProbe -ID 1, 100 -SensorTree $SensorTree 38 | # Multiple IDs are possible 39 | 40 | 1 | Get-PRTGProbe 41 | # Piping is also possible 42 | #> 43 | [CmdletBinding( 44 | DefaultParameterSetName = 'ReturnAll', 45 | SupportsShouldProcess = $false, 46 | ConfirmImpact = 'Low' 47 | )] 48 | Param( 49 | # ID of the PRTG object 50 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'ID', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 51 | [ValidateNotNullOrEmpty()] 52 | [ValidateScript( {$_ -gt 0})] 53 | [Alias('ObjID', 'ID')] 54 | [int[]] 55 | $ObjectId, 56 | 57 | # Name of the Probe 58 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'Name', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 59 | [String[]] 60 | $Name, 61 | 62 | # sensortree from PRTG Server 63 | [ValidateNotNullOrEmpty()] 64 | [xml] 65 | $SensorTree = $script:PRTGSensorTree 66 | ) 67 | 68 | begin { 69 | $queryParam = @{ 70 | "Type" = "probenode" 71 | "SensorTree" = $SensorTree 72 | "Verbose" = $false 73 | } 74 | } 75 | 76 | process { 77 | $result = @() 78 | 79 | switch ($PsCmdlet.ParameterSetName) { 80 | 'ID' { 81 | foreach ($item in $ObjectId) { 82 | $result += Get-PRTGObject -ObjectID $item @queryParam 83 | } 84 | } 85 | 86 | 'Name' { 87 | foreach ($item in $Name) { 88 | $result += Get-PRTGObject -Name $item @queryParam 89 | } 90 | } 91 | 92 | Default { 93 | $result = Get-PRTGObject @queryParam 94 | } 95 | } 96 | 97 | $result 98 | } 99 | 100 | end {} 101 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/device/Get-PRTGDevice.ps1: -------------------------------------------------------------------------------- 1 | function Get-PRTGDevice { 2 | <# 3 | .Synopsis 4 | Get-PRTGDevice 5 | 6 | .DESCRIPTION 7 | Returns one or more devices from sensortree 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Get-PRTGDevice 17 | 18 | Query all devices from the default sensortree (global variable after connect to PRTG server) 19 | 20 | .EXAMPLE 21 | Get-PRTGDevice -SensorTree $SensorTree 22 | 23 | Query devices by name from a non default sensortree 24 | 25 | .EXAMPLE 26 | Get-PRTGDevice -Name "Device01" 27 | 28 | Query devices by name 29 | 30 | .EXAMPLE 31 | Get-PRTGDevice -Name "Device01", "Device*" 32 | 33 | Multiple names are possible 34 | 35 | .EXAMPLE 36 | "Device01" | Get-PRTGDevice 37 | 38 | Piping is also possible 39 | 40 | .EXAMPLE 41 | Get-PRTGDevice -ObjectId 1 42 | 43 | Query devices by object ID 44 | 45 | .EXAMPLE 46 | 1 | Get-PRTGDevice 47 | 48 | Piping is also possible 49 | #> 50 | [CmdletBinding( 51 | DefaultParameterSetName = 'ReturnAll', 52 | SupportsShouldProcess = $false, 53 | ConfirmImpact = 'Low' 54 | )] 55 | Param( 56 | # ID of the PRTG object 57 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'ID', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 58 | [ValidateNotNullOrEmpty()] 59 | [ValidateScript( { $_ -gt 0 })] 60 | [Alias('ObjID', 'ID')] 61 | [int[]] 62 | $ObjectId, 63 | 64 | # Name of the device 65 | [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'Name', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 66 | [String[]] 67 | $Name, 68 | 69 | # sensortree from PRTG Server 70 | [ValidateNotNullOrEmpty()] 71 | [xml] 72 | $SensorTree = $script:PRTGSensorTree 73 | ) 74 | 75 | begin { 76 | $queryParam = @{ 77 | "Type" = "device" 78 | "SensorTree" = $SensorTree 79 | "Verbose" = $false 80 | } 81 | } 82 | 83 | process { 84 | $result = @() 85 | 86 | switch ($PsCmdlet.ParameterSetName) { 87 | 'ID' { 88 | New-Variable -Name result -Force 89 | foreach ($item in $ObjectId) { 90 | $result += Get-PRTGObject -ObjectID $item @queryParam 91 | } 92 | } 93 | 94 | 'Name' { 95 | foreach ($item in $Name) { 96 | $result += Get-PRTGObject -Name $item @queryParam 97 | } 98 | } 99 | 100 | Default { 101 | $result = Get-PRTGObject @queryParam 102 | } 103 | } 104 | 105 | $result 106 | } 107 | 108 | end {} 109 | } 110 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Move-PRTGObjectPosition.ps1: -------------------------------------------------------------------------------- 1 | function Move-PRTGObjectPosition { 2 | <# 3 | .Synopsis 4 | Move-PRTGObjectPosition 5 | 6 | .DESCRIPTION 7 | Moves an object in PRTG hierarchy 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | .LINK 19 | https://github.com/AndiBellstedt/PoShPRTG 20 | 21 | .EXAMPLE 22 | Move-PRTGObject -ObjectId 1 -Direction up 23 | 24 | Move object with ID 1 one position up inside the group/structure 25 | #> 26 | [CmdletBinding( 27 | DefaultParameterSetName = 'Default', 28 | SupportsShouldProcess = $true, 29 | ConfirmImpact = 'medium' 30 | )] 31 | Param( 32 | # ID of the object to resume 33 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 34 | [ValidateNotNullOrEmpty()] 35 | [ValidateScript( { $_ -gt 0 } )] 36 | [Alias('ObjID', 'ID')] 37 | [int[]] 38 | $ObjectId, 39 | 40 | # Message to associate with the pause event 41 | [Parameter(Mandatory = $true)] 42 | [ValidateSet("up", "down", "top", "bottom")] 43 | [string] 44 | $Direction, 45 | 46 | # Url for PRTG Server 47 | [ValidateNotNullOrEmpty()] 48 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 49 | [String] 50 | $Server = $script:PRTGServer, 51 | 52 | # User for PRTG Authentication 53 | [ValidateNotNullOrEmpty()] 54 | [String] 55 | $User = $script:PRTGUser, 56 | 57 | # Password or PassHash for PRTG Authentication 58 | [ValidateNotNullOrEmpty()] 59 | [String] 60 | $Pass = $script:PRTGPass 61 | ) 62 | 63 | begin {} 64 | 65 | process { 66 | foreach ($id in $ObjectId) { 67 | $body = @{ 68 | id = $id 69 | newpos = $Direction 70 | username = $User 71 | passhash = $Pass 72 | } 73 | 74 | if ($pscmdlet.ShouldProcess("objID $Id", "Move $Direction")) { 75 | try { 76 | Write-Log -LogText "Move object ID $id $Direction ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 77 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/setposition.htm" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 78 | } catch { 79 | Write-Log -LogText "Failed to move object ID $id. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 80 | } 81 | } 82 | } 83 | } 84 | 85 | end {} 86 | } 87 | -------------------------------------------------------------------------------- /PoShPRTG/tests/general/Manifest.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe "Validating the module manifest" { 2 | $moduleRoot = (Resolve-Path "$global:testroot\..").Path 3 | $manifest = ((Get-Content "$moduleRoot\PoShPRTG.psd1") -join "`n") | Invoke-Expression 4 | Context "Basic resources validation" { 5 | $files = Get-ChildItem "$moduleRoot\functions" -Recurse -File | Where-Object Name -like "*.ps1" 6 | It "Exports all functions in the public folder" -TestCases @{ files = $files; manifest = $manifest } { 7 | 8 | $functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '<=').InputObject 9 | $functions | Should -BeNullOrEmpty 10 | } 11 | It "Exports no function that isn't also present in the public folder" -TestCases @{ files = $files; manifest = $manifest } { 12 | $functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '=>').InputObject 13 | $functions | Should -BeNullOrEmpty 14 | } 15 | 16 | It "Exports none of its internal functions" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } { 17 | $files = Get-ChildItem "$moduleRoot\internal\functions" -Recurse -File -Filter "*.ps1" 18 | $files | Where-Object BaseName -In $manifest.FunctionsToExport | Should -BeNullOrEmpty 19 | } 20 | } 21 | 22 | Context "Individual file validation" { 23 | It "The root module file exists" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } { 24 | Test-Path "$moduleRoot\$($manifest.RootModule)" | Should -Be $true 25 | } 26 | 27 | foreach ($format in $manifest.FormatsToProcess) { 28 | It "The file $format should exist" -TestCases @{ moduleRoot = $moduleRoot; format = $format } { 29 | Test-Path "$moduleRoot\$format" | Should -Be $true 30 | } 31 | } 32 | 33 | foreach ($type in $manifest.TypesToProcess) { 34 | It "The file $type should exist" -TestCases @{ moduleRoot = $moduleRoot; type = $type } { 35 | Test-Path "$moduleRoot\$type" | Should -Be $true 36 | } 37 | } 38 | 39 | foreach ($assembly in $manifest.RequiredAssemblies) { 40 | if ($assembly -like "*.dll") { 41 | It "The file $assembly should exist" -TestCases @{ moduleRoot = $moduleRoot; assembly = $assembly } { 42 | Test-Path "$moduleRoot\$assembly" | Should -Be $true 43 | } 44 | } else { 45 | It "The file $assembly should load from the GAC" -TestCases @{ moduleRoot = $moduleRoot; assembly = $assembly } { 46 | { Add-Type -AssemblyName $assembly } | Should -Not -Throw 47 | } 48 | } 49 | } 50 | 51 | foreach ($tag in $manifest.PrivateData.PSData.Tags) { 52 | It "Tags should have no spaces in name" -TestCases @{ tag = $tag } { 53 | $tag -match " " | Should -Be $false 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Set-PRTGObjectAlarmAcknowledgement.ps1: -------------------------------------------------------------------------------- 1 | function Set-PRTGObjectAlarmAcknowledgement { 2 | <# 3 | .Synopsis 4 | Set-PRTGObjectAlarmAcknowledgement 5 | 6 | .DESCRIPTION 7 | Acknowledge an alarm on a PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | .LINK 19 | https://github.com/AndiBellstedt/PoShPRTG 20 | 21 | .EXAMPLE 22 | Set-PRTGObjectAlarmAcknowledgement -ObjectId 1 23 | 24 | Set alarm on object 1 25 | 26 | .EXAMPLE 27 | Set-PRTGObjectAlarmAcknowledgement -ObjectId 1 -Message "Done by User01" 28 | 29 | Set alarm on object 1 with indidual message 30 | #> 31 | [CmdletBinding( 32 | DefaultParameterSetName = 'Default', 33 | SupportsShouldProcess = $true, 34 | ConfirmImpact = 'medium' 35 | )] 36 | [Alias('Set-PRTGObjectAlamAcknowledgement')] # in because of typo in cmdletname in previous version, not to produce a breaking change with the typo fix 37 | Param( 38 | # ID of the object to resume 39 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 40 | [ValidateNotNullOrEmpty()] 41 | [ValidateScript( { $_ -gt 0 } )] 42 | [Alias('ObjID', 'ID')] 43 | [int[]] 44 | $ObjectId, 45 | 46 | # Message to associate with the pause event] 47 | [string] 48 | $Message, 49 | 50 | # Url for PRTG Server 51 | [ValidateNotNullOrEmpty()] 52 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 53 | [String] 54 | $Server = $script:PRTGServer, 55 | 56 | # User for PRTG Authentication 57 | [ValidateNotNullOrEmpty()] 58 | [String] 59 | $User = $script:PRTGUser, 60 | 61 | # Password or PassHash for PRTG Authentication 62 | [ValidateNotNullOrEmpty()] 63 | [String] 64 | $Pass = $script:PRTGPass 65 | ) 66 | 67 | Begin {} 68 | 69 | Process { 70 | foreach ($id in $ObjectId) { 71 | $body = @{ 72 | id = $id 73 | username = $User 74 | passhash = $Pass 75 | } 76 | if ($Message) { $body.Add("ackmsg", $Message) } 77 | 78 | if ($pscmdlet.ShouldProcess("objID $Id", "Acknowledge alarm on object")) { 79 | try { 80 | Write-Log -LogText "Acknowledge alarm on object ID $id ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 81 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/acknowledgealarm.htm" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 82 | } catch { 83 | Write-Log -LogText "Failed to acknowledge alarm on object ID $id. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 84 | } 85 | } 86 | } 87 | } 88 | 89 | End {} 90 | } 91 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Receive-PRTGObjectProperty.ps1: -------------------------------------------------------------------------------- 1 | function Receive-PRTGObjectProperty { 2 | <# 3 | .Synopsis 4 | Receive-PRTGObject 5 | 6 | .DESCRIPTION 7 | Query an object property directly from PRTGserver and returns. 8 | Difference to Get-PRTGObjectProperty is, that "Get-PRTGObjectProperty" is working on a modfified sensortree variable in the memory and not on livedata from PRTGServer 9 | 10 | .NOTES 11 | Author: Andreas Bellstedt 12 | 13 | adopted from PSGallery Module "PSPRTG" 14 | Author: Sam-Martin 15 | Github: https://github.com/Sam-Martin/prtg-powershell 16 | 17 | .LINK 18 | https://github.com/AndiBellstedt/PoShPRTG 19 | 20 | .EXAMPLE 21 | PS C:\>Receive-PRTGObject -ObjectId 1 -PropertyName "staus" 22 | PS C:\>Receive-PRTGObject -ID 1 -Name "staus" 23 | 24 | Query property of object 1 live from PRTG server. (not using the value in the sensor tree) 25 | 26 | .EXAMPLE 27 | PS C:\>Receive-PRTGObject -ObjectId 1 -PropertyName "staus" -Server "https://prtg.corp.customer.com" -User "admin" -Pass "1111111" 28 | 29 | Query property of object 1 live from PRTG server. (not using the value in the sensor tree) 30 | #> 31 | [CmdletBinding( 32 | DefaultParameterSetName = 'Default', 33 | SupportsShouldProcess = $false, 34 | ConfirmImpact = 'low' 35 | )] 36 | param( 37 | # ID of the object to pause/resume 38 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 39 | [ValidateNotNullOrEmpty()] 40 | [ValidateScript( { $_ -gt 0 } )] 41 | [Alias('ObjId', 'ID')] 42 | [int[]] 43 | $ObjectId, 44 | 45 | # Name of the object's property to get 46 | [Parameter(Mandatory = $true)] 47 | [ValidateNotNullOrEmpty()] 48 | [Alias('Name')] 49 | [string] 50 | $PropertyName, 51 | 52 | # Url for PRTG Server 53 | [ValidateNotNullOrEmpty()] 54 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 55 | [String] 56 | $Server = $script:PRTGServer, 57 | 58 | # User for PRTG Authentication 59 | [ValidateNotNullOrEmpty()] 60 | [String] 61 | $User = $script:PRTGUser, 62 | 63 | # Password or PassHash for PRTG Authentication 64 | [ValidateNotNullOrEmpty()] 65 | [String] 66 | $Pass = $script:PRTGPass 67 | ) 68 | 69 | Begin { 70 | $body = @{ 71 | id = 0 72 | name = $PropertyName 73 | username = $User 74 | passhash = $Pass 75 | } 76 | } 77 | 78 | Process { 79 | foreach ($ID in $ObjectID) { 80 | $body.id = $ID 81 | # Try to get objectproperty from PRTG 82 | Write-Log -LogText "Get objectproperty for object ID $ID. ($Server)" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 83 | try { 84 | $Result = (Invoke-RestMethod -UseBasicParsing -Uri "$Server/api/getobjectstatus.htm" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop).prtg.result 85 | return $result 86 | } catch { 87 | Write-Log -LogText "Failed to get objectproperty from prtg. ($Server) Message:$($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 88 | } 89 | } 90 | } 91 | 92 | End { 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /PoShPRTG/PoShPRTG.psm1: -------------------------------------------------------------------------------- 1 | $script:ModuleRoot = $PSScriptRoot 2 | $script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\PoShPRTG.psd1").ModuleVersion 3 | 4 | # Detect whether at some level dotsourcing was enforced 5 | $script:doDotSource = Get-PSFConfigValue -FullName PoShPRTG.Import.DoDotSource -Fallback $false 6 | if ($PoShPRTG_dotsourcemodule) { $script:doDotSource = $true } 7 | 8 | <# 9 | Note on Resolve-Path: 10 | All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator. 11 | This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS. 12 | Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist. 13 | This is important when testing for paths. 14 | #> 15 | 16 | # Detect whether at some level loading individual module files, rather than the compiled module was enforced 17 | $importIndividualFiles = Get-PSFConfigValue -FullName PoShPRTG.Import.IndividualFiles -Fallback $false 18 | if ($PoShPRTG_importIndividualFiles) { $importIndividualFiles = $true } 19 | if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true } 20 | if ("" -eq '') { $importIndividualFiles = $true } 21 | 22 | function Import-ModuleFile { 23 | <# 24 | .SYNOPSIS 25 | Loads files into the module on module import. 26 | 27 | .DESCRIPTION 28 | This helper function is used during module initialization. 29 | It should always be dotsourced itself, in order to proper function. 30 | 31 | This provides a central location to react to files being imported, if later desired 32 | 33 | .PARAMETER Path 34 | The path to the file to load 35 | 36 | .EXAMPLE 37 | PS C:\> . Import-ModuleFile -File $function.FullName 38 | 39 | Imports the file stored in $function according to import policy 40 | #> 41 | [CmdletBinding()] 42 | Param ( 43 | [string] 44 | $Path 45 | ) 46 | 47 | $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath 48 | if ($doDotSource) { 49 | . $resolvedPath 50 | } else { 51 | $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) 52 | } 53 | } 54 | 55 | #region Load individual files 56 | if ($importIndividualFiles) { 57 | # Execute Preimport actions 58 | foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) { 59 | . Import-ModuleFile -Path $path 60 | } 61 | 62 | # Import all internal functions 63 | foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) { 64 | . Import-ModuleFile -Path $function.FullName 65 | } 66 | 67 | # Import all public functions 68 | foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) { 69 | . Import-ModuleFile -Path $function.FullName 70 | } 71 | 72 | # Execute Postimport actions 73 | foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) { 74 | . Import-ModuleFile -Path $path 75 | } 76 | 77 | # End it here, do not load compiled code below 78 | return 79 | } 80 | #endregion Load individual files 81 | 82 | #region Load compiled code 83 | "" 84 | #endregion Load compiled code -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Receive-PRTGObject.ps1: -------------------------------------------------------------------------------- 1 | function Receive-PRTGObject { 2 | <# 3 | .Synopsis 4 | Receive-PRTGObject 5 | 6 | .DESCRIPTION 7 | Query an object directly from PRTGserver and returns. 8 | Difference to Get-PRTGObject is, that "Get-PRTGObject" is working on a modfified sensortree variable in the memory and not on livedata from PRTGServer 9 | 10 | .NOTES 11 | Author: Andreas Bellstedt 12 | 13 | adopted from PSGallery Module "PSPRTG" 14 | Author: Sam-Martin 15 | Github: https://github.com/Sam-Martin/prtg-powershell 16 | 17 | .LINK 18 | https://github.com/AndiBellstedt/PoShPRTG 19 | 20 | .EXAMPLE 21 | PS C:\>Receive-PRTGObject 22 | 23 | Receive devices live from PRTG server (not using the cached sensor tree info) 24 | 25 | .EXAMPLE 26 | PS C:\>Receive-PRTGObject -Content sensors 27 | 28 | Receive sensors live from PRTG server (not using the cached sensor tree info) 29 | 30 | #> 31 | [CmdletBinding( 32 | DefaultParameterSetName = 'Default', 33 | SupportsShouldProcess = $false, 34 | ConfirmImpact = 'low' 35 | )] 36 | Param( 37 | # Number of maximal results 38 | [int] 39 | $NumResults = 99999, 40 | 41 | # Properties to query 42 | [string] 43 | $Columns = "objid,type,name,tags,active,host", 44 | 45 | # Type of device to query 46 | [ValidateSet("sensortree", "groups", "sensors", "devices", "tickets", "messages", "values", "channels", "reports", "storedreports", "ticketdata")] 47 | [string] 48 | $Content = "devices", 49 | 50 | # sorting the output 51 | [string] 52 | $SortBy = "objid", 53 | 54 | # Direction to sort the output 55 | [ValidateSet("Desc", "Asc")] 56 | [string] 57 | $SortDirection = "Desc", 58 | 59 | # Filter hashtable to filter out objects from the query 60 | [hashtable] 61 | $Filters, 62 | 63 | # Url for PRTG Server 64 | [ValidateNotNullOrEmpty()] 65 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 66 | [String] 67 | $Server = $script:PRTGServer, 68 | 69 | # User for PRTG Authentication 70 | [ValidateNotNullOrEmpty()] 71 | [String] 72 | $User = $script:PRTGUser, 73 | 74 | # Password or PassHash for PRTG Authentication 75 | [ValidateNotNullOrEmpty()] 76 | [String] 77 | $Pass = $script:PRTGPass 78 | ) 79 | 80 | $SortDirectionPRTGStyle = if ($SortDirection -eq "Desc") { "-" }else { '' } 81 | $body = @{ 82 | content = $content; 83 | count = $numResults; 84 | output = "xml"; 85 | columns = $columns; 86 | sortby = "$SortDirectionPRTGStyle$SortBy"; 87 | username = $User 88 | passhash = $Pass 89 | } 90 | 91 | foreach ($FilterName in $Filters.keys) { 92 | $body.Add($FilterName, $Filters.$FilterName) 93 | } 94 | 95 | # Try to get the PRTG device tree 96 | try { 97 | $prtgDeviceTree = Invoke-RestMethod -UseBasicParsing -Uri "$Server/api/table.xml" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 98 | } catch { 99 | Write-Log -LogText "Failed to get PRTG Device tree $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 100 | return 101 | } 102 | 103 | $prtgDeviceTree 104 | } 105 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Receive-PRTGObjectStatus.ps1: -------------------------------------------------------------------------------- 1 | Function Receive-PRTGObjectStatus { 2 | <# 3 | .Synopsis 4 | Receive-PRTGObjectStatus 5 | 6 | .DESCRIPTION 7 | Query the status of an object directly from PRTGserver and returns. 8 | Difference to Get-PRTGObject is, that "Get-PRTGObject" is working on a modfified sensortree variable in the memory and not on livedata from PRTGServer 9 | 10 | .NOTES 11 | Author: Andreas Bellstedt 12 | 13 | adopted from PSGallery Module "PSPRTG" 14 | Author: Sam-Martin 15 | Github: https://github.com/Sam-Martin/prtg-powershell 16 | 17 | .LINK 18 | https://github.com/AndiBellstedt/PoShPRTG 19 | 20 | .EXAMPLE 21 | PS C:\>Receive-PRTGObjectStatus -ObjectId 1 22 | PS C:\>Receive-PRTGObjectStatus -ID 1 23 | 24 | Query current status of object 1 live from PRTG server. (not using the value in the sensor tree) 25 | 26 | .EXAMPLE 27 | PS C:\>Receive-PRTGObjectStatus -ObjectId 1 -Server "https://prtg.corp.customer.com" -User "admin" -Pass "1111111" 28 | 29 | Query current status of object 1 live from PRTG server. (not using the value in the sensor tree) 30 | #> 31 | [CmdletBinding( 32 | DefaultParameterSetName = 'Default', 33 | SupportsShouldProcess = $false, 34 | ConfirmImpact = 'low' 35 | )] 36 | [OutputType([PSCustomObject])] 37 | Param( 38 | # ID of the object to pause/resume 39 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 40 | [Alias('ID')] 41 | [ValidateScript( { $_ -gt 0 })] 42 | [int[]] 43 | $ObjectID, 44 | 45 | # Url for PRTG Server 46 | [ValidateNotNullOrEmpty()] 47 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 48 | [String] 49 | $Server = $script:PRTGServer, 50 | 51 | # User for PRTG Authentication 52 | [ValidateNotNullOrEmpty()] 53 | [String] 54 | $User = $script:PRTGUser, 55 | 56 | # Password or PassHash for PRTG Authentication 57 | [ValidateNotNullOrEmpty()] 58 | [String] 59 | $Pass = $script:PRTGPass 60 | ) 61 | 62 | Begin { 63 | $StatusMapping = @{ 64 | 1 = "Unknown" 65 | 2 = "Scanning" 66 | 3 = "Up" 67 | 4 = "Warning" 68 | 5 = "Down" 69 | 6 = "No Probe" 70 | 7 = "Paused by User" 71 | 8 = "Paused by Dependency" 72 | 9 = "Paused by Schedule" 73 | 10 = "Unusual" 74 | 11 = "Not Licensed" 75 | 12 = "Paused Until" 76 | } 77 | } 78 | 79 | Process { 80 | foreach ($ID in $ObjectId) { 81 | try { 82 | $statusID = (Receive-PRTGObjectProperty -ObjectId $ID -PropertyName 'status' -Server $Server -User $User -Pass $Pass -ErrorAction Stop -Verbose:$false) 83 | } catch { 84 | Write-Log -LogText "Unable to get object status from prtg. ($Server) Message:$($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 85 | return 86 | } 87 | 88 | $hash = @{ 89 | 'objid' = $ID 90 | "status" = $StatusMapping[[int]$statusID] 91 | "status_raw" = $statusID 92 | } 93 | 94 | $result = New-Object -TypeName PSCustomObject -Property $hash 95 | $result 96 | } 97 | } 98 | 99 | end {} 100 | } 101 | -------------------------------------------------------------------------------- /PoShPRTG/tests/general/FileIntegrity.Tests.ps1: -------------------------------------------------------------------------------- 1 | $moduleRoot = (Resolve-Path "$global:testroot\..").Path 2 | 3 | . "$global:testroot\general\FileIntegrity.Exceptions.ps1" 4 | 5 | Describe "Verifying integrity of module files" { 6 | BeforeAll { 7 | function Get-FileEncoding { 8 | <# 9 | .SYNOPSIS 10 | Tests a file for encoding. 11 | 12 | .DESCRIPTION 13 | Tests a file for encoding. 14 | 15 | .PARAMETER Path 16 | The file to test 17 | #> 18 | [CmdletBinding()] 19 | Param ( 20 | [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)] 21 | [Alias('FullName')] 22 | [string] 23 | $Path 24 | ) 25 | 26 | if ($PSVersionTable.PSVersion.Major -lt 6) { 27 | [byte[]]$byte = get-content -Encoding byte -ReadCount 4 -TotalCount 4 -Path $Path 28 | } else { 29 | [byte[]]$byte = Get-Content -AsByteStream -ReadCount 4 -TotalCount 4 -Path $Path 30 | } 31 | 32 | if ($byte[0] -eq 0xef -and $byte[1] -eq 0xbb -and $byte[2] -eq 0xbf) { 'UTF8 BOM' } 33 | elseif ($byte[0] -eq 0xfe -and $byte[1] -eq 0xff) { 'Unicode' } 34 | elseif ($byte[0] -eq 0 -and $byte[1] -eq 0 -and $byte[2] -eq 0xfe -and $byte[3] -eq 0xff) { 'UTF32' } 35 | elseif ($byte[0] -eq 0x2b -and $byte[1] -eq 0x2f -and $byte[2] -eq 0x76) { 'UTF7' } 36 | else { 'Unknown' } 37 | } 38 | } 39 | 40 | Context "Validating PS1 Script files" { 41 | $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.ps1" | Where-Object FullName -NotLike "$moduleRoot\tests\*" 42 | 43 | foreach ($file in $allFiles) { 44 | $name = $file.FullName.Replace("$moduleRoot\", '') 45 | 46 | It "[$name] Should have UTF8 encoding with Byte Order Mark" -TestCases @{ file = $file } { 47 | Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' 48 | } 49 | 50 | It "[$name] Should have no trailing space" -TestCases @{ file = $file } { 51 | ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0 }).LineNumber | Should -BeNullOrEmpty 52 | } 53 | 54 | $tokens = $null 55 | $parseErrors = $null 56 | $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) 57 | 58 | It "[$name] Should have no syntax errors" -TestCases @{ parseErrors = $parseErrors } { 59 | $parseErrors | Should -BeNullOrEmpty 60 | } 61 | 62 | foreach ($command in $global:BannedCommands) { 63 | if ($global:MayContainCommand["$command"] -notcontains $file.Name) { 64 | It "[$name] Should not use $command" -TestCases @{ tokens = $tokens; command = $command } { 65 | $tokens | Where-Object Text -EQ $command | Should -BeNullOrEmpty 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | Context "Validating help.txt help files" { 73 | $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.help.txt" | Where-Object FullName -NotLike "$moduleRoot\tests\*" 74 | 75 | foreach ($file in $allFiles) { 76 | $name = $file.FullName.Replace("$moduleRoot\", '') 77 | 78 | It "[$name] Should have UTF8 encoding" -TestCases @{ file = $file } { 79 | Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' 80 | } 81 | 82 | It "[$name] Should have no trailing space" -TestCases @{ file = $file } { 83 | ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0 } | Measure-Object).Count | Should -Be 0 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Rename-PRTGObject.ps1: -------------------------------------------------------------------------------- 1 | function Rename-PRTGObject { 2 | <# 3 | .Synopsis 4 | Rename-PRTGObject 5 | 6 | .DESCRIPTION 7 | Rename an PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | adopted from PSGallery Module "PSPRTG" 19 | Author: Sam-Martin 20 | Github: https://github.com/Sam-Martin/prtg-powershell 21 | 22 | .LINK 23 | https://github.com/AndiBellstedt/PoShPRTG 24 | 25 | .EXAMPLE 26 | Rename-PRTGObject -ObjectId 1 -NewName "NewName" 27 | 28 | Rename object with ID 1 to "NewName" 29 | 30 | .EXAMPLE 31 | Get-PRTGObject "MyDevice" | Rename-PRTGObject -NewName "MyNewDevice" 32 | 33 | Rename "MyDevice" to "MyNewDevice" 34 | #> 35 | [CmdletBinding( 36 | DefaultParameterSetName = 'Default', 37 | SupportsShouldProcess = $true, 38 | ConfirmImpact = 'medium' 39 | )] 40 | Param( 41 | # ID of the object to pause/resume 42 | [Parameter(Mandatory = $true)] 43 | [ValidateNotNullOrEmpty()] 44 | [ValidateScript( { $_ -gt 0 } )] 45 | [int] 46 | $ObjectId, 47 | 48 | # New name of the object 49 | [string] 50 | $NewName, 51 | 52 | # Url for PRTG Server 53 | [ValidateNotNullOrEmpty()] 54 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 55 | [String] 56 | $Server = $script:PRTGServer, 57 | 58 | # User for PRTG Authentication 59 | [ValidateNotNullOrEmpty()] 60 | [String] 61 | $User = $script:PRTGUser, 62 | 63 | # Password or PassHash for PRTG Authentication 64 | [ValidateNotNullOrEmpty()] 65 | [String] 66 | $Pass = $script:PRTGPass, 67 | 68 | # SensorTree from PRTG Server 69 | [ValidateNotNullOrEmpty()] 70 | [xml] 71 | $SensorTree = $script:PRTGSensorTree 72 | ) 73 | 74 | $body = @{ 75 | id = $ObjectId 76 | value = $NewName 77 | username = $User 78 | passhash = $Pass 79 | } 80 | 81 | Write-Log -LogText "Get object details from object ID $ObjectId. ($Server)" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 82 | try { 83 | $object = Get-PRTGObject -ID $ObjectId -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 84 | } catch { 85 | Write-Log -LogText $_.exception.message -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 86 | return 87 | } 88 | 89 | if ($pscmdlet.ShouldProcess("objID $ObjectId", "Rename PRTG object to '$NewName'")) { 90 | # Set in PRTG 91 | try { 92 | Write-Log -LogText "Set new name ""$($NewName)"" on object ID $ObjectId. ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 93 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/rename.htm" -Method Get -Body $body -Verbose:$false 94 | } catch { 95 | Write-Log -LogText "Failed to rename object ID $ObjectId. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 96 | return 97 | } 98 | 99 | # Set on SensorTree variable 100 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ObjectId)]/name").InnerText = $object.Name 101 | 102 | # Write output 103 | Get-PRTGObject -ID $ObjectId -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Get-PRTGObjectProperty.ps1: -------------------------------------------------------------------------------- 1 | function Get-PRTGObjectProperty { 2 | <# 3 | .Synopsis 4 | Get-PRTGObjectProperty 5 | 6 | .DESCRIPTION 7 | Get a specific property from an PRTG object out of the sensor tree 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Get-PRTGObjectProperty -ObjectId 1 -PropertyName Name, tags 17 | 18 | Get name and tags from object with ID 1 19 | 20 | .EXAMPLE 21 | Get-PRTGObject -Name "Myobject" | Get-PRTGObjectProperty -Name Name, status 22 | 23 | Get name and status from object "Myobject" 24 | #> 25 | [CmdletBinding( 26 | DefaultParameterSetName = 'ReturnAll', 27 | SupportsShouldProcess = $false, 28 | ConfirmImpact = 'Low' 29 | )] 30 | [OutputType([PSCustomObject])] 31 | Param( 32 | # ID of the object to pause/resume 33 | [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 34 | [ValidateNotNullOrEmpty()] 35 | [ValidateScript({ $_ -gt 0 })] 36 | [Alias('objID', 'ID')] 37 | [int[]] 38 | $ObjectID, 39 | 40 | # Name of the object's property to get 41 | [Parameter(Position = 1, ParameterSetName = 'Name')] 42 | [Alias('Name')] 43 | [ValidateNotNullOrEmpty()] 44 | [string[]] 45 | $PropertyName, 46 | 47 | # SensorTree from PRTG Server 48 | [ValidateNotNullOrEmpty()] 49 | [xml] 50 | $SensorTree = $script:PRTGSensorTree 51 | ) 52 | 53 | Begin {} 54 | 55 | Process { 56 | foreach ($ID in $ObjectID) { 57 | Write-Log -LogText "Get object details from object ID $ID." -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 58 | try { 59 | $Object = Get-PRTGObject -ID $ID -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 60 | } catch { 61 | Write-Log -LogText $_.exception.message -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 62 | return 63 | } 64 | 65 | $ObjectProperty = $Object | Get-Member -MemberType Property, NoteProperty | Select-Object -ExpandProperty Name 66 | $hash = @{} 67 | if ($PropertyName) { 68 | # Parameterset: Name 69 | foreach ($item in $PropertyName) { 70 | $PropertiesToQuery = $ObjectProperty | Where-Object { $_ -like $item } 71 | foreach ($PropertyItem in $PropertiesToQuery) { 72 | if ($hash.$PropertyItem) { 73 | Write-Log -LogText "Property $PropertyItem already existis! Skipping this one." -LogType Warning -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 74 | } else { 75 | Write-Log -LogText "Get property $PropertyItem from object ID $ID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 76 | $hash.Add($PropertyItem, $object.$PropertyItem) 77 | } 78 | } 79 | } 80 | $result = New-Object -TypeName PSCustomObject -Property $hash 81 | } else { 82 | #Parameterset: ReturnAll 83 | Write-Log -LogText "Get all properties from object ID $ID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 84 | foreach ($PropertyItem in $ObjectProperty) { 85 | $hash.Add($PropertyItem, $object.$PropertyItem) 86 | } 87 | } 88 | $result = New-Object -TypeName PSCustomObject -Property $hash 89 | 90 | Write-Log -LogText "Found $($result.count) $(if($result.count -eq 1){"property"}else{"properties"}) in object ID $ID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 91 | $result 92 | } 93 | } 94 | 95 | End {} 96 | } 97 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Set-PRTGObjectPriority.ps1: -------------------------------------------------------------------------------- 1 | function Set-PRTGObjectPriority { 2 | <# 3 | .Synopsis 4 | Set-PRTGObjectPriority 5 | 6 | .DESCRIPTION 7 | Set priority value on a PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | .LINK 19 | https://github.com/AndiBellstedt/PoShPRTG 20 | 21 | .EXAMPLE 22 | Set-PRTGObjectPriority -ObjectId 1 -Priority 3 23 | 24 | Set priority on object 1 25 | 26 | .EXAMPLE 27 | Set-PRTGObjectPriority -ObjectId 1 -Priority 3 -PassThru 28 | 29 | Set priority on object 1 and output the object 30 | 31 | .EXAMPLE 32 | Set-PRTGObjectPriority -ObjectId 1 -Priority 3 -Server "https://prtg.corp.customer.com" -User "admin -Pass "1111111" 33 | 34 | Set priority on object 1 on explicitly specified server with custom credentials. 35 | #> 36 | [CmdletBinding( 37 | DefaultParameterSetName = 'Default', 38 | SupportsShouldProcess = $true, 39 | ConfirmImpact = 'medium' 40 | )] 41 | Param( 42 | # ID of the object to resume 43 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 44 | [ValidateNotNullOrEmpty()] 45 | [ValidateScript( { $_ -gt 0 } )] 46 | [Alias('ObjID', 'ID')] 47 | [int[]] 48 | $ObjectId, 49 | 50 | # Priority value to set 51 | [Parameter(Mandatory = $true)] 52 | [ValidateNotNullOrEmpty()] 53 | [ValidatePattern("[1-5]")] 54 | [int] 55 | $Priority, 56 | 57 | # returns the changed object 58 | [Switch] 59 | $PassThru, 60 | 61 | # Url for PRTG Server 62 | [ValidateNotNullOrEmpty()] 63 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 64 | [String] 65 | $Server = $script:PRTGServer, 66 | 67 | # User for PRTG Authentication 68 | [ValidateNotNullOrEmpty()] 69 | [String] 70 | $User = $script:PRTGUser, 71 | 72 | # Password or PassHash for PRTG Authentication 73 | [ValidateNotNullOrEmpty()] 74 | [String] 75 | $Pass = $script:PRTGPass, 76 | 77 | # sensortree from PRTG Server 78 | [ValidateNotNullOrEmpty()] 79 | [xml] 80 | $SensorTree = $script:PRTGSensorTree 81 | ) 82 | 83 | Begin {} 84 | 85 | Process { 86 | foreach ($id in $ObjectId) { 87 | $body = @{ 88 | id = $id 89 | prio = $Priority 90 | username = $User 91 | passhash = $Pass 92 | } 93 | 94 | if ($pscmdlet.ShouldProcess("objID $Id", "Set priority $Priority to object")) { 95 | #Set in PRTG 96 | try { 97 | Write-Log -LogText "Set priority $Priority to object ID $id ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 98 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/setpriority.htm" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 99 | } catch { 100 | Write-Log -LogText "Failed to set priortiy object ID $id. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 101 | } 102 | 103 | #Set on SensorTree Variable 104 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ID)]/priority").InnerText = $Priority 105 | 106 | #Write-Output 107 | if ($PassThru) { Get-PRTGObject -ObjectID $id -SensorTree $SensorTree -Verbose:$false } 108 | } 109 | } 110 | } 111 | 112 | End {} 113 | } -------------------------------------------------------------------------------- /PoShPRTG/tests/pester.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | $TestGeneral = $true, 3 | 4 | $TestFunctions = $true, 5 | 6 | [ValidateSet('None', 'Normal', 'Detailed', 'Diagnostic')] 7 | [Alias('Show')] 8 | $Output = "None", 9 | 10 | $Include = "*", 11 | 12 | $Exclude = "" 13 | ) 14 | 15 | Write-PSFMessage -Level Important -Message "Starting Tests" 16 | 17 | Write-PSFMessage -Level Important -Message "Importing Module" 18 | 19 | $global:testroot = $PSScriptRoot 20 | $global:__pester_data = @{ } 21 | 22 | Remove-Module PoShPRTG -ErrorAction Ignore 23 | Import-Module "$PSScriptRoot\..\PoShPRTG.psd1" 24 | Import-Module "$PSScriptRoot\..\PoShPRTG.psm1" -Force 25 | 26 | # Need to import explicitly so we can use the configuration class 27 | Import-Module Pester 28 | 29 | Write-PSFMessage -Level Important -Message "Creating test result folder" 30 | $null = New-Item -Path "$PSScriptRoot\..\.." -Name TestResults -ItemType Directory -Force 31 | 32 | $totalFailed = 0 33 | $totalRun = 0 34 | 35 | $testresults = @() 36 | $config = [PesterConfiguration]::Default 37 | $config.TestResult.Enabled = $true 38 | 39 | #region Run General Tests 40 | if ($TestGeneral) { 41 | Write-PSFMessage -Level Important -Message "Modules imported, proceeding with general tests" 42 | foreach ($file in (Get-ChildItem "$PSScriptRoot\general" | Where-Object Name -like "*.Tests.ps1")) { 43 | if ($file.Name -notlike $Include) { continue } 44 | if ($file.Name -like $Exclude) { continue } 45 | 46 | Write-PSFMessage -Level Significant -Message " Executing $($file.Name)" 47 | $config.TestResult.OutputPath = Join-Path "$PSScriptRoot\..\..\TestResults" "TEST-$($file.BaseName).xml" 48 | $config.Run.Path = $file.FullName 49 | $config.Run.PassThru = $true 50 | $config.Output.Verbosity = $Output 51 | $results = Invoke-Pester -Configuration $config 52 | foreach ($result in $results) { 53 | $totalRun += $result.TotalCount 54 | $totalFailed += $result.FailedCount 55 | $result.Tests | Where-Object Result -ne 'Passed' | ForEach-Object { 56 | $testresults += [pscustomobject]@{ 57 | Block = $_.Block 58 | Name = "It $($_.Name)" 59 | Result = $_.Result 60 | Message = $_.ErrorRecord.DisplayErrorMessage 61 | } 62 | } 63 | } 64 | } 65 | } 66 | #endregion Run General Tests 67 | 68 | $global:__pester_data.ScriptAnalyzer | Out-Host 69 | 70 | #region Test Commands 71 | if ($TestFunctions) { 72 | Write-PSFMessage -Level Important -Message "Proceeding with individual tests" 73 | foreach ($file in (Get-ChildItem "$PSScriptRoot\functions" -Recurse -File | Where-Object Name -like "*Tests.ps1")) { 74 | if ($file.Name -notlike $Include) { continue } 75 | if ($file.Name -like $Exclude) { continue } 76 | 77 | Write-PSFMessage -Level Significant -Message " Executing $($file.Name)" 78 | $config.TestResult.OutputPath = Join-Path "$PSScriptRoot\..\..\TestResults" "TEST-$($file.BaseName).xml" 79 | $config.Run.Path = $file.FullName 80 | $config.Run.PassThru = $true 81 | $config.Output.Verbosity = $Output 82 | $results = Invoke-Pester -Configuration $config 83 | foreach ($result in $results) { 84 | $totalRun += $result.TotalCount 85 | $totalFailed += $result.FailedCount 86 | $result.Tests | Where-Object Result -ne 'Passed' | ForEach-Object { 87 | $testresults += [pscustomobject]@{ 88 | Block = $_.Block 89 | Name = "It $($_.Name)" 90 | Result = $_.Result 91 | Message = $_.ErrorRecord.DisplayErrorMessage 92 | } 93 | } 94 | } 95 | } 96 | } 97 | #endregion Test Commands 98 | 99 | $testresults | Sort-Object Describe, Context, Name, Result, Message | Format-List 100 | 101 | if ($totalFailed -eq 0) { 102 | Write-PSFMessage -Level Critical -Message "All $totalRun tests executed without a single failure!" 103 | } else { 104 | Write-PSFMessage -Level Critical -Message "$totalFailed tests out of $totalRun tests failed!" 105 | } 106 | 107 | if ($totalFailed -gt 0) { 108 | throw "$totalFailed / $totalRun tests failed!" 109 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Set-PRTGObjectProperty.ps1: -------------------------------------------------------------------------------- 1 | function Set-PRTGObjectProperty { 2 | <# 3 | .Synopsis 4 | Set-PRTGObjectProperty 5 | 6 | .DESCRIPTION 7 | Set the property of an PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | adopted from PSGallery Module "PSPRTG" 19 | Author: Sam-Martin 20 | Github: https://github.com/Sam-Martin/prtg-powershell 21 | 22 | .LINK 23 | https://github.com/AndiBellstedt/PoShPRTG 24 | 25 | .EXAMPLE 26 | Set-PRTGObjectProperty -ObjectId 1 -PropertyName "Name" -PropertyValue "NewValue" 27 | 28 | Set object property in PRTG 29 | 30 | .EXAMPLE 31 | Set-PRTGObjectProperty -ObjectId 1 -PropertyName "Name" -PropertyValue "NewValue" -Server "https://prtg.corp.customer.com" -User "admin -Pass "1111111" 32 | 33 | Set object property in PRTG 34 | #> 35 | [CmdletBinding( 36 | DefaultParameterSetName = 'Default', 37 | SupportsShouldProcess = $true, 38 | ConfirmImpact = 'medium' 39 | )] 40 | Param( 41 | # ID of the object to pause/resume 42 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 43 | [ValidateNotNullOrEmpty()] 44 | [ValidateScript( { $_ -ge 0 } )] 45 | [Alias('ObjID', 'ID')] 46 | [int[]] 47 | $ObjectId, 48 | 49 | # Name of the object's property to set 50 | [Parameter(Mandatory = $true)] 51 | [ValidateNotNullOrEmpty()] 52 | [string] 53 | $PropertyName, 54 | 55 | # Value to which to set the property of the object 56 | [string] 57 | $PropertyValue, 58 | 59 | # returns the changed object 60 | [Switch] 61 | $PassThru, 62 | 63 | # Url for PRTG Server 64 | [ValidateNotNullOrEmpty()] 65 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 66 | [String] 67 | $Server = $script:PRTGServer, 68 | 69 | # User for PRTG Authentication 70 | [ValidateNotNullOrEmpty()] 71 | [String] 72 | $User = $script:PRTGUser, 73 | 74 | # Password or PassHash for PRTG Authentication 75 | [ValidateNotNullOrEmpty()] 76 | [String] 77 | $Pass = $script:PRTGPass, 78 | 79 | # sensortree from PRTG Server 80 | [ValidateNotNullOrEmpty()] 81 | [xml] 82 | $SensorTree = $script:PRTGSensorTree 83 | ) 84 | 85 | Begin {} 86 | 87 | Process { 88 | foreach ($id in $ObjectId) { 89 | $body = @{ 90 | id = $id 91 | action = 1 92 | name = $PropertyName 93 | value = $PropertyValue 94 | username = $User 95 | passhash = $Pass 96 | } 97 | 98 | if ($pscmdlet.ShouldProcess("objID $Id", "Set property '$PropertyName' to '$PropertyValue' on PRTG object")) { 99 | # Set property in PRTG 100 | try { 101 | Write-Log -LogText "Set property ""$PropertyName"" to ""$PropertyValue"" on object ID $id ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 102 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/setobjectproperty.htm" -Method Get -Body $body -Verbose:$false 103 | } catch { 104 | Write-Log -LogText "Failed to set value $PropertyValue on property $PropertyName. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -Error -NoFileStatus 105 | } 106 | 107 | # Set property in SensorTree Variable 108 | if ($PropertyName -eq "id") { 109 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($id)]").SetAttribute($PropertyName, $PropertyValue) 110 | } 111 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($id)]/$PropertyName").InnerText = $PropertyValue 112 | 113 | # Write-Output 114 | if ($PassThru) { Get-PRTGObject -ObjectID $id -SensorTree $SensorTree -Verbose:$false } 115 | } 116 | } 117 | } 118 | 119 | End {} 120 | } 121 | -------------------------------------------------------------------------------- /PoShPRTG/internal/functions/Compare-ObjectProperty.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | .VERSION 1.0.0.4 3 | .GUID 32a4f2d6-b021-4a38-8b6a-d76ceef2b02d 4 | .AUTHOR Jeffrey Snover 5 | .COMPANYNAME 6 | .COPYRIGHT 7 | .TAGS 8 | .LICENSEURI 9 | .PROJECTURI 10 | .ICONURI 11 | .EXTERNALMODULEDEPENDENCIES 12 | .REQUIREDSCRIPTS 13 | .EXTERNALSCRIPTDEPENDENCIES 14 | .RELEASENOTES 15 | #> 16 | function Compare-ObjectProperty { 17 | <# 18 | 19 | .DESCRIPTION 20 | Determine the difference in properties between two objects. 21 | 22 | NOTE - if a property's type does not have a CompareTo() function, the property is converted to a 23 | string for the comparison 24 | 25 | .EXAMPLE 26 | $o1= @{a1=1;a2=2;b1=3;b2=4} 27 | PS C:\> $o2= @{a1=1;a2=2;b1=3;b2=5} 28 | PS C:\> Compare-ObjectProperty $o1 $o2 29 | 30 | Property Value SideIndicator 31 | -------- ----- ------------- 32 | b2 4 <= 33 | b2 5 => 34 | 35 | .EXAMPLE 36 | $o1= @{a1=1;a2=2;b1=3;b2=4} 37 | PS C:\> $o2= @{a1=1;a2=2;b1=3;b2=5} 38 | PS C:\> Compare-ObjectProperty $o1 $o2 -PropertyFilter a* 39 | 40 | .EXAMPLE 41 | $o1= @{a1=1;a2=2;b1=3;b2=4} 42 | PS C:\> $o2= @{a1=1;a2=2;b1=3;b2=5} 43 | PS C:\> Compare-ObjectProperty $o1 $o2 -IncludeEqual 44 | Property Value SideIndicator 45 | -------- ----- ------------- 46 | a1 1 == 47 | a2 2 == 48 | b1 3 == 49 | b2 4 <= 50 | b2 5 => 51 | #> 52 | [CmdletBinding()] 53 | param( 54 | # ReferenceObject 55 | [Parameter(Mandatory = $true)] 56 | [object] 57 | ${ReferenceObject}, 58 | 59 | # DifferenceObject 60 | [Parameter(Mandatory = $true)] 61 | [object] 62 | ${DifferenceObject}, 63 | 64 | # You can specify which properties to compare using a wildcard (using the -LIKE operator) 65 | [String[]] 66 | ${PropertyFilter} = "*", 67 | 68 | # Don't include any properties that are different 69 | [switch] 70 | ${ExcludeDifferent}, 71 | 72 | # Include the properties which are equal 73 | [switch] 74 | ${IncludeEqual} 75 | ) 76 | 77 | 78 | #region Helper Routines 79 | function Convert-PropertyToHash { 80 | param([Parameter(Position = 0)]$inputObject) 81 | 82 | if ($null -eq $inputObject) { 83 | return @{} 84 | } elseif ($inputObject -is [HashTable]) { 85 | # We have to clone the hashtable because we are going to Remove Keys and if we don't 86 | # clone it, we'll modify the original object 87 | return $inputObject.clone() 88 | } else { 89 | $h = @{} 90 | foreach ($p in (Get-Member -InputObject $inputObject -MemberType Properties).Name) { 91 | $h.$p = $inputObject.$p 92 | } 93 | return $h 94 | } 95 | } 96 | #endregion 97 | 98 | $refH = Convert-PropertyToHash $ReferenceObject 99 | $diffH = Convert-PropertyToHash $DifferenceObject 100 | 101 | foreach ($filter in $PropertyFilter) { 102 | foreach ($p in $refH.keys | Where-Object { $_ -like $filter }) { 103 | if (! ($diffH.Contains($p)) -and !($ExcludeDifferent)) { 104 | New-Object PSObject -Property @{ SideIndicator = "<="; Property = $p; Value = $($refH.$p) } 105 | } else { 106 | # We convert these to strings and do a string comparison because there are all sorts of .NET 107 | # objects whose comparison functions don't yeild the expected results. 108 | if ($refH.$p -AND ($refH.$p | Get-Member -MemberType Method -Name CompareTo)) { 109 | $Different = $refH.$p -ne $diffH.$p 110 | } else { 111 | $Different = ("" + $refH.$p) -ne ("" + $diffH.$p) 112 | } 113 | if ($Different -and !($ExcludeDifferent)) { 114 | New-Object PSObject -Property @{ SideIndicator = "<="; Property = $p; Value = $($refH.$p) } 115 | New-Object PSObject -Property @{ SideIndicator = "=>"; Property = $p; Value = $($diffH.$p) } 116 | } elseif ($IncludeEqual) { 117 | New-Object PSObject -Property @{ SideIndicator = "=="; Property = $p; Value = $($refH.$p) } 118 | } 119 | $diffH.Remove($p) 120 | } 121 | } 122 | } 123 | 124 | if (-not $ExcludeDifferent) { 125 | foreach ($p in $diffH.keys | Where-Object { $_ -like $PropertyFilter }) { 126 | New-Object PSObject -Property @{ SideIndicator = "=>"; Property = $p; Value = $($diffH.$p) } 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Receive-PRTGObjectDetail.ps1: -------------------------------------------------------------------------------- 1 | function Receive-PRTGObjectDetail { 2 | <# 3 | .Synopsis 4 | Receive-PRTGObjectDetail 5 | 6 | .DESCRIPTION 7 | Query status information for an object directly from PRTGserver. 8 | (function not working on sensortree variable in memory) 9 | 10 | .NOTES 11 | Author: Andreas Bellstedt 12 | 13 | .LINK 14 | https://github.com/AndiBellstedt/PoShPRTG 15 | 16 | .EXAMPLE 17 | PS C:\>Receive-PRTGObjectDetail -ObjectId 1 18 | PS C:\>Receive-PRTGObjectDetail -ID 1 19 | 20 | Query object details of object 1 live from PRTG server. (not using the value in the sensor tree) 21 | 22 | .EXAMPLE 23 | PS C:\>Receive-PRTGObjectDetail -ObjectId 1 -Server "https://prtg.corp.customer.com" -User "admin" -Pass "1111111" 24 | 25 | Query object details of object 1 live from PRTG server. (not using the value in the sensor tree) 26 | #> 27 | [CmdletBinding( 28 | DefaultParameterSetName = 'Default', 29 | SupportsShouldProcess = $false, 30 | ConfirmImpact = 'low' 31 | )] 32 | param( 33 | # ID of the object to pause/resume 34 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [ValidateScript( { $_ -gt 0 } )] 37 | [Alias('ObjId', 'ID')] 38 | [int] 39 | $ObjectId, 40 | 41 | # Name of the object's property to get 42 | [ValidateNotNullOrEmpty()] 43 | [string] 44 | $PropertyName, 45 | 46 | # Url for PRTG Server 47 | [ValidateNotNullOrEmpty()] 48 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 49 | [String] 50 | $Server = $script:PRTGServer, 51 | 52 | # User for PRTG Authentication 53 | [ValidateNotNullOrEmpty()] 54 | [String] 55 | $User = $script:PRTGUser, 56 | 57 | # Password or PassHash for PRTG Authentication 58 | [ValidateNotNullOrEmpty()] 59 | [String] 60 | $Pass = $script:PRTGPass 61 | ) 62 | 63 | Begin { 64 | $body = @{ 65 | id = 0 66 | username = $User 67 | passhash = $Pass 68 | } 69 | } 70 | 71 | Process { 72 | foreach ($ID in $ObjectID) { 73 | $body.id = $ID 74 | Write-Log -LogText "Get details for object ID $ID. ($Server)" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 75 | try { 76 | $Object = (Invoke-RestMethod -Uri "$Server/api/getsensordetails.xml" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop).sensordata 77 | } catch { 78 | Write-Log -LogText "Failed to get details for object from prtg. ($Server) Message:$($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 79 | } 80 | 81 | $ObjectProperty = $Object | Get-Member -MemberType Property | Select-Object -ExpandProperty Name 82 | $hash = @{} 83 | if ($PropertyName) { 84 | #Parameterset: Name 85 | foreach ($item in $PropertyName) { 86 | $PropertiesToQuery = $ObjectProperty | Where-Object { $_ -like $item } 87 | foreach ($PropertyItem in $PropertiesToQuery) { 88 | if ($hash.$PropertyItem) { 89 | Write-Log -LogText "Property $PropertyItem already existis! Skipping this one." -LogType Warning -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 90 | } else { 91 | Write-Log -LogText "Get property $PropertyItem from object ID $ID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 92 | $hash.Add($PropertyItem, $object.$PropertyItem) 93 | } 94 | } 95 | } 96 | $result = New-Object -TypeName PSCustomObject -Property $hash 97 | } else { 98 | #Parameterset: ReturnAll 99 | Write-Log -LogText "Get all properties from object ID $ID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 100 | foreach ($PropertyItem in $ObjectProperty) { 101 | $hash.Add($PropertyItem, $object.$PropertyItem.'#cdata-section') 102 | } 103 | } 104 | $result = New-Object -TypeName PSCustomObject -Property $hash 105 | 106 | Write-Log -LogText "Found $($result.count) $(if($result.count -eq 1){"property"}else{"properties"}) in object ID $ID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 107 | $result 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Remove-PRTGObject.ps1: -------------------------------------------------------------------------------- 1 | function Remove-PRTGObject { 2 | <# 3 | .Synopsis 4 | Remove-PRTGObject 5 | 6 | .DESCRIPTION 7 | Remove an object from PRTGserver and returns. 8 | Difference to Get-PRTGObject is, that "Get-PRTGObject" is working on a modfified sensortree variable in the memory and not on livedata from PRTGServer 9 | 10 | .PARAMETER WhatIf 11 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 12 | 13 | .PARAMETER Confirm 14 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 15 | 16 | .NOTES 17 | Author: Andreas Bellstedt 18 | 19 | adopted from PSGallery Module "PSPRTG" 20 | Author: Sam-Martin 21 | Github: https://github.com/Sam-Martin/prtg-powershell 22 | 23 | .LINK 24 | https://github.com/AndiBellstedt/PoShPRTG 25 | 26 | .EXAMPLE 27 | PS C:\>Remove-PRTGObject -ObjectId 1 28 | 29 | Remove object with ID 1 30 | 31 | .EXAMPLE 32 | PS C:\>Get-PRTGObject -ObjectId 1 | Remove-PRTGObject 33 | 34 | Remove object with ID 1 35 | #> 36 | [CmdletBinding( 37 | DefaultParameterSetName = 'Default', 38 | SupportsShouldProcess = $true, 39 | ConfirmImpact = 'high' 40 | )] 41 | [OutputType([Boolean])] 42 | Param( 43 | # ID of the object to delete 44 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 45 | [Alias('objID', 'ID')] 46 | [ValidateNotNullOrEmpty()] 47 | [ValidateScript( { $_ -gt 0 } )] 48 | [int[]] 49 | $ObjectID, 50 | 51 | # Url for PRTG Server 52 | [ValidateNotNullOrEmpty()] 53 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 54 | [String] 55 | $Server = $script:PRTGServer, 56 | 57 | # User for PRTG Authentication 58 | [ValidateNotNullOrEmpty()] 59 | [String] 60 | $User = $script:PRTGUser, 61 | 62 | # Password or PassHash for PRTG Authentication 63 | [ValidateNotNullOrEmpty()] 64 | [String] 65 | $Pass = $script:PRTGPass, 66 | 67 | # sensortree from PRTG Server 68 | [ValidateNotNullOrEmpty()] 69 | [xml] 70 | $SensorTree = $script:PRTGSensorTree, 71 | 72 | # returns the deleted object 73 | [Switch] 74 | $PassThru 75 | ) 76 | 77 | Begin {} 78 | 79 | Process { 80 | $Deleted = @() 81 | 82 | foreach ($ID in $ObjectID) { 83 | $body = @{ 84 | id = $ID 85 | approve = 1 86 | username = $User 87 | passhash = $Pass 88 | } 89 | 90 | # Check for object on sensor tree 91 | try { 92 | $Object = Get-PRTGObject -ObjectID $id -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 93 | } catch { 94 | Write-Log -LogText "Cannot find object ID $ID" -LogType Error -LogScope $MyInvocation.MyCommand.Name -Error -NoFileStatus 95 | break 96 | } 97 | 98 | if ($pscmdlet.ShouldProcess("$($Object.name) objID $ID", "Remove PRTG object")) { 99 | #Remove in PRTG 100 | try { 101 | Write-Log -LogText "Remove object ID $ID ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 102 | $Result = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/deleteobject.htm " -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 103 | } catch { 104 | Write-Log -LogText "Failed to delete object $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -Error -NoFileStatus 105 | break 106 | } 107 | 108 | #Remove on SensorTree 109 | if ($Result.StatusCode -eq 200) { 110 | $ToDelete = $SensorTree.SelectSingleNode("//*[id='$ID']") 111 | while ($ToDelete -ne $null) { 112 | $Deleted = $ToDelete.ParentNode.RemoveChild($ToDelete) 113 | $ToDelete = $SensorTree.SelectSingleNode("//*[id='$ID']") 114 | } 115 | Write-Log -LogText "Remove object ID $($ID) with name ""$($Deleted.name)"". ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 116 | } else { 117 | Write-Log -LogText "Failed to delete object ID $($Deleted.ObjID). ($($Server)) Message:$($Result.StatusCode) $($Result.StatusDescription)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 118 | break 119 | } 120 | 121 | #Write-Uutput 122 | if ($PassThru) { Set-TypesNamesToPRTGObject -PRTGObject $Deleted } 123 | } 124 | } 125 | } 126 | 127 | End {} 128 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Enable-PRTGObject.ps1: -------------------------------------------------------------------------------- 1 | function Enable-PRTGObject { 2 | <# 3 | .Synopsis 4 | Enable-PRTGObject 5 | 6 | .DESCRIPTION 7 | Enables an (paused) PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | adopted from PSGallery Module "PSPRTG" 19 | Author: Sam-Martin 20 | Github: https://github.com/Sam-Martin/prtg-powershell 21 | 22 | .LINK 23 | https://github.com/AndiBellstedt/PoShPRTG 24 | 25 | .EXAMPLE 26 | Enable-PRTGObject -ObjectId 1 27 | 28 | Enables object with ID 1 29 | #> 30 | [CmdletBinding( 31 | DefaultParameterSetName = 'Default', 32 | SupportsShouldProcess = $true, 33 | ConfirmImpact = 'medium' 34 | )] 35 | Param( 36 | # ID of the object to resume 37 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 38 | [ValidateNotNullOrEmpty()] 39 | [ValidateScript( { $_ -gt 0 } )] 40 | [Alias('ObjID', 'ID')] 41 | [int[]] 42 | $ObjectId, 43 | 44 | # do action regardless of current status of sensor 45 | [Switch] 46 | $Force, 47 | 48 | # Not waiting for sensor status update in PRTG server (faster reply on large batch jobs) 49 | [Switch] 50 | $NoWaitOnStatus, 51 | 52 | # Url for PRTG Server 53 | [ValidateNotNullOrEmpty()] 54 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 55 | [String] 56 | $Server = $script:PRTGServer, 57 | 58 | # User for PRTG Authentication 59 | [ValidateNotNullOrEmpty()] 60 | [String] 61 | $User = $script:PRTGUser, 62 | 63 | # Password or PassHash for PRTG Authentication 64 | [ValidateNotNullOrEmpty()] 65 | [String] 66 | $Pass = $script:PRTGPass, 67 | 68 | # Sensortree from PRTG Server 69 | [ValidateNotNullOrEmpty()] 70 | [xml] 71 | $SensorTree = $script:PRTGSensorTree 72 | ) 73 | 74 | begin {} 75 | 76 | process { 77 | foreach ($id in $ObjectId) { 78 | $body = @{ 79 | id = $id 80 | action = 1 81 | username = $User 82 | passhash = $Pass 83 | } 84 | 85 | $StatusBefore = Receive-PRTGObjectStatus -ObjectID $id -Server $Server -User $User -Pass $Pass -Verbose:$false 86 | if (-not $StatusBefore) { 87 | Write-Log "Failed to current status for object ID $id. Skipping object" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 88 | break 89 | } 90 | 91 | if ($pscmdlet.ShouldProcess("objID $Id", "Enable PRTG object")) { 92 | try { 93 | #Enable in PRTG 94 | Write-Log -LogText "Enable object ID $id ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 95 | if ( ($StatusBefore.status_raw -in (7, 8, 9, 12)) -or $Force ) { 96 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/pause.htm" -Method Get -Body $Body -Verbose:$false -Debug:$false -ErrorAction Stop 97 | } else { 98 | Write-Log -LogText "Object ID $id is already enabled" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 99 | break 100 | } 101 | } catch { 102 | Write-Log -LogText "Failed to enable object ID $id. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 103 | break 104 | } 105 | 106 | #Receive new status 107 | $SafeguardBreakCount = 15 108 | $count = 0 109 | if ($NoWaitOnStatus) { $break = $true } else { $break = $false } 110 | do { 111 | $StatusAfter = Receive-PRTGObjectStatus -ObjectID $id -Server $Server -User $User -Pass $Pass -Verbose:$false 112 | if ($StatusBefore.status_raw -ne $StatusAfter.status_raw) { $break = $true } 113 | if ($count -ge $SafeguardBreakCount) { 114 | Write-Log -LogText "Error receiving enable-status from object $id! break status query." -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 115 | $break = $true 116 | } 117 | Start-Sleep -Seconds 1 118 | $count ++ 119 | } until($break) 120 | 121 | #Set in SensorTree variable 122 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ID)]/status_raw").InnerText = $StatusAfter.status_raw 123 | } 124 | } 125 | } 126 | 127 | end {} 128 | } 129 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Remove-PRTGObjectTAG.ps1: -------------------------------------------------------------------------------- 1 | function Remove-PRTGObjectTAG { 2 | <# 3 | .Synopsis 4 | Remove-PRTGObjectTAG 5 | 6 | .DESCRIPTION 7 | Remove a text from the tags property of an PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | .LINK 19 | https://github.com/AndiBellstedt/PoShPRTG 20 | 21 | .EXAMPLE 22 | PS C:\>Remove-PRTGObjectTAG -ObjectId 1 -TAGName "MyTAG" 23 | 24 | Remove "MyTAG" from an object with ID 1 25 | #> 26 | [CmdletBinding( 27 | DefaultParameterSetName = 'Default', 28 | SupportsShouldProcess = $true, 29 | ConfirmImpact = 'medium' 30 | )] 31 | Param( 32 | # ID of the object to pause/resume 33 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 34 | [ValidateNotNullOrEmpty()] 35 | [ValidateScript( { $_ -gt 0 } )] 36 | [Alias('ObjID', 'ID')] 37 | [int] 38 | $ObjectId, 39 | 40 | # Name of the object's property to set 41 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $false)] 42 | [ValidateNotNullOrEmpty()] 43 | [string[]] 44 | $TAGName, 45 | 46 | # Url for PRTG Server 47 | [ValidateNotNullOrEmpty()] 48 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 49 | [String] 50 | $Server = $script:PRTGServer, 51 | 52 | # User for PRTG Authentication 53 | [ValidateNotNullOrEmpty()] 54 | [String] 55 | $User = $script:PRTGUser, 56 | 57 | # Password or PassHash for PRTG Authentication 58 | [ValidateNotNullOrEmpty()] 59 | [String] 60 | $Pass = $script:PRTGPass, 61 | 62 | # sensortree from PRTG Server 63 | [ValidateNotNullOrEmpty()] 64 | [xml] 65 | $SensorTree = $script:PRTGSensorTree, 66 | 67 | # Skip errors if an tag is not present 68 | [Switch] 69 | $Force, 70 | 71 | # Output the deleted object 72 | [Switch] 73 | $PassThru 74 | ) 75 | 76 | Begin {} 77 | 78 | Process { 79 | foreach ($ID in $ObjectId) { 80 | $break = $false 81 | 82 | #Get the object 83 | Write-Log -LogText "Gather object tags from object ID $ID." -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 84 | try { 85 | $Object = Get-PRTGObject -ID $ID -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 86 | } catch { 87 | Write-Log -LogText $_.exception.message -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 88 | return 89 | } 90 | 91 | #Build and check TAG lists 92 | $TAGListExisting = $Object.tags.Split(' ') 93 | $TAGListToSet = $Object.tags 94 | $TAGListCount = 0 95 | 96 | foreach ($TAG in $TAGName) { 97 | if ($TAG -in $TAGListExisting) { 98 | $TAGListToSet = $TAGListToSet -replace [regex]::Escape($TAG), '' 99 | $TAGListCount++ 100 | } else { 101 | if ($Force) { 102 | Write-Log -LogText "Skipping tag ""$($TAG)"", because it is not present on object id $ID" -LogType Warning -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Warning 103 | } else { 104 | Write-Log -LogText "Tag ""$($TAG)"" is not present on object id $ID" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 105 | $break = $true 106 | break 107 | } 108 | } 109 | } 110 | 111 | $TAGListToSet = $TAGListToSet.Trim() 112 | if ($break) { break } 113 | 114 | #set TAG list to PRTG object 115 | $MessageText = "Remove $($TAGListCount) $(if($TAGListCount -eq 1) {"tag"} else {"tags"})" 116 | if ($pscmdlet.ShouldProcess("objID $ID", $MessageText)) { 117 | Write-Log -LogText $MessageText -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 118 | try { 119 | #Set in PRTG 120 | Set-PRTGObjectProperty -ObjectId $ID -PropertyName tags -PropertyValue $TAGListToSet -Server $Server -User $User -Pass $Pass -ErrorAction Stop -Verbose:$false 121 | 122 | #Set on object to return 123 | $Object.tags = $TAGListToSet 124 | 125 | #Set in SensorTree variable 126 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ID)]/tags").InnerText = $TAGListToSet 127 | } catch { 128 | Write-Log -LogText $_.exception.message -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 129 | if (-not $Force) { break } 130 | } 131 | } 132 | 133 | #output the object 134 | if ($PassThru) { $Object } 135 | 136 | #clear up the variable mess 137 | Remove-Variable TAG, TAGListExisting, TAGListToSet, Object, MessageText -Force -ErrorAction Ignore -Confirm:$false -Verbose:$false -Debug:$false -WhatIf:$false 138 | } 139 | } 140 | 141 | End {} 142 | } 143 | -------------------------------------------------------------------------------- /PoShPRTG/PoShPRTG.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Script module or binary module file associated with this manifest 3 | RootModule = 'PoShPRTG.psm1' 4 | 5 | # Version number of this module. 6 | ModuleVersion = '1.5.2.0' 7 | 8 | # ID used to uniquely identify this module 9 | GUID = '2dd57587-dbe7-4f17-a5fb-3c9d9999af76' 10 | 11 | # Author of this module 12 | Author = 'Andreas Bellstedt' 13 | 14 | # Company or vendor of this module 15 | CompanyName = '' 16 | 17 | # Copyright statement for this module 18 | Copyright = 'Copyright (c) 2019 Andreas Bellstedt' 19 | 20 | # Description of the functionality provided by this module 21 | Description = @' 22 | PoShPRTG is a comprehensive module for administering PRTG NETWORK MONITOR (www.paessler.com/prtg). 23 | 24 | It eases the rollout-/deployment process for new machines and managment of existing machines with all there sensors. 25 | The shipped cmdlets are used to call the PRTG API (http://prtg.paessler.com/api.htm?username=demo&password=demodemo) 26 | 27 | All cmdlets are build with 28 | - powershell regular verbs 29 | - mostly with pipeling availabilties 30 | - comprehensive logging on verbose and debug channel 31 | '@ 32 | 33 | # Minimum version of the Windows PowerShell engine required by this module 34 | PowerShellVersion = '3.0' 35 | 36 | # Modules that must be imported into the global environment prior to importing 37 | # this module 38 | RequiredModules = @( 39 | @{ ModuleName = 'PSFramework'; ModuleVersion = '0.9.25.107' } 40 | ) 41 | 42 | # Processor architecture (None, X86, Amd64) required by this module 43 | ProcessorArchitecture = 'None' 44 | 45 | # Assemblies that must be loaded prior to importing this module 46 | # RequiredAssemblies = @('bin\PoShPRTG.dll') 47 | 48 | # Type files (.ps1xml) to be loaded when importing this module 49 | # TypesToProcess = @('xml\PoShPRTG.Types.ps1xml') 50 | 51 | # Format files (.ps1xml) to be loaded when importing this module 52 | # FormatsToProcess = @('xml\PoShPRTG.Format.ps1xml') 53 | 54 | # Functions to export from this module 55 | FunctionsToExport = @( 56 | #basic functions 57 | # Basic functions are mostly adopted from the PRTG API documentation. 58 | # Some of the basic functions are adopted from PSGallery Module "PSPRTG" 59 | # Author: Sam-Martin 60 | # Github: https://github.com/Sam-Martin/prtg-powershell 61 | 'Connect-PRTGServer', 62 | 'Get-PRTGSensorTree', 63 | 'Get-PRTGProbe', 64 | 'Get-PRTGObject', 65 | 'Receive-PRTGObject', 66 | 'Copy-PRTGObject', 67 | 'Set-PRTGObjectProperty', 68 | 'Disable-PRTGObject', 69 | 'Enable-PRTGObject', 70 | 'Remove-PRTGObject', 71 | 'Rename-PRTGObject', 72 | 'Get-PRTGObjectProperty', 73 | 'Receive-PRTGObjectProperty', 74 | 'Receive-PRTGObjectStatus', 75 | 'Get-PRTGDevice', 76 | 'Get-PRTGSensor', 77 | 'Get-PRTGGroup', 78 | 'Disconnect-PRTGServer', 79 | 'Set-PRTGObjectPriority', 80 | 'Invoke-PRTGSensorTreeRefresh', 81 | 'Test-PRTGObjectNotification', 82 | 'Receive-PRTGObjectDetail', 83 | 'Invoke-PRTGObjectRefresh', 84 | 'Set-PRTGObjectAlarmAcknowledgement', 85 | 'Move-PRTGObjectPosition', 86 | 'Get-PRTGObjectTAG', 87 | 'Add-PRTGObjectTAG', 88 | 'Remove-PRTGObjectTAG', 89 | 'Find-PRTGObject', 90 | 'Show-PRTGTemplateSummaryFromObjectTAG', 91 | 'Compare-PRTGDeviceSensorsFromTemplateTAG', 92 | 93 | # Rollout- / Deployment functions 94 | 'New-PRTGDefaultFolderStructureToProbe', 95 | 'New-PRTGDeviceFromTemplate' 96 | ) 97 | 98 | # Cmdlets to export from this module 99 | CmdletsToExport = '' 100 | 101 | # Variables to export from this module 102 | VariablesToExport = '' 103 | 104 | # Aliases to export from this module 105 | AliasesToExport = @() 106 | 107 | # List of all modules packaged with this module 108 | ModuleList = @() 109 | 110 | # List of all files packaged with this module 111 | FileList = @() 112 | 113 | # Private data to pass to the module specified in ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 114 | PrivateData = @{ 115 | 116 | #Support for PowerShellGet galleries. 117 | PSData = @{ 118 | 119 | # Tags applied to this module. These help with module discovery in online galleries. 120 | Tags = @( 121 | 'PoShPRTG', 122 | 'PSPRTG', 123 | 'PRTG', 124 | 'PRTGNetworkMonitor', 125 | 'PRTG_Network_Monitor', 126 | 'PowerShell', 127 | 'Automation', 128 | 'Management', 129 | 'Monitoring', 130 | 'PSEdition_Desktop', 131 | 'Windows' 132 | ) 133 | 134 | # A URL to the license for this module. 135 | LicenseUri = 'https://github.com/AndiBellstedt/PoShPRTG/blob/master/license' 136 | 137 | # A URL to the main website for this project. 138 | ProjectUri = 'https://github.com/AndiBellstedt/PoShPRTG' 139 | 140 | # A URL to an icon representing this module. 141 | IconUri = 'https://github.com/AndiBellstedt/PoShPRTG/blob/master/assets/PoShPrtg_128x128.png' 142 | 143 | # ReleaseNotes of this module 144 | ReleaseNotes = 'https://github.com/AndiBellstedt/PoShPRTG/blob/master/PoShPRTG/changelog.md' 145 | 146 | } # End of PSData hashtable 147 | 148 | } # End of PrivateData hashtable 149 | 150 | # HelpInfo URI of this module 151 | HelpInfoURI = 'https://github.com/AndiBellstedt/PoShPRTG' 152 | 153 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 154 | # DefaultCommandPrefix = '' 155 | 156 | } 157 | -------------------------------------------------------------------------------- /PoShPRTG/functions/deployment/New-PRTGDefaultFolderStructureToProbe.ps1: -------------------------------------------------------------------------------- 1 | function New-PRTGDefaultFolderStructureToProbe { 2 | <# 3 | .Synopsis 4 | New-PRTGDefaultFolderStructureToProbe 5 | 6 | .DESCRIPTION 7 | Copy a new folder/group structure to a destination 8 | Primary intension is to copy a tempalte folderstructure to a new probe 9 | 10 | .PARAMETER WhatIf 11 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 12 | 13 | .PARAMETER Confirm 14 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 15 | 16 | .NOTES 17 | Author: Andreas Bellstedt 18 | 19 | .LINK 20 | https://github.com/AndiBellstedt/PoShPRTG 21 | 22 | .EXAMPLE 23 | New-PRTGDefaultFolderStructureToProbe 24 | 25 | Required values will be queried by gridview-selection 26 | 27 | .EXAMPLE 28 | New-PRTGDefaultFolderStructureToProbe -TemplateFolderStructureID (Get-PRTGObject -Name "Template_group_name") -ProbeID (Get-PRTGProbes | Out-GridView -Title "Please select destination probe" -OutputMode Single) 29 | 30 | Creates group (folder) structure in a probe from "Template_group_name". 31 | All groups beneath the object "Template_group_name" will be copied to the probe selected by the Out-GridView cmdlet 32 | 33 | .EXAMPLE 34 | New-PRTGDefaultFolderStructureToProbe -TemplateFolderStructureID (Get-PRTGGroup -Name "MyTemplateGroup").objId -ProbeID (Get-PRTGProbes "NewProbe").ObjId 35 | 36 | Copy group structure beneath group "MyTemplateGroup" to the probe "NewProbe" 37 | #> 38 | [CmdletBinding( 39 | DefaultParameterSetName = 'Default', 40 | SupportsShouldProcess = $true, 41 | ConfirmImpact = 'Medium' 42 | )] 43 | Param( 44 | # ID of the group that contains the structure to be copied to the destination probe 45 | [ValidateNotNullOrEmpty()] 46 | [int] 47 | $TemplateFolderStructureID = (Get-PRTGObject -Name "Groups for new customer" -Type group -SensorTree $script:PRTGSensorTree -Verbose:$false | Select-Object -ExpandProperty ObjID), 48 | 49 | # ID of the destination probe 50 | [ValidateNotNullOrEmpty()] 51 | [int] 52 | $ProbeID = (Get-PRTGProbe -SensorTree $script:PRTGSensorTree -Verbose:$false | Sort-Object fullname | Select-Object Name, objID | Out-GridView -Title "Please select destination probe" -OutputMode Single | Select-Object -ExpandProperty ObjID), 53 | 54 | # Url for PRTG Server 55 | [ValidateNotNullOrEmpty()] 56 | [ValidateScript( { if ( ($_.StartsWith("http")) ) { $true }else { $false } })] 57 | [String] 58 | $Server = $script:PRTGServer, 59 | 60 | # User for PRTG Authentication 61 | [ValidateNotNullOrEmpty()] 62 | [String] 63 | $User = $script:PRTGUser, 64 | 65 | # Password or PassHash for PRTG Authentication 66 | [ValidateNotNullOrEmpty()] 67 | [String] 68 | $Pass = $script:PRTGPass, 69 | 70 | # SensorTree from PRTG Server 71 | [ValidateNotNullOrEmpty()] 72 | [xml] 73 | $SensorTree = $script:PRTGSensorTree 74 | ) 75 | 76 | $logscope = $MyInvocation.MyCommand.Name 77 | 78 | #Get tempalte to copy 79 | $TemplateFolderStructure = Get-PRTGObject -ID $TemplateFolderStructureID -Type group -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 80 | if (-not $TemplateFolderStructure.group) { 81 | Write-Log -LogText "Template folder not found or no groups under structure." -LogType Error -LogScope $logscope -NoFileStatus -Error 82 | return 83 | } 84 | 85 | #Get target object 86 | $ProbeNewCustomer = Get-PRTGObject -ID $ProbeID -Type probenode -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 87 | if (-not $ProbeNewCustomer) { 88 | Write-Log -LogText "No Probe specified." -LogType Error -LogScope $logscope -NoFileStatus -Error 89 | return 90 | } 91 | 92 | $count = 0 93 | $TotalCount = $TemplateFolderStructure.group.count 94 | $Copied = @() 95 | foreach ($item in $TemplateFolderStructure.group) { 96 | $count++ 97 | Write-Progress -Activity "Copy data structure" -Status "Progress: $count of $($TotalCount)" -PercentComplete ($count / $TotalCount * 100) 98 | if ($pscmdlet.ShouldProcess($ProbeNewCustomer.name, "Deploy ""$($item.name)")) { 99 | Write-Log -LogText "Deploy objID $($item.ID[0]) ""$($item.name)"" to objID $($ProbeNewCustomer.objID) ""$($ProbeNewCustomer.name)""" -LogType Set -LogScope $logscope -NoFileStatus 100 | Remove-Variable ErrorEvent -Force -ErrorAction Ignore -Verbose:$false -Debug:$false -WhatIf:$false -Confirm:$false 101 | try { 102 | $CopyObject = Copy-PRTGObject -ObjectId $item.ID[0] -TargetID $ProbeNewCustomer.ObjID -Name $item.name -Server $Server -User $User -Pass $Pass -SensorTree $SensorTree -Verbose:$false -ErrorVariable ErrorEvent -ErrorAction Stop 103 | Enable-PRTGObject -ObjectId $CopyObject.objid -Force -NoWaitOnStatus -Server $Server -User $User -Pass $Pass -SensorTree $SensorTree -Verbose:$false -ErrorVariable ErrorEvent -ErrorAction Stop 104 | $Copied += $CopyObject 105 | Remove-Variable CopyObject -Scope script -Force -ErrorAction Ignore -Verbose:$false -Debug:$false -WhatIf:$false -Confirm:$false 106 | } catch { 107 | Write-Log -LogText "Error occured while deploying new folder structure! $($_.exception.message)" -LogType Error -LogScope $logscope -Error -NoFileStatus 108 | return 109 | } 110 | } 111 | Remove-Variable item -Force -ErrorAction Ignore -Verbose:$false -Debug:$false -WhatIf:$false -Confirm:$false 112 | } 113 | Write-Log -LogText "$($Copied.count) objects copied" -LogType Info -LogScope $logscope -NoFileStatus 114 | 115 | Write-Log -LogText "Refresh PRTG SensorTree" -LogType Query -LogScope $logscope -NoFileStatus 116 | $SensorTree = Invoke-PRTGSensorTreeRefresh -Server $Server -User $User -Pass $Pass -PassThru -Verbose:$false 117 | } 118 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Disable-PRTGObject.ps1: -------------------------------------------------------------------------------- 1 | function Disable-PRTGObject { 2 | <# 3 | .Synopsis 4 | Disable-PRTGObject 5 | 6 | .DESCRIPTION 7 | Pause an PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | adopted from PSGallery Module "PSPRTG" 19 | Author: Sam-Martin 20 | Github: https://github.com/Sam-Martin/prtg-powershell 21 | 22 | .LINK 23 | https://github.com/AndiBellstedt/PoShPRTG 24 | 25 | .EXAMPLE 26 | Disable-PRTGObject -ObjectId 1 27 | 28 | Disable object with ID 1 for unlimited time 29 | No message is used. 30 | 31 | .EXAMPLE 32 | Disable-PRTGObject -ObjectId 1 -Message "Done by User01" 33 | 34 | Disable object with ID 1 for unlimited time with specified message. 35 | 36 | .EXAMPLE 37 | Disable-PRTGObject -ObjectId 1 -Message "Done by User01" -Minutes 1 38 | 39 | Disable object with ID 1 for one minute with specified message. 40 | #> 41 | [CmdletBinding( 42 | DefaultParameterSetName = 'Default', 43 | SupportsShouldProcess = $true, 44 | ConfirmImpact = 'medium' 45 | )] 46 | Param( 47 | # ID of the object to pause 48 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 49 | [ValidateNotNullOrEmpty()] 50 | [ValidateScript( { $_ -gt 0 } )] 51 | [Alias('ObjID')] 52 | [int[]] 53 | $ObjectId, 54 | 55 | # Message to associate with the pause event 56 | [string] 57 | $Message, 58 | 59 | # Length of time in minutes to pause the object, $null for indefinite 60 | [int] 61 | $Minutes = $null, 62 | 63 | # do action regardless of current status of sensor 64 | [Switch] 65 | $Force, 66 | 67 | # Not waiting for sensor status update in PRTG server (faster reply on large batch jobs) 68 | [Switch] 69 | $NoWaitOnStatus, 70 | 71 | # Url for PRTG Server 72 | [ValidateNotNullOrEmpty()] 73 | [ValidateScript( { if ($_.StartsWith("http")) { $true } else { $false } } )] 74 | [String] 75 | $Server = $script:PRTGServer, 76 | 77 | # User for PRTG Authentication 78 | [ValidateNotNullOrEmpty()] 79 | [String] 80 | $User = $script:PRTGUser, 81 | 82 | # Password or PassHash for PRTG Authentication 83 | [ValidateNotNullOrEmpty()] 84 | [String] 85 | $Pass = $script:PRTGPass, 86 | 87 | # Sensortree from PRTG Server 88 | [ValidateNotNullOrEmpty()] 89 | [xml] 90 | $SensorTree = $script:PRTGSensorTree 91 | ) 92 | 93 | begin {} 94 | 95 | process { 96 | foreach ($id in $ObjectId) { 97 | $body = @{ 98 | id = $id 99 | action = 0 100 | username = $User 101 | passhash = $Pass 102 | } 103 | if ($Minutes) { $body.Add("duration", $Minutes) } 104 | if ($Message) { $body.Add("pausemsg", $Message) } 105 | 106 | if ($pscmdlet.ShouldProcess("objID $Id", "Disable PRTG object")) { 107 | try { 108 | if ($Minutes) { 109 | Write-Log -LogText "Disable object ID $id for $Minutes minutes. $(if($Message){"Message:$Message "})($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 110 | } else { 111 | Write-Log -LogText "Permanent disable object ID $id. $(if($Message){"Message:$Message "})($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 112 | } 113 | $StatusBefore = Receive-PRTGObjectStatus -ObjectID $id -Server $Server -User $User -Pass $Pass -Verbose:$false 114 | if ( ($StatusBefore.status_raw -notin (7, 8, 9, 12)) -or $Force ) { 115 | $null = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/pause$(if($Minutes){"objectfor"}).htm" -Method Get -Body $body -Verbose:$false -Debug:$false -ErrorAction Stop 116 | } else { 117 | Write-Log -LogText "Object ID $id is already disabled" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 118 | break 119 | } 120 | 121 | if ($NoWaitOnStatus) { 122 | if ($Minutes) { 123 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ID)]/status_raw").InnerText = 12 124 | } else { 125 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ID)]/status_raw").InnerText = 7 126 | } 127 | } else { 128 | $break = $false 129 | do { 130 | $StatusAfter = Receive-PRTGObjectStatus -ObjectID $id -Server $Server -User $User -Pass $Pass -Verbose:$false 131 | if ($StatusBefore.status_raw -ne $StatusAfter.status_raw) { 132 | $break = $true 133 | } 134 | Start-Sleep -Seconds 1 135 | } until ($break) 136 | 137 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ID)]/status_raw").InnerText = $StatusAfter.status_raw 138 | } 139 | } catch { 140 | Write-Log -LogText "Failed to disable object ID $id. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 141 | break 142 | } 143 | } 144 | } 145 | } 146 | 147 | end {} 148 | } 149 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Add-PRTGObjectTAG.ps1: -------------------------------------------------------------------------------- 1 | function Add-PRTGObjectTAG { 2 | <# 3 | .Synopsis 4 | Add-PRTGObjectTAG 5 | 6 | .DESCRIPTION 7 | Add a text to the tags property of an PRTG object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | .LINK 19 | https://github.com/AndiBellstedt/PoShPRTG 20 | 21 | .EXAMPLE 22 | Add-PRTGObjectTAG -ObjectId 1 -TAGName "NewName" 23 | 24 | Add TAG "NewName" to object 1 25 | #> 26 | [CmdletBinding( 27 | DefaultParameterSetName = 'Default', 28 | SupportsShouldProcess = $true, 29 | ConfirmImpact = 'medium' 30 | )] 31 | Param( 32 | # ID of the object to pause/resume 33 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 34 | [ValidateNotNullOrEmpty()] 35 | [ValidateScript( { $_ -gt 0 })] 36 | [Alias('ObjID', 'ID')] 37 | [int] 38 | $ObjectId, 39 | 40 | # Name of the object's property to set 41 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $false)] 42 | [ValidateNotNullOrEmpty()] 43 | [string[]] 44 | $TAGName, 45 | 46 | # Url for PRTG Server 47 | [ValidateNotNullOrEmpty()] 48 | [ValidateScript( { if ( ($_.StartsWith("http")) ) { $true } else { $false } } )] 49 | [String] 50 | $Server = $script:PRTGServer, 51 | 52 | # User for PRTG Authentication 53 | [ValidateNotNullOrEmpty()] 54 | [String] 55 | $User = $script:PRTGUser, 56 | 57 | # Password or PassHash for PRTG Authentication 58 | [ValidateNotNullOrEmpty()] 59 | [String] 60 | $Pass = $script:PRTGPass, 61 | 62 | # sensortree from PRTG Server 63 | [ValidateNotNullOrEmpty()] 64 | [xml] 65 | $SensorTree = $script:PRTGSensorTree, 66 | 67 | # skip errors if an tag is not present 68 | [Switch] 69 | $Force, 70 | 71 | # returns the changed object 72 | [Switch] 73 | $PassThru 74 | ) 75 | 76 | Begin {} 77 | 78 | Process { 79 | foreach ($ID in $ObjectId) { 80 | $break = $false 81 | #Get the object 82 | Write-Log -LogText "Gather object tags from object ID $ID." -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 83 | try { 84 | $Object = Get-PRTGObject -ID $ID -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 85 | } catch { 86 | Write-Log -LogText $_.exception.message -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 87 | break 88 | } 89 | 90 | #Build and check TAG lists 91 | if ($Object.tags) { 92 | [array]$TAGListExisting = $Object.tags.Split(' ') 93 | } 94 | $TAGListToAdd = @() 95 | foreach ($TAG in $TAGName) { 96 | if ($TAG.Contains(' ')) { 97 | Write-Log -LogText "The tag ""$($TAG)"" contains invalid space characters! Space characters will be removed." -LogType Warning -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Warning 98 | $TAG = $TAG.Replace(' ', '') 99 | } 100 | if ($TAG -in $TAGListExisting) { 101 | if ($Force) { 102 | Write-Log -LogText "Skipping tag ""$($TAG)"", because it is already set on object id $ID" -LogType Warning -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Warning 103 | } else { 104 | Write-Log -LogText "Tag ""$($TAG)"" is already set on object id $ID" -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 105 | $break = $true 106 | break 107 | } 108 | } else { 109 | $TAGListToAdd += $TAG 110 | } 111 | } 112 | if ($break) { break } 113 | if ($TAGListExisting) { 114 | $TAGListToSet = "$($TAGListExisting) $([string]::Join(' ',$TAGListToAdd))" 115 | } else { 116 | $TAGListToSet = [string]::Join(' ', $TAGListToAdd) 117 | } 118 | $TAGListToSet = $TAGListToSet.Trim() 119 | 120 | #set TAG list to PRTG object 121 | if ($TAGListToAdd) { 122 | $MessageText = "Add $($TAGListToAdd.count) $(if($TAGListToAdd.count -eq 1) {"tag"} else {"tags"}) ($([string]::Join(' ',$TAGListToAdd)))" 123 | if ($pscmdlet.ShouldProcess("objID $ID", $MessageText)) { 124 | Write-Log -LogText $MessageText -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 125 | try { 126 | #Set in PRTG 127 | Set-PRTGObjectProperty -ObjectId $ID -PropertyName tags -PropertyValue $TAGListToSet -Server $Server -User $User -Pass $Pass -ErrorAction Stop -Verbose:$false 128 | 129 | #Set on object to return 130 | $Object.tags = $TAGListToSet 131 | 132 | #Set in SensorTree variable 133 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ID)]/tags").InnerText = $TAGListToSet 134 | } catch { 135 | Write-Log -LogText $_.exception.message -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 136 | if (-not $Force) { break } 137 | } 138 | } 139 | } else { 140 | Write-Log -LogText "No tags to set. Skipping object ID $($Object.objId)" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 141 | } 142 | 143 | #output the object 144 | if ($PassThru) { $Object } 145 | 146 | #clear up the variable mess 147 | Remove-Variable TAG, TAGListExisting, TAGListToAdd, TAGListToSet, Object, MessageText -Force -ErrorAction Ignore -Verbose:$false -Debug:$false -WhatIf:$false 148 | } 149 | } 150 | 151 | End {} 152 | } 153 | -------------------------------------------------------------------------------- /PoShPRTG/functions/deployment/Show-PRTGTemplateSummaryFromObjectTAG.ps1: -------------------------------------------------------------------------------- 1 | function Show-PRTGTemplateSummaryFromObjectTAG { 2 | <# 3 | .Synopsis 4 | Show-PRTGTemplateRoles 5 | 6 | .DESCRIPTION 7 | Display a list of template roles found under a groups and devices under a prtg structure 8 | 9 | .NOTES 10 | Author: Andreas Bellstedt 11 | 12 | .LINK 13 | https://github.com/AndiBellstedt/PoShPRTG 14 | 15 | .EXAMPLE 16 | Show-PRTGTemplateSummaryFromObjectTAG 17 | 18 | Display list of tags witch began with "Template_" and can be found under PRTG Core Server object. 19 | This is the default set of parameters. 20 | 21 | .EXAMPLE 22 | Show-PRTGTemplateSummaryFromObjectTAG -TemplateBaseID 100 23 | 24 | Display a list of tags witch began with "Template_" and are based under the group or device with the object ID 100. 25 | 26 | .EXAMPLE 27 | Show-PRTGTemplateSummaryFromObjectTAG -TemplateBaseID 100 -TemplateTAGNameIdentifier "MyPersonalTemplate-" 28 | 29 | Display a list of tags witch began with "MyPersonalTemplate-" and are based under the group or device with the object ID 100. 30 | #> 31 | [CmdletBinding( 32 | DefaultParameterSetName = 'Default', 33 | SupportsShouldProcess = $false, 34 | ConfirmImpact = 'Low' 35 | )] 36 | Param( 37 | # ID of the object to copy 38 | [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 39 | [ValidateNotNullOrEmpty()] 40 | [ValidateScript( {$_ -gt 0})] 41 | [Alias('objID', 'ID', 'ObjectId')] 42 | [int] 43 | $TemplateBaseID = 1, 44 | 45 | # Filter value to identify template tags 46 | [ValidateNotNullOrEmpty()] 47 | [ValidateScript( {$_ -notcontains ("*", "?")})] 48 | [string] 49 | $TemplateTAGNameIdentifier = "Template_", 50 | 51 | # Include matching as non matching objects 52 | [switch] 53 | $IncludeNonMatching, 54 | 55 | # SensorTree from PRTG Server 56 | [ValidateNotNullOrEmpty()] 57 | [xml] 58 | $SensorTree = $script:PRTGSensorTree 59 | ) 60 | 61 | begin {} 62 | 63 | process { 64 | #Build Template Role object for comparing against devices 65 | $TemplateRoleDevices = Get-PRTGObject -ObjectID $TemplateBaseID -Recursive -Type group, device -SensorTree $SensorTree | Where-Object tags -Match ([regex]::Escape($TemplateTAGNameIdentifier)) 66 | $TemplateRoleDevicesTypeGroup = $TemplateRoleDevices | Group-Object type -NoElement | Select-Object Count, Name, @{N = "Text"; E = { "$($_.count) $($_.Name.tolower())$(if($_.count -ne 1){"s"})"}} 67 | 68 | #Start to create new object 69 | if ($TemplateRoleDevices.tags) { 70 | [array]$TemplateRoles = $TemplateRoleDevices.tags.split(' ') | Where-Object { $_ -Match ([regex]::Escape($TemplateTAGNameIdentifier)) } | Sort-Object -Unique | ForEach-Object { New-Object -TypeName psobject -Property @{"RoleName" = $_ } } 71 | Write-Log -LogText "Found $($TemplateRoles.count) role$(if($TemplateRole.count -ne 1){"s"}) from $([string]::Join("and ",$TemplateRoleDevicesTypeGroup.Text)) in templatebase object ID $TemplateBaseID" -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 72 | } else { 73 | Write-Log -LogText "No template roles found in object ID $TemplateBaseID" -LogType Warning -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 74 | return 75 | } 76 | 77 | foreach ($TemplateRole in $TemplateRoles) { 78 | Write-Log -LogText "Building object for ""$($TemplateRole.RoleName)"" from $([string]::Join("and ",$TemplateRoleDevicesTypeGroup.Text)) under templatebase object ID $TemplateBaseID" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 79 | 80 | [array]$device = $TemplateRoleDevices | Where-Object {$_.tags.split(' ') -eq $TemplateRole.RoleName} 81 | Add-Member -InputObject $TemplateRole -MemberType NoteProperty -Force -Name DeviceCount -Value ([array]($device | Where-Object objId -ne $TemplateBaseID)).count 82 | Add-Member -InputObject $TemplateRole -MemberType NoteProperty -Force -Name Device -Value ([array]($device | Where-Object objId -ne $TemplateBaseID)) 83 | if ($device.sensor) { 84 | [array]$sensor = $device.sensor | Where-Object {$_.tags.split(' ') -eq $TemplateRole.RoleName} 85 | Add-Member -InputObject $TemplateRole -MemberType NoteProperty -Force -Name SensorCount -Value $sensor.count 86 | Add-Member -InputObject $TemplateRole -MemberType NoteProperty -Force -Name Sensor -Value ([array](Set-TypesNamesToPRTGObject -PRTGObject $sensor)) 87 | } else { 88 | Add-Member -InputObject $TemplateRole -MemberType NoteProperty -Force -Name SensorCount -Value 0 89 | Add-Member -InputObject $TemplateRole -MemberType NoteProperty -Force -Name Sensor -Value $null 90 | } 91 | Remove-Variable device, sensor -Force -ErrorAction Ignore -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false 92 | } 93 | 94 | if ($IncludeNonMatching) { 95 | Write-Log -LogText "Searching for objects not matching TAG-identifier ""$($TemplateTAGNameIdentifier)"" in $([string]::Join("and ",$TemplateRoleDevicesTypeGroup.Text)) under templatebase object ID $TemplateBaseID" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 96 | [array]$device = $TemplateRoleDevices | Where-Object {$_.tags -notmatch ([regex]::Escape($TemplateTAGNameIdentifier))} 97 | [array]$sensor = $TemplateRoleDevices.sensor | Where-Object {$_.tags -notmatch ([regex]::Escape($TemplateTAGNameIdentifier))} 98 | if ( ($device | Where-Object objId -ne $TemplateBaseID) -or ($sensor) ) { 99 | Write-Log -LogText "found non matching objects. Inserting an empty RoleName object." -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 100 | $TemplateRoles += New-Object -TypeName psobject -Property @{ 101 | "RoleName" = "" 102 | "DeviceCount" = if ($device | Where-Object objId -ne $TemplateBaseID) { ([array]($device | Where-Object objId -ne $TemplateBaseID)).count } else { 0 } 103 | "Device" = if ($device | Where-Object objId -ne $TemplateBaseID) { ([array]($device | Where-Object objId -ne $TemplateBaseID)) } else { $null } 104 | "SensorCount" = if ($sensor) { $sensor.Count } else { 0 } 105 | "Sensor" = if ($sensor) { [array]([array](Set-TypesNamesToPRTGObject -PRTGObject $sensor)) } else { $null } 106 | } 107 | } 108 | Remove-Variable device, sensor -Force -ErrorAction Ignore -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false 109 | } 110 | 111 | $TemplateRoles 112 | } 113 | 114 | end {} 115 | } -------------------------------------------------------------------------------- /PoShPRTG/tests/general/Help.Tests.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | The original test this is based upon was written by June Blender. 4 | After several rounds of modifications it stands now as it is, but the honor remains hers. 5 | 6 | Thank you June, for all you have done! 7 | 8 | .DESCRIPTION 9 | This test evaluates the help for all commands in a module. 10 | 11 | .PARAMETER SkipTest 12 | Disables this test. 13 | 14 | .PARAMETER CommandPath 15 | List of paths under which the script files are stored. 16 | This test assumes that all functions have their own file that is named after themselves. 17 | These paths are used to search for commands that should exist and be tested. 18 | Will search recursively and accepts wildcards, make sure only functions are found 19 | 20 | .PARAMETER ModuleName 21 | Name of the module to be tested. 22 | The module must already be imported 23 | 24 | .PARAMETER ExceptionsFile 25 | File in which exceptions and adjustments are configured. 26 | In it there should be two arrays and a hashtable defined: 27 | $global:FunctionHelpTestExceptions 28 | $global:HelpTestEnumeratedArrays 29 | $global:HelpTestSkipParameterType 30 | These can be used to tweak the tests slightly in cases of need. 31 | See the example file for explanations on each of these usage and effect. 32 | #> 33 | [CmdletBinding()] 34 | Param ( 35 | [switch] 36 | $SkipTest, 37 | 38 | [string[]] 39 | $CommandPath = @("$global:testroot\..\functions", "$global:testroot\..\internal\functions"), 40 | 41 | [string] 42 | $ModuleName = "PoShPRTG", 43 | 44 | [string] 45 | $ExceptionsFile = "$global:testroot\general\Help.Exceptions.ps1" 46 | ) 47 | if ($SkipTest) { return } 48 | . $ExceptionsFile 49 | 50 | $includedNames = (Get-ChildItem $CommandPath -Recurse -File | Where-Object Name -like "*.ps1").BaseName 51 | $commands = Get-Command -Module (Get-Module $ModuleName) -CommandType Cmdlet, Function, Workflow | Where-Object Name -in $includedNames 52 | 53 | ## When testing help, remember that help is cached at the beginning of each session. 54 | ## To test, restart session. 55 | 56 | 57 | foreach ($command in $commands) { 58 | $commandName = $command.Name 59 | 60 | # Skip all functions that are on the exclusions list 61 | if ($global:FunctionHelpTestExceptions -contains $commandName) { continue } 62 | 63 | # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets 64 | $Help = Get-Help $commandName -ErrorAction SilentlyContinue 65 | 66 | Describe "Test help for $commandName" { 67 | 68 | # If help is not found, synopsis in auto-generated help is the syntax diagram 69 | It "should not be auto-generated" -TestCases @{ Help = $Help } { 70 | $Help.Synopsis | Should -Not -BeLike '*`[``]*' 71 | } 72 | 73 | # Should be a description for every function 74 | It "gets description for $commandName" -TestCases @{ Help = $Help } { 75 | $Help.Description | Should -Not -BeNullOrEmpty 76 | } 77 | 78 | # Should be at least one example 79 | It "gets example code from $commandName" -TestCases @{ Help = $Help } { 80 | ($Help.Examples.Example | Select-Object -First 1).Code | Should -Not -BeNullOrEmpty 81 | } 82 | 83 | # Should be at least one example description 84 | It "gets example help from $commandName" -TestCases @{ Help = $Help } { 85 | ($Help.Examples.Example.Remarks | Select-Object -First 1).Text | Should -Not -BeNullOrEmpty 86 | } 87 | 88 | Context "Test parameter help for $commandName" { 89 | 90 | $common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable' 91 | 92 | $parameters = $command.ParameterSets.Parameters | Sort-Object -Property Name -Unique | Where-Object Name -notin $common 93 | $parameterNames = $parameters.Name 94 | $HelpParameterNames = $Help.Parameters.Parameter.Name | Sort-Object -Unique 95 | foreach ($parameter in $parameters) { 96 | $parameterName = $parameter.Name 97 | $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ $parameterName 98 | 99 | # Should be a description for every parameter 100 | It "gets help for parameter: $parameterName : in $commandName" -TestCases @{ parameterHelp = $parameterHelp } { 101 | $parameterHelp.Description.Text | Should -Not -BeNullOrEmpty 102 | } 103 | 104 | $codeMandatory = $parameter.IsMandatory.toString() 105 | It "help for $parameterName parameter in $commandName has correct Mandatory value" -TestCases @{ parameterHelp = $parameterHelp; codeMandatory = $codeMandatory } { 106 | $parameterHelp.Required | Should -Be $codeMandatory 107 | } 108 | 109 | if ($HelpTestSkipParameterType[$commandName] -contains $parameterName) { continue } 110 | 111 | $codeType = $parameter.ParameterType.Name 112 | 113 | if ($parameter.ParameterType.IsEnum) { 114 | # Enumerations often have issues with the typename not being reliably available 115 | $names = $parameter.ParameterType::GetNames($parameter.ParameterType) 116 | # Parameter type in Help should match code 117 | It "help for $commandName has correct parameter type for $parameterName" -TestCases @{ parameterHelp = $parameterHelp; names = $names } { 118 | $parameterHelp.parameterValueGroup.parameterValue | Should -be $names 119 | } 120 | } elseif ($parameter.ParameterType.FullName -in $HelpTestEnumeratedArrays) { 121 | # Enumerations often have issues with the typename not being reliably available 122 | $names = [Enum]::GetNames($parameter.ParameterType.DeclaredMembers[0].ReturnType) 123 | It "help for $commandName has correct parameter type for $parameterName" -TestCases @{ parameterHelp = $parameterHelp; names = $names } { 124 | $parameterHelp.parameterValueGroup.parameterValue | Should -be $names 125 | } 126 | } else { 127 | # To avoid calling Trim method on a null object. 128 | $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() } 129 | # Parameter type in Help should match code 130 | It "help for $commandName has correct parameter type for $parameterName" -TestCases @{ helpType = $helpType; codeType = $codeType } { 131 | $helpType | Should -be $codeType 132 | } 133 | } 134 | } 135 | foreach ($helpParm in $HelpParameterNames) { 136 | # Shouldn't find extra parameters in help. 137 | It "finds help parameter in code: $helpParm" -TestCases @{ helpParm = $helpParm; parameterNames = $parameterNames } { 138 | $helpParm -in $parameterNames | Should -Be $true 139 | } 140 | } 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /PoShPRTG/functions/core/Connect-PRTGServer.ps1: -------------------------------------------------------------------------------- 1 | function Connect-PRTGServer { 2 | <# 3 | .Synopsis 4 | Connect-PRTGServer 5 | 6 | .DESCRIPTION 7 | Connect to PRTG Server, creates global variables with connection data and the current sensor tree from PRTG Core Server. 8 | The global variables are used as default parameters in other PRTG-module cmdlets to interact with PRTG. 9 | 10 | Connect-PRTGServer needs to be run at first when starting to work. 11 | 12 | .PARAMETER Server 13 | Name of the PRTG server to connect to 14 | 15 | .EXAMPLE 16 | Connect-PRTGServer -Server "PRTG.CORP.COMPANY.COM" -Credential (Get-Credential "prtgadmin") 17 | 18 | Connects to "PRTG.CORP.COMPANY.COM" via HTTPS protocol and the specified credentials. 19 | Connection will be set as default PRTG Connection for any further action. 20 | 21 | .EXAMPLE 22 | $connection = Connect-PRTGServer -Server "PRTG.CORP.COMPANY.COM" -Credential (Get-Credential "prtgadmin") -DoNotRegisterConnection -DoNotQuerySensorTree -PassThru 23 | 24 | Connects to "PRTG.CORP.COMPANY.COM" via HTTPS protocol and output the connection/session object the the variale $connection, 25 | but does not register the PRTG Connection for automatically useage with other commands. Instead the commands can be triggered 26 | against this connection by using the -Session Parameter. 27 | 28 | This enables to work with multiple PRTG servers at a time. 29 | 30 | .EXAMPLE 31 | Connect-PRTGServer -Server "PRTG.CORP.COMPANY.COM" -User "prtgadmin" -Hash 123456789 -Protocol HTTP 32 | 33 | Connects to "PRTG.CORP.COMPANY.COM" via unencrypted HTTP protocal and with a previously queried loginhash. 34 | The Hash is NOT the users password! The hash has to be queried from PRTG logon service. 35 | 36 | Due to this exposes security related data/ login credentials, this is not the recommended login method. 37 | 38 | .NOTES 39 | Author: Andreas Bellstedt 40 | 41 | .LINK 42 | https://github.com/AndiBellstedt/PoShPRTG 43 | #> 44 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] 45 | [CmdletBinding( 46 | DefaultParameterSetName = 'Credential', 47 | SupportsShouldProcess = $false, 48 | ConfirmImpact = 'Medium' 49 | )] 50 | [OutputType([XML])] 51 | Param( 52 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 53 | [Alias("ComputerName", "Hostname", "Host", "ServerName")] 54 | [String] 55 | $Server, 56 | 57 | # The credentials to login to PRTG 58 | [Parameter(Mandatory = $true, ParameterSetName = 'Credential')] 59 | [System.Management.Automation.PSCredential] 60 | $Credential, 61 | 62 | # The user name to login to PRTG 63 | [Parameter(Mandatory = $true, ParameterSetName = 'Hash')] 64 | [String] 65 | $User, 66 | 67 | # A PRTG login hash value 68 | [Parameter(Mandatory = $true, ParameterSetName = 'Hash')] 69 | [ValidateNotNullOrEmpty()] 70 | [String] 71 | $Hash, 72 | 73 | # Specifies if the connection is done with http or https 74 | [ValidateSet("HTTP", "HTTPS")] 75 | [ValidateNotNullOrEmpty()] 76 | [String] 77 | $Protocol = "HTTPS", 78 | 79 | # Only login. No query of sensortree object 80 | [Alias('QuickConnect', 'NoSensorTree')] 81 | [Switch] 82 | $DoNotQuerySensorTree, 83 | 84 | # Do not register the connection/session as default PRTG server connection 85 | [Alias('NoRegistration')] 86 | [Switch] 87 | $DoNotRegisterConnection, 88 | 89 | # Output the sensortree object after login 90 | [Switch] 91 | $PassThru 92 | ) 93 | 94 | begin {} 95 | 96 | process { 97 | if ($Server -match '//') { 98 | if ($Server -match '\/\/(?(\w+|\.)+)') { $Server = $Matches["Server"] } 99 | Remove-Variable -Name Matches -Force -Verbose:$false -Debug:$false -Confirm:$false 100 | } 101 | 102 | if ($protocol -eq 'HTTP') { 103 | Write-PSFMessage -Level Important -Message "Unsecure $($protocol) connection with possible security risk detected. Please consider switch to HTTPS!" -Tag "Connection" 104 | $prefix = 'http://' 105 | } else { 106 | Write-PSFMessage -Level System -Message "Using secure $($protocol) connection." -Tag "Connection" 107 | $prefix = 'https://' 108 | } 109 | 110 | if ($PsCmdlet.ParameterSetName -eq 'Credential') { 111 | if (($credential.UserName.Split('\')).count -gt 1) { 112 | $User = $credential.UserName.Split('\')[1] 113 | } else { 114 | $User = $credential.UserName 115 | } 116 | $pass = $credential.GetNetworkCredential().Password 117 | 118 | Write-PSFMessage -Level Verbose -Message "Authenticate user '$($User)' to PRTG server '$($Prefix)$($server)'" -Tag "Connection" 119 | $Hash = Invoke-WebRequest -Uri "$($prefix)$($server)/api/getpasshash.htm?username=$($User)&password=$($Pass)" -UseBasicParsing -Verbose:$false -Debug:$false -ErrorAction Stop | Select-Object -ExpandProperty content 120 | } 121 | 122 | Write-PSFMessage -Level System -Message "Creating PoShPRTG.Connection" -Tag "Connection" 123 | $session = [PSCustomObject]@{ 124 | PSTypeName = "PoShPRTG.Connection" 125 | Server = $Prefix + $server 126 | UserName = $User 127 | Hash = ($Hash | ConvertTo-SecureString -AsPlainText -Force) 128 | DefaultConnection = $false 129 | SensorTree = $null 130 | TimeStampCreated = Get-Date 131 | TimeStampModified = Get-Date 132 | } 133 | 134 | if (-not $DoNotQuerySensorTree) { 135 | $sensorTree = Invoke-PRTGSensorTreeRefresh -Server $session.Server -User $session.UserName -Pass ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $script:PRTGSession.Hash ))) -PassThru 136 | 137 | $session.SensorTree = $sensorTree 138 | $session.TimeStampModified = Get-Date 139 | } 140 | 141 | if (-not $DoNotRegisterConnection) { 142 | # Make the connection the default connection for further commands 143 | $session.DefaultConnection = $true 144 | $session.TimeStampModified = Get-Date 145 | 146 | $script:PRTGSession = $session 147 | $script:PRTGServer = $script:PRTGSession.Server 148 | $script:PRTGUser = $script:PRTGSession.UserName 149 | $script:PRTGPass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $script:PRTGSession.Hash )) 150 | 151 | Write-PSFMessage -Level Significant -Message "Connected to PRTG '($($script:PRTGSession.Server))' as '$($script:PRTGSession.UserName)' as default connection" -Tag "Connection" 152 | } 153 | 154 | if ($PassThru) { 155 | Write-PSFMessage -Level System -Message "Outputting PoShPRTG.Connection object" -Tag "Connection" 156 | $session 157 | } 158 | } 159 | 160 | end {} 161 | } 162 | -------------------------------------------------------------------------------- /PoShPRTG/functions/object/Copy-PRTGObject.ps1: -------------------------------------------------------------------------------- 1 | function Copy-PRTGObject { 2 | <# 3 | .Synopsis 4 | Copy-PRTGObject 5 | 6 | .DESCRIPTION 7 | Copy a PRTG Object 8 | 9 | .PARAMETER WhatIf 10 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 11 | 12 | .PARAMETER Confirm 13 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 14 | 15 | .NOTES 16 | Author: Andreas Bellstedt 17 | 18 | adopted from PSGallery Module "PSPRTG" 19 | Author: Sam-Martin 20 | Github: https://github.com/Sam-Martin/prtg-powershell 21 | 22 | .LINK 23 | https://github.com/AndiBellstedt/PoShPRTG 24 | 25 | .EXAMPLE 26 | Copy-PRTGObject -ObjectId 1 -TargetID 2 -Name "NewName" 27 | 28 | Duplicates object 1 to group with ID 2 with name "NewName". 29 | #> 30 | [CmdletBinding( 31 | DefaultParameterSetName = 'Default', 32 | SupportsShouldProcess = $true, 33 | ConfirmImpact = 'Medium' 34 | )] 35 | Param( 36 | # ID of the object to copy 37 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 38 | [ValidateNotNullOrEmpty()] 39 | [ValidateScript( { $_ -gt 0 } )] 40 | [Alias('SourceID', 'objID')] 41 | [int] 42 | $ObjectID, 43 | 44 | # ID of the target parent object 45 | [Parameter(Mandatory = $true)] 46 | [ValidateNotNullOrEmpty()] 47 | [int] 48 | $TargetID, 49 | 50 | # Name of the newly cloned object 51 | [string] 52 | $Name, 53 | 54 | # Url for PRTG Server 55 | [ValidateNotNullOrEmpty()] 56 | [ValidateScript( { if (($_.StartsWith("http"))) { $true } else { $false } } )] 57 | [String] 58 | $Server = $script:PRTGServer, 59 | 60 | # User for PRTG Authentication 61 | [ValidateNotNullOrEmpty()] 62 | [String] 63 | $User = $script:PRTGUser, 64 | 65 | # Password or PassHash for PRTG Authentication 66 | [ValidateNotNullOrEmpty()] 67 | [String] 68 | $Pass = $script:PRTGPass, 69 | 70 | # Sensortree from PRTG Server 71 | [ValidateNotNullOrEmpty()] 72 | [xml] 73 | $SensorTree = $script:PRTGSensorTree 74 | ) 75 | 76 | begin {} 77 | 78 | process { 79 | $body = @{ 80 | id = $ObjectId 81 | name = $Name 82 | targetid = $TargetID 83 | username = $User 84 | passhash = $Pass 85 | } 86 | 87 | # get source object from sensor tree 88 | try { 89 | $SourceObject = Get-PRTGObject -ObjectID $ObjectID -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 90 | if (-not $Name) { $body.name = $SourceObject.name } 91 | } catch { 92 | Write-Log -LogText "Cannot find object to clone. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -Error -NoFileStatus 93 | return 94 | } 95 | 96 | # get target object from sensor tree 97 | try { 98 | $TargetObject = Get-PRTGObject -ObjectID $TargetID -SensorTree $SensorTree -Verbose:$false -ErrorAction Stop 99 | } catch { 100 | Write-Log -LogText "Cannot find target object. $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -Error -NoFileStatus 101 | return 102 | } 103 | 104 | if ($pscmdlet.ShouldProcess("objID $TargetID '$($TargetObject.Name)'", "Clone object id $($SourceObject.ObjID) as '$Name'")) { 105 | #Try to clone the object 106 | try { 107 | Write-Log -LogText "Try to clone $($SourceObject.Type) with objID $($SourceObject.ObjID) to target with objID $TargetID. New name of the $($SourceObject.Type) in the target: ""$Name"" ($Server)" -LogType Set -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 108 | $Result = Invoke-WebRequest -UseBasicParsing -Uri "$Server/api/duplicateobject.htm" -Method Get -Body $body -Verbose:$false -Debug:$false 109 | } catch { 110 | Write-Log -LogText "Failed to clone object $($_.exception.message)" -LogType Error -LogScope $MyInvocation.MyCommand.Name -Error -NoFileStatus 111 | return 112 | } 113 | Write-Log -LogText "$($SourceObject.Type) cloned to targetID $TargetID. Try to recieve new object from prtg ($Server)" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 114 | 115 | # Pluralise the type. Needed for receiving the copied object by "type" 116 | $TypePlural = $SourceObject.Type + 's' 117 | # Fetch the ID of the object we just added 118 | [array]$result = (Receive-PRTGObject -numResults 100 -columns "objid,name,type,tags,active,status" -SortBy "objid" -content $TypePlural -Filters @{"filter_name" = $Name } -Server $Server -User $User -Pass $Pass -Verbose:$false).$TypePlural.item | Where-Object objid -ne $ObjectId 119 | 120 | #output the object if result contains an objectID 121 | if ($result.ObjID) { 122 | 123 | #check for duplicated results 124 | if ($Result.Count -gt 1) { 125 | Write-Log -LogText "Recieve $($Result.Count) $TypePlural from prtg ($Server). Assume highest ID as the new $($SourceObject.Type)." -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 126 | $result = $result | Sort-Object -Property objid -Descending | Select-Object -First 1 127 | } else { 128 | Write-Log -LogText "Recieve $($Result.Count) $($SourceObject.Type) ($($Result.objid)) from prtg ($Server)." -LogType Info -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 129 | } 130 | 131 | $newChild = $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($ObjectID)]").CloneNode($true) 132 | $newObjectInSensorTree = $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($TargetID)]").AppendChild( $newChild ) 133 | $newObjectInSensorTree.SetAttribute("id", $Result.objID) 134 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($TargetID)]//*[id=$($ObjectID)]/name").InnerText = $Result.name 135 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($TargetID)]//*[id=$($ObjectID)]/url").InnerText = $newObjectInSensorTree.url.Split('=')[0] + '=' + $Result.objID 136 | $SensorTree.SelectSingleNode("/prtg/sensortree/nodes/group//*[id=$($TargetID)]//*[id=$($ObjectID)]/id").InnerText = $Result.objID 137 | 138 | # Ouput result 139 | $Result = Get-PRTGObject -ObjectID $Result.objID -SensorTree $SensorTree -Verbose:$false 140 | $Result 141 | } else { 142 | #if result contains an empty item 143 | Write-Log -LogText "No items recieved after cloning! Unkown issue ($Server)." -LogType Error -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -Error 144 | Remove-Variable result -Force -ErrorAction SilentlyContinue -Verbose:$false -Debug:$false -WhatIf:$false 145 | return 146 | } 147 | } 148 | } 149 | 150 | end {} 151 | } -------------------------------------------------------------------------------- /PoShPRTG/internal/functions/Write-Log.ps1: -------------------------------------------------------------------------------- 1 | function Write-Log { 2 | <# 3 | .Synopsis 4 | Write-Log / Log 5 | Logs text to the console and/or to a file. 6 | 7 | .DESCRIPTION 8 | A comprehensive helper function for structured logging. 9 | Writes one or more messages to the different available outputchannels of the powershell and to one or more logfiles. 10 | 11 | .NOTES 12 | Version: 2.4 13 | Author: Andreas Bellstedt 14 | History: 01.07.2016 - First Version 15 | 07.08.2016 - add logging to differnt output channels and more flexibility in parameters 16 | 14.08.2016 - changing synopsis position to powershell best practices. (before funktion block) 17 | 27.01.2017 - add parameters $Type and $logscope to easy logging structur and prevent the need of global variables for (status)types 18 | 29.01.2017 - change debug output procedure for better handling 19 | 20 | .EXAMPLE 21 | Examples without logging to a file. Only console output is done. The following examples only produces output 22 | if the verbosepreference in current session is set to "continue", or the -verbose switch is specified: 23 | 24 | Write-Log -LogText "This is a Message" 25 | Write-Log "This is a Message" 26 | Log "This is a Message" 27 | "This is a Message" | Write-Log 28 | -> VERBOSE: [2016-08-08 08:08:08] [NOFILE] This is a Message 29 | 30 | "This is a Message" , "This is anonther Message" | Write-Log -LogType Info 31 | -> VERBOSE: [2016-08-08 08:08:08] [NOFILE] [INFO ] This is a Message 32 | VERBOSE: [2016-08-08 08:08:08] [NOFILE] [INFO ] This is another Message 33 | 34 | .EXAMPLE 35 | Examples without logging to a file. Only console output is done. The following examples produces output 36 | irrespective of the verbosepreference: 37 | 38 | Write-Log -LogText "This is a Message" -LogType Warning -LogScope "Function01" -Warning 39 | -> WARNING: [2016-08-08 08:08:08] [NOFILE] [WARNING] [FUNCTION01] This is a Message 40 | 41 | Write-Log -LogText "This is a Message" -LogType Error -LogScope "Function01" -Error 42 | -> Write-Log : [2016-08-07 16:13:28] [NOFILE] [ERROR ] [FUNCTION01] This is a Message 43 | + Write-Log -LogText "This is a Message" -Error 44 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 | + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException 46 | + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Write-Log 47 | 48 | Write-Log -LogText "This is a Message" -Console 49 | Write-Log -LogText "This is a Message" -Visible 50 | -> [2016-08-08 08:08:08] [NOFILE] This is a Message 51 | 52 | Write-Log -LogText "This is a Message" -Console -Warning 53 | -> WARNING: [2016-08-08 08:08:08] [NOFILE] This is a Message 54 | 55 | Write-Log -LogText "This is a Message" -Console -NoFileStatus 56 | -> [2016-08-08 08:08:08] This is a Message 57 | 58 | Write-Log -LogText "This is a Message" -Console -NoFileStatus -NoTimeStamp 59 | -> This is a Message 60 | 61 | .EXAMPLE 62 | Examples without logging to a file. Only console output is done. The following examples produces output 63 | to the debug channel: 64 | 65 | Write-Log -LogText "This is a Message" -DebugOutput 66 | -> DEBUG: [2016-08-08 08:08:08] [NOFILE] This is a Message 67 | 68 | Write-Log -LogText "This is a Message" -DebugOutput -Warning 69 | -> DEBUG: WARNING: [2016-08-08 08:08:08] [NOFILE] This is a Message 70 | 71 | .EXAMPLE 72 | Examples without logging to a file. 73 | 74 | Write-Log -LogText "This is a Message" -LogFile 'C:\Administration\Logs\Logfile.log' 75 | -> VERBOSE: [2016-08-08 08:08:08] [FILE ] This is a Message 76 | 77 | Write-Log -LogText "This is a Message" -LogFile 'C:\Administration\Logs\Logfile.log', 'C:\Administration\Logs\Logfile-Errors.log' -Warning 78 | -> WARNING: [2016-08-08 08:08:08] [FILE ] This is a Message 79 | 80 | Write-Log -LogText "This is a Message" -LogFile 'C:\Administration\Logs\Logfile.log', 'C:\Administration\Logs\Logfile-Errors.log' -Error 81 | -> Write-Log : [2016-08-08 08:08:08] [FILE ] This is a Message 82 | In Zeile:1 Zeichen:1 83 | + Write-Log -LogText "This is a Message" -LogFile 'C:\Administration\Lo ... 84 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 85 | + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException 86 | + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Write-Log 87 | #> 88 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")] 89 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")] 90 | 91 | [CmdletBinding( 92 | DefaultParameterSetName = 'VerboseOutput', 93 | ConfirmImpact = "Low" 94 | )] 95 | [Alias('Log')] 96 | Param( 97 | #The message to be logged 98 | [parameter( Mandatory = $true, 99 | Position = 0, 100 | ValueFromPipeline = $true )] 101 | [Alias('Text', 'Message')] 102 | [string]$LogText, 103 | 104 | #The kind of event/action what is happening while the message is logged 105 | [parameter( Mandatory = $false, 106 | Position = 1)] 107 | [Alias('Type')] 108 | [ValidateSet('Warning', 'Info', 'Query', 'Set', 'Error')] 109 | [string]$LogType, 110 | 111 | #The name of the function or the scriptpart where the log event happens 112 | [parameter( Mandatory = $false, 113 | Position = 2 )] 114 | [Alias('Scope')] 115 | [string]$LogScope, 116 | 117 | #The name of the logfile(s) where the message should be logged 118 | [parameter( Mandatory = $false )] 119 | [Alias('File')] 120 | [string[]]$LogFile, 121 | 122 | #Suppress the timestamp in the logged output 123 | [parameter( Mandatory = $false )] 124 | [switch]$NoTimeStamp, 125 | 126 | #Suppress the info, wether the logged output is written to file or only displayed in the outputchannel 127 | [parameter( Mandatory = $false )] 128 | [switch]$NoFileStatus, 129 | 130 | #Specifies that LogText is displayed as text in the debug-channel, not in the verbose-channel 131 | [parameter( Mandatory = $false, 132 | ParameterSetName = 'DebugOutput' )] 133 | [switch]$DebugOutput, 134 | 135 | #Specifies that LogText is displayed as text in the console window, not in the verbose-channel 136 | [parameter( Mandatory = $false, 137 | ParameterSetName = 'ConsoleOutput' )] 138 | [Alias('Visible')] 139 | [switch]$Console, 140 | 141 | #Specifies that LogText is displayed as (red) error message in the console window, not in the verbose-channel 142 | [parameter( Mandatory = $false, 143 | ParameterSetName = 'ErrorOutput' )] 144 | [switch]$Error, 145 | 146 | #Logs the LogText as warrning message to the console 147 | [parameter( Mandatory = $false, 148 | ParameterSetName = 'VerboseOutput' )] 149 | [parameter( Mandatory = $false, 150 | ParameterSetName = 'DebugOutput' )] 151 | [parameter( Mandatory = $false, 152 | ParameterSetName = 'ConsoleOutput' )] 153 | [switch]$Warning 154 | ) 155 | 156 | begin { 157 | switch ($LogType) { 158 | 'Warning' { $Type = '[WARNING] ' } 159 | 'Info' { $Type = '[INFO ] ' } 160 | 'Query' { $Type = '[QUERY ] ' } 161 | 'Set' { $Type = '[SET ] ' } 162 | 'Error' { $Type = '[ERROR ] ' } 163 | Default { $Type = '[INFO ] ' } 164 | } 165 | 166 | if ($logScope) { $LogScope = "[$($LogScope.ToUpper())] " } 167 | if ($NoFileStatus) { $status = '' } else { $status = "[NOFILE] " } 168 | if ($NoTimeStamp) { $logDate = '' } else { $logDate = "[$(Get-Date -Format "yyyy-MM-dd HH:mm:ss")] " } 169 | 170 | #turn of confimation for debug actions 171 | If (($DebugPreference -eq 'Inquire') -and ($PsCmdlet.ParameterSetName -eq 'DebugOutput')) { 172 | $DebugPreferenceOrg = $DebugPreference 173 | $DebugPreference = 'Continue' 174 | } 175 | } 176 | 177 | process { 178 | if ($LogFile) { 179 | foreach ($File in $LogFile) { 180 | "$($logDate)$($Type)$($LogScope)$($LogText)" | Out-File -FilePath $File -Append 181 | } 182 | } 183 | 184 | $message = "$($logDate)$($status)$($Type)$($LogScope)$($LogText)" 185 | switch ($PsCmdlet.ParameterSetName) { 186 | 'VerboseOutput' { 187 | if ($Warning) { Write-Warning $message } else { write-verbose $message } 188 | } 189 | 190 | 'DebugOutput' { 191 | if ($Warning) { Write-Debug "WARNING: $message" } else { Write-Debug $message } 192 | } 193 | 194 | 'ConsoleOutput' { 195 | if ($Warning) { 196 | Write-Host "WARNING: $message" -ForegroundColor $Host.PrivateData.WarningForegroundColor -BackgroundColor $Host.PrivateData.WarningBackgroundColor 197 | } else { 198 | Write-Host $message 199 | } 200 | } 201 | 202 | 'ErrorOutput' { Write-error $message } 203 | } 204 | } 205 | 206 | end { 207 | $DebugPreference = $DebugPreferenceOrg 208 | Remove-Variable message, logDate, status, Type, DebugPreferenceOrg -Force -ErrorAction Ignore -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false 209 | } 210 | } -------------------------------------------------------------------------------- /PoShPRTG/xml/TypeDefinition.ps1: -------------------------------------------------------------------------------- 1 | #Author: Andreas Bellstedt 2 | 3 | #region PRTG 4 | $TypeName = "PRTG" 5 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 6 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName ObjID -Value { [int32]$this.id[0] } -Force 7 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Type -Value { $this.LocalName.substring(0, 1).toupper() + $this.LocalName.substring(1).tolower() } -Force 8 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Status -Value { 9 | switch ($this.status_raw) { 10 | '1' { "Unknown" } 11 | '2' { "Scanning" } 12 | '3' { "Up" } 13 | '4' { "Warning" } 14 | '5' { "Down" } 15 | '6' { "No Probe" } 16 | '7' { "Paused by User" } 17 | '8' { "Paused by Dependency" } 18 | '9' { "Paused by Schedule" } 19 | '10' { "Unusual" } 20 | '11' { "Not Licensed" } 21 | '12' { "Paused Until" } 22 | Default { $null } 23 | } 24 | } -Force 25 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName CommentExist -Value { [boolean]$this.Hascomment } -Force 26 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, Type, Status, Tags, Active, Priority, CommentExist, URL -DefaultDisplayProperty ObjID -DefaultKeyPropertySet ObjID -Force 27 | <# 28 | Get-TypeData -TypeName $TypeName 29 | Remove-TypeData -TypeName $TypeName 30 | #> 31 | 32 | #endregion PRTG 33 | 34 | 35 | #region PRTG.SENSORTREE 36 | $TypeName = "PRTG.SensorTree" 37 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 38 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName ObjID -Value { [int32]$this.prtg.sensortree.nodes.group.id[0] } -Force 39 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Type -Value { $this.prtg.sensortree.nodes.group.LocalName } -Force 40 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Name -Value { $this.prtg.sensortree.nodes.group.name } -Force 41 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName URL -Value { $this.prtg.sensortree.nodes.group.url } -Force 42 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Priority -Value { $this.prtg.sensortree.nodes.group.priority } -Force 43 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Status_raw -Value { $this.prtg.sensortree.nodes.group.status_raw } -Force 44 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Active -Value { $this.prtg.sensortree.nodes.group.active } -Force 45 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Probes -Value { $this.prtg.sensortree.nodes.group.probenode } -Force 46 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Tags -Value { $this.prtg.sensortree.nodes.group.tags } -Force 47 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, Type, Status, Tags, Probes -DefaultDisplayProperty ObjID -DefaultKeyPropertySet ObjID -Force 48 | <# 49 | Get-TypeData -TypeName $TypeName 50 | Remove-TypeData -TypeName $TypeName 51 | #> 52 | 53 | #endregion PRTG.SENSORTREE 54 | 55 | 56 | #region PRTG.OBJECT 57 | $TypeName = "PRTG.Object" 58 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 59 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -Force -MemberName Fullname -Value { 60 | $x = $this.id[0] 61 | $count = 0 62 | $local:FullName = "{$($this.Name)}" 63 | if ($x -ne 0) { 64 | if ($this.ParentNode) { $parent = $this.ParentNode } 65 | do { 66 | $x = $parent.id[0] 67 | if ($x -ne 0) { 68 | $local:FullName = "{$($parent.name)}." + $local:FullName 69 | if ($parent.ParentNode) { $parent = $parent.ParentNode } 70 | $count++ 71 | } 72 | } until($x -eq 0) 73 | } 74 | return $FullName 75 | } 76 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -Force -MemberName tagsInherited -Value { 77 | $x = $this.id[0] 78 | $count = 0 79 | $local:tags = "" 80 | if ($x -ne 0) { 81 | if ($this.ParentNode) { $parent = $this.ParentNode } 82 | do { 83 | $x = $parent.id[0] 84 | if ($x -ne 0) { 85 | $local:tags = "$($parent.tags)$(if($parent.tags){" "})" + $local:tags 86 | if ($parent.ParentNode) { $parent = $parent.ParentNode } 87 | $count++ 88 | } 89 | } until($x -eq 0) 90 | } 91 | return $local:tags.Trim() 92 | } 93 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -Force -MemberName tagsAll -Value { 94 | return ($this.tagsInherited + " " + $this.tags).Trim() 95 | } 96 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -Force -MemberName Hierarchy -Value { 97 | $x = $this.id[0] 98 | $count = 1 99 | if ($x -ne 0) { 100 | if ($this.ParentNode) { $parent = $this.ParentNode } 101 | do { 102 | $x = $parent.id[0] 103 | if ($x -ne 0) { 104 | if ($parent.ParentNode) { $parent = $parent.ParentNode } 105 | $count++ 106 | } 107 | } until($x -eq 0) 108 | } 109 | return $count 110 | } 111 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -Force -MemberName Parent -Value { $this.ParentNode.Name } 112 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName ObjID -Value { [int32]$this.id[0] } -Force 113 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, Type, Status, Tags, TagsInherited, Active, Priority, CommentExist, URL, Hierarchy, Fullname -DefaultDisplayProperty ObjID -DefaultKeyPropertySet ObjID -Force 114 | <# 115 | Get-TypeData -TypeName $TypeName 116 | Remove-TypeData -TypeName $TypeName 117 | #> 118 | 119 | #endregion PRTG.OBJECT 120 | 121 | 122 | #region PRTG.OBJECT.PROBENODE 123 | $part = "probenode" 124 | $TypeName = "PRTG.Object.$($part.substring(0,1).toupper())$($part.substring(1).tolower())" 125 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 126 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Members -Value { 127 | $collection = @() 128 | if ($this.device) { $collection += $this.device } 129 | if ($this.group) { $collection += $this.group } 130 | return $collection 131 | } -Force 132 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, Type, Status, Tags, TagsInherited, Active, Priority, CommentExist, DeviceIcon, URL, Group, Device, Hierarchy, Fullname -DefaultDisplayProperty ObjID -DefaultKeyPropertySet ObjID -Force 133 | 134 | #endregion PRTG.OBJECT.PROBENODE 135 | 136 | 137 | #region PRTG.OBJECT.GROUP 138 | $part = "group" 139 | $TypeName = "PRTG.Object.$($part.substring(0,1).toupper())$($part.substring(1).tolower())" 140 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 141 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -MemberName Members -Value { 142 | $collection = @() 143 | if ($this.device) { $collection += $this.device } 144 | if ($this.group) { $collection += $this.group } 145 | return $collection 146 | } -Force 147 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, Type, Status, Tags, TagsInherited, Active, Priority, CommentExist, DeviceIcon, URL, Group, Device, Hierarchy, Fullname -DefaultDisplayProperty ObjID -DefaultKeyPropertySet ObjID -Force 148 | 149 | #endregion PRTG.OBJECT.GROUP 150 | 151 | 152 | #region PRTG.OBJECT.DEVICE 153 | $part = "device" 154 | $TypeName = "PRTG.Object.$($part.substring(0,1).toupper())$($part.substring(1).tolower())" 155 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 156 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, Type, Status, Host, Tags, TagsInherited, Active, Priority, CommentExist, DeviceIcon, URL, Sensor, Hierarchy, Fullname -DefaultDisplayProperty ObjID -DefaultKeyPropertySet ObjID -Force 157 | 158 | #endregion PRTG.OBJECT.DEVICE 159 | 160 | 161 | #region PRTG.OBJECT.SENSOR 162 | $part = "sensor" 163 | $TypeName = "PRTG.Object.$($part.substring(0,1).toupper())$($part.substring(1).tolower())" 164 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 165 | Update-TypeData -TypeName $TypeName -MemberType ScriptProperty -Force -MemberName IntervalText -Value { 166 | $timespan = [timespan]::fromseconds($this.interval) 167 | if ($timespan.TotalDays -ge 1) { 168 | "$($timespan.TotalDays) days" 169 | } elseif ($timespan.TotalHours -ge 1) { 170 | "$($timespan.TotalHours) hours" 171 | } elseif ($timespan.TotalMinutes -ge 1) { 172 | "$($timespan.TotalMinutes) minutes" 173 | } 174 | } 175 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, Type, Status, IntervalText, Interval, Sensorkind, SensorType, DataMode, LastValue, StatusMessage, Tags, TagsInherited, Active, Priority, CommentExist, URL, Parent, Hierarchy, Fullname -DefaultDisplayProperty ObjID -DefaultKeyPropertySet ObjID -Force 176 | 177 | #endregion PRTG.OBJECT.SENSOR 178 | 179 | 180 | #region PRTG.OBJECT.COMPARE 181 | $part = "compare" 182 | $TypeName = "PRTG.Object.$($part.substring(0,1).toupper())$($part.substring(1).tolower())" 183 | Write-PSFMessage -Level Verbose -Message "Update TypeData $TypeName" 184 | Update-TypeData -TypeName $TypeName -DefaultDisplayPropertySet ObjID, Name, SideIndicator, SideIndicatorStatus, SideIndicatorDescription, PropertyDifferenceReport, Fullname -DefaultDisplayProperty Name -DefaultKeyPropertySet ObjID -Force 185 | 186 | 187 | 188 | #endregion PRTG.OBJECT.DEVICE 189 | 190 | 191 | #region Test 192 | <# 193 | Get-TypeData -TypeName $TypeName 194 | Remove-TypeData -TypeName $TypeName 195 | Get-PRTGObject -Type $part -SensorTree $PRTG | gm 196 | Get-PRTGObject -Type $part -SensorTree $PRTG | select * -Last 1 | fl * 197 | $object = Get-PRTGObject -Type $part -SensorTree $PRTG | select * -Last 1 198 | $object.pstypenames.Insert(0,$TypeName) 199 | $object.pstypenames.Insert(1,"PRTG.Object") 200 | $object.pstypenames.Insert(2,"PRTG") 201 | $object.pstypenames 202 | $object | gm 203 | $object 204 | $object | ft 205 | $object.pstypenames.remove($TypeName) 206 | $object.pstypenames.remove("PRTG.Object") 207 | $object.pstypenames.remove("PRTG") 208 | #> 209 | 210 | #endregion Test 211 | -------------------------------------------------------------------------------- /PoShPRTG/functions/deployment/Compare-PRTGDeviceSensorsFromTemplateTAG.ps1: -------------------------------------------------------------------------------- 1 | function Compare-PRTGDeviceSensorsFromTemplateTAG { 2 | <# 3 | .Synopsis 4 | Compare-PRTGDeviceSensorsFromTemplateTAG 5 | 6 | .DESCRIPTION 7 | Compares all sensors on a device by all the tags on the device against a (template) object. 8 | In default all tags starting with "Template_" are used for comparing. 9 | 10 | .NOTES 11 | Author: Andreas Bellstedt 12 | 13 | .LINK 14 | https://github.com/AndiBellstedt/PoShPRTG 15 | 16 | .EXAMPLE 17 | Compare-PRTGDeviceSensorsFromTemplateTAG -DeviceID 200 -TemplateBaseID 100 18 | 19 | Invokes comparisan of device with ID 200 against template device with ID 100 20 | 21 | .EXAMPLE 22 | Compare-PRTGDeviceSensorsFromTemplateTAG -DeviceID 200 -TemplateBaseID 100 -TemplateTAGNameIdentifier "MyPersonalTemplate_" 23 | 24 | Invokes comparisan of device with ID 200 against template device with ID 100 and use "MyPersonalTemplate_" as identifier for templates 25 | 26 | .EXAMPLE 27 | Get-PRTGDevice -Name "MyDevice" | Compare-PRTGDeviceSensorsFromTemplateTAG -TemplateBaseID (Get-PRTGProbe -Name "MyTemplateProbe").ObjID -IncludeEqual 28 | 29 | Invokes comparisan of "MyDevice" against all template devices beneath MyTemplateProbe 30 | #> 31 | [CmdletBinding( 32 | DefaultParameterSetName = 'Default', 33 | SupportsShouldProcess = $false, 34 | ConfirmImpact = 'Low' 35 | )] 36 | Param( 37 | # ID of the object to copy 38 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 39 | [ValidateNotNullOrEmpty()] 40 | [ValidateScript( { $_ -gt 0 })] 41 | [Alias('objID', 'ID', 'ObjectId')] 42 | [int] 43 | $DeviceID, 44 | 45 | # Base id of the template 46 | [ValidateNotNullOrEmpty()] 47 | [int] 48 | $TemplateBaseID = 1, 49 | 50 | # Filter text identifier for template tags in a device 51 | [ValidateNotNullOrEmpty()] 52 | [String] 53 | $TemplateTAGNameIdentifier = "Template_", 54 | 55 | # Compare properties inside a sensor as well as the existence inside the template 56 | [switch] 57 | $ComparePropertiesInObject, 58 | 59 | # Output objects that meet the template, as well as diffs to template 60 | [switch] 61 | $IncludeEqual, 62 | 63 | # SensorTree from PRTG Server 64 | [ValidateNotNullOrEmpty()] 65 | [xml] 66 | $SensorTree = $script:PRTGSensorTree 67 | ) 68 | 69 | begin {} 70 | 71 | process { 72 | Write-Log -LogText "Getting device to validate with object ID $($DeviceID)" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 73 | $DevicesToValidate = Get-PRTGDevice -ObjectId $DeviceID -SensorTree $SensorTree 74 | 75 | Write-Log -LogText "Getting role summary table from templatebase object ID $($TemplateBaseID)" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 76 | $TemplateTAGSummary = Show-PRTGTemplateSummaryFromObjectTAG -TemplateBaseID $TemplateBaseID -TemplateTAGNameIdentifier $TemplateTAGNameIdentifier -SensorTree $SensorTree 77 | 78 | foreach ($Device in $DevicesToValidate) { 79 | Write-Log -LogText "Getting roles summary table for object ""$($device.name)"" (objID $($device.ObjID))" -LogType Query -LogScope $MyInvocation.MyCommand.Name -NoFileStatus -DebugOutput 80 | $DeviceTAGSummary = Show-PRTGTemplateSummaryFromObjectTAG -TemplateBaseID $Device.ObjID -TemplateTAGNameIdentifier $TemplateTAGNameIdentifier -SensorTree $SensorTree -IncludeNonMatching 81 | 82 | $result = @() 83 | foreach ($DeviceTAGSummaryItem in $DeviceTAGSummary) { 84 | if ($DeviceTAGSummaryItem.rolename) { 85 | #if item is a "named" role 86 | $Reference = ($DeviceTAGSummaryItem).sensor 87 | $Difference = ($TemplateTAGSummary | Where-Object rolename -eq "$($DeviceTAGSummaryItem.RoleName)").sensor 88 | if ($Reference -and $Difference) { 89 | $ResultItem = Compare-Object -ReferenceObject $Reference -DifferenceObject $Difference -Property Name -PassThru -IncludeEqual 90 | 91 | if ($ComparePropertiesInObject) { 92 | #if comparision on objectdetails/-properties is requested -> compare properties against template 93 | foreach ($SensorItem in ($ResultItem | Where-Object SideIndicator -eq "==")) { 94 | #get the reference sensor 95 | $ReferenceSensor = $Reference | Where-Object name -like $SensorItem.name 96 | 97 | #compare properties on sensor against template 98 | $differentProperty = Compare-ObjectProperty -ReferenceObject $SensorItem -DifferenceObject $ReferenceSensor -PropertyFilter "sensortype", "priority", "sensorkind", "interval", "tags", "Type", "IntervalText", "name" 99 | if ($differentProperty) { 100 | $SensorItem.SideIndicator += "!" 101 | Add-Member -InputObject $SensorItem -MemberType NoteProperty -Force -Name "PropertyDifferenceReport" -Value ( [string]::Join(", ", ($differentProperty | ForEach-Object { "Difference on property $($_.property)=""$($_.Value)"" on $(if($_.SideIndicator -eq '<='){"device"}else{"template"})" })) ) 102 | } else { 103 | Add-Member -InputObject $SensorItem -MemberType NoteProperty -Force -Name "PropertyDifferenceReport" -Value $null 104 | } 105 | } 106 | } 107 | 108 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name SideIndicatorStatus ` 109 | -Value (. { 110 | switch ($_.SideIndicator) { 111 | '<=' { "WARNING" } 112 | '=>' { "WARNING" } 113 | '==!' { "WARNING" } 114 | '==' { "OK" } 115 | } 116 | }) 117 | } 118 | 119 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorDescription" ` 120 | -Value (. { 121 | switch ($_.SideIndicator) { 122 | '<=' { "In device but not in template" } 123 | '=>' { "In template but not in device" } 124 | '==!' { "Match in device and template, but difference in Properties! Look at PropertyDifferenceReport" } 125 | '==' { "Match in device and template" } 126 | } 127 | }) 128 | } 129 | } else { 130 | #no sensors in device or no sensors in template 131 | if (-not $Reference -and $Difference) { 132 | #no sensors in device but some sensors in template 133 | $ResultItem = $Difference 134 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicator" -Value "=>" } 135 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorStatus" -Value "WARNING" } 136 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorDescription" -Value "In template but not in device" } 137 | } elseif ($Reference -and -not $Difference) { 138 | #no sensors in template but some sensors in device 139 | $ResultItem = $Reference 140 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicator" -Value "<=" } 141 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorStatus" -Value "WARNING" } 142 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorDescription" -Value "In device but not in template" } 143 | } elseif (-not $Reference -and -not $Difference) { 144 | #no sensors in device and no sensors in template 145 | $ResultItem = "" 146 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicator" -Value "!!" } 147 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorStatus" -Value "WARNING" } 148 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorDescription" -Value "No objects found" } 149 | } 150 | } 151 | } else { 152 | #if item is a "NonMatching"-object (without a name in RoleName property) 153 | $ResultItem = $DeviceTAGSummaryItem.Sensor 154 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicator" -Value "!!" } 155 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorStatus" -Value "WARNING" } 156 | $ResultItem | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Force -Name "SideIndicatorDescription" -Value "Object not matching any template" } 157 | } 158 | 159 | $ResultItem | ForEach-Object { if ($_.pstypenames[0] -ne "PRTG.Object.Compare") { $_.pstypenames.Insert(0, "PRTG.Object.Compare") } } 160 | 161 | $result += $ResultItem 162 | } 163 | 164 | if (-not $IncludeEqual) { 165 | $result = $result | Where-Object SideIndicator -ne '==' 166 | } 167 | 168 | $result 169 | } 170 | } 171 | 172 | end {} 173 | } --------------------------------------------------------------------------------