├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── Config ├── EUCMonitoringDashboards.yaml ├── EUCMonitoringDatasource.yaml ├── Get-CADCOverview.ps1 ├── Get-CVADOverview.ps1 ├── Install-VisualizationSetup.ps1 ├── Testing │ ├── EUCMonitor-old.ps1 │ └── Easy-EUCMonitor.ps1 ├── Uninstall-VisualizationSetup.ps1 └── telegraf.conf ├── Dashboards ├── CADC-CSvServers.json ├── CADC-GSLBvServers.json ├── CADC-LBvServers.json ├── CADC-Overview.json ├── CVAD-DeliveryGroups.json ├── CVAD-Overview.json └── CVAD-Timeshift.json ├── Installation.md ├── LICENSE ├── PSGallery ├── EUCMonitoringRedux.psd1 ├── EUCMonitoringRedux.psm1 ├── Private │ ├── Connect-CitrixADC.ps1 │ ├── Disconnect-CitrixADC.ps1 │ ├── Get-CADCNitroValue.ps1 │ ├── Get-DonutHTML.ps1 │ ├── Get-InfluxURI.ps1 │ ├── Test-CVADworkerhealth.ps1 │ ├── Test-Service.ps1 │ ├── Test-URL.ps1 │ ├── Test-ValidCert.ps1 │ └── Write-EUCError.ps1 └── Public │ ├── ConvertTo-EUCResultHtml.ps1 │ ├── ConvertTo-InfluxLineProtocol.ps1 │ ├── Get-CADCcache.ps1 │ ├── Get-CADCcsvserver.ps1 │ ├── Get-CADCgatewayuser.ps1 │ ├── Get-CADCgslbvserver.ps1 │ ├── Get-CADChttp.ps1 │ ├── Get-CADCip.ps1 │ ├── Get-CADClbvserver.ps1 │ ├── Get-CADCssl.ps1 │ ├── Get-CADCsslcertkey.ps1 │ ├── Get-CADCsystem.ps1 │ ├── Get-CADCtcp.ps1 │ ├── Get-CADCvaluetemplate.ps1 │ ├── Get-CVADlicense.ps1 │ ├── Get-CVADworkerhealth.ps1 │ ├── Get-CVADworkload.ps1 │ ├── Get-InfluxTimestamp.ps1 │ ├── Get-RDSlicense.ps1 │ ├── Get-RDSworkerhealth.ps1 │ ├── Get-RDSworkload.ps1 │ ├── Send-EUCResultToInfluxDB.ps1 │ ├── Start-EUCMonitor.ps1 │ └── Test-EUCServer.ps1 ├── Readme.md ├── TODO.md ├── Tests ├── ConvertTo-InfluxLineProtocol.tests.ps1 ├── EUCMonitoringRedux.tests.ps1 ├── Get-InfluxTimestamp.tests.ps1 └── Main.tests.ps1 └── appveyor.yml /.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 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dave@bretty.me.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Contributions to **EUCMONITORINGREDUX** are highly encouraged and desired. Below are some guidelines that will help make the process as smooth as possible. 4 | 5 | # Getting Started 6 | 7 | - Make sure you have a [GitHub account](https://github.com/signup/free) 8 | - Submit a new issue, assuming one does not already exist. 9 | - Clearly describe the issue including steps to reproduce when it is a bug. 10 | - Make sure you fill in the earliest version that you know has the issue. 11 | - Fork the repository on GitHub 12 | 13 | # Suggesting Enhancements 14 | 15 | I want to know what you think is missing from this module and how it can be made better. 16 | 17 | - When submitting an issue for an enhancement, please be as clear as possible about why you think the enhancement is needed and what the benefit of it would be. 18 | 19 | # Making Changes 20 | 21 | - From your fork of the repository, create a topic branch where work on your change will take place. 22 | - To quickly create a topic branch based on master; `git checkout -b my_contribution master`. Please avoid working directly on the `master` branch. 23 | - Make commits of logical units. 24 | - Check for unnecessary whitespace with `git diff --check` before committing. 25 | - Please follow the prevailing code conventions in the repository. Differences in style make the code harder to understand for everyone. 26 | - Make sure your commit messages are in the proper format. 27 | 28 | ``` 29 | Add more cowbell to Get-Something.ps1 30 | 31 | The functionaly of Get-Something would be greatly improved if there was a little 32 | more 'pizzazz' added to it. I propose a cowbell. Adding more cowbell has been 33 | shown in studies to both increase one's mojo, and cement one's status 34 | as a rock legend. 35 | ``` 36 | 37 | - Make sure you have added all the necessary Pester tests for your changes. 38 | - Run _all_ PESTER tests in the module to assure nothing else was accidentally broken. 39 | - PS1 files must contain only one function 40 | 41 | # Documentation 42 | 43 | I am infallible and as such my documenation needs no corectoin. In the highly 44 | unlikely event that that is _not_ the case, commits to update or add documentation 45 | are highly apprecaited. 46 | 47 | # Submitting Changes 48 | 49 | - Push your changes to a topic branch in your fork of the repository. 50 | - Submit a pull request to the **master** branch in the main repository. 51 | - Once the pull request has been reviewed and accepted, it will be merged with the master branch. 52 | 53 | # Additional Resources 54 | 55 | - [General GitHub documentation](https://help.github.com/) 56 | - [GitHub forking documentation](https://guides.github.com/activities/forking/) 57 | - [GitHub pull request documentation](https://help.github.com/send-pull-requests/) 58 | - [GitHub Flow guide](https://guides.github.com/introduction/flow/) 59 | - [GitHub's guide to contributing to open source projects](https://guides.github.com/activities/contributing-to-open-source/) 60 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | 5 | ## Current Behavior 6 | 7 | 8 | 9 | ## Possible Solution 10 | 11 | 12 | 13 | ## Steps to Reproduce (for bugs) 14 | 15 | 16 | 1. 17 | 2. 18 | 3. 19 | 4. 20 | 21 | ## Context 22 | 23 | 24 | 25 | ## Your Environment 26 | 27 | * Module Version used: 28 | * Citrix Versions used: -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for submitting a pull request! Please provide enough information so that others can review your pull request: 2 | 3 | **Summary** 4 | 5 | 6 | 7 | This PR fixes/implements the following **bugs/features** 8 | 9 | * [ ] Bug 1 10 | * [ ] Bug 2 11 | * [ ] Feature 1 12 | * [ ] Feature 2 13 | * [ ] Breaking changes 14 | 15 | 16 | 17 | Explain the **motivation** for making this change. What existing problem does the pull request solve? 18 | 19 | 20 | 21 | Does the code pass AppVeyor? 22 | * [ ] Yes 23 | 24 | 25 | 26 | **Closing issues** 27 | 28 | 29 | Fixes # -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .vscode 33 | euc-monitoring.json 34 | 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [0.1.2] - 2019-08-01 8 | 9 | ### Added 10 | 11 | **New Dashboard** - CADC LBvServers 12 | **New Dashboard** - CADC CSvServers 13 | **New Dashboard** - CADC GSLBvServers 14 | 15 | ### Fixed 16 | 17 | - Update Dashboards from the "Export for sharing externally" and dashboard import. This includes trying provisioning files, and then reverting to grafana http api. 18 | - Cast certain ADC Nitro Values to [int64] instead of [int] including csvserver, lbvserver, gslbvserver instances. - Thanks to [Kevin Schwartzmiller](https://twitter.com/KSchwartzmiller) for the find 19 | 20 | ### Changed 21 | 22 | - Made the error log output from the Get-CADC functions consistent with each other. 23 | - Insert links between similarly grouped dashboards 24 | - Renamed CVAD-DeliveryGroupsDetails.json to CVAD-DeliveryGroups.json 25 | 26 | ## [0.1.1] - 2019-07-29 27 | 28 | ### Added 29 | 30 | - Leaves InfluxData, log files, and scripts in place during Uninstall-VisualizationSetup 31 | - **New Dashboard** - CVAD Delivery Group Details 32 | - **New Dashboard** - CVAD TimeShift 33 | 34 | ### Fixed 35 | 36 | - Fixes [#2](https://github.com/littletoyrobots/EUCMonitoringRedux/issues/2) - Have the Install-VisualizationSetup insert Grafana yaml files in the newly downloaded directory. 37 | 38 | ### Changed 39 | 40 | - Moved the incomplete scripts to Testing directory in Config 41 | - Assets for screenshots moved to its own branch 42 | 43 | ## [0.1.0] - 2019-07-26 44 | 45 | ### Added 46 | 47 | - First public version. 48 | - Figuring out if I like this Changelog format 49 | - **New Dashboard** - CADC Overview 50 | - **New Dashboard** - CVAD Overview 51 | 52 | ### Fixed 53 | 54 | - Fixes [#1](https://github.com/littletoyrobots/EUCMonitoringRedux/issues/1) - Answers questions about current usage of Invoke-CommandAs, which is not yet ready for inclusion as it creates an additional dependency. 55 | 56 | ## [0.0.0] - YYYY-MM-DD 57 | 58 | ### Added 59 | 60 | - New features 61 | 62 | ### Changed 63 | 64 | - Changes in existing functionality 65 | 66 | ### Fixed 67 | 68 | - Any bug fixes, with referenced case using the following format 69 | - Fixes [#0](https://github.com/littletoyrobots/EUCMonitoringRedux/issues/0) - Short Blurb 70 | 71 | ### Removed 72 | 73 | - Any now removed features 74 | 75 | ### Security 76 | 77 | - In case of vulnerabilities 78 | -------------------------------------------------------------------------------- /Config/EUCMonitoringDashboards.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: "EUCMonitoring" 5 | orgId: 1 6 | folder: "" 7 | type: file 8 | disableDeletion: false 9 | editable: false 10 | options: 11 | path: C:/Monitoring/EUCMonitoringRedux-master/Dashboards 12 | -------------------------------------------------------------------------------- /Config/EUCMonitoringDatasource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: EUCMonitoring 5 | type: influxdb 6 | access: proxy 7 | database: EUCMonitoring 8 | url: http://localhost:8086 9 | -------------------------------------------------------------------------------- /Config/Get-CADCOverview.ps1: -------------------------------------------------------------------------------- 1 | $BaseDir = "C:\Monitoring" 2 | Import-Module (Join-Path -Path $BaseDir -ChildPath "EUCMonitoringRedux-master\PSGallery\EUCMonitoringRedux.psd1") 3 | 4 | # This is going to default to silent output, so that all that is output is in InfluxLineProtocol 5 | $VerbosePreference = 'SilentlyContinue' # 6 | # Set this if you want to debug, or see the verbose output of this script. 7 | #$VerbosePreference = 'Continue' 8 | 9 | # You can have multiple Gateways as long as the creds work on each, and they're accessible 10 | $CitrixADCGateways = "10.1.2.3", "10.1.2.3" 11 | # I prefer readon-only users. You don't really want to test run someone else's script with nsroot, do you? 12 | $ADCUser = "notnsroot" 13 | 14 | # Generate a credential for storage by running this as the account telegraf runs under. You probably know a 15 | # better way of doing this. This is just an example. 16 | #> Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath "C:\Monitoring\ADCcred.txt" 17 | 18 | $ADCPass = Get-Content -Path (Join-Path -Path $BaseDir -ChildPath "ADCcred.txt") | ConvertTo-SecureString 19 | $ADCCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ADCUser, $ADCPass 20 | 21 | # If you're just testing and want to see the output without generating a credential file, this script can 22 | # also be run by commenting the above lines and uncommenting this. 23 | # $ADCCred = Get-Credential 24 | 25 | $ADCErrorLog = Join-Path -Path $BaseDir -ChildPath "ADC-Errors.txt" 26 | $ADCErrorHistory = Join-Path -Path $BaseDir -ChildPath "ADC-ErrorHistory.txt" 27 | 28 | # Do the things. 29 | # Its nice to have all the timestamps be the same when you're graphing in Grafana later. 30 | 31 | try { $Timestamp = Get-InfluxTimestamp } 32 | catch { 33 | "[$(Get-Date)] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" | Out-File $ADCErrorLog -Append 34 | "[$(Get-Date)] [$($myinvocation.mycommand)] Exception Caught - Getting Timestamp" | Out-File $ADCErrorLog -Append 35 | Throw "[$($myinvocation.mycommand)] Error getting InfluxTimestamp" 36 | } 37 | 38 | # We want the current log to only have just what's wrong with the latest run. 39 | if (Test-Path $ADCErrorLog) { 40 | Remove-Item -Path $ADCErrorLog -Force 41 | } 42 | 43 | foreach ($ADC in $CitrixADCGateways) { 44 | try { 45 | $ADCParams = @{ 46 | ADC = $ADC; # Example value = "10.1.2.3","10.1.2.4" 47 | Credential = $ADCCred; 48 | ErrorLog = $ADCErrorLog 49 | } 50 | 51 | Get-CADCcache @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 52 | Get-CADCcsvserver @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 53 | Get-CADCgatewayuser @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 54 | Get-CADCgslbvserver @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 55 | Get-CADChttp @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 56 | Get-CADCip @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 57 | Get-CADClbvserver @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 58 | Get-CADCssl @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 59 | Get-CADCsystem @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 60 | Get-CADCtcp @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 61 | 62 | # If everything looks normal but you're not getting ADC data in your dashboard, its probably due 63 | # to this function. Exceptions have been made for wildcards, '=' and '@' symbols, but certs are 64 | # weird. Comment out this next function out. 65 | Get-CADCsslcertkey @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 66 | 67 | } 68 | catch { 69 | Write-Verbose "[$(Get-Date)] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 70 | Write-Verbose "[$(Get-Date)] [$($myinvocation.mycommand)] Exiting uncleanly - ADC: $ADC" 71 | "[$(Get-Date)] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" | Out-File $ADCErrorLog -Append 72 | "[$(Get-Date)] [$($myinvocation.mycommand)] Exception Caught - ADC: $ADC" | Out-File $ADCErrorLog -Append 73 | } 74 | } 75 | 76 | 77 | # If this file exists, we have errors, currently. 78 | if (Test-Path $ADCErrorLog) { 79 | Get-Content $ADCErrorLog | Out-File $ADCErrorHistory -Append 80 | 81 | # Maybe if you care, add something to send one or both log files. 82 | <# 83 | $MailParams = @{ 84 | To = "sysops@domain.com" 85 | From = "EUCMonitoring@domain.com" 86 | Subject = "ADC Errors" 87 | Body = (Get-Content $ADCErrorLog) 88 | SmtpServer = "smtp.domain.com" 89 | Attachments = $ADCErrorHistory 90 | } 91 | Send-MailMessage @MailParams 92 | #> 93 | } -------------------------------------------------------------------------------- /Config/Get-CVADOverview.ps1: -------------------------------------------------------------------------------- 1 | $BaseDir = "C:\Monitoring" 2 | 3 | # Keep this Verbose while testing, change to SilentlyContinue when complete. 4 | $VerbosePreference = 'SilentlyContinue' 5 | # $VerbosePreference = 'Continue' 6 | 7 | $CVADSites = @( # Keep the prepended comma so that the sites work as expected. 8 | , ("ddc1.mydomain.com", "ddc2.mydomain.com") # DDCs in Site 1 9 | # , ("ddc3.mydomain.com", "ddc4.mydomain.com") # DDCs in Site 2 10 | ) 11 | 12 | # Assume the easy-install. 13 | Import-Module (Join-Path -Path $BaseDir -ChildPath "EUCMonitoringRedux-master\PSGallery\EUCMonitoringRedux.psd1") 14 | # Import-Module EUCMonitoringRedux 15 | 16 | <# Citrix Cloud? 17 | Obtain a Citrix Cloud automation credential as follows: 18 | 19 | Login to https://citrix.cloud.com/ 20 | Navigate to "Identity and Access Management". 21 | Click "API Access". 22 | Enter a name for Secure Client and click Create Client. 23 | Once Secure Client is created, download Secure Client Credentials file (ie. downloaded to C:\Monitoring) 24 | Note the Customer ID located in this same page, this is case senstitive. 25 | #> 26 | # Set-XDCredentials -CustomerId "%Customer ID%" -SecureClientFile "C:\Monitoring\secureclient.csv" -ProfileType CloudApi -StoreAs "CloudAdmin" 27 | 28 | # Here's a poor man's log rotation. 29 | $WorkloadErrorLog = Join-Path $BaseDir -ChildPath "Workload-Errors.txt" 30 | $WorkloadErrorHistory = Join-Path -Path $BaseDir -ChildPath "Workload-ErrorHistory.txt" 31 | 32 | if (Test-Path $WorkloadErrorLog) { 33 | Remove-Item -Path $WorkloadErrorLog -Force 34 | } 35 | 36 | try { $Timestamp = Get-InfluxTimestamp } 37 | catch { 38 | "[$(Get-Date)] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" | Out-File $ADCErrorLog -Append 39 | "[$(Get-Date)] [$($myinvocation.mycommand)] Exception Caught - Getting Timestamp" | Out-File $ADCErrorLog -Append 40 | Throw "[$($myinvocation.mycommand)] Error getting InfluxTimestamp" 41 | } 42 | 43 | foreach ($Site in $CVADSites) { 44 | Try { 45 | $CVADWorkloadParams = @{ 46 | Broker = $Site 47 | 48 | # If you want to uncomment and fill these out, go for it. If not, it will auto-discover 49 | # and return a value for each permutation with machines associated. Each will allow for 50 | # multiple values 51 | # SiteName = "" 52 | # ZoneName = "" 53 | # DesktopGroupName = "" 54 | # CatalogName = "" 55 | 56 | SingleSession = $true 57 | MultiSession = $true 58 | ErrorLog = $WorkloadErrorLog 59 | } 60 | 61 | Get-CVADworkload @CVADWorkloadParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 62 | } 63 | catch { 64 | Write-Verbose "[$(Get-Date)] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 65 | Write-Verbose "[$(Get-Date)] [$($myinvocation.mycommand)] Exiting uncleanly - Site: $($Site -join ', ')" 66 | "[$(Get-Date)] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" | Out-File $WorkloadErrorLog -Append 67 | "[$(Get-Date)] [$($myinvocation.mycommand)] Exception Caught - Site: $($Site -join ', ')" | Out-File $WorkloadErrorLog -Append 68 | } 69 | } 70 | 71 | 72 | # If this file exists, we have errors, currently. 73 | if (Test-Path $WorkloadErrorLog) { 74 | Get-Content $WorkloadErrorLog | Out-File $WorkloadErrorHistory -Append 75 | 76 | # Maybe if you care, add something to send one or both log files. 77 | <# 78 | $MailParams = @{ 79 | To = "sysops@domain.com" 80 | From = "EUCMonitoring@domain.com" 81 | Subject = "Workload Errors" 82 | Body = (Get-Content $ADCErrorLog) 83 | SmtpServer = "smtp.domain.com" 84 | Attachments = $WorkloadErrorHistory 85 | } 86 | Send-MailMessage @MailParams 87 | #> 88 | } -------------------------------------------------------------------------------- /Config/Install-VisualizationSetup.ps1: -------------------------------------------------------------------------------- 1 | function Install-VisualizationSetup { 2 | <# 3 | .SYNOPSIS 4 | Sets up the EUC Monitoring Platform Influx / Grafana platform 5 | .DESCRIPTION 6 | Sets up the EUC Monitoring Platform Influx / Grafana platform. Requires internet connection to Github. 7 | .PARAMETER MonitoringPath 8 | Determines the 9 | .INPUTS 10 | None 11 | .OUTPUTS 12 | None 13 | .NOTES 14 | Current Version: 1.1 15 | Creation Date: 19/03/2018 16 | .CHANGE CONTROL 17 | Name Version Date Change Detail 18 | Hal Lange 1.0 16/04/2018 Initial Creation of Installer 19 | Adam Yarborough 1.1 11/07/2018 Integration of Hal's work and updating. 20 | .PARAMETER MonitoringPath 21 | Folder path to download files needed for monitoring process 22 | .EXAMPLE 23 | None Required 24 | 25 | #> 26 | 27 | 28 | [CmdletBinding()] 29 | param ( 30 | [parameter(Mandatory = $false, ValueFromPipeline = $true)][string]$MonitoringPath = "C:\Monitoring", 31 | # [parameter(Mandatory = $false, ValueFromPipeline = $true)][string]$DashboardPath = (Join-Path -Path (get-location).Path -ChildPath "Dashboard"), 32 | [parameter(Mandatory = $false, ValueFromPipeline = $true)][string]$GrafanaVersion , 33 | [parameter(Mandatory = $false, ValueFromPipeline = $true)][string]$InfluxVersion , 34 | [parameter(Mandatory = $false, ValueFromPipeline = $true)][string]$NSSMVersion, 35 | [parameter(Mandatory = $false, ValueFromPipeline = $true)][string]$TelegrafVersion 36 | ) 37 | 38 | begin { 39 | If (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { 40 | Throw "You must be administrator in order to execute this script" 41 | } 42 | [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" 43 | } 44 | 45 | process { 46 | #Base Directory for Install 47 | Write-Output "[$(Get-Date)] Install location set to $MonitoringPath" 48 | # Get the dashboard config. 49 | if ( test-path $MonitoringPath ) { 50 | Write-Output "[$(Get-Date)] $MonitoringPath directory already Present" 51 | } 52 | else { 53 | New-Item $MonitoringPath -ItemType Directory 54 | Write-Output "[$(Get-Date)] EUCMonitoring directory created: $MonitoringPath" 55 | } 56 | 57 | #open FW for Grafana 58 | Write-Output "[$(Get-Date)] Opening Firewall Rules for Grafana" 59 | 60 | 61 | $Catch = New-NetFirewallRule -DisplayName "EUCMonitoring-grafana-server" -Direction Inbound -LocalPort 3000 -Protocol TCP -Action Allow -Description "Allow Grafana Server" 62 | Write-Output "[$(Get-Date)] Opening Firewall Rules for InfluxDB" 63 | $Catch = New-NetFirewallRule -DisplayName "EUCMonitoring-influxdb" -Direction Inbound -LocalPort 8086 -Protocol TCP -Action Allow -Description "Allow InfluxDB Server" -AsJob 64 | 65 | # This is a simple copy and unzip function. If provided a web address, it will fetch before 66 | # unzipping in the target location. Using $catch as a temp variable so that the console output 67 | # looks nicer. 68 | function GetAndInstall ( $Product, $DownloadFile, $Dest ) { 69 | $DownloadLocation = (Get-Item Env:Temp).value #Use the Temp folder as Temp Download location 70 | $zipFile = "$DownloadLocation\$Product.zip" 71 | Write-Output "[$(Get-Date)] Downloading $Product to $zipfile" 72 | if ( ($DownloadFile -match "http://") -or ($DownloadFile -match "https://") ) { 73 | $Catch = Invoke-WebRequest $DownloadFile -outFile $zipFile 74 | } 75 | else { 76 | Copy-Item $DownloadFile -Destination "$DownloadLocation\$Product.zip" 77 | } 78 | 79 | Write-Output "[$(Get-Date)] Installing $Product to $Dest" 80 | # Expand-Archive -LiteralPath "$DownloadLocation\$Product.zip" 81 | $shell = New-Object -ComObject shell.application 82 | $zip = $shell.NameSpace($ZipFile) 83 | foreach ( $item in $zip.items() ) { 84 | $shell.Namespace($Dest).CopyHere($item) 85 | } 86 | $Catch = "" 87 | Write-Verbose $Catch 88 | } 89 | 90 | 91 | #Install Grafana 92 | GetAndInstall "Grafana" $GrafanaVersion $MonitoringPath 93 | $Grafana = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'graf' }).FullName 94 | 95 | #Install InfluxDB 96 | GetAndInstall "InfluxDB" $InfluxVersion $MonitoringPath 97 | $Influx = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'infl' }).FullName 98 | # When taking in a user supplied path, need to change, this will make sure there's a appended '/' 99 | # then strip away drive letter and change backslashs to forward( '\' to '/' ), and get rid of any 100 | # double slashes. Then we'll updated the influxdb.conf. We do this so that we can have a separate 101 | # folder for InfluxDB data, independant of the version, that will survive an uninstall if needed. 102 | $IDataPath = "$MonitoringPath/".replace((resolve-path $MonitoringPath).Drive.Root, '').replace("\", "/").Replace("//", "/") 103 | $content = [System.IO.File]::ReadAllText("$Influx\influxdb.conf").Replace("/var/lib/influxdb", "/$($IDataPath)InfluxData/var/lib/influxdb") 104 | [System.IO.File]::WriteAllText("$Influx\influxdb.conf", $content) 105 | Write-Output "[$(Get-Date)] Setting ENV variable for InfluxDB" 106 | [Environment]::SetEnvironmentVariable("Home", $Influx, "Machine") 107 | 108 | #Install NSSM 109 | GetAndInstall "NSSM" $NSSMVersion $MonitoringPath 110 | 111 | #Install Telegraf 112 | GetAndInstall "Telegraf" $TelegrafVersion $MonitoringPath 113 | 114 | #Setup Services 115 | $NSSM = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'nssm' }).FullName 116 | $NSSMEXE = "$nssm\win64\nssm.exe" 117 | Write-Output "[$(Get-Date)] Installing EUCMonitoring-grafana-server as a service" 118 | & $nssmexe Install "EUCMonitoring-grafana-server" $Grafana\bin\grafana-server.exe 119 | # & $nssmexe Set "Grafana Server" DisplayName "Grafana Server" 120 | Write-Output "[$(Get-Date)] Installing EUCMonitoring-influxdb as a service" 121 | & $nssmexe Install "EUCMonitoring-influxdb" $Influx\influxd.exe -config influxdb.conf 122 | # & $nssmexe Set "InfluxDB Server" DisplayName "InfluxDB Server" 123 | Write-Output "[$(Get-Date)] Starting InfluxDB" 124 | start-service "EUCMonitoring-influxdb" 125 | 126 | Write-Output "[$(Get-Date)] Creating EUCMonitoring database on InfluxDB" 127 | & $Influx\influx.exe -execute 'Create Database EUCMonitoring' 128 | 129 | # Import needed 130 | Write-Output "[$(Get-Date)] Starting Grafana" 131 | start-service "EUCMonitoring-grafana-server" 132 | Write-Output "[$(Get-Date)] Importing Grafana plugins (will error if no net access)" 133 | 134 | Push-Location $grafana\bin 135 | # & .\Grafana-cli.exe plugins install btplc-status-dot-panel 136 | # & .\Grafana-cli.exe plugins install vonage-status-panel 137 | # & .\Grafana-cli.exe plugins install briangann-datatable-panel 138 | & .\Grafana-cli.exe plugins install grafana-piechart-panel 139 | Pop-Location 140 | 141 | # We restart the grafana service here because it may complain 142 | Write-Output "[$(Get-Date)] Restarting Grafana Server" 143 | stop-service "EUCMonitoring-grafana-server" 144 | start-service "EUCMonitoring-grafana-server" 145 | 146 | # If you try and import datasources / dashboards before Grafana is fully up, it will error. 147 | Write-Output "[$(Get-Date)] Sleeping for 15 seconds before continuing" 148 | start-sleep 15 149 | 150 | # Build the headers. 151 | $pair = "admin:admin" 152 | $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) 153 | $base64 = [System.Convert]::ToBase64String($bytes) 154 | $basicAuthValue = "Basic $base64" 155 | $headers = @{ Authorization = $basicAuthValue } 156 | 157 | Write-Output "[$(Get-Date)] Setting up Grafana Datasource" 158 | $datasourceURI = "http://localhost:3000/api/datasources" 159 | 160 | # Setup the datasource. 161 | $Body = @{ 162 | name = "EUCMonitoring" 163 | type = "influxdb" 164 | url = "http://localhost:8086" 165 | database = "EUCMonitoring" 166 | access = "proxy" 167 | basicAuth = $false 168 | isDefault = $true 169 | } 170 | $Catch = Invoke-WebRequest -Uri $datasourceURI -Method Post -Body (Convertto-Json $Body) -Headers $headers -ContentType "application/json" -UseBasicParsing 171 | 172 | Write-Output "[$(Get-Date)] Setting up Grafana Dashboards" 173 | $dashs = get-childitem (join-path $MonitoringPath -ChildPath "EUCMonitoringRedux-master/Dashboards") | Where-Object { $_.Name -match '.json' } 174 | 175 | # $dashboardURI = "http://localhost:3000/api/dashboards/import" 176 | foreach ( $dashboard in $dashs ) { 177 | Write-Output "[$(Get-Date)] Uploading $dashboard" 178 | $Prepend = '{"dashboard":' 179 | $Append = ',"overwrite":true,"inputs":[{"name":"DS_EUCMONITORING","type":"datasource","pluginId":"influxdb","value":"EUCMonitoring"}],"folderId":0}' 180 | $DashParams = @{ 181 | Method = "Post" 182 | Uri = "http://localhost:3000/api/dashboards/import" 183 | Headers = $headers 184 | Body = ($Prepend + (Get-Content $dashboard.FullName) + $Append) 185 | ContentType = "application/json;charset=UTF-8" 186 | UseBasicParsing = $true 187 | } 188 | $Catch = Invoke-WebRequest @DashParams 189 | } 190 | 191 | # Purely to pass variable checks 192 | $Catch = "" 193 | Write-Verbose $Catch 194 | 195 | Write-Output "[$(Get-Date)] Copying config scripts to $MonitoringPath" 196 | # Copy the sample scripts to the base dir 197 | $DashScripts = get-childitem . | Where-Object { $_.Name -match '.ps1' } 198 | # If this is empty, try the Dashboard/ folder 199 | if ($null -eq $DashScripts) { 200 | $DashScriptPath = join-path $MonitoringPath -ChildPath "EUCMonitoringRedux-master/Config" 201 | $DashScripts = Get-ChildItem $DashScriptPath | Where-Object { $_.Name -match '.ps1' } 202 | } 203 | foreach ($DashScript in $DashScripts) { 204 | Copy-Item -Path $DashScript.fullname -Destination $MonitoringPath 205 | } 206 | 207 | 208 | $Telegraf = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'Telegraf' }).FullName 209 | Write-Output "[$(Get-Date)] Configuring Telegraf" 210 | Write-Output "[$(Get-Date)] Overwriting Telegraf config" 211 | @" 212 | [agent] 213 | interval = "5m" 214 | [[outputs.influxdb]] 215 | url = "http://127.0.0.1:8086" # Required 216 | database = "EUCMonitoring" # Required 217 | 218 | [[inputs.exec]] 219 | # Use forward slashes for the path. Change if needed. 220 | commands = [ 221 | "powershell.exe -NoProfile -ExecutionPolicy Bypass -File `'$(Join-Path $MonitoringPath -ChildPath "Get-CADCOverview.ps1")`'", 222 | "powershell.exe -NoProfile -ExecutionPolicy Bypass -File `'$(Join-Path $MonitoringPath -ChildPath "Get-CVADOverview.ps1")`'" 223 | ] 224 | timeout = "5m" 225 | data_format = "influx" 226 | "@ -replace '\\', '/' | Out-File (Join-Path $Telegraf -ChildPath "telegraf.conf" ) -Force -Encoding utf8 227 | Write-Output "[$(Get-Date)] Installing telegraf as a service" 228 | Start-Process "$Telegraf\telegraf.exe" -ArgumentList "--service install --service-name=EUCMonitoring-telegraf --service-display-name=EUCMonitoring-telegraf --config=$Telegraf\telegraf.conf" -Wait 229 | 230 | Write-Output "`nNOTE: Grafana, Influx, and Telegraf are now installed as services." 231 | Get-Service EUCMonitoring* | Select-Object Status, Name, StartType 232 | Write-Output "`nTo follow up, configure Telegraf instance in $MonitoringPath as described in Installation.md by testing" 233 | Write-Output "the input.exec scripts and start the service as appopriate user." 234 | } 235 | 236 | end { 237 | 238 | } 239 | } 240 | 241 | # If you want to overwrite any of these with local paths to the .zip, you can and it will work for 242 | # offline installs. 243 | $Params = @{ 244 | MonitoringPath = "C:\Monitoring" 245 | GrafanaVersion = "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-6.3.3.windows-amd64.zip" 246 | InfluxVersion = "https://dl.influxdata.com/influxdb/releases/influxdb-1.7.7_windows_amd64.zip" 247 | NSSMVersion = "https://www.nssm.cc/release/nssm-2.24.zip" 248 | TelegrafVersion = "https://dl.influxdata.com/telegraf/releases/telegraf-1.11.4_windows_amd64.zip" 249 | } 250 | Install-VisualizationSetup @Params -------------------------------------------------------------------------------- /Config/Testing/EUCMonitor-old.ps1: -------------------------------------------------------------------------------- 1 | 2 | Import-Module EUCMonitoringRedux 3 | $TimeStamp = Get-InfluxTimestamp 4 | 5 | # Workload 6 | $XdDesktopParams = @{ 7 | ComputerName = $null; # Put your brokers here. Example value = "ddc1", "ddc2" 8 | XdDesktop = $true; 9 | XdServer = $false; 10 | WorkerHealth = $true; 11 | BootThreshold = 7; 12 | Highload = 8000 13 | } 14 | Test-EUCWorkload @XdDesktopParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 15 | 16 | $XdServerParams = @{ 17 | ComputerName = $null; # Put your brokers here. Example value = "ddc1", "ddc2" 18 | XdDesktop = $false; 19 | XdServer = $true; 20 | WorkerHealth = $true; 21 | BootThreshold = 7; 22 | Highload = 8000 23 | } 24 | Test-EUCWorkload @XdServerParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 25 | 26 | # Netscalers 27 | $ADCParams = @{ 28 | CitrixADC = $null; 29 | SystemStats = $true; 30 | GatewayUsers = $false; 31 | LoadBalance = $false; 32 | ContentSwitch = $false; 33 | Cache = $false; # Not yet implemented 34 | Compression = $false; # Not yet implementeed 35 | SSLOffload = $false; # Not yet implemented 36 | Credential = $ADCCred 37 | } 38 | Test-EUCADC @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 39 | 40 | # Netscaler Gateways, now called Citrix ADC Gateway 41 | $ADCCred = (Get-Credential) # This won't work. 42 | $ADCParams = @{ 43 | CitrixADC = $null; # Example value = "10.1.2.5" 44 | SystemStats = $false; 45 | GatewayUsers = $true; 46 | LoadBalance = $true; 47 | ContentSwitch = $true; 48 | Cache = $false; # Not yet implemented 49 | Compression = $false; # Not yet implementeed 50 | SSLOffload = $false; # Not yet implemented 51 | Credential = $ADCCred 52 | } 53 | Test-EUCADC @ADCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 54 | 55 | # Licensing 56 | $RDSLicenseParams = @{ 57 | ComputerName = $null; # Example value = "rds-license1", "rds-license2" 58 | RdsLicense = $true; 59 | XdLicense = $false 60 | } 61 | Test-EUCLicense @RDSLicenseParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 62 | 63 | $XdLicenseParams = @{ 64 | ComputerName = $null; # Example value = "xd-license1", "xd-license2" 65 | RdsLicense = $false; 66 | XdLicense = $true 67 | } 68 | Test-EUCLicense @XdLicenseParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 69 | 70 | # Server checks. 71 | $ADParams = @{ 72 | Series = "AD"; 73 | ComputerName = $null; # Example value = "dc1", "dc2" 74 | Ports = 389, 636; 75 | Services = "Netlogon", "ADWS", "NTDS"; 76 | ValidCertPort = 636 77 | } 78 | Test-EUCServer @ADParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 79 | 80 | $SQLParams = @{ 81 | Series = "SQL"; 82 | ComputerName = $null; # Example value = "sql1", "sql2" 83 | Ports = 1433; 84 | Services = "MSSQLServer" 85 | } 86 | Test-EUCServer @SQLParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 87 | 88 | $AppVParams = @{ 89 | Series = "AppV"; 90 | ComputerName = $null; # Example value = "appv1", "appv2" 91 | Ports = 8080; 92 | Services = "W3SVC" 93 | } 94 | Test-EUCServer @AppVParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 95 | 96 | $StorefrontParams = @{ 97 | Series = "Storefront"; 98 | ComputerName = $null; # Example value = "storefront1", "storefront2" 99 | Ports = 80, 443; 100 | Services = "W3SVC", "NetTcpPortSharing", "CitrixSubscriptionsStore", "WAS", "CitrixDefaultDomainService", "CitrixCredentialWallet", "CitrixConfigurationReplication"; 101 | HTTPPath = "/Citrix/StoreWeb"; 102 | HTTPPort = 80 103 | HTTPSPath = "/Citrix/StoreWeb"; 104 | HTTPSPort = 443 105 | ValidCertPort = 443; 106 | } 107 | Test-EUCServer @StorefrontParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 108 | 109 | $DirectorParams = @{ 110 | Series = "Director"; 111 | ComputerName = $null; # Example value = "director1", "director2" 112 | Ports = 80, 443; 113 | HTTPPath = "/Director/LogOn.aspx?cc=true"; 114 | HTTPPort = 80; 115 | HTTPSPath = "/Director/LogOn.aspx?cc=true"; 116 | HTTPSPort = 443 117 | } 118 | Test-EUCServer @DirectorParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 119 | 120 | $XdControllerParams = @{ 121 | Series = "XdController"; 122 | ComputerName = $null; # Example value = "ddc1", "ddc2" 123 | Ports = 80; 124 | Services = "CitrixBrokerService", "CitrixHighAvailabilityService", "CitrixConfigSyncService", "CitrixConfigurationService", "CitrixConfigurationLogging", "CitrixDelegatedAdmin", "CitrixADIdentityService", "CitrixMachineCreationService", "CitrixHostService", "CitrixEnvTest", "CitrixMonitor", "CitrixAnalytics", "CitrixAppLibrary", "CitrixOrchestration" 125 | } 126 | Test-EUCServer @XdControllerParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 127 | 128 | $ProvisioningParams = @{ 129 | Series = "Provisioning"; 130 | ComputerName = $null; # Example value = "pvs1", "pvs2" 131 | Ports = 54321; 132 | Services = "BNPXE", "BNTFTP", "PVSTSB", "soapserver", "StreamService" 133 | } 134 | Test-EUCServer @ProvisioningParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 135 | 136 | $WEMParams = @{ 137 | Series = "WEM"; 138 | ComputerName = $null; # Example value = "wembroker1", "wembroker2" 139 | Ports = 8286; 140 | Services = "Norskale Infrastructure Service" 141 | } 142 | Test-EUCServer @WEMParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 143 | 144 | $UPSParams = @{ 145 | Series = "UPS"; 146 | ComputerName = $null; # Example Value = "print1", "print2" 147 | Ports = 7229; 148 | Services = "UpSvc", "CitrixXTEServer" 149 | } 150 | Test-EUCServer @UPSParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 151 | 152 | # FAS, CC 153 | $FASParams = @{ 154 | Series = "FAS"; 155 | ComputerName = $null; # Example Value = "fas1", "fas2" 156 | Ports = 135; 157 | Services = "CitrixFederatedAuthenticationService" 158 | } 159 | Test-EUCServer @FASParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 160 | 161 | 162 | $CCParams = @{ 163 | Series = "CC"; 164 | ComputerName = $null; # Example Value = "cc1", "cc2" 165 | Ports = 80; 166 | Services = "CitrixWorkspaceCloudADProvider", "CitrixWorkspaceCloudAgentDiscovery", "CitrixWorkspaceCloudAgentLogger", "CitrixWorkspaceCloudAgentSystem", "CitrixWorkspaceCloudAgentWatchDog", "CitrixWorkspaceCloudCredentialProvider", "CitrixWorkspaceCloudWebRelayProvider", "CitrixConfigSyncService", "CitrixHighAvailabilityService", "Citrix NetScaler Cloud Gateway", "XaXdCloudProxy", "RemoteHCLServer", "SessionManagerProxy" 167 | } 168 | Test-EUCServer @CCParams | ConvertTo-InfluxLineProtocol -Timestamp $TimeStamp 169 | 170 | -------------------------------------------------------------------------------- /Config/Uninstall-VisualizationSetup.ps1: -------------------------------------------------------------------------------- 1 | function Uninstall-VisualizationSetup { 2 | <# 3 | .SYNOPSIS 4 | Removes up the EUC Monitoring Platform Influx / Grafana platform 5 | .DESCRIPTION 6 | Removes the EUC Monitoring Platform Influx / Grafana platform 7 | .PARAMETER MonitoringPath 8 | Determines the 9 | .PARAMETER QuickConfig 10 | Interactive JSON file creation based on default values 11 | .INPUTS 12 | None 13 | .OUTPUTS 14 | None 15 | .NOTES 16 | Current Version: 1.0 17 | Creation Date: 19/03/2018 18 | .CHANGE CONTROL 19 | Name Version Date Change Detail 20 | Hal Lange 1.0 16/04/2018 Initial Creation of Installer 21 | Adam Yarborough 1.1 11/07/2018 Integration of Hal's work and updating. 22 | Adam Yarborough 1.2 12/07/2018 Remove only Grafana, Influx, and NSSM 23 | items from $MonitoringPath 24 | Ryan Butler 1.3 24/07/2018 Error and item checking 25 | .PARAMETER MonitoringPath 26 | Folder path to download files needed for monitoring process 27 | .EXAMPLE 28 | None Required 29 | 30 | #> 31 | [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] 32 | param ( 33 | [parameter(Mandatory = $false, ValueFromPipeline = $true)]$MonitoringPath = "C:\Monitoring" 34 | ) 35 | 36 | begin { 37 | If (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { 38 | Throw "You must be administrator in order to execute this script" 39 | } 40 | } 41 | 42 | process { 43 | Write-Warning "Please back up anything of value in $MonitoringPath before proceeding, just in case." 44 | if ($PSCmdlet.ShouldProcess("Remove Influx, Grafana, Telegraf Services")) { 45 | #Removing Services 46 | $Grafana = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'grafana' }).FullName 47 | $Influx = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'influxdb' }).FullName 48 | $NSSM = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'nssm' }).FullName 49 | $Telegraf = (get-childitem $MonitoringPath | Where-Object { $_.Name -match 'telegraf' }).FullName 50 | 51 | Write-Output "[$(Get-Date)] Removing Telegraf service" 52 | stop-service EUCMonitoring-telegraf 53 | $TelegrafEXE = "$Telegraf\telegraf.exe" 54 | & $TelegrafEXE --service uninstall --service-name=EUCMonitoring-telegraf 55 | $NSSMEXE = "$nssm\win64\nssm.exe" 56 | if (test-path $NSSMEXE) { 57 | #Remove Grafana Service 58 | Write-Output "[$(Get-Date)] Removing Grafana Server service" 59 | try { 60 | & $nssmexe Stop "EUCMonitoring-grafana-server" 61 | } 62 | catch { 63 | Write-Warning $($_.Exception.Message) 64 | } 65 | 66 | try { 67 | & $nssmexe Remove "EUCMonitoring-grafana-server" confirm 68 | } 69 | catch { 70 | Write-Warning $($_.Exception.Message) 71 | } 72 | 73 | #Remove Influx Service 74 | Write-Output "[$(Get-Date)] Removing InfluxDB Server service" 75 | try { 76 | & $nssmexe Stop "EUCMonitoring-influxdb" 77 | } 78 | catch { 79 | Write-Warning $($_.Exception.Message) 80 | } 81 | 82 | try { 83 | & $nssmexe Remove "EUCMonitoring-influxdb" confirm 84 | } 85 | catch { 86 | Write-Warning $($_.Exception.Message) 87 | } 88 | } 89 | else { 90 | Write-Warning "[$(Get-Date)] NSSM.EXE NOT FOUND. Skipping services." 91 | } 92 | } 93 | 94 | if ($PSCmdlet.ShouldProcess("Remove program directories")) { 95 | #Remove service Directories, all of them. Scorched earth. 96 | Write-Output "[$(Get-Date)] Removing program directories" 97 | if (-not ([string]::IsNullOrWhiteSpace($Grafana))) { 98 | Write-Output "[$(Get-Date)] Removing $Grafana" 99 | Remove-Item -path $Grafana -Recurse 100 | } 101 | if (-not ([string]::IsNullOrWhiteSpace($Influx))) { 102 | Write-Output "[$(Get-Date)] Removing $Influx" 103 | Remove-Item -path $Influx -Recurse 104 | } 105 | if (-not ([string]::IsNullOrWhiteSpace($NSSM))) { 106 | Write-Output "[$(Get-Date)] Removing $NSSM" 107 | Remove-Item -path $NSSM -Recurse 108 | } 109 | if (-not ([string]::IsNullOrWhiteSpace($Telegraf))) { 110 | Write-Output "[$(Get-Date)] Removing $Telegraf" 111 | Remove-Item -path $Telegraf -Recurse 112 | } 113 | 114 | Write-Output "*** NOTE: Not all files removed from $MonitoringPath" 115 | Write-Output "*** NOTE: Please review manually" 116 | } 117 | 118 | #Remove Variable 119 | if ($PSCmdlet.ShouldProcess("Remove HOME Environment Variable")) { 120 | Write-Output "[$(Get-Date)] Removing HOME Environment Variable" 121 | try { 122 | Remove-Item Env:\Home -ErrorAction stop 123 | } 124 | catch { 125 | write-warning "Issues removing Influx DB environment variable Home. Probably already deleted." 126 | } 127 | } 128 | 129 | #open FW for Grafana 130 | if ($PSCmdlet.ShouldProcess("Remove firewall rules")) { 131 | Write-Output "[$(Get-Date)] Removing Firewall Rules for Grafana and InfluxDB" 132 | try { 133 | Remove-NetFirewallRule -DisplayName "EUCMonitoring-grafana-server" -ErrorAction stop 134 | } 135 | catch { 136 | Write-Warning $($_.Exception.Message) 137 | } 138 | 139 | try { 140 | Remove-NetFirewallRule -DisplayName "EUCMonitoring-influxdb" -ErrorAction stop 141 | } 142 | catch { 143 | Write-Warning $($_.Exception.Message) 144 | } 145 | } 146 | } 147 | 148 | end { 149 | } 150 | } 151 | 152 | Uninstall-VisualizationSetup -------------------------------------------------------------------------------- /Config/telegraf.conf: -------------------------------------------------------------------------------- 1 | [agent] 2 | interval = "5m" 3 | [[outputs.influxdb]] 4 | url = "http://127.0.0.1:8086" # Required 5 | database = "EUCMonitoring" # Required 6 | 7 | [[inputs.exec]] 8 | # Use forward slashes for the path. Change if needed. 9 | commands = [ 10 | "powershell.exe -NoProfile -ExecutionPolicy Bypass -File 'C:/Monitoring/Get-CADCOverview.ps1'", 11 | "powershell.exe -NoProfile -ExecutionPolicy Bypass -File 'C:/Monitoring/Get-CADVOverview.ps1'" 12 | ] 13 | timeout = "5m" 14 | data_format = "influx" -------------------------------------------------------------------------------- /Installation.md: -------------------------------------------------------------------------------- 1 | # Installation instructions for EUCMonitoringRedux 2 | 3 | ## Pre-requisites 4 | 5 | #### Citrix On-Premises 6 | 7 | - For Citrix Apps and Desktops, the location that you want to run this script from must have the XenDesktop Powershell SDK Installed. This is most easily installed by just installing Citrix Studio. 8 | 9 | #### Citrix Cloud 10 | 11 | The Server that you want to run this script from must have the Remote [PowerShell SDK for Applications and Desktops Service](http://download.apps.cloud.com/CitrixPoshSdk.exe): 12 | 13 | Obtain a Citrix Cloud automation credential as follows: 14 | 15 | - Login to 16 | - Navigate to "Identity and Access Management". 17 | - Click "API Access". 18 | - Enter a name for Secure Client and click Create Client. 19 | - Once Secure Client is created, download Secure Client Credentials file (ie. downloaded to C:\Monitoring) 20 | 21 | Note the Customer ID located in this same page, this is case sensitive. 22 | 23 | ```Powershell 24 | Set-XDCredentials -CustomerId "%Customer ID%" -SecureClientFile "C:\Monitoring\secureclient.csv" -ProfileType CloudApi -StoreAs "CloudAdmin" 25 | ``` 26 | 27 | NOTE: In the provided scripts **Broker** or **CloudConnector** should be set as the Citrix Cloud Connectors for the site, the cloud connectors will proxy the connection directly to the Delivery Controller as they are not directly accessible. 28 | 29 | #### Others 30 | 31 | RDS / VMware support will be forthcoming 32 | 33 | ## Method 1 - The local try-it-out method 34 | 35 | **This will not work out of the gate. You will have to edit provided scripts.** You will also need to change VerbosePreference to SilentlyContinue. This is done so that you will be gently reminded to confirm your targets and credentials are what you expect them to be, and running under the right context. 36 | 37 | NOTE: This will install local instances of influxdb, grafana, and telegraf agent on your machine to `C:\Monitoring`. 38 | 39 | In powershell, running as Administrator, 40 | 41 | ```powershell 42 | [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" 43 | Invoke-WebRequest -Uri "https://github.com/littletoyrobots/EUCMonitoringRedux/archive/master.zip" -OutFile "C:\Windows\Temp\Master.zip" 44 | New-Item -ItemType directory -Path "C:\Monitoring" | Out-Null 45 | Expand-Archive -Path "C:\Windows\Temp\Master.zip" -DestinationPath "C:\Monitoring" 46 | Set-Location C:\Monitoring\EUCMonitoringRedux-master\Config 47 | .\Install-VisualizationSetup.ps1 48 | Set-Location C:\Monitoring 49 | ``` 50 | 51 | Note: Thanks to [Eric Haavarstein](https://twitter.com/xenappblog) for the simplified install script! 52 | 53 | ### Configure Telegraf 54 | 55 | Telegraf will run powershell scripts for you and push the data straight into your target data source, as long as they output to the correct format. I have some simple scripts to return objects in powershell, and then convert those objects to Influx Line Protocol so that telegraf can handle the transport for me. 56 | 57 | 1. Go to the base directory, `C:\Monitoring` 58 | 1. Edit each of the script files and give them a test run in powershell console. You'll need to change parameters for your environment. You should see no error messages or verbose output when you're complete. 59 | 1. Measure the execution of each of these scripts for your environment. By default, the telegraf.conf file is configured to poll every 5 minutes. If any script takes longer than that to execute, there will be issues down the line. This is also a good time to verify that invoking the scripts does not affect your environment's performance negatively. 60 | 61 | ```powershell 62 | $LastCmd = Get-History -Count 1 63 | $LastCmd.EndExecutionTime.Subtract($LastCmd.StartExecutionTime).TotalSeconds 64 | ``` 65 | 66 | 1. Test the telegraf instance and verify no errors in output 67 | 68 | ```powershell 69 | set-location C:\Monitoring\telegraf 70 | .\telegraf.exe --config telegraf.conf --test 71 | ``` 72 | 73 | 1. Set the telegraf service Log On to a user with appropriate permissions to run the scripts. Read-Only administrator role should be fine. 74 | 1. Start the EUCMonitoring-telegraf service. 75 | 76 | ### Log into Grafana 77 | 78 | NOTE: As this grows, more scripts and dashboards will be created. There might be one big easy script eventually, or a json fed script that calls the smaller functions, but for now, we're starting small. 79 | 80 | 1. When the testing is complete, browse to `http://localhost:3000` 81 | 1. The initial login will be username: `admin` password: `admin`, and you'll be prompted to change it. Please do. 82 | 1. After login, at the top of the page, there will be a drop down where you can select the dashboards you wish to see. 83 | 84 | Note: You might need to give it some time to populate, and the drop-downs are set to refresh on page-load. As the default refresh is five minutes, expect the page to have your values. 85 | 86 | Note: With the try-it-out method, you won't be able to save current variables from the dropdown by default. This is an [open issue](https://github.com/grafana/grafana/issues/11778) with grafana and provisioned dashboards, but will be addressed soon. If you use the long term installation option, you can import the dashboards manually. 87 | 88 | ### Uninstall 89 | 90 | Note: If you made any changes to the Install-VisualizationSetup, edit Uninstall-VisualizationSetup appropriately. 91 | 92 | In powershell, running as Administrator 93 | 94 | ```powershell 95 | set-location Path\to\EUCMonitoringRedux\Dashboard 96 | .\Uninstall-VisualizationSetup.ps1 97 | ``` 98 | 99 | ## Method 2 - Setup environment for long term 100 | 101 | 1. Install the module. It will soon be available in the PSgallery, but until then, you can create an EUCMonitoringRedux folder in `C:\program files\WindowsPowerShell\Modules\` and copy the PSGallery directory contents there. You'll need to update any scripts invoked by telegraf to import the module by name instead of by path. 102 | 1. Install influxdb and grafana on dedicated host. There are many wonderful guides on this online, most involve a linux box somewhere. There are even [Raspberry Pi](https://www.influxdata.com/blog/running-the-tick-stack-on-a-raspberry-pi/) installs 103 | 1. Create an EUCMonitoring database on influx 104 | 105 | ```influxql 106 | InfluxDB shell 1.7.x 107 | > CREATE DATABASE EUCMonitoring 108 | ``` 109 | 110 | 1. Unzip telegraf on the endpoint you wish to run the scripts from. Edit telegraf.conf outputs.influxdb url to your long term instance with the database "EUCMonitoring", and to include any scripts you want in the input.exec section after testing them. See `Config\telegraf.conf` for extremely simplified example. 111 | 1. From command prompt, run a single telegraf collection, outputting metrics to stdout and make sure you see no errors. 112 | 113 | ```cmd 114 | set-location C:\Monitoring\telegraf 115 | .\telegraf.exe --config telegraf.conf --test 116 | ``` 117 | 118 | Note that the encoding is UTF8, so using Notepad++ is preferred. You might get weird encoding errors. 119 | 120 | 1. Next, to setup as a separate From an elevated command prompt 121 | 122 | ```cmd 123 | telegraf.exe --service install --service-name=EUCMonitoring-telegraf --service-display-name=EUCMonitoring-telegraf --config=C:\Full\Path\To\telegraf.conf 124 | ``` 125 | 126 | 1. Reevaluate the method of storing credentials in the sample scripts, or write your own. You might find you want to user something like Marc Kellerman's [Invoke-CommandAs](https://github.com/mkellerman/invoke-commandas) if you want to run the telegraf agent as its default system user. 127 | 1. In Grafana, configure EUCMonitoring as an InfluxDB data source 128 | 1. Start importing dashboards to the grafana server, making sure to select the EUCMonitoring data source. 129 | 130 | ## Post Install 131 | 132 | ### Authentication 133 | 134 | Look into authentication of Influx and Grafana. You can create custom dashboards only visible particular users and departments. Update your telegraf.conf if you change InfluxDB's authentication. 135 | 136 | ### Make your own custom dashboards, or edit some of those provided 137 | 138 | You know your environment better than anyone else. 139 | 140 | Telegraf has an impressive list of [input plugins](https://github.com/influxdata/telegraf/tree/master/plugins/inputs) to collect data. You can easily collect whatever data your application exposes and then create a Grafana dashboard for it. For example, you could use [win_perf_counters](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/win_perf_counters) and Win10-1809+ / Server 2019's new [User Input Delay Counters](https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/rds-rdsh-performance-counters) to monitor specific applications that you care about, if you wanted to install the telegraf agent on your workers. 141 | 142 | ### Create a playlist 143 | 144 | You can use playlists to cycle dashboards on TVs without user control. [Learn More](https://grafana.com/docs/reference/playlist/) 145 | 146 | ### Browse the Grafana Dashboards 147 | 148 | Don't feel like you have to do it all. There are tons of great dashboards already created for multiple environments. Here are some suggestions: 149 | 150 | - [Unifi Dashboards](https://grafana.com/grafana/dashboards?search=unifi) 151 | - [vSphere Dashboards](https://grafana.com/grafana/dashboards?search=vsphere) 152 | 153 | ### Set up alerting 154 | 155 | While the sample scripts have a section for emailing error logs, that might not be exactly what you're looking for. [Grafana alerts](https://grafana.com/docs/alerting/notifications/) can be set to Email, Slack, PagerDuty, and more. 156 | 157 | ### Share 158 | 159 | Have a great script or dashboard suggestion you'd like to see implemented? Check out [Contributing](https://github.com/littletoyrobots/EUCMonitoringRedux/blob/master/.github/CONTRIBUTING.md) or stop by the #-eucmonitoring channel on [World of EUC on Slack](https://communityinviter.com/apps/worldofeuc/world-of-euc-project) and share it with us! 160 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /PSGallery/EUCMonitoringRedux.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'EUCMonitoring' 3 | # 4 | # Generated by: David Brett 5 | # 6 | # Generated on: 19/03/2018 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'EUCMonitoringRedux.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '2.0' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = 'd3d30bcf-1d6d-4b08-b8ac-504d41bb7f18' 22 | 23 | # Author of this module 24 | Author = 'Adam Yarborough' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Unknown' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) 2019 Adam Yarborough. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'This module will install and configure the Community Driven EUC Monitoring Module that has the ability to Monitor your Citrix EUC Platform' 34 | 35 | # Minimum version of the Windows PowerShell engine required by this module 36 | # PowerShellVersion = '' 37 | 38 | # Name of the Windows PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the Windows PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @( 73 | # Influx / Telegraf related 74 | 'ConvertTo-InfluxLineProtocol', 75 | 'Get-InfluxTimestamp', 76 | 77 | # ADC Functions 78 | 'Get-CADCcache', 79 | 'Get-CADCcsvserver', 80 | 'Get-CADCgatewayuser', 81 | 'Get-CADCgslbvserver', 82 | 'Get-CADChttp', 83 | 'Get-CADCip', 84 | 'Get-CADClbvserver', 85 | 'Get-CADCssl', 86 | 'Get-CADCsslcertkey', 87 | 'Get-CADCsystem', 88 | 'Get-CADCtcp', 89 | 90 | # License Functions 91 | 'Get-CVADlicense', 92 | 'Get-RDSlicense', 93 | 94 | # CVAD workload Functions 95 | 'Get-CVADworkload', 96 | 'Get-CVADworkerhealth', 97 | 98 | # RDS workload Functions 99 | 'Get-RDSworkload', 100 | 'Get-RDSworkerhealth', 101 | 102 | # Generic test for ports / services / etc. 103 | 'Test-EUCServer' 104 | ) 105 | 106 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 107 | CmdletsToExport = @() 108 | 109 | # Variables to export from this module 110 | VariablesToExport = '*' 111 | 112 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 113 | AliasesToExport = @() 114 | 115 | # DSC resources to export from this module 116 | # DscResourcesToExport = @() 117 | 118 | # List of all modules packaged with this module 119 | # ModuleList = @() 120 | 121 | # List of all files packaged with this module 122 | # FileList = @() 123 | 124 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 125 | PrivateData = @{ 126 | 127 | PSData = @{ 128 | 129 | # Tags applied to this module. These help with module discovery in online galleries. 130 | # Tags = @() 131 | 132 | # A URL to the license for this module. 133 | # LicenseUri = '' 134 | 135 | # A URL to the main website for this project. 136 | # ProjectUri = '' 137 | 138 | # A URL to an icon representing this module. 139 | # IconUri = '' 140 | 141 | # ReleaseNotes of this module 142 | # ReleaseNotes = '' 143 | 144 | } # End of PSData hashtable 145 | 146 | } # End of PrivateData hashtable 147 | 148 | # HelpInfo URI of this module 149 | # HelpInfoURI = '' 150 | 151 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 152 | # DefaultCommandPrefix = '' 153 | 154 | } 155 | 156 | -------------------------------------------------------------------------------- /PSGallery/EUCMonitoringRedux.psm1: -------------------------------------------------------------------------------- 1 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) 2 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) 3 | 4 | #Dot source the files 5 | Foreach ($import in @($Public + $Private)) { 6 | Try { 7 | . $import.fullname 8 | } 9 | Catch { 10 | Write-Error -Message "Failed to import function $($import.fullname): $_" 11 | } 12 | } -------------------------------------------------------------------------------- /PSGallery/Private/Connect-CitrixADC.ps1: -------------------------------------------------------------------------------- 1 | function Connect-CitrixADC { 2 | <# 3 | .SYNOPSIS 4 | Logs into a Citrix NetScaler. 5 | .DESCRIPTION 6 | Logs into a NetScaler ADC and returns variable called $NSSession to be used to invoke NITRO Commands. 7 | .PARAMETER ADC 8 | Citrix ADC IP (NSIP) 9 | .PARAMETER Credential 10 | Credential to be used for login. 11 | .PARAMETER Timeout 12 | Timeout in seconds for the session, defaults to 180. 13 | .PARAMETER ErrorLog 14 | File path for error logs to be appended. 15 | .OUTPUTS 16 | Microsoft.PowerShell.Commands.WebRequestSession 17 | .EXAMPLE 18 | Connect-CitrixADC -ADC "10.11.12.13" -Credential (Get-Credential) 19 | .NOTES 20 | Version 1.0 Adam Yarborough 20190715 21 | 22 | .LINK 23 | https://github.com/littletoyrobots/EUCMonitoringRedux 24 | #> 25 | 26 | [CmdletBinding()] 27 | Param ( 28 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 29 | [ValidateNotNullOrEmpty()] 30 | [Alias("NSIP")] 31 | [string]$ADC, 32 | 33 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 34 | [ValidateNotNullOrEmpty()] 35 | [pscredential]$Credential, 36 | 37 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 38 | [int]$Timeout = 180, 39 | 40 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 41 | [Alias("LogPath")] 42 | [string]$ErrorLog 43 | ) 44 | 45 | Begin { 46 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Changing TLS Settings to tls12, tls11, tls" 47 | [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" 48 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Trusting self-signed certs" 49 | # source: https://blogs.technet.microsoft.com/bshukla/2010/04/12/ignoring-ssl-trust-in-powershell-system-net-webclient/ 50 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 51 | } 52 | 53 | Process { 54 | # Strip the Secure Password back to a basic text password 55 | # $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.Password) 56 | # $UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) 57 | 58 | # Set up the JSON Payload to send to the netscaler 59 | $PayLoad = ConvertTo-JSON @{ 60 | "login" = @{ 61 | "username" = $Credential.UserName; 62 | "password" = $Credential.GetNetworkCredential().Password 63 | "timeout" = $Timeout 64 | } 65 | } 66 | 67 | $saveSession = @{ } 68 | 69 | # Connect to CitrixADC 70 | $Session = $null 71 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Connecting to ADC $ADC using NITRO" 72 | try { 73 | $Params = @{ 74 | uri = "https://$ADC/nitro/v1/config/login" 75 | # uri = "$ADC/nitro/v1/config/login" 76 | body = $PayLoad 77 | SessionVariable = "saveSession" 78 | Headers = @{"Content-Type" = "application/vnd.com.citrix.netscaler.login+json" } 79 | Method = "POST" 80 | } 81 | 82 | $Response = Invoke-RestMethod @Params -ErrorAction Stop 83 | if ('ERROR' -eq $Response.severity) { 84 | throw "Error. See response: `n$($response | Format-List -Property * | Out-String)" 85 | } 86 | 87 | # Build Script ADC Session Variable 88 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Connection successful" 89 | $Session = New-Object -TypeName PSObject 90 | $Session | Add-Member -NotePropertyName ADC -NotePropertyValue $ADC -TypeName String 91 | $Session | Add-Member -NotePropertyName WebSession -NotePropertyValue $saveSession -TypeName Microsoft.PowerShell.Commands.WebRequestSession 92 | 93 | return $Session 94 | } 95 | catch { 96 | if ($ErrorLog) { 97 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 98 | } 99 | else { 100 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 101 | } 102 | throw $_ 103 | } 104 | } 105 | 106 | End { 107 | if ($null -eq $Session) { 108 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] No session returned" 109 | } 110 | else { 111 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] ADC session returned" 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /PSGallery/Private/Disconnect-CitrixADC.ps1: -------------------------------------------------------------------------------- 1 | function Disconnect-CitrixADC { 2 | <# 3 | .SYNOPSIS 4 | Logs out of a Citrix NetScaler. 5 | 6 | .DESCRIPTION 7 | Logs out of a Citrix NetScaler and clears the NSSession Global Variable. 8 | 9 | .PARAMETER ADCSession 10 | CitrixADC Rest WebSession. 11 | 12 | .PARAMETER ErrorLog 13 | File path for error logs to be appended. 14 | #> 15 | 16 | [CmdletBinding()] 17 | Param ( 18 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 19 | [ValidateNotNullOrEmpty()] 20 | $ADCSession, 21 | 22 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 23 | [Alias("LogPath")] 24 | [string]$ErrorLog 25 | ) 26 | 27 | Begin { 28 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Disconnecting from $($ADCSession.ADC) using NITRO" 29 | } # Begin 30 | 31 | Process { 32 | $ADC = $ADCSession.ADC 33 | # Validate That the IP Address is valid 34 | # Test-IP $NSIP 35 | 36 | # Check to see if a valid NSSession is active. If not then quit the function 37 | if ($null -eq $ADC) { 38 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Not a valid ADC address" 39 | break 40 | } 41 | if ($null -eq $ADCSession.WebSession) { 42 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] No valid ADC session found, quitting." 43 | break 44 | } 45 | 46 | # Set up the JSON Payload to send to the netscaler 47 | $PayLoad = ConvertTo-JSON @{ 48 | "logout" = @{} 49 | } 50 | 51 | # Logout of the NetScaler 52 | try { 53 | if ($null -eq $ADCSession.WebSession) { 54 | throw 'Must be logged into Citrix ADC first' 55 | } 56 | 57 | $Params = @{ 58 | Uri = "https://$ADC/nitro/v1/config/logout" 59 | Body = $PayLoad 60 | Websession = $ADCSession.WebSession 61 | Headers = @{"Content-Type" = "application/vnd.com.citrix.netscaler.logout+json"} 62 | Method = "POST" 63 | } 64 | 65 | $Response = Invoke-RestMethod @Params # -ErrorAction Stop 66 | if ('ERROR' -eq $Response.severity) { 67 | throw "Error. See response: `n$($response | Format-List -Property * | Out-String)" 68 | } 69 | } 70 | catch { 71 | if ($ErrorLog) { 72 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 73 | } 74 | else { 75 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 76 | } 77 | throw $_ 78 | } 79 | 80 | } # Process 81 | 82 | End { 83 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Logout Success" 84 | } # End 85 | } -------------------------------------------------------------------------------- /PSGallery/Private/Get-CADCNitroValue.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCNitroValue { 2 | <# 3 | .SYNOPSIS 4 | Logs into a Citrix NetScaler. 5 | .DESCRIPTION 6 | Logs into a NetScaler ADC and returns variable called $NSSession to be used to invoke NITRO Commands. 7 | .PARAMETER ADC 8 | Citrix ADC IP (NSIP) 9 | .PARAMETER Credential 10 | Credential to be used for login. 11 | .PARAMETER ADCUserName 12 | UserName to be used for login. 13 | .PARAMETER ADCPassword 14 | Password to be used for login 15 | #> 16 | [CmdletBinding()] 17 | Param ( 18 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 19 | [ValidateNotNullOrEmpty()] 20 | $ADCSession, 21 | 22 | [parameter(Mandatory = $true, ValueFromPipeline = $false, ParameterSetName = 'Stat')] 23 | [ValidateNotNullOrEmpty()] 24 | $Stat, 25 | 26 | [parameter(Mandatory = $true, ValueFromPipeline = $false, ParameterSetName = 'Config')] 27 | [ValidateNotNullOrEmpty()] 28 | $Config, 29 | 30 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 31 | [Alias("LogPath")] 32 | [string]$ErrorLog 33 | ) 34 | 35 | Begin { 36 | 37 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Changing TLS Settings to tls12, tls11, tls" 38 | [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" 39 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Trusting self-signed certs" 40 | # source: https://blogs.technet.microsoft.com/bshukla/2010/04/12/ignoring-ssl-trust-in-powershell-system-net-webclient/ 41 | 42 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 43 | if ($PSBoundParameters.ContainsKey('Stat')) { 44 | $Uri = "https://$($ADCSession.ADC)/nitro/v1/stat/$Stat" 45 | # $Uri = "$($ADCSession.ADC)/nitro/v1/stat/$Stat" 46 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Getting stat: $Stat" 47 | $NitroName = $Stat 48 | } 49 | if ($PSBoundParameters.ContainsKey('Config')) { 50 | $Uri = "https://$($ADCSession.ADC)/nitro/v1/config/$Config" 51 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Getting config: $Config" 52 | #$Uri = "$($ADCSession.ADC)/nitro/v1/config/$Config" 53 | $NitroName = $Config 54 | } 55 | 56 | } # Begin 57 | 58 | Process { 59 | #$ADC = $ADCSession.ADC 60 | $Session = $ADCSession.WebSession 61 | $Method = "GET" 62 | $ContentType = 'application/json' 63 | $Results = @() 64 | 65 | try { 66 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Fetching from $Uri" 67 | $Params = @{ 68 | uri = $Uri; 69 | WebSession = $Session; 70 | ContentType = $ContentType; 71 | Method = $Method 72 | } 73 | $NitroValue = Invoke-RestMethod @Params -ErrorAction Stop 74 | 75 | if ($null -eq $NitroValue) { 76 | if ($ErrorLog) { 77 | Write-EUCError -Message "[$($myinvocation.mycommand)] $Uri - No values returned" -Path $ErrorLog 78 | } 79 | else { 80 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Uri - No values returned" 81 | } 82 | } 83 | elseif (0 -ne $NitroValue.Errorcode) { 84 | if ($ErrorLog) { 85 | Write-EUCError -Message "[$($myinvocation.mycommand)] [Severity: $($NitroValue.Severity)] $($NitroValue.Message)" -Path $ErrorLog 86 | } 87 | else { 88 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [Severity: $($NitroValue.Severity)] $($NitroValue.Message)" 89 | } 90 | throw "[Severity: $($NitroValue.Severity)] $($NitroValue.Message)" 91 | } 92 | else { 93 | $Results += $NitroValue.$NitroName 94 | } 95 | } 96 | catch { 97 | if ($ErrorLog) { 98 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 99 | } 100 | else { 101 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 102 | } 103 | throw $_ 104 | } 105 | 106 | return $Results 107 | } 108 | 109 | End { 110 | if ($Results) { 111 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /PSGallery/Private/Get-DonutHTML.ps1: -------------------------------------------------------------------------------- 1 | function Get-DonutHTML { 2 | <# 3 | .SYNOPSIS 4 | Build a HTML Output Monitoring Page 5 | .DESCRIPTION 6 | Takes the output from the monitoring checks and pulls together a html monitoring page 7 | .PARAMETER DonutFile 8 | HTML Data Output File 9 | .PARAMETER HTMLInput 10 | HTML Input File 11 | .PARAMETER DonutHeight 12 | Donut Height 13 | .PARAMETER DonutWidth 14 | Donut Width 15 | .PARAMETER DonutGood 16 | Donut Height 17 | .PARAMETER DonutBad 18 | Donut Width 19 | .PARAMETER DonutStroke 20 | Donut Width 21 | .PARAMETER ServiceName 22 | Donut Service Name 23 | .NOTES 24 | Current Version: 1.0 25 | Creation Date: 07/02/2018 26 | .CHANGE CONTROL 27 | Name Version Date Change Detail 28 | David Brett 1.0 07/02/2018 Function Creation 29 | Adam Yarborough 1.1 12/06/2018 Change to return string 30 | Alex Spicola 1.2 11/09/2018 Worker donut site name 31 | .EXAMPLE 32 | None Required 33 | #> 34 | 35 | [CmdletBinding()] 36 | 37 | Param 38 | ( 39 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$DonutHeight, 40 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$DonutWidth, 41 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$DonutGoodColour, 42 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$DonutBadColour, 43 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$DonutStroke, 44 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$SeriesName, 45 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$SeriesUpCount, 46 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$SeriesDownCount, 47 | [parameter(ValueFromPipeline = $false)][switch]$Worker, 48 | [parameter(Mandatory = $false, ValueFromPipeline = $true)]$SiteName 49 | ) 50 | 51 | # Sort out up and down count 52 | if (0 -eq $SeriesDownCount) { 53 | $SeriesUpCount = 100 54 | } 55 | else { 56 | $full = $SeriesUpCount + $SeriesDownCount 57 | $Single = 100 / $Full 58 | $SeriesUpCount = $Single * $SeriesUpCount 59 | $SeriesDownCount = $Single * $SeriesDownCount 60 | } 61 | 62 | $HTML = "" 63 | $HTML += "" 64 | $HTML += "" 65 | $HTML += "" 66 | 67 | if ( $Worker ) { 68 | $HTML += "" 69 | $HTML += "" 70 | $HTML += "$SiteName $SeriesName" 71 | $HTML += "" 72 | } 73 | else { 74 | $HTML += "" 75 | $HTML += "" 76 | $HTML += "$SeriesName" 77 | $HTML += "" 78 | } 79 | 80 | $HTML += "" 81 | $HTML += "" 82 | 83 | $HTML 84 | } 85 | -------------------------------------------------------------------------------- /PSGallery/Private/Get-InfluxURI.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Get-InfluxURI { 4 | <# 5 | .SYNOPSIS 6 | Creates a URI for the EUCMonitoring instance from JSON data or passed Object 7 | .DESCRIPTION 8 | Creates a URI for the EUCMonitoring instance from JSON data or passed Object 9 | .PARAMETER JSONFile 10 | Specify path to your config file to run checks against. This would be your EUCMonitoring.json, or your 11 | test configs. Specifying a JSONFile override any ConfigObject passed to it. This is mainly 12 | used in unit testing to validate the test suites before production. 13 | .PARAMETER ConfigObject 14 | Specifies the ports to run checks against. This should already be in the target location. 15 | .PARAMETER Services 16 | Specifies the windows services to run checks against 17 | .NOTES 18 | Current Version: 1.0 19 | Creation Date: 14/05/2018 20 | .CHANGE CONTROL 21 | Name Version Date Change Detail 22 | Adam Yarborough 1.0 22/02/2018 Function Creation 23 | 24 | .EXAMPLE 25 | Test-Template -JSONFile "C:\Monitoring\EUCMonitoring.json" 26 | #> 27 | [CmdletBinding()] 28 | Param 29 | ( 30 | [Parameter(ValueFromPipeline)]$ConfigObject 31 | ) 32 | 33 | # XXX CHANGEME XXX 34 | Write-Verbose "Starting Get-InfluxURI." 35 | # Initialize Empty Results 36 | 37 | <# 38 | if ( $JSONFile ) { 39 | $ConfigObject = Get-Content -Raw -Path $JSONFile | ConvertFrom-Json 40 | } 41 | 42 | #> 43 | 44 | $DB = $ConfigObject.Global.Influx.InfluxDB 45 | $Server = $ConfigObject.Global.Influx.InfluxServer 46 | $Protocol = $ConfigObject.Global.Influx.Protocol 47 | $Port = $ConfigObject.Global.Influx.Port 48 | 49 | "$Protocol`://$Server`:$Port/write?db=$DB" 50 | } -------------------------------------------------------------------------------- /PSGallery/Private/Test-Service.ps1: -------------------------------------------------------------------------------- 1 | function Test-Service { 2 | 3 | <# 4 | .SYNOPSIS 5 | Tests a service passed into the function 6 | .DESCRIPTION 7 | Tests a service passed into the function 8 | .PARAMETER ServerName 9 | The Server Name to test the service on 10 | .PARAMETER ServiceName 11 | The Service Name to test 12 | .NOTES 13 | Current Version: 1.0 14 | Creation Date: 19/02/2018 15 | .CHANGE CONTROL 16 | Name Version Date Change Detail 17 | James Kindon 1.0 27/03/2017 Function Creation 18 | David Brett 1.1 19/02/2018 Edited Function to accept input variables and return status 19 | David Brett 1.2 16/06/2018 Updated Parameters and switched function to advanced 20 | .EXAMPLE 21 | None Required 22 | #> 23 | 24 | [CmdletBinding()] 25 | Param 26 | ( 27 | [parameter(Mandatory = $false, ValueFromPipeline = $true)]$ServerName, 28 | [parameter(Mandatory = $false, ValueFromPipeline = $true)]$ServiceName 29 | ) 30 | 31 | begin { } 32 | 33 | process { 34 | # Get Service Status 35 | Write-Verbose "Testing Service Status for $ServiceName on $ServerName" 36 | $ServiceStatus = (Get-Service -ErrorAction SilentlyContinue -ComputerName $ServerName -Name $ServiceName).Status 37 | if ($ServiceStatus -eq "Running") { 38 | Write-Verbose "$ServiceName on $ServerName is Running" 39 | } 40 | else { 41 | Write-Verbose "$ServiceName on $ServerName is Degraded or Stopped" 42 | } 43 | 44 | return $ServiceStatus 45 | } 46 | 47 | end { } 48 | } 49 | -------------------------------------------------------------------------------- /PSGallery/Private/Test-URL.ps1: -------------------------------------------------------------------------------- 1 | function Test-URL { 2 | <# 3 | .SYNOPSIS 4 | Tests connectivity to a URL 5 | .DESCRIPTION 6 | Tests connectivity to a URL 7 | .PARAMETER Url 8 | The URL to be tested 9 | .NOTES 10 | Current Version: 1.0 11 | Creation Date: 07/02/2018 12 | .CHANGE CONTROL 13 | Name Version Date Change Detail 14 | David Brett 1.0 07/02/2018 Function Creation 15 | Adam Yarborough 1.1 05/06/2018 Change to true/false 16 | David Brett 1.2 16/06/2018 Updated Function Parameters 17 | Adam Yarborough 1.3 30/11/2018 Rearrange and 18 | .EXAMPLE 19 | None Required 20 | #> 21 | 22 | [CmdletBinding()] 23 | Param 24 | ( 25 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$Url 26 | ) 27 | 28 | Begin { 29 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting $($myinvocation.mycommand)" 30 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Setting Security Protocol" 31 | [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" 32 | } #Begin 33 | 34 | Process { 35 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Connecting to URL: $URL" 36 | $HTTP_Status = 400 37 | 38 | # Setup Request Object 39 | $HTTP_Request = [System.Net.WebRequest]::Create("$URL") 40 | 41 | #Check for Response 42 | try { 43 | $HTTP_Response = $HTTP_Request.GetResponse() 44 | } 45 | catch { 46 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Failure" 47 | return $false 48 | break 49 | } 50 | 51 | #Extract Response Code 52 | $HTTP_Status = [int]$HTTP_Response.StatusCode 53 | 54 | If ($HTTP_Status -eq 200) { 55 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Status 200 - OK" 56 | $HTTP_Response.Close() 57 | return $true 58 | } 59 | else { 60 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Status $HTTP_Status" 61 | $HTTP_Response.Close() 62 | return $false 63 | } 64 | } # Process 65 | 66 | End { 67 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)]" 68 | } # End 69 | } 70 | -------------------------------------------------------------------------------- /PSGallery/Private/Test-ValidCert.ps1: -------------------------------------------------------------------------------- 1 | function Test-ValidCert { 2 | <# 3 | .SYNOPSIS 4 | Checks the validity of a remote certificate presented on a port, as seen by host the function is run on 5 | .DESCRIPTION 6 | Checks the validity of a remote certificate presented on a port, as seen by host the function is run on. 7 | .PARAMETER ComputerName 8 | Host you want to check the certificate of. Can be hostname or IP. 9 | .PARAMETER Port 10 | Specifies the ports to run checks against 11 | .NOTES 12 | Current Version: 1.0 13 | Creation Date: 14/05/2018 14 | .CHANGE CONTROL 15 | Name Version Date Change Detail 16 | Adam Yarborough 1.0 22/02/2018 Function Creation 17 | David Brett 1.1 16/06/2018 Updated Function Parameters 18 | Ryan Butler 1.2 09/08/2018 Validate on date vs Chain to avoid 19 | odd PS conditions. 20 | Adam Yarborough 1.3 04/09/2019 Added more verbose messages 21 | .CREDIT 22 | Original code by Rob VandenBrink, https://bit.ly/2IDf5Gd 23 | .OUTPUT 24 | Returns boolean value. $true / $false 25 | .EXAMPLE 26 | None Required 27 | #> 28 | 29 | [CmdletBinding()] 30 | param ( 31 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$ComputerName, 32 | [parameter(Mandatory = $true, ValueFromPipeline = $true)][int]$Port 33 | ) 34 | begin { 35 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)]" 36 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Changing TLS Settings" 37 | [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" 38 | } 39 | 40 | process { 41 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Testing Valid Cert on $ComputerName Port: $Port" 42 | try { 43 | $TcpSocket = New-Object Net.Sockets.TcpClient($ComputerName, $Port) 44 | $tcpstream = $TcpSocket.GetStream() 45 | $Callback = { param($sender, $cert, $chain, $errors) return $true } 46 | $SSLStream = New-Object -TypeName System.Net.Security.SSLStream -ArgumentList @($tcpstream, $True, $Callback) 47 | 48 | try { 49 | $SSLStream.AuthenticateAsClient($ComputerName) 50 | $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($SSLStream.RemoteCertificate) 51 | } 52 | catch { Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Could not authenticate as client to $ComputerName on $Port" } 53 | finally { 54 | $SSLStream.Dispose() 55 | } 56 | } 57 | catch { Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Could not connect to $ComputerName on $Port to test Cert" } 58 | 59 | if ($null -eq $Certificate) { 60 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] No authenticated certificate returned" 61 | return $false 62 | } 63 | else { 64 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] DNSNameList: $($Certificate.DNSNameList -join ', ') " 65 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Issuer: $($Certificate.Issuer)" 66 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Valid from: $($Certificate.NotBefore) - $($Certificate.NotAfter)" 67 | if ($Certificate.Verify()) { 68 | $daysleft = $Certificate.NotAfter - (get-date) 69 | if ($daysleft.Days -le 5) { 70 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Cert about to expire" 71 | return $false 72 | } 73 | else { 74 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Certificate valid" 75 | return $true 76 | } 77 | } 78 | else { 79 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Certificate not valid" 80 | return $false 81 | } 82 | } 83 | } 84 | 85 | end { 86 | # Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] " 87 | } 88 | } -------------------------------------------------------------------------------- /PSGallery/Private/Write-EUCError.ps1: -------------------------------------------------------------------------------- 1 | function Write-EUCError { 2 | [CmdletBinding()] 3 | Param 4 | ( 5 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 6 | [ValidateNotNullOrEmpty()] 7 | [Alias("LogContent")] 8 | [string]$Message, 9 | 10 | [Parameter(Mandatory = $false)] 11 | [Alias('LogPath')] 12 | [string]$Path 13 | ) 14 | 15 | Begin { 16 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)]" 17 | } 18 | 19 | Process { 20 | # Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] " 21 | if (($null -ne $Path) -and ("" -ne $Path)) { 22 | if (-Not (Test-Path $Path)) { 23 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Creating Log File: $Path" 24 | try { 25 | New-Item $Path -Force -ItemType File | Out-Null 26 | } 27 | catch { 28 | Write-Error "Unable to create log file at $Path" 29 | } 30 | } 31 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] > $Message" 32 | "[$(Get-Date)] $Message" | Out-File -FilePath $Path -Append 33 | } 34 | else { 35 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] No log path." 36 | } 37 | 38 | if ($EventLog) { 39 | if (![System.Diagnostics.EventLog]::SourceExists("EUCMonitoring")) { 40 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Adding EUCMonitoring Event Source." 41 | New-EventLog -LogName Application -Source "EUCMonitoring" 42 | } 43 | 44 | Write-EventLog -Logname "Application" -Source "EUCMonitoring" -EventID 17034 -EntryType Information -message "$Message" -category "17034" 45 | } 46 | } 47 | 48 | End { 49 | # Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)]" 50 | } 51 | } -------------------------------------------------------------------------------- /PSGallery/Public/ConvertTo-EUCResultHtml.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-EUCResultHTML { 2 | [cmdletbinding(ConfirmImpact = "High")] 3 | Param ( 4 | [Object[]]$Results, 5 | [string]$FilePath, 6 | [string]$ErrorLog, 7 | [int]$RefreshDuration = 300 8 | ) 9 | 10 | Begin { 11 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)]" 12 | } 13 | Process { 14 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)]" 15 | 16 | # If outfile exists - delete it 17 | if (test-path $HTMLOutputFileFull) { 18 | Remove-Item $HTMLOutputFileFull 19 | } 20 | 21 | 22 | # Write HTML Header Information 23 | "" | Out-File $HTMLOutputFileFull -Append 24 | "" | Out-File $HTMLOutputFileFull -Append 25 | 26 | # Write CSS Style 27 | "" | Out-File $HTMLOutputFileFull -Append 31 | 32 | if ( $RefreshDuration -ne 0 ) { 33 | '' | Out-File $HTMLOutputFileFull -Append 34 | } 35 | 36 | ################# 37 | # Title Section # 38 | ################# 39 | $Title = "EUCMonitoring" 40 | $LogoFile = "EUCMonitoring.png" 41 | "" | Out-File $HTMLOutputFileFull -Append 42 | "" | Out-File $HTMLOutputFileFull -Append 43 | "" | Out-File $HTMLOutputFileFull -Append 46 | "" | Out-File $HTMLOutputFileFull -Append 49 | "" | Out-File $HTMLOutputFileFull -Append 50 | "
" | Out-File $HTMLOutputFileFull -Append 44 | $title | Out-File $HTMLOutputFileFull -Append 45 | "" | Out-File $HTMLOutputFileFull -Append 47 | "" | Out-File $HTMLOutputFileFull -Append 48 | "
" | Out-File $HTMLOutputFileFull -Append 51 | 52 | ########################## 53 | # Infrastructure Section # 54 | ########################## 55 | 56 | # Write Infrastructure Table Header 57 | "" | Out-File $HTMLOutputFileFull -Append 58 | "" | Out-File $HTMLOutputFileFull -Append 59 | 60 | $Height = 50 61 | $Width = 50 62 | $UpColor = "rgba(221, 70, 70, 0.9)" 63 | $DownColor = "rgba(67, 137, 203, 0.95)" 64 | 65 | # $InfraData = "" 66 | 67 | # List of Series you don't want in the infrastructure section 68 | $NonInfraSeriesNames = @( 69 | "XdWorker", "XdWorkerHealth", 70 | "RdsWorker", "RdsWorkerHealth", 71 | "XdLicense", "RdsLicense", # These are the license numbers, not services 72 | "CitrixADClbvserver", "CitrixADCcsvserver", "CitrixADCgatewayusers" # ADC Stats, not up/down 73 | ) 74 | 75 | $InfraSeriesResults = $Results | Where-Object { $_.Series -notcontains $NonInfraSeriesNames } 76 | $InfraResultNames = $InfraSeriesResults | Select-Object -ExpandProperty Series -Unique 77 | 78 | 79 | $TotalInf = $InfraResultNames.Count 80 | if ($TotalInf -gt 0) { 81 | # Bug Fix #57 -> Alex Spicola 82 | if ($TotalInf -gt 1) { $TotalInf-- } else { $TotalInf = 1 } 83 | $ColumnPercent = 100 / [int]$totalinf 84 | } 85 | 86 | Write-Verbose "$ColumnPercent" 87 | foreach ($Name in $InfraSeriesNames) { 88 | 89 | $Up = 0 90 | $Down = 0 91 | 92 | $SeriesResults = $InfraSeriesResults | Where-Object { $_.Series -eq $Name } 93 | 94 | $SeriesResults.PSObject.Properties | ForEach-Object { 95 | if ($null -eq $_.Value) { } 96 | elseif ($_.Name -eq "Series") { } 97 | # If its a string, we'll treat it as a tag 98 | elseif ($_.Value -is [string]) { } 99 | else { 100 | if ( $_.Value -eq 1 ) { $Up++ } 101 | elseif ( $_.Value -eq 0 ) { $Down++ } 102 | else { } # Discard other values. 103 | } 104 | } 105 | 106 | 107 | # Renames 108 | switch ($Name) { 109 | "Xenserver" { $SeriesName = "Citrix HV"; break } 110 | "Storefront" { $SeriesName = "StoreFront"; break } 111 | "XdLicensing" { $SeriesName = "Citrix Licensing"; break } 112 | "RdsLicensing" { $SeriesName = "RDS Licensing"; break } 113 | "NetScaler" { $SeriesName = "Citrix ADC"; break } 114 | default { $SeriesName = $Name; break } 115 | } 116 | 117 | $Params = @{ 118 | Height = $Height; 119 | Width = $Width; 120 | DonutGoodColour = $UpColor; 121 | DonutBadColour = $DownColor; 122 | DonutStroke = $DonutStroke; 123 | SeriesName = $SeriesName; 124 | SeriesUpCount = $Up; 125 | SeriesDownCount = $Down; 126 | Worker = $false; 127 | SiteName = $null 128 | } 129 | Get-DonutHTML @Params | Out-File $HTMLOutputFileFull -Append 130 | 131 | } 132 | 133 | 134 | "" | Out-File $HTMLOutputFileFull -Append 135 | "
" | Out-File $HTMLOutputFileFull -Append 136 | 137 | ################## 138 | # Worker Objects # 139 | ################## 140 | 141 | 142 | 143 | # Last Run Details 144 | "" | Out-File $HTMLOutputFileFull -Append 145 | "" | Out-File $HTMLOutputFileFull -Append 146 | "
" | Out-File $HTMLOutputFileFull -Append 147 | $LastRun = Get-Date 148 | "Last Run Date: $LastRun" | Out-File $HTMLOutputFileFull -Append 149 | "
" | Out-File $HTMLOutputFileFull -Append 150 | "" | Out-File $HTMLOutputFileFull -Append 151 | 152 | # Write the Worker Table Footer 153 | "
" | Out-File $HTMLOutputFileFull -Append 154 | 155 | # Write HTML Footer Information 156 | "" | Out-File $HTMLOutputFileFull -Append 157 | "" | Out-File $HTMLOutputFileFull -Append 158 | } 159 | 160 | End { 161 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)]" 162 | } 163 | } -------------------------------------------------------------------------------- /PSGallery/Public/ConvertTo-InfluxLineProtocol.ps1: -------------------------------------------------------------------------------- 1 | Function ConvertTo-InfluxLineProtocol { 2 | <# 3 | .SYNOPSIS 4 | Takes any psobject, and returns a string in Influx Line Protocol. Not good with objects that have arrays as 5 | properties currently. 6 | 7 | .DESCRIPTION 8 | This will take almost any object and return a string in Influx Line Protocol. It's opinionated about how it 9 | does this, and will make any properties that are strings and turn them into tags. Others will be added 10 | however powershell evaluates them in a string. This can lead to mixed results with properties that contain 11 | arrays. There are other implementations of this, but 12 | 13 | .PARAMETER InputObject 14 | Target object(s) of results you wish to convert into Influx LIne Protocol 15 | 16 | .PARAMETER Series 17 | Specifies the series name for the returned string. Will override any Series properties of passed object. 18 | 19 | .PARAMETER Timestamp 20 | Specifies a influx line protocol supported timestamp, best tested with nanosecond format. 21 | 22 | .PARAMETER IncludeTimeStamp 23 | Will fetch current timestamp in nanosecond format and include it in returned string. 24 | 25 | .EXAMPLE 26 | ConvertTo-InfluxLineProtocol $MyObj -IncludeTimeStamp 27 | 28 | .EXAMPLE 29 | $Timestamp = Get-InfluxTimeStamp 30 | $MyObj | ConvertTo-InfluxLineProtocol -Timestamp $Timestamp 31 | 32 | .NOTES 33 | General notes 34 | #> 35 | [CmdletBinding()] 36 | [OutputType([String])] 37 | Param( 38 | [Parameter(ValueFromPipeline)] 39 | [object[]]$InputObject, 40 | 41 | [Parameter(Mandatory = $false)] 42 | [string]$Series, 43 | 44 | [int64]$Timestamp = 0, 45 | 46 | [switch]$IncludeTimeStamp 47 | ) 48 | Begin { 49 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] `$Timestamp = $Timestamp" 50 | if (($null -ne $Timestamp) -and (0 -ne $Timestamp)) { 51 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Using provided timestamp: $Timestamp" 52 | $IncludeTimeStamp = $true 53 | } 54 | elseif ($IncludeTimeStamp) { 55 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Fetching timestamp" 56 | try { $Timestamp = Get-InfluxTimestamp } 57 | catch { Throw "[$($myinvocation.mycommand)] Error getting InfluxTimestamp" } 58 | } 59 | else { 60 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] No timestamp" 61 | $IncludeTimeStamp = $false 62 | } 63 | } #BEGIN 64 | 65 | Process { 66 | # We grab here so that all output will have the same associated timestamp. 67 | # Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Converting results to Influx Line Protocol" 68 | 69 | foreach ($Obj in $InputObject) { 70 | # Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Converting obj $($Result.Host)" 71 | 72 | <# 73 | The format for Influx Line Protocol looks like 74 | SeriesName,tag1=tagvalue DataPoint1=datavalue timestamp 75 | 76 | I've decided to treat all strings as tags, everything else as data parameters. 77 | #> 78 | 79 | $ParamString = "" 80 | # If you specified the Series, use it, else look for a Series property in the object. 81 | # The \W matches any non-word character, \$& anchors all text matched 82 | # "What ever !@#" -replace "\W", "\$&" yields "What\ ever\ \!\@\#" 83 | if ("" -ne $Series) { $SeriesString = $Series -replace "\W", "\$&" } 84 | else { $SeriesString = "$($Obj.Series)" -replace "\W", "\$&" } 85 | 86 | # If we can't find a specified Series name or a Series property, then we won't be able to convert 87 | # propertly. Abort! 88 | if ("" -eq $SeriesString) { 89 | throw "[$($myinvocation.mycommand)] Series Blank" 90 | } 91 | 92 | $Obj.PSObject.Properties | ForEach-Object { 93 | # Skip the empty values, they'll cause errors 94 | if ($null -eq $_.Value) { 95 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $($_.Name)'s value is null... skipping" 96 | } 97 | elseif ("" -eq $_.Value) { 98 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $($_.Name)'s value is empty string... skipping" 99 | } 100 | 101 | # We've already determined series above.. 102 | elseif ($_.Name -eq "Series") { } 103 | 104 | # If its a string, we'll treat it as a tag 105 | elseif ($_.Value -is [string]) { 106 | # So, this is a little tricky, Thank you GoDaddy certs... 107 | # And digicert, and wildcart certs. 108 | $SeriesString += ",$($_.Name)=$($_.Value.trim() -replace ',', '\,' -replace '=', '\=' -replace '"', '\"')" 109 | } 110 | 111 | # Handles Integers, Floatin Points and Booleans just fine. Hopefully the ToString() of 112 | # whatever value you plays well. 113 | else { 114 | if ( $ParamString -eq "" ) { $ParamString = "$($_.Name)=$($_.Value)" } 115 | else { $ParamString += ",$($_.Name)=$($_.Value)" } 116 | } 117 | } 118 | 119 | 120 | if (("" -ne $ParamString) -and ("" -ne $SeriesString)) { 121 | # Was using \W instead of Regex::Escape() for special character inclusion. 122 | # $& refers to the match. Changed mind on this. Will mangle hostnames. 123 | 124 | $SeriesString = $SeriesString -replace " ", "\ " 125 | # $SeriesString = $SeriesString -replace "\W", "\$&" 126 | $ParamString = $ParamString -replace " ", "\ " 127 | # $ParamString = $ParamString -replace "\W", "\$&" 128 | 129 | 130 | if ($IncludeTimeStamp) { $PostParams = "$SeriesString $ParamString $timeStamp" } 131 | else { $PostParams = "$SeriesString $ParamString" } 132 | # Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $PostParams" 133 | 134 | Write-Output $PostParams 135 | } 136 | else { 137 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] No additional test data" 138 | } 139 | 140 | } 141 | 142 | } #PROCESS 143 | 144 | End { 145 | # Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)]" 146 | } 147 | } 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCcache.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCcache { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway integrated caching from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway integrated caching from NITRO by polling 8 | $ADC/nitro/v1/stats/cache and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCcache -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | 28 | [CmdletBinding()] 29 | param ( 30 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 31 | [ValidateNotNullOrEmpty()] 32 | [Alias("NSIP")] 33 | [string]$ADC, 34 | 35 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 36 | [ValidateNotNullOrEmpty()] 37 | [pscredential]$Credential, 38 | 39 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 40 | [Alias("LogPath")] 41 | [string]$ErrorLog 42 | ) 43 | 44 | Begin { 45 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 46 | try { 47 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 48 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 49 | } 50 | catch { 51 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 52 | throw $_ 53 | } 54 | } 55 | 56 | Process { 57 | try { 58 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "cache" 59 | 60 | foreach ($cache in $Results) { 61 | $RecentHitPcnt = [int64]$cache.cacherecentpercenthit 62 | $RecentMissPcnt = 100 - $RecentHitPcnt 63 | 64 | $CurrentHits = [int64]$cache.cachecurhits 65 | $CurrentMiss = [int64]$cache.cachecurmisses 66 | 67 | $HitsPcnt = [int64]$cache.cachepercenthit 68 | $MissPcnt = 100 - $HitsPcnt 69 | 70 | $HitsRate = [int64]$cache.cachehitsrate 71 | $RequestsRate = [int64]$cache.cacherequestsrate 72 | $MissRate = [int64]$cache.cachemissesrate 73 | 74 | $TotalHits = [int64]$cache.cachetothits 75 | $TotalMisses = [int64]$cache.cachetotmisses 76 | 77 | # Verbose values for testings 78 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $ADC - cache" 79 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] RecentHitPcnt: $RecentHitPcnt, RecentMissPcnt: $RecentMissPcnt" 80 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] HitsPcnt: $HitsPcnt, MissPcnt: $MissPcnt" 81 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] CurrentHits: $CurrentHits, CurrentMiss: $CurrentMiss" 82 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] HitsRate: $HitsRate, RequestsRate: $RequestsRate, MissRate: $MissRate" 83 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalHits: $TotalHits, TotalMisses: $TotalMisses" 84 | 85 | [PSCustomObject]@{ 86 | Series = "CADCcache" 87 | PSTypeName = 'EUCMonitoring.CADCcache' 88 | ADC = $ADC 89 | RecentHitPcnt = $RecentHitPcnt 90 | RecentMissPcnt = $RecentMissPcnt 91 | CurrentHits = $CurrentHits 92 | CurrentMiss = $CurrentMiss 93 | HitsPcnt = $HitsPcnt 94 | MissPcnt = $MissPcnt 95 | HitsRate = $HitsRate 96 | RequestRate = $RequestsRate 97 | MissRate = $MissRate 98 | TotalHits = $TotalHits 99 | TotalMisses = $TotalMisses 100 | } 101 | } 102 | } 103 | catch { 104 | if ($ErrorLog) { 105 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 106 | } 107 | else { 108 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 109 | } 110 | throw $_ 111 | } 112 | } 113 | 114 | End { 115 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 116 | Disconnect-CitrixADC -ADCSession $ADCSession 117 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 118 | } 119 | } 120 | 121 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCcsvserver.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCcsvserver { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway content switching virtual servers from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway content switching virtual servers from NITRO by polling 8 | $ADC/nitro/v1/stats/csvserver and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCcsvserver -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | 28 | [CmdletBinding()] 29 | param ( 30 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 31 | [ValidateNotNullOrEmpty()] 32 | [Alias("NSIP")] 33 | [string]$ADC, 34 | 35 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 36 | [ValidateNotNullOrEmpty()] 37 | [pscredential]$Credential, 38 | 39 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 40 | [Alias("LogPath")] 41 | [string]$ErrorLog 42 | ) 43 | 44 | Begin { 45 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 46 | try { 47 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 48 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 49 | } 50 | catch { 51 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 52 | throw $_ 53 | } 54 | } 55 | 56 | Process { 57 | try { 58 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "csvserver" 59 | 60 | foreach ($csvserver in $Results) { 61 | $Name = $csvserver.name 62 | $State = $csvserver.state 63 | $Health = [int64]$csvserver.vslbhealth 64 | 65 | # Rates 66 | $HitsRate = [int64]$csvserver.hitsrate 67 | $RequestsRate = [int64]$csvserver.requestsrate 68 | $RequestBytesRate = [int64]$csvserver.requestbytesrate 69 | $ResponsesRate = [int64]$csvserver.responsesrate 70 | $ResponseBytesRate = [int64]$csvserver.responsebytesrate 71 | 72 | #Totals 73 | $TotalHits = [int64]$csvserver.tothits 74 | $TotalRequests = [int64]$csvserver.totalrequests 75 | $TotalResponses = [int64]$csvserver.totalresponses 76 | 77 | # Current Connections 78 | $EstablishedConnections = [int64]$csvserver.establishedconn 79 | $CurrentClientConnections = [int64]$csvserver.curclntconnections 80 | $CurrentServerConnections = [int64]$csvserver.cursrvrconnections 81 | 82 | if ($Health -eq 100) { $Status = 2 } 83 | elseif ($Health -gt 0) { 84 | $Status = 1 85 | if ($ErrorLog) { 86 | Write-EUCError -Message "[CADCcsvserver] $ADC - $Name`: DEGRADED" -Path $ErrorLog 87 | } 88 | else { 89 | Write-Verbose "[$(Get-Date)] [CADCcsvserver] $ADC - $Name`: - DEGRADED" 90 | } 91 | } 92 | else { 93 | $Status = 0 94 | if ($ErrorLog) { 95 | Write-EUCError -Message "[CADCcsvserver] $ADC - $Name`: DOWN" -Path $ErrorLog 96 | } 97 | else { 98 | Write-Verbose "[$(Get-Date)] [CADCcsvserver] $ADC - $Name`: DOWN" 99 | } 100 | } 101 | 102 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - Health: $Health, HitsRate: $HitsRate, RequestsRate: $RequestsRate, ResponsesRate: $ResponsesRate" 103 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - TotalHits: $TotalHits, TotalRequests: $TotalRequests, TotalResponses: $TotalResponses" 104 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - Current Client Connections: $CurrentClientConnections, Current Server Connections: $CurrentServerConnections" 105 | [PSCustomObject]@{ 106 | Series = "CADCcsvserver" 107 | PSTypeName = 'EUCMonitoring.CADCcsvserver' 108 | ADC = $ADC 109 | Name = $Name 110 | Status = $Status 111 | State = $State 112 | Health = $Health 113 | HitsRate = $HitsRate 114 | RequestsRate = $RequestsRate 115 | RequestByteRate = $RequestBytesRate 116 | ResponsesRate = $ResponsesRate 117 | ResponseByteRate = $ResponseBytesRate 118 | TotalHits = $TotalHits 119 | TotalRequests = $TotalRequests 120 | TotalResponses = $TotalResponses 121 | EstablishedConnections = $EstablishedConnections 122 | CurrentClientConnections = $CurrentClientConnections 123 | CurrentServerConnections = $CurrentServerConnections 124 | } 125 | } 126 | } 127 | catch { 128 | if ($ErrorLog) { 129 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 130 | } 131 | else { 132 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 133 | } 134 | throw $_ 135 | } 136 | } 137 | 138 | End { 139 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 140 | Disconnect-CitrixADC -ADCSession $ADCSession 141 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 142 | } 143 | } 144 | 145 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCgatewayuser.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCgatewayuser { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway users from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway users from NITRO by polling 8 | $ADC/nitro/v1/config/aaasession & $ADC/nitro/v1/config/vpnicaconnection and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCgatewayuser -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Config "vpnicaconnection" 58 | $VPNUsers = $Results.Count 59 | 60 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Config "aaasession" 61 | $ICAUsers = $Results.Count 62 | 63 | $TotalUsers = $VPNUsers + $ICAUsers 64 | 65 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $ADC - VPNUsers: $VPNUsers, ICAUsers: $ICAUsers, Total: $TotalUsers" 66 | [PSCustomObject]@{ 67 | Series = "CADCgatewayuser" 68 | PSTypeName = 'EUCMonitoring.CADCgatewayuser' 69 | ADC = $ADC 70 | VPNUsers = $VPNUsers 71 | ICAUsers = $ICAUsers 72 | TotalUsers = $TotalUsers 73 | } 74 | } 75 | catch { 76 | if ($ErrorLog) { 77 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 78 | } 79 | else { 80 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 81 | } 82 | throw $_ 83 | } 84 | } 85 | 86 | End { 87 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned 1 value(s)" 88 | Disconnect-CitrixADC -ADCSession $ADCSession 89 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCgslbvserver.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCgslbvserver { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway global server load balancing virtual servers from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway global server load balancing virtual servers from NITRO by polling 8 | $ADC/nitro/v1/stats/gslbvserver and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCgslbvserver -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | Begin { 43 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 44 | try { 45 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 46 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 47 | } 48 | catch { 49 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 50 | throw $_ 51 | } 52 | } 53 | 54 | Process { 55 | try { 56 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "gslbvserver" 57 | 58 | foreach ($gslbvserver in $Results) { 59 | $Name = $gslbvserver.name 60 | $State = $gslbvserver.state 61 | $Health = [int64]$gslbvserver.vslbhealth 62 | 63 | # Rates 64 | $HitsRate = [int64]$gslbvserver.hitsrate 65 | $RequestsRate = [int64]$gslbvserver.requestsrate 66 | $RequestBytesRate = [int64]$gslbvserver.requestbytesrate 67 | $ResponsesRate = [int64]$gslbvserver.responsesrate 68 | $ResponseBytesRate = [int64]$gslbvserver.responsebytesrate 69 | 70 | #Totals 71 | $TotalHits = [int64]$gslbvserver.tothits 72 | $TotalRequests = [int64]$gslbvserver.totalrequests 73 | $TotalResponses = [int64]$gslbvserver.totalresponses 74 | 75 | # Current Connections 76 | $EstablishedConnections = [int64]$gslbvserver.establishedconn 77 | $CurrentClientConnections = [int64]$gslbvserver.curclntconnections 78 | $CurrentServerConnections = [int64]$gslbvserver.cursrvrconnections 79 | 80 | if ($Health -eq 100) { $Status = 2 } 81 | elseif ($Health -gt 0) { 82 | $Status = 1 83 | if ($ErrorLog) { 84 | Write-EUCError -Message "[CADCgslbvserver] $ADC - $Name`: DEGRADED" -Path $ErrorLog 85 | } 86 | else { 87 | Write-Verbose "[$(Get-Date)] [CADCgslbvserver] $ADC - $Name`: DEGRADED" 88 | } 89 | } 90 | else { 91 | $Status = 0 92 | if ($ErrorLog) { 93 | Write-EUCError -Message "[CADCgslbvserver] $Name - DOWN" -Path $ErrorLog 94 | } 95 | else { 96 | Write-Verbose "[$(Get-Date)] [CADCgslbvserver] $Name - DOWN" 97 | } 98 | } 99 | 100 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - Health: $Health, HitsRate: $HitsRate, RequestsRate: $RequestsRate, ResponsesRate: $ResponsesRate" 101 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - TotalHits: $TotalHits, TotalRequests: $TotalRequests, TotalResponses: $TotalResponses" 102 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - Current Client Connections: $CurrentClientConnections, Current Server Connections: $CurrentServerConnections" 103 | [PSCustomObject]@{ 104 | Series = "CADCgslbvserver" 105 | PSTypeName = 'EUCMonitoring.CADCgslbvserver' 106 | ADC = $ADC 107 | Name = $Name 108 | Status = $Status 109 | State = $State 110 | Health = $Health 111 | HitsRate = $HitsRate 112 | RequestsRate = $RequestsRate 113 | RequestByteRate = $RequestBytesRate 114 | ResponsesRate = $ResponsesRate 115 | ResponseByteRate = $ResponseBytesRate 116 | TotalHits = $TotalHits 117 | TotalRequests = $TotalRequests 118 | TotalResponses = $TotalResponses 119 | EstablishedConnections = $EstablishedConnections 120 | CurrentClientConnections = $CurrentClientConnections 121 | CurrentServerConnections = $CurrentServerConnections 122 | } 123 | } 124 | } 125 | catch { 126 | if ($ErrorLog) { 127 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 128 | } 129 | else { 130 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 131 | } 132 | throw $_ 133 | } 134 | } 135 | 136 | End { 137 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 138 | Disconnect-CitrixADC -ADCSession $ADCSession 139 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADChttp.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADChttp { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway http from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway http from NITRO by polling 8 | $ADC/nitro/v1/stats/protocolhttp and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADChttp -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "protocolhttp" 58 | 59 | foreach ($http in $Results) { 60 | # Requests 61 | $TotalRequests = [int64]$http.httptotrequests 62 | $RequestsRate = [int64]$http.httprequestsrate 63 | $TotalRequestsBytes = [int64]$https.httptotrxrequestbytes 64 | $RequestsBytesRate = [int64]$http.httprxrequestbytesrate 65 | 66 | # Responses 67 | $TotalResponses = [int64]$http.httptotresponses 68 | $ResponsesRate = [int64]$http.httpresponsesrate 69 | $TotalResponsesBytes = [int64]$http.httptotrxresponsebytes 70 | $ResponsesBytesRate = [int64]$http.httprxresponsebytesrate 71 | 72 | # Gets / Posts / Other 73 | $TotalGets = [int64]$http.httptotgets 74 | $GetsRate = [int64]$http.httpgetsrate 75 | $TotalPosts = [int64]$http.httptotposts 76 | $PostsRate = [int64]$http.httppostsrate 77 | $TotalOthers = [int64]$http.httptotothers 78 | $OthersRate = [int64]$http.httpothersrate 79 | 80 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalRequests: $TotalRequests, RequestsRate: $RequestsRate" 81 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalRequestsBytes: $TotalRequestsBytes, RequestsBytesRate: $RequestsBytesRate" 82 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalResponses: $TotalResponses, ResponsesRate: $ResponsesRate" 83 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalResponsesBytes: $TotalResponsesBytes, ResponsesBytesRate: $ResponsesBytesRate" 84 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalGets: $TotalGets, GetsRate: $GetsRate" 85 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalPosts: $TotalPosts, PostsRate: $PostsRate" 86 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalOthers: $TotalOthers, OthersRate: $OthersRate" 87 | 88 | [PSCustomObject]@{ 89 | Series = "CADChttp" 90 | PSTypeName = 'EUCMonitoring.CADChttp' 91 | ADC = $ADC 92 | TotalRequests = $TotalRequests 93 | RequestsRate = $RequestsRate 94 | TotalRequestsBytes = $TotalRequestsBytes 95 | RequestsBytesRate = $RequestsBytesRate 96 | TotalResponses = $TotalResponses 97 | ResponsesRate = $ResponsesRate 98 | TotalResponsesBytes = $TotalResponsesBytes 99 | ResponsesBytesRate = $ResponsesBytesRate 100 | TotalGets = $TotalGets 101 | GetsRate = $GetsRate 102 | TotalPosts = $TotalPosts 103 | PostsRate = $PostsRate 104 | TotalOthers = $TotalOthers 105 | OthersRate = $OthersRate 106 | } 107 | } 108 | } 109 | catch { 110 | if ($ErrorLog) { 111 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 112 | } 113 | else { 114 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 115 | } 116 | throw $_ 117 | } 118 | } 119 | 120 | End { 121 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 122 | Disconnect-CitrixADC -ADCSession $ADCSession 123 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 124 | } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCip.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCip { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway ip from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway ip from NITRO by polling 8 | $ADC/nitro/v1/stats/protocolip and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCip -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "protocolip" 58 | 59 | foreach ($ip in $Results) { 60 | # Rx 61 | $TotalRxPackets = [int64]$ip.iptotrxpkts 62 | $RxPacketsRate = [int64]$ip.iprxpktsrate 63 | $TotalRxBytes = [int64]$ip.iptotrxbytes 64 | $RxBytesRate = [int64]$ip.iprxbytesrate 65 | $TotalRxMbits = [int64]$ip.iptotrxmbits 66 | $RxMbitsRate = [int64]$ip.iprxmbitsrate 67 | 68 | # Tx 69 | $TotalTxPackets = [int64]$ip.iptottxpkts 70 | $TxPacketsRate = [int64]$ip.iptxpktsrate 71 | $TotalTxBytes = [int64]$ip.iptottxbytes 72 | $TxBytesRate = [int64]$ip.iptxbytesrate 73 | $TotalTxMbits = [int64]$ip.iptottxmbits 74 | $TxMbitsRate = [int64]$ip.iptxmbitsrate 75 | 76 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalRxPackets: $TotalRxPackets, RxPacketsRate: $RxPacketsRate" 77 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalRxBytes: $TotalRxBytes, RxBytesRate: $RxBytesRate" 78 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalRxMbits: $TotalRxMbits, RxMbitsRate: $RxMbitsRate" 79 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalTxPackets: $TotalTxPackets, TxPacketsRate: $TxPacketsRate" 80 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalTxBytes: $TotalTxBytes, TxBytesRate: $TxBytesRate" 81 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalTxMits: $TotalTxMbits, TxMbitsRate: $TxMbitsRate" 82 | 83 | [PSCustomObject]@{ 84 | Series = "CADCip" 85 | PSTypeName = 'EUCMonitoring.CADCip' 86 | ADC = $ADC 87 | TotalRxPackets = $TotalRxPackets 88 | RxPacketsRate = $RxPacketsRate 89 | TotalRxBytes = $TotalRxBytes 90 | RxBytesRate = $RxBytesRate 91 | TotalRxMbits = $TotalRxMbits 92 | RxMbitsRate = $RxMbitsRate 93 | TotalTxPackets = $TotalTxPackets 94 | TxPacketsRate = $TxPacketsRate 95 | TotalTxBytes = $TotalTxBytes 96 | TxBytesRate = $TxBytesRate 97 | TotalTxMits = $TotalTxMbits 98 | TxMbitsRate = $TxMbitsRate 99 | } 100 | } 101 | } 102 | catch { 103 | if ($ErrorLog) { 104 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 105 | } 106 | else { 107 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 108 | } 109 | throw $_ 110 | } 111 | } 112 | 113 | End { 114 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 115 | Disconnect-CitrixADC -ADCSession $ADCSession 116 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADClbvserver.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADClbvserver { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway load balancing virtual servers from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway load balancing virtual servers from NITRO by polling 8 | $ADC/nitro/v1/stats/lbvserver and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADClbvserver -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "lbvserver" 58 | 59 | foreach ($lbvserver in $Results) { 60 | $Name = $lbvserver.name 61 | $State = $lbvserver.state 62 | $Health = [int64]$lbvserver.vslbhealth 63 | 64 | # Rates 65 | $HitsRate = [int64]$lbvserver.hitsrate 66 | $RequestsRate = [int64]$lbvserver.requestsrate 67 | $RequestBytesRate = [int64]$lbvserver.requestbytesrate 68 | $ResponsesRate = [int64]$lbvserver.responsesrate 69 | $ResponseBytesRate = [int64]$lbvserver.responsebytesrate 70 | 71 | #Totals 72 | $TotalHits = [int64]$lbvserver.tothits 73 | $TotalRequests = [int64]$lbvserver.totalrequests 74 | $TotalResponses = [int64]$lbvserver.totalresponses 75 | 76 | # Current Connections 77 | $EstablishedConnections = [int64]$lbvserver.establishedconn 78 | $CurrentClientConnections = [int64]$lbvserver.curclntconnections 79 | $CurrentServerConnections = [int64]$lbvserver.cursrvrconnections 80 | 81 | if ($Health -eq 100) { $Status = 2 } 82 | elseif ($Health -gt 0) { 83 | $Status = 1 84 | if ($ErrorLog) { 85 | Write-EUCError -Message "[CADClbvserver] $ADC - $Name`: - DEGRADED" -Path $ErrorLog 86 | } 87 | else { 88 | Write-Verbose "[$(Get-Date)] [CADClbvserver] $ADC - $Name`: - DEGRADED" 89 | } 90 | } 91 | else { 92 | $Status = 0 93 | if ($ErrorLog) { 94 | Write-EUCError -Message "[CADClbvserver] $ADC - $Name`: DOWN" -Path $ErrorLog 95 | } 96 | else { 97 | Write-Verbose "[$(Get-Date)] [CADClbvserver] $ADC - $Name`: DOWN" 98 | } 99 | } 100 | 101 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - Health: $Health, HitsRate: $HitsRate, RequestsRate: $RequestsRate, ResponsesRate: $ResponsesRate" 102 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - TotalHits: $TotalHits, TotalRequests: $TotalRequests, TotalResponses: $TotalResponses" 103 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Name - Current Client Connections: $CurrentClientConnections, Current Server Connections: $CurrentServerConnections" 104 | [PSCustomObject]@{ 105 | Series = "CADClbvserver" 106 | PSTypeName = 'EUCMonitoring.CADClbvserver' 107 | ADC = $ADC 108 | Name = $Name 109 | Status = $Status 110 | State = $State 111 | Health = $Health 112 | HitsRate = $HitsRate 113 | RequestsRate = $RequestsRate 114 | RequestByteRate = $RequestBytesRate 115 | ResponsesRate = $ResponsesRate 116 | ResponseByteRate = $ResponseBytesRate 117 | TotalHits = $TotalHits 118 | TotalRequests = $TotalRequests 119 | TotalResponses = $TotalResponses 120 | EstablishedConnections = $EstablishedConnections 121 | CurrentClientConnections = $CurrentClientConnections 122 | CurrentServerConnections = $CurrentServerConnections 123 | } 124 | } 125 | } 126 | catch { 127 | if ($ErrorLog) { 128 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 129 | } 130 | else { 131 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 132 | } 133 | throw $_ 134 | } 135 | } 136 | 137 | End { 138 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 139 | Disconnect-CitrixADC -ADCSession $ADCSession 140 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 141 | } 142 | } 143 | 144 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCssl.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCssl { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway ssl from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway ssl from NITRO by polling 8 | $ADC/nitro/v1/stats/ssl and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCssl -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "ssl" 58 | 59 | foreach ($ssl in $Results) { 60 | $TotalSessions = [int64]$ssl.ssltotsessions 61 | $SessionsRate = [int64]$ssl.sslsessionsrate 62 | $TotalTransactions = [int64]$ssl.ssltottransactions 63 | $TransactionsRate = [int64]$ssl.ssltransactionsrate 64 | 65 | $TotalNewSessions = [int64]$ssl.ssltotnewsessions 66 | $NewSessionsRate = [int64]$ssl.sslnewsessionsrate 67 | $TotalSessionMiss = [int64]$ssl.ssltotsessionmiss 68 | $SessionsMissRate = [int64]$ssl.sslsessionmissrate 69 | $TotalSessionHits = [int64]$ssl.ssltotsessionhits 70 | $SessionsHitsRate = [int64]$ssl.sslsessionhitsrate 71 | 72 | $TotalBackendSessions = [int64]$ssl.sslbetotsessions 73 | $BackendSessionsRate = [int64]$ssl.sslbesessionsrate 74 | 75 | if ($ssl.sslenginestatus -eq 1) { 76 | $EngineStatus = "UP" 77 | } 78 | else { 79 | if ($ErrorLog) { 80 | Write-EUCError -Message "[$($myinvocation.mycommand)] $ADC - SSLEngineStatus: DOWN" -Path $ErrorLog 81 | } 82 | else { 83 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $ADC - SSLEngineStatus: DOWN" 84 | } 85 | $EngineStatus = "DOWN" 86 | } 87 | 88 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalSessions: $TotalSessions, SessionRate: $SessionsRate" 89 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalTransactions: $TotalTransactions, TransactionRate: $TransactionsRate" 90 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalNewSessions: $TotalNewSessions, NewSessionRate: $NewSessionsRate" 91 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalSessionMiss: $TotalSessionMiss, SessionsMissRate: $SessionsMissRate" 92 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalSessionHits: $TotalSessionHits, SessionsHitsRate: $SessionsHitsRate" 93 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalBackendSessions: $TotalBackendSessions, BackendSessionsRate: $BackendSessionsRate" 94 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] EngineStatus: $EngineStatus" 95 | 96 | [PSCustomObject]@{ 97 | Series = "CADCssl" 98 | PSTypeName = 'EUCMonitoring.CADCssl' 99 | ADC = $ADC 100 | TotalSessions = $TotalSessions 101 | SessionsRate = $SessionsRate 102 | TotalTransactions = $TotalTransactions 103 | TransactionsRate = $TransactionsRate 104 | TotalNewSessions = $TotalNewSessions 105 | NewSessionsRate = $NewSessionsRate 106 | TotalSessionMiss = $TotalSessionMiss 107 | SessionsMissRate = $SessionsMissRate 108 | TotalSessionHits = $TotalSessionHits 109 | SessionsHitsRate = $SessionsHitsRate 110 | TotalBackendSessions = $TotalBackendSessions 111 | BackendSessionsRate = $BackendSessionsRate 112 | EngineStatus = $EngineStatus 113 | } 114 | } 115 | } 116 | catch { 117 | if ($ErrorLog) { 118 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 119 | } 120 | else { 121 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 122 | } 123 | throw $_ 124 | } 125 | } 126 | 127 | End { 128 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 129 | Disconnect-CitrixADC -ADCSession $ADCSession 130 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 131 | } 132 | } 133 | 134 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCsslcertkey.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCsslcertkey { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway ssl certificates from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway ssl certificates from NITRO by polling 8 | $ADC/nitro/v1/config/sslcertkey and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCsslcertkey -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Config "sslcertkey" 58 | 59 | foreach ($sslcertkey in $Results) { 60 | $CertKey = $sslcertkey.CertKey 61 | # $Subject = (($sslcertkey.Subject -split 'CN=')[1]).split('/')[0] # ! There's something off here for Go-Daddy Root cert. 62 | 63 | $Subject = ($sslcertkey.Subject -split 'CN=')[1] 64 | if ($null -eq $Subject) { 65 | # -and ("ROOT_CERT" -in $sslcertkey.CertificateType)) { 66 | $Subject = ($sslcertkey.Subject -split 'OU=')[1] 67 | } 68 | 69 | $Issuer = ($sslcertkey.Issuer -split 'CN=')[1] 70 | if ($null -eq $Issuer) { 71 | # -and ("ROOT_CERT" -in $sslcertkey.CertificateType)) { 72 | $Issuer = ($sslcertkey.Issuer -split 'OU=')[1] 73 | } 74 | 75 | $Status = $sslcertkey.Status 76 | $DaysToExpiration = $sslcertkey.DaysToExpiration 77 | if ($DaysToExpiration -lt 7) { 78 | if ($ErrorLog) { 79 | Write-EUCError -Message "[$($myinvocation.mycommand)] $ADC - $Subject - DaysToExpiration: $DaysToExpiration" -Path $ErrorLog 80 | } 81 | else { 82 | Write-Verbose "[$(Get-Date)] [$($myinvocation.mycommand)] $ADC - $Subject - DaysToExpiration: $DaysToExpiration" 83 | } 84 | } 85 | 86 | $IsClientCert = "CLNT_CERT" -in $sslcertkey.CertificateType 87 | $IsServerCert = "SRVR_CERT" -in $sslcertkey.CertificateType 88 | 89 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Certkey - Status: $Status, DaysToExpiration: $DaysToExpiration" 90 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Client: $IsClientCert, Server: $IsServerCert" 91 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Subject: $Subject" 92 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Issuer: $Issuer" 93 | 94 | [PSCustomObject]@{ 95 | Series = "CADCsslcertkey" 96 | PSTypeName = 'EUCMonitoring.CADCsslcertkey' 97 | ADC = $ADC 98 | CertKey = $CertKey 99 | Subject = $Subject.trim() 100 | Issuer = $Issuer.trim() 101 | Status = $Status 102 | DaysToExpiration = $DaysToExpiration 103 | IsClientCert = $IsClientCert 104 | IsServerCert = $IsServerCert 105 | } 106 | } 107 | } 108 | catch { 109 | if ($ErrorLog) { 110 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 111 | } 112 | else { 113 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 114 | } 115 | throw $_ 116 | } 117 | } 118 | 119 | End { 120 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 121 | Disconnect-CitrixADC -ADCSession $ADCSession 122 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 123 | } 124 | } 125 | 126 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCsystem.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCsystem { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway system from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway system from NITRO by polling 8 | $ADC/nitro/v1/stats/system and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCsystem -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "system" 58 | 59 | foreach ($System in $Results) { 60 | # Requests 61 | $NumCpus = $System.numcpus 62 | $MgmtCpuUsagePcnt = $System.Mgmtcpuusagepcnt 63 | $CpuUsagePcnt = $System.cpuusagepcnt 64 | $MemUsagePcnt = $System.memusagepcnt 65 | $PktCpuUsagePcnt = $System.pktcpuusagepcnt 66 | $ResCpuUsagePcnt = $System.rescpuusagepcnt 67 | 68 | # $Config = Get-CADCNitroValue -ADCSession $ADCSession -Config "nsconfig" 69 | # $DaysChanged = $Config.lastconfigchangedtime 70 | # $DaysSaved = $Config.lastconfigsavedtime 71 | 72 | $Errors = @() 73 | if ($CpuUsagePcnt -gt 90) { 74 | $Errors += "HighCPU" 75 | } 76 | if ($MemUsagePct -gt 90) { 77 | $Errors += "HighMEM" 78 | } 79 | if ($Errors.Count -ge 1) { 80 | if ($ErrorLog) { 81 | Write-EUCError -Message "[$($myinvocation.mycommand)] $ADC - System: $($Errors -join ' ')" -Path $ErrorLog 82 | } 83 | else { 84 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $ADC - System: $($Errors -join ' ')" 85 | } 86 | } 87 | 88 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $ADC - NumCpus: $NumCpus Cpu: $CpuUsagePcnt Mgmt: $MgmtCpuUsagePcnt " 89 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $ADC - Pkt: $PktCpuUsagePcnt Mem: $MemUsagePcnt Res: $ResCpuUsagePcnt" 90 | 91 | 92 | 93 | [PSCustomObject]@{ 94 | Series = "CADCsystem" 95 | PSTypeName = 'EUCMonitoring.CADCsystem' 96 | ADC = $ADC 97 | MgmtCpuUsagePcnt = $MgmtCpuUsagePcnt 98 | CpuUsagePcnt = $CpuUsagePcnt 99 | MemUsagePcnt = $MemUsagePcnt 100 | PktCpuUsagePcnt = $PktCpuUsagePcnt 101 | ResCpuUsagePcnt = $ResCpuUsagePcnt 102 | NumCpus = $NumCpus 103 | # DaysSinceConfigLastChanged = $DaysChanged 104 | # DaysSinceConfigLastSaved = $DaysSaved 105 | } 106 | } 107 | } 108 | catch { 109 | if ($ErrorLog) { 110 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 111 | } 112 | else { 113 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 114 | } 115 | throw $_ 116 | } 117 | } 118 | 119 | End { 120 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 121 | Disconnect-CitrixADC -ADCSession $ADCSession 122 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 123 | } 124 | } 125 | 126 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCtcp.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCtcp { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway tcp from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway tcp from NITRO by polling 8 | $ADC/nitro/v1/stats/protocoltcp and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCtcp -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "protocoltcp" 58 | 59 | foreach ($tcp in $Results) { 60 | # Rx 61 | $TotalRxPackets = [int64]$tcp.tcptotrxpkts 62 | $RxPacketsRate = [int64]$tcp.tcprxpktsrate 63 | $TotalRxBytes = [int64]$tcp.tcptotrxbytes 64 | $RxBytesRate = [int64]$tcp.tcprxbytesrate 65 | 66 | # Tx 67 | $TotalTxPackets = [int64]$tcp.tcptottxpkts 68 | $TxPacketsRate = [int64]$tcp.tcptxpktsrate 69 | $TotalTxBytes = [int64]$tcp.tcptottxbytes 70 | $TxBytesRate = [int64]$tcp.tcptxbytesrate 71 | 72 | $ActiveServerConnections = [int64]$tcp.activeserverconn 73 | $CurClientConnEstablished = [int64]$tcp.tcpcurclientconnestablished 74 | $CurServerConnEstablished = [int64]$tcp.tcpcurserverconnestablished 75 | $CurrentClientConnections = [int64]$tcp.tcpcurclientconn 76 | $CurrentServerConnections = [int64]$tcp.tcpcurserverconn 77 | 78 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalRxPackets: $TotalRxPackets, RxPacketsRate: $RxPacketsRate" 79 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalRxBytes: $TotalRxBytes, RxBytesRate: $RxBytesRate" 80 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalTxPackets: $TotalTxPackets, TxPacketsRate: $TxPacketsRate" 81 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] TotalTxBytes: $TotalTxBytes, TxBytesRate: $TxBytesRate" 82 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] ActiveServerConnections: $ActiveServerConnections" 83 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] CurClientConnEstablished: $CurClientConnEstablished, CurServerConnEstablished: $CurServerConnEstablished" 84 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] CurClientConnections: $CurrentClientConnections, CurServerConnections: $CurrentServerConnections" 85 | 86 | [PSCustomObject]@{ 87 | Series = "CADCtcp" 88 | PSTypeName = 'EUCMonitoring.CADCtcp' 89 | ADC = $ADC 90 | TotalRxPackets = $TotalRxPackets 91 | RxPacketsRate = $RxPacketsRate 92 | TotalRxBytes = $TotalRxBytes 93 | RxBytesRate = $RxBytesRate 94 | TotalTxPackets = $TotalTxPackets 95 | TxPacketsRate = $TxPacketsRate 96 | TotalTxBytes = $TotalTxBytes 97 | TxBytesRate = $TxBytesRate 98 | ActiveServerConnections = $ActiveServerConnections 99 | CurClientConnEstablished = $CurClientConnEstablished 100 | CurServerConnEstablished = $CurServerConnEstablished 101 | CurClientConnections = $CurrentClientConnections 102 | CurServerConnections = $CurrentServerConnections 103 | } 104 | } 105 | } 106 | catch { 107 | if ($ErrorLog) { 108 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 109 | } 110 | else { 111 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 112 | } 113 | throw $_ 114 | } 115 | } 116 | 117 | End { 118 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 119 | Disconnect-CitrixADC -ADCSession $ADCSession 120 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CADCvaluetemplate.ps1: -------------------------------------------------------------------------------- 1 | function Get-CADCvaluetemplate { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on Citrix ADC gateway valuetemplate from NITRO 5 | 6 | .DESCRIPTION 7 | Gets basic stats on Citrix ADC gateway valuetemplate from NITRO by polling 8 | $ADC/nitro/v1/stats/valuetemplate and returning useful values. 9 | 10 | .PARAMETER ADC 11 | Alias: NSIP 12 | IP or DNS name of Citrix ADC Gateway 13 | 14 | .PARAMETER Credential 15 | ADC Credentials 16 | 17 | .PARAMETER ErrorLog 18 | Alias: LogPath 19 | Path to a file where any errors can be appended to 20 | 21 | .EXAMPLE 22 | Get-CADCvaluetemplate -ADC 10.1.2.3 -Credential (Get-Credential) -ErrorLog "C:\Monitoring\ADC-Errors.txt" 23 | 24 | .NOTES 25 | 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 30 | [ValidateNotNullOrEmpty()] 31 | [Alias("NSIP")] 32 | [string]$ADC, 33 | 34 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 35 | [ValidateNotNullOrEmpty()] 36 | [pscredential]$Credential, 37 | 38 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 39 | [Alias("LogPath")] 40 | [string]$ErrorLog 41 | ) 42 | 43 | Begin { 44 | # Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Starting session to $ADC" 45 | try { 46 | $ADCSession = Connect-CitrixADC -ADC $ADC -Credential $Credential 47 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC established" 48 | } 49 | catch { 50 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Connection to $ADC failed" 51 | throw $_ 52 | } 53 | } 54 | 55 | Process { 56 | try { 57 | # ! Changeme "valuetemplate" 58 | $Results = Get-CADCNitroValue -ADCSession $ADCSession -Stat "valuetemplate" 59 | 60 | foreach ($valuetemplate in $Results) { 61 | # Cast the big values coming over as strings to [int64] 62 | # $TotalRxPackets = [int64]$ip.iptotrxpkts 63 | # Its fine to keep normal integers as regular values. 64 | # $RxPacketsRate = $ip.iprxpktsrate 65 | 66 | # If an error condition can be found, such as degraded states, do your logging here. 67 | <# 68 | if ($Health -eq 100) { $Status = 2 } 69 | elseif ($Health -gt 0) { 70 | $Status = 1 71 | if ($ErrorLog) { 72 | Write-EUCError -Message "[CADCcsvserver] $ADC - $Name`: DEGRADED" -Path $ErrorLog 73 | } 74 | else { 75 | Write-Verbose "[$(Get-Date)] [CADCcsvserver] $ADC - $Name`: DEGRADED" 76 | } 77 | } 78 | else { 79 | $Status = 0 80 | if ($ErrorLog) { 81 | Write-EUCError -Message "[CitrixADCcsvserver] $ADC - $Name`: DOWN" -Path $ErrorLog 82 | } 83 | else { 84 | Write-Verbose "[$(Get-Date)] [CitrixADCcsvserver] $ADC - $Name`: DOWN" 85 | } 86 | } 87 | #> 88 | 89 | # Verbose values for testings 90 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $ADC - valuetemplate" 91 | 92 | [PSCustomObject]@{ 93 | Series = "CADCvaluetemplate" # ! Changeme 94 | PSTypeName = 'EUCMonitoring.CADCvaluetemplate' # ! Changeme 95 | ADC = $ADC 96 | # Additional values here... 97 | 98 | } 99 | } 100 | } 101 | catch { 102 | if ($ErrorLog) { 103 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 104 | } 105 | else { 106 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 107 | } 108 | throw $_ 109 | } 110 | } 111 | 112 | End { 113 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 114 | Disconnect-CitrixADC -ADCSession $ADCSession 115 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Disconnected" 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-CVADlicense.ps1: -------------------------------------------------------------------------------- 1 | Function Get-CVADlicense { 2 | <# 3 | .SYNOPSIS 4 | Returns some simple stats on a License Server 5 | 6 | .DESCRIPTION 7 | Returns some simple stats on a License Server 8 | 9 | .PARAMETER ComputerName 10 | Target License server to grab information from. 11 | 12 | .PARAMETER LicenseType 13 | Citrix License Type, commonly XDT / MPS 14 | 15 | #> 16 | [CmdletBinding()] 17 | Param( 18 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 19 | [ValidateNotNullOrEmpty()] 20 | [string[]]$ComputerName, 21 | 22 | [parameter(ValueFromPipeline = $true)] 23 | [string[]]$LicenseType, 24 | 25 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 26 | [Alias("LogPath")] 27 | [string]$ErrorLog 28 | ) 29 | 30 | Begin { 31 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Loading Citrix.Licensing Powershell Snapins" 32 | $ctxsnap = Get-PSSnapin -Registered Citrix.Licensing.* -ErrorAction SilentlyContinue | Add-PSSnapin -PassThru 33 | 34 | if ($null -eq $ctxsnap) { 35 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Citrix.Licensing Powershell Snapins Load Failed" 36 | Throw "Unable to load Citrix.Licensing Powershell Snapins" 37 | } 38 | else { 39 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Citrix.Licensing Powershell Snapins Loaded" 40 | } 41 | } 42 | 43 | Process { 44 | $Results = @() 45 | 46 | foreach ($Computer in $ComputerName) { 47 | try { 48 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Getting license certificate from $Computer" 49 | 50 | $Cert = Get-LicCertificate -AdminAddress $Computer -ErrorAction STOP 51 | 52 | if (($null -eq $LicenseType) -or ("" -eq $LicenseType)) { 53 | $LicenseType = "MPS", "XDT" # "MDT", "XDS" are deprecated. 54 | } 55 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Getting all available Citrix licenses from $Computer" 56 | $LicResults = Get-LicInventory -AdminAddress $Computer -CertHash $cert.CertHash 57 | 58 | foreach ($Type in $LicenseType) { 59 | 60 | $TotalAvailable = 0 61 | $TotalIssued = 0 62 | $TotalLicenses = 0 63 | 64 | #Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Getting license type $Type " 65 | foreach ($License in $LicResults) { 66 | if ($License.LicenseProductName -eq $Type) { 67 | $TotalAvailable += ($License.LicensesAvailable - $License.LicenseOverdraft) - $License.LicensesInUse 68 | $TotalIssued += $License.LicensesInUse 69 | $TotalLicenses += ($License.LicensesAvailable - $License.LicenseOverdraft) 70 | } 71 | } 72 | 73 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Type: $Type, Available: $TotalAvailable, Issued: $TotalIssued, Total: $TotalLicenses" 74 | 75 | $Results += [PSCustomObject]@{ 76 | PSTypeName = 'EUCMonitoring.CVADlicense' 77 | Series = "CVADLicense" 78 | Server = $Computer 79 | Type = $Type 80 | AvailableLicenses = $TotalAvailable 81 | IssuedLicenses = $TotalIssued 82 | TotalLicenses = $TotalLicenses 83 | } 84 | } 85 | 86 | } 87 | catch [System.InvalidOperationException] { 88 | if ($ErrorLog) { 89 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message) - Ensure Citrix Licensing WMI service started" -Path $ErrorLog 90 | } 91 | else { 92 | Write-Verbose "[$(Get-Date)] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message) - Ensure Citrix Licensing WMI service started" 93 | } 94 | throw $_ 95 | } 96 | catch { 97 | if ($ErrorLog) { 98 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 99 | } 100 | else { 101 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 102 | } 103 | throw $_ 104 | } 105 | 106 | } 107 | 108 | if ($Results.Count -gt 0) { 109 | return $Results 110 | } 111 | } 112 | 113 | End { 114 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 115 | } 116 | } 117 | 118 | # Test-XdLicense -ComputerName "xen-license" -Verbose -------------------------------------------------------------------------------- /PSGallery/Public/Get-CVADworkerhealth.ps1: -------------------------------------------------------------------------------- 1 | Function Get-CVADworkerhealth { 2 | <# 3 | .SYNOPSIS 4 | Gets basic stats on worker health of Citrix Virtual Apps and Desktops 5 | 6 | .DESCRIPTION 7 | Get basic stats on Citrix Virtual Apps and Desktops, including Boot Threshold, Load Threshold, Disk Space 8 | and Disk Queue thresholds, connectivity errors, etc. 9 | 10 | .PARAMETER Broker 11 | Alias: CloudConnector, DeliveryController, AdminAddress 12 | These are the Citrix VAD Brokers you want to test against. These are the delivery controllers, or cloud 13 | connectors, typically invoked in the Citrix PSSnapin as AdminAddress. 14 | 15 | .PARAMETER SiteName 16 | Alias: Site 17 | Allows you to specify the name of the Sites you want queried. If not specified, will query 18 | against all Sites for the broker. 19 | 20 | .PARAMETER ZoneName 21 | Alias: Zone 22 | Allows you to specify the name of the Zones you want queried. If not specified, will query 23 | against all Zones for the broker 24 | 25 | .PARAMETER CatalogName 26 | Alias: Catalog 27 | Allows you to specify the name of the Machine Catalogs you want queried. If not specified, will query 28 | against all catalogs for the broker 29 | 30 | .PARAMETER DeliveryGroupName 31 | Alias: DeliveryGroup, DesktopGroupName 32 | Also known as DeliveryGroup, this specifies the group of machines 33 | 34 | .PARAMETER SingleSession 35 | Alias: Desktop 36 | Single session delivery groups, usually desktops 37 | 38 | .PARAMETER MultiSession 39 | Alias: Server 40 | Return values for multi session delivery groups, usually applications 41 | 42 | .PARAMETER ActiveOnly 43 | Only test machines that are not in maintenance mode. 44 | 45 | .PARAMETER AllSessionTypes 46 | Return values for all session types. 47 | 48 | .PARAMETER BootThreshold 49 | Number of days since machine boot. If the boot time of a machine is greater than this 50 | value, it will be considered to have failed this health check 51 | 52 | .PARAMETER LoadThreshold 53 | Machine Load threshold, from 0 - 10000. If the CVAD LoadIndex of a machines is greater than this value, 54 | it will be considered to have failed this health check. 55 | 56 | .PARAMETER DiskSpaceThreshold 57 | Percentage used of system drive. If the used percentage for a system drive of a machine is greater than 58 | this value, it will be considered to have failed this health check 59 | 60 | .PARAMETER DiskQueueThreshold 61 | Percentage used of system drive. If the used percentage for a system drive of a machine is greater than 62 | this value, it will be considered to have failed this health check 63 | 64 | .PARAMETER ErrorLog 65 | Parameter description 66 | 67 | .EXAMPLE 68 | Get-CVADworkerhealth -A 69 | 70 | .NOTES 71 | Need to have the permissions to run Get-BrokerSite, Get-BrokerMachine, Get-BrokerDesktopGroup against 72 | Delivery Controllers you intend to query 73 | Need to have the permissions to run Get-CimInstance against all endpoints you intend to query. 74 | #> 75 | 76 | [cmdletbinding()] 77 | Param( 78 | [parameter(Mandatory = $true, ValueFromPipeline = $true)] 79 | [ValidateNotNullOrEmpty()] 80 | [Alias("CloudConnector", "DeliveryController", "AdminAddress")] 81 | [string[]]$Broker, 82 | 83 | [Alias("Site")] 84 | [string[]]$SiteName, 85 | 86 | [Alias("Zone")] 87 | [string[]]$ZoneName, 88 | 89 | [Alias("DesktopGroupName", "DeliveryGroup")] 90 | [string[]]$DeliveryGroupName, 91 | 92 | [Alias("Catalog")] 93 | [string[]]$CatalogName, 94 | 95 | [Alias("Desktop")] 96 | [switch]$SingleSession, 97 | 98 | [Alias("Server")] 99 | [switch]$MultiSession, 100 | 101 | [switch]$ActiveOnly, 102 | 103 | [switch]$AllSessionTypes, 104 | 105 | # Tests 106 | # [switch]$ConnectivityTests, 107 | 108 | [int]$BootThreshold, 109 | 110 | [int]$LoadThreshold, 111 | 112 | [int]$DiskSpaceThreshold, 113 | 114 | [int]$DiskQueueThreshold, 115 | 116 | # [switch]$AllSessionTypes, # AllSessionsTypes, AllTests 117 | 118 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 119 | [Alias("LogPath")] 120 | [string]$ErrorLog 121 | ) 122 | 123 | Begin { 124 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Loading Citrix Powershell Snapins" 125 | $Snapins = "Citrix.Broker.Admin.V2", "Citrix.Configuration.Admin.V2" 126 | foreach ($Snapin in $Snapins) { 127 | if ($null -eq (Get-PSSnapin | Where-Object { $_.Name -eq $Snapin })) { 128 | Add-PSSnapin $Snapin -ErrorAction Stop 129 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] $Snapin loaded successfully" 130 | } 131 | else { 132 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] $Snapin already loaded" 133 | } 134 | } 135 | } 136 | 137 | Process { 138 | $ResultCount = 0 139 | 140 | try { 141 | $PrevSuccess = $false 142 | foreach ($AdminAddress in $Broker) { 143 | # Skip if previously successful 144 | if ($PrevSuccess) { 145 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Previous collection success, skipping $AdminAddress" 146 | continue 147 | } 148 | # Skip if broker not pingable 149 | if (-Not (Test-Connection -ComputerName $AdminAddress -Count 1 -Quiet -ErrorAction SilentlyContinue)) { 150 | if ($ErrorLog) { 151 | Write-EUCError -Path $ErrorLog "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Connection Failure to Broker: $AdminAddress" 152 | } 153 | else { 154 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Connection Failure to Broker: $AdminAddress" 155 | } 156 | continue 157 | } 158 | 159 | # Fetch the basic values if not previously specified. 160 | if ($null -eq $SiteName) { $SiteName = (Get-BrokerSite -AdminAddress $AdminAddress).Name } 161 | if ($null -eq $ZoneName) { $ZoneName = (Get-ConfigZone -AdminAddress $AdminAddress).Name } 162 | 163 | # We want to default to everything unless otherwise specified 164 | if ($null -eq $DeliveryGroupName) { 165 | $SessionSupport = @() 166 | # Default behavior is to both. 167 | if ((-Not $SingleSession) -And (-Not $MultiSession)) { 168 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] No specified session support. Assuming all." 169 | $AllSessionTypes = $true 170 | } 171 | if ($SingleSession -or $AllSessionTypes) { 172 | $SessionSupport += "SingleSession" 173 | } 174 | if ($MultiSession -or $AllSessionTypes) { 175 | $SessionSupport += "MultiSession" 176 | } 177 | $DeliveryGroupName += (Get-BrokerDesktopGroup -AdminAddress $AdminAddress -SessionSupport $SessionSupport).Name 178 | } 179 | 180 | # Realistically, this will generally just iterate once. 181 | foreach ($Site in $SiteName) { 182 | # This too... 183 | foreach ($Zone in $ZoneName) { 184 | 185 | if ($null -eq $CatalogName) { 186 | $CatalogName = (Get-BrokerCatalog -AdminAddress $AdminAddress -ZoneName $Zone).Name 187 | } 188 | 189 | foreach ($CatName in $CatalogName) { 190 | foreach ($DesktopGroup in $DeliveryGroupName) { 191 | $DG = Get-BrokerDesktopGroup -AdminAddress $AdminAddress -Name $DesktopGroup 192 | $Params = @{ 193 | AdminAddress = $AdminAddress; 194 | ZoneName = $Zone; 195 | CatalogName = $CatName; 196 | DesktopGroupName = $DesktopGroup; 197 | MaxRecordCount = 99999 198 | } 199 | if ($ActiveOnly) { 200 | $Params.InMaintenanceMode = $false; 201 | } 202 | 203 | $Machines = Get-BrokerMachine @Params 204 | 205 | # We don't care about empty Delivery Groups 206 | if ($null -ne $Machines) { 207 | #$MachineNames = $Machines.DNSName 208 | $Params = @{ 209 | Broker = $AdminAddress; 210 | SiteName = $Site; 211 | ZoneName = $Zone; 212 | CatalogName = $CatName; 213 | DesktopGroupName = $DesktopGroup; 214 | SessionSupport = $DG.SessionSupport; 215 | MachineDNSNames = $Machines.DNSName 216 | } 217 | 218 | # Add conditional parameters 219 | if ($ErrorLog) { 220 | $Params.ErrorLog = $ErrorLog 221 | } 222 | 223 | if ($BootThreshold) { $Params.BootThreshold = $BootThreshold } 224 | if ($LoadThreshold) { $Params.LoadThreshold = $LoadThreshold } 225 | if ($DiskSpaceThreshold) { $Params.DiskSpaceThreshold = $DiskSpaceThreshold } 226 | if ($DiskQueueThreshold) { $Params.DiskQueueThreshold = $DiskQueueThreshold } 227 | 228 | Test-CVADworkerhealth @Params 229 | 230 | $ResultCount++ 231 | } 232 | } 233 | } 234 | } 235 | } 236 | 237 | $PrevSuccess = $true 238 | } 239 | if (-Not $PrevSuccess) { 240 | throw "Connection Failure to Broker(s): $($Broker -join ' ')" 241 | } 242 | } 243 | catch { 244 | if ($ErrorLog) { 245 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 246 | } 247 | else { 248 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 249 | } 250 | throw $_ 251 | } 252 | } 253 | 254 | End { 255 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $ResultCount value(s)" 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-InfluxTimestamp.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Returns current timestamp in nanosecond format 4 | 5 | .DESCRIPTION 6 | Long description 7 | 8 | .PARAMETER DateTime 9 | Optional parameter if you want to specify a datetime 10 | 11 | .EXAMPLE 12 | Get-InfluxTimeStamp 13 | 14 | .EXAMPLE 15 | Get-InfluxTimeStamp -DateTime (Get-Date) 16 | 17 | .NOTES 18 | General notes 19 | #> 20 | 21 | function Get-InfluxTimestamp { 22 | [CmdletBinding()] 23 | Param ( 24 | [datetime]$DateTime 25 | ) 26 | 27 | Begin { 28 | 29 | } # Begin 30 | 31 | Process { 32 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Converting to Universal Time" 33 | 34 | if ($null -eq $DateTime) { 35 | $DateTime = Get-Date 36 | } 37 | 38 | $utcDate = $DateTime.ToUniversalTime() 39 | # Convert to a Unix time as a double, noticed that it gets all the seconds down in the decimal if cast as a double. 40 | $unixTime = [double]((Get-Date -Date $utcDate -UFormat %s)) 41 | # multiply seconds to move the decimal place. 42 | $nano = $unixTime * 1000000000 43 | # casting as an int64 gets rid of the decimal and scientific notation. 44 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Returning $([int64]$nano)" 45 | return [int64]$nano 46 | } # Process 47 | 48 | End { 49 | # Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)]" 50 | } # End 51 | } 52 | 53 | # Get-InfluxTimestamp -------------------------------------------------------------------------------- /PSGallery/Public/Get-RDSlicense.ps1: -------------------------------------------------------------------------------- 1 | function Get-RDSLicense { 2 | <# 3 | .SYNOPSIS 4 | Returns RDS Licensing info 5 | 6 | .DESCRIPTION 7 | Returns 8 | 9 | .PARAMETER ComputerName 10 | Gets the TSLicenseKeyPack on the specified computers. 11 | 12 | Type the NetBIOS name, an IP Address, or a fully qualified domain name (FQDN) of a remote computer. 13 | 14 | .PARAMETER LicenseType 15 | The 'TypeAndModel' of the license pack. If specified, will return only the licenses of that TypeAndModel. 16 | If unspecified, includes all but "Built-in TS Per Device Cal" 17 | 18 | .PARAMETER IgnoreBuiltIn 19 | 20 | 21 | .OUTPUTS 22 | System.Management.Automation.PSCustomObject 23 | 24 | .EXAMPLE 25 | Get-RDSLicenseStat -ComputerName "rdslic1", "rdslic2" 26 | 27 | .EXAMPLE 28 | Get-RDSLicenseStat -ComputerName "rdslic1.domain.org" -LicenseType "RDS Per User CAL" 29 | 30 | #> 31 | 32 | [cmdletbinding()] 33 | Param( 34 | [Parameter(ValueFromPipeline)] 35 | [ValidateNotNullOrEmpty()] 36 | [string[]]$ComputerName, 37 | 38 | [Parameter(ValueFromPipeline, Mandatory = $false)] 39 | [string[]]$LicenseType = "", 40 | 41 | [Parameter(ValueFromPipeline)] 42 | [switch]$IgnoreBuiltIn, 43 | 44 | [Parameter(ValueFromPipeline)] 45 | [switch]$ErrorObj, 46 | 47 | [parameter(Mandatory = $false, ValueFromPipeline = $true)] 48 | [Alias("LogPath")] 49 | [string]$ErrorLog 50 | ) 51 | 52 | Begin { 53 | Write-Verbose "[$(Get-Date) BEGIN ] [$($myinvocation.mycommand)] Setting ErrorActionPreference" 54 | $PrevError = $ErrorActionPreference 55 | $ErrorActionPreference = "STOP" 56 | } #BEGIN 57 | 58 | Process { 59 | $Results = @() 60 | 61 | foreach ($Computer in $ComputerName) { 62 | try { 63 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Getting all available RDS licenses from $Computer" 64 | 65 | # Check to see if we need to revert to Dcom for communication 66 | [regex]$rx = "\d\.\d$" 67 | $data = Test-WSMan $Computer -ErrorAction STOP 68 | if ($rx.match($data.ProductVersion).value -eq '3.0') { 69 | $Session = New-CimSession -ComputerName $ComputerName 70 | } 71 | else { 72 | # We're older and need to revert to dcom 73 | $opt = New-CimSessionOption -Protocol Dcom 74 | $Session = New-CimSession -ComputerName $Computer -SessionOption $opt 75 | } 76 | 77 | if (($null -eq $LicenseType) -or ("" -eq $LicenseType)) { 78 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Querying available licenses" 79 | 80 | if ($IgnoreBuiltIn) { 81 | $LicenseType = $Session | Get-CimInstance -ClassName Win32_TSLicenseKeyPack -ErrorAction STOP | ` 82 | Where-Object TypeAndModel -NotLike "Built-in TS Per Device Cal" | ` 83 | Select-Object -ExpandProperty TypeAndModel -Unique -ErrorAction Stop 84 | } 85 | else { 86 | $LicenseType = $Session | Get-CimInstance -ClassName Win32_TSLicenseKeyPack -ErrorAction STOP | ` 87 | Select-Object -ExpandProperty TypeAndModel -Unique -ErrorAction Stop 88 | } 89 | } 90 | 91 | foreach ($Type in $LicenseType) { 92 | $TotalAvailable = 0 93 | $TotalIssued = 0 94 | $TotalLicenses = 0 95 | 96 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] Getting license type $Type" 97 | 98 | $LicResults = $Session | Get-CimInstance -ClassName Win32_TSLicenseKeyPack -ErrorAction Stop | ` 99 | Where-Object TypeAndModel -eq $Type | ` 100 | Select-Object TypeAndModel, IssuedLicenses, AvailableLicenses, TotalLicenses -ErrorAction Stop 101 | 102 | foreach ($License in $LicResults) { 103 | $TotalIssued += $License.IssuedLicenses 104 | $TotalAvailable += $License.AvailableLicenses 105 | $TotalLicenses += $License.TotalLicenses 106 | } 107 | 108 | if ($TotalIssued -gt $TotalLicenses) { 109 | if ($ErrorLog) { 110 | Write-EUCError -Message "[$($myinvocation.mycommand)] $Computer - License Overcommit of Type: $Type" -Path $ErrorLog 111 | } 112 | else { 113 | Write-Verbose "[$(Get-Date)] [$($myinvocation.mycommand)] $Computer - License Overcommit of Type: $Type" 114 | } 115 | } 116 | 117 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] $Type, Available: $TotalAvailable, Issued: $TotalIssued, Total: $TotalLicenses" 118 | 119 | $Results += [PSCustomObject]@{ 120 | PSTypeName = 'EUCMonitoring.RDSlicense' 121 | Series = "RDSlicense" 122 | Server = $Computer 123 | Type = $Type 124 | AvailableLicenses = $TotalAvailable 125 | IssuedLicenses = $TotalIssued 126 | TotalLicenses = $TotalLicenses 127 | } 128 | } 129 | } 130 | catch { 131 | $ErrorActionPreference = $PrevError 132 | if ($ErrorLog) { 133 | Write-EUCError -Message "[$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" -Path $ErrorLog 134 | } 135 | else { 136 | Write-Verbose "[$(Get-Date) PROCESS] [$($myinvocation.mycommand)] [$($_.Exception.GetType().FullName)] $($_.Exception.Message)" 137 | } 138 | throw $_ 139 | } 140 | } 141 | 142 | if ($Results.Count -gt 0) { 143 | return $Results 144 | } 145 | } #PROCESS 146 | 147 | End { 148 | Write-Verbose "[$(Get-Date) END ] [$($myinvocation.mycommand)] Returned $($Results.Count) value(s)" 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /PSGallery/Public/Get-RDSworkerhealth.ps1: -------------------------------------------------------------------------------- 1 | function Get-RDSworkerhealth { 2 | throw "Not yet implemented" 3 | } -------------------------------------------------------------------------------- /PSGallery/Public/Get-RDSworkload.ps1: -------------------------------------------------------------------------------- 1 | function Get-RDSworkload { 2 | throw "Not yet implemented" 3 | } -------------------------------------------------------------------------------- /PSGallery/Public/Send-EUCResultToInfluxDB.ps1: -------------------------------------------------------------------------------- 1 | function Send-EUCResultToInfluxDB { 2 | [CmdletBinding()] 3 | Param( 4 | [parameter()]$Protocol = "http", 5 | [parameter()]$InfluxDBServer = "localhost", 6 | [parameter()]$Port = 8086, 7 | [parameter()]$DB = "EUCMonitoring", 8 | [parameter(Mandatory = $true, ValueFromPipeline = $true)]$InfluxLineData 9 | ) 10 | 11 | # We want all results to represent the same moment in time, even if that's not true for 12 | # collation reasons. This is why this step happens at the end. 13 | # Credit to Ryan Revord for providing workable examples here. 14 | # $timestamp = Get-InfluxTimestamp 15 | $InfluxURI = "$Protocol`://$Server`:$Port/write?db=$DB" 16 | try { 17 | Write-Verbose "[$(Get-Date) PROCESS] Pushing results to $InfluxURI" 18 | Invoke-RestMethod -Method "POST" -Uri $InfluxUri -Body $InfluxLineData 19 | } 20 | catch { 21 | Write-Verbose "[$(Get-Date) PROCESS] Failed" 22 | Write-EUCError 23 | throw $_ 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /PSGallery/Public/Start-EUCMonitor.ps1: -------------------------------------------------------------------------------- 1 | function Start-EUCMonitor { 2 | [cmdletbinding(SupportsShouldProcess)] 3 | Param () 4 | if ($PSCmdlet.ShouldProcess("Starting EUCMonitor")) { 5 | throw "Not yet implemented" 6 | } 7 | } -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/2vqac71nlbma0vx2?svg=true)](https://ci.appveyor.com/project/littletoyrobots/eucmonitoringredux) 2 | 3 | # EUCMonitoringRedux 4 | 5 | ## Project Description 6 | 7 | This is a continuation of the [EUCMonitoring Platform](http://bretty.me.uk/free-citrix-xendesktop-7-monitoring-platform/) that is based on Powershell and FREE! It will check all the key components of your End User Computer estate and give you a visual dashboard as to its current health. It is currently focused on Citrix but will eventually be branched out to cover VMware and Microsoft Technologies. 8 | 9 | This continuation is organized in such a way that you could take advantage of the [Telegraf](https://www.influxdata.com/time-series-platform/telegraf/) agent for collecting and reporting metrics. In its simplest form, the telegraf agent works like a scheduled task running every 5 minutes, invoking a powershell script that outputs to [Influx Line Protocol](https://docs.influxdata.com/influxdb/v1.7/write_protocols/line_protocol_tutorial/). Telegraf takes that output and redirects it to any of its supported time series databases. I like [InfluxDB (v1)](https://www.influxdata.com/products/influxdb-overview/). We then configure Grafana to point to InfluxDB and visualize the results. It will create a local log file of any errors it finds, and will eventually make the static HTML file as well. 10 | 11 | ## Motivation 12 | 13 | [Dave Bretty](https://bretty.me.uk) initially created this in order to provide a birds eye view of what's happening in the environment. Along with others in the community, I wanted to extend the functionality he initially created, especially in a more dynamic dashboard. I also want to be able to use building a monitoring platform as a way of teaching new users about various EUC platforms, by way of knowing what to look for. This also allows me a way to provide a read-only view into my environment to various individuals, create a web interface that can be used to drive office kiosk dashboards, and create custom dashboards for different groups. 14 | 15 | Hopefully, even outside of the dashboards, you'll find some useful scripts in here as well. 16 | 17 | ## Sample Dashboards 18 | 19 | Citrix Virtual Apps and Desktops Overview Dashboard 20 | ![CVAD-Overview](../assets/CVAD-Overview-Preview.jpg) 21 | 22 | Citrix ADC Overview Dashboard 23 | ![CADC-Overview](../assets/CADC-Overview-Preview.png) 24 | 25 | ## Installation 26 | 27 | For install suggestions, check out [Installation.md](https://github.com/littletoyrobots/EUCMonitoringRedux/blob/master/Installation.md). 28 | 29 | ## Share 30 | 31 | Have a great script or dashboard suggestion you'd like to see implemented? Check out [Contributing](https://github.com/littletoyrobots/EUCMonitoringRedux/blob/master/.github/CONTRIBUTING.md) or stop by the #-eucmonitoring channel on [World of EUC on Slack](https://communityinviter.com/apps/worldofeuc/world-of-euc-project) and share it with us! 32 | 33 | ## Contributors 34 | 35 | Adam Yarborough [@littletoyrobots](https://twitter.com/littletoyrobots) | Dave Brett [@dbretty](https://twitter.com/dbretty) | James Kindon [@james_kindon](https://twitter.com/james_kindon) | Ryan Butler [@ryan_c_butler](https://twitter.com/Ryan_C_Butler) | David Wilkinson [@WilkyIT](https://twitter.com/WilkyIT) | Hal Lange [@hal_lange](https://twitter.com/hal_lange) | Ryan Revord [@rsrevord](https://twitter.com/rsrevord) | Alex Spicola [@alexspicola](https://twitter.com/AlexSpicola) 36 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Todo List 2 | 3 | 1. Look into [Invoke-CommandAs](https://github.com/mkellerman/invoke-commandas) into invoke certain parts as 4 | a different user (allowing Telegraf to run as system) 5 | 1. Breakout dashboard folders for target visualization 6 | - Grafana Dashboards 7 | - Azure Monitoring Dashboards 8 | - Chronograf Dashboards 9 | 1. Breakout common dashboard scripts / examples scripts to separate folder 10 | 1. Add parameters to support passing a session to the get-CADC commands. 11 | 1. Refactor Get-CADCNitroValue to take credentials and IP as well as ADCSession. 12 | 1. Static HTML page -> ConvertTo-EUCResultHTML.ps1 13 | 1. Read config and tests from JSON file 14 | 1. Get Start-EUCMonitor.ps1 working for legacy support 15 | 1. Make in-place upgrade script, preferably named Upgrade-VisualizationSetup.ps1 16 | 1. Make in-place dashboard upgrade script that can target either local or remote grafana instances (with proper credentials) 17 | -------------------------------------------------------------------------------- /Tests/ConvertTo-InfluxLineProtocol.tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | . "$env:APPVEYOR_BUILD_FOLDER\PSGallery\Public\ConvertTo-InfluxLineProtocol.ps1" 4 | 5 | Describe "how to test converting to Influx Line Protocol" { 6 | It "Should return `$null for empty InputObject" { 7 | $null | ConvertTo-InfluxLineProtocol | Should -Be $null 8 | } 9 | 10 | It "Should return a known value for a known object" { 11 | [PSCustomObject]@{ 12 | Series = "SeriesName" 13 | Tag = "TagName" 14 | Value = 1234 15 | } | ConvertTo-InfluxLineProtocol | Should -Be "SeriesName,Tag=TagName Value=1234" 16 | } 17 | 18 | It "Should return a known value for a known object with a supplied timestamp" { 19 | [PSCustomObject]@{ 20 | Series = "SeriesName" 21 | Tag = "TagName" 22 | Value = 1234 23 | } | ConvertTo-InfluxLineProtocol -Timestamp 5678 | Should -Be "SeriesName,Tag=TagName Value=1234 5678" 24 | } 25 | 26 | It "Should properly escape commas" { 27 | [PSCustomObject]@{ 28 | Series = "SeriesName" 29 | Tag = "Tag,Name" 30 | Value = 1234 31 | } | ConvertTo-InfluxLineProtocol | Should -Be "SeriesName,Tag=Tag\,Name Value=1234" 32 | } 33 | 34 | It "Should properly escape `=" { 35 | [PSCustomObject]@{ 36 | Series = "SeriesName" 37 | Tag = "Tag=Name" 38 | Value = 1234 39 | } | ConvertTo-InfluxLineProtocol | Should -Be "SeriesName,Tag=Tag\=Name Value=1234" 40 | } 41 | 42 | It "Should properly escape spaces" { 43 | [PSCustomObject]@{ 44 | Series = "SeriesName" 45 | Tag = "Tag Name" 46 | Value = 1234 47 | } | ConvertTo-InfluxLineProtocol | Should -Be "SeriesName,Tag=Tag\ Name Value=1234" 48 | } 49 | 50 | It "Should ignore empty string properties" { 51 | [PSCustomObject]@{ 52 | Series = "SeriesName" 53 | Tag = "TagName" 54 | Tag1 = "" 55 | Value = 1234 56 | } | ConvertTo-InfluxLineProtocol | Should -Be "SeriesName,Tag=TagName Value=1234" 57 | } 58 | 59 | It "Should ignore `$null valued properties" { 60 | [PSCustomObject]@{ 61 | Series = "SeriesName" 62 | Tag = "TagName" 63 | Var1 = $null 64 | Value = 1234 65 | } | ConvertTo-InfluxLineProtocol | Should -Be "SeriesName,Tag=TagName Value=1234" 66 | } 67 | 68 | It "Should throw error if no Series specified" { 69 | { [PSCustomObject]@{ 70 | Tag = "TagName" 71 | Value = 1234 72 | } | ConvertTo-InfluxLineProtocol } | Should -Throw "[ConvertTo-InfluxLineProtocol] Series Blank" 73 | } 74 | 75 | It "Should return known value if Series specified" { 76 | [PSCustomObject]@{ 77 | Tag = "TagName" 78 | Value = 1234 79 | } | ConvertTo-InfluxLineProtocol -Series "SeriesName" | Should -Be "SeriesName,Tag=TagName Value=1234" 80 | } 81 | 82 | It "Should override Series if specified by parameter" { 83 | [PSCustomObject]@{ 84 | Series = "SeriesName" 85 | Tag = "TagName" 86 | Value = 1234 87 | } | ConvertTo-InfluxLineProtocol -Series "NewSeriesName" | Should -Be "NewSeriesName,Tag=TagName Value=1234" 88 | } 89 | } -------------------------------------------------------------------------------- /Tests/EUCMonitoringRedux.tests.ps1: -------------------------------------------------------------------------------- 1 | # AppVeyor Testing 2 | $manifest = "$env:APPVEYOR_BUILD_FOLDER\PSGallery\EUCMonitoringRedux.psd1" 3 | $module = "$env:APPVEYOR_BUILD_FOLDER\PSGallery\EUCMonitoringRedux.psm1" 4 | 5 | # Local Testing 6 | # $manifest = "Path\EUCMonitoring\PSGallery\EUCMonitoring.psd1" 7 | # $module = "Path\EUCMonitoring\PSGallery\EUCMonitoring.psm1" 8 | 9 | Describe 'Module Metadata Validation' { 10 | it 'Script fileinfo should be ok' { 11 | { Test-ModuleManifest $manifest -ErrorAction Stop } | Should Not Throw 12 | } 13 | 14 | it 'Import module should be ok' { 15 | { Import-Module $module -Force -ErrorAction Stop } | Should Not Throw 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Tests/Get-InfluxTimestamp.tests.ps1: -------------------------------------------------------------------------------- 1 | # By default, Influx assumes UNIX timestamp with nanosecond precision 2 | 3 | # Minimum valid timestamp is -9223372036854775806 4 | # Maxmimum value timestamp is 9223372036854775806 5 | $projectRoot = $env:APPVEYOR_BUILD_FOLDER 6 | 7 | . "$env:APPVEYOR_BUILD_FOLDER\PSGallery\Public\Get-InfluxTimestamp.ps1" 8 | 9 | Describe "how to test timestamp functions" { 10 | 11 | It "does not throw errors" { 12 | { (Get-InfluxTimestamp) } | Should -Not -Throw 13 | } 14 | 15 | It "returns an int64" { 16 | (Get-InfluxTimestamp) | Should -BeOfType [int64] 17 | } 18 | 19 | It "returns a value less than the max" { 20 | { Get-InfluxTimestamp -lt 9223372036854775806 } | Should -Be $true 21 | } 22 | 23 | It "returns a value greater than the lowest accepted value" { 24 | { Get-InfluxTimestamp -gt -9223372036854775806 } | Should -Be $true 25 | } 26 | 27 | It "returns a known value for a known datetime" { 28 | Get-InfluxTimestamp -DateTime ([datetime]"01/01/2020 00:00") | Should -Be 1577854800000000000 29 | } 30 | } -------------------------------------------------------------------------------- /Tests/Main.tests.ps1: -------------------------------------------------------------------------------- 1 | <# AppVeyor Testing 2 | 3 | #> 4 | $projectRoot = $env:APPVEYOR_BUILD_FOLDER 5 | 6 | # Local Testing 7 | # $projectRoot = "Path\Scripts" 8 | 9 | Describe "General project validation" { 10 | 11 | $scripts = Get-ChildItem "$projectRoot\PSGallery" -Recurse -Include *.ps1, *.psm1 12 | 13 | # TestCases are splatted to the script so we need hashtables 14 | $testCase = $scripts | Foreach-Object { @{file = $_ } } 15 | It "Script should be valid powershell" -TestCases $testCase { 16 | param($file) 17 | 18 | $file.fullname | Should Exist 19 | 20 | $contents = Get-Content -Path $file.fullname -ErrorAction Stop 21 | $errors = $null 22 | $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors) 23 | $errors.Count | Should Be 0 24 | } 25 | 26 | $scriptAnalyzerRules = Get-ScriptAnalyzerRule 27 | It " should pass ScriptAnalyzer" -TestCases $testCase { 28 | param($file) 29 | $analysis = Invoke-ScriptAnalyzer -Path $file.fullname -ExcludeRule @('PSAvoidUsingConvertToSecureStringWithPlainText') -Severity @('Warning', 'Error') 30 | 31 | forEach ($rule in $scriptAnalyzerRules) { 32 | if ($analysis.RuleName -contains $rule) { 33 | $analysis | 34 | Where-Object RuleName -EQ $rule -outvariable failures | 35 | Out-Default 36 | $failures.Count | Should Be 0 37 | } 38 | 39 | } 40 | } 41 | 42 | } 43 | 44 | Describe "Function validation" { 45 | 46 | $scripts = Get-ChildItem "$projectRoot\PSGallery" -Recurse -Include *.ps1 47 | $testCase = $scripts | Foreach-Object { @{file = $_ } } 48 | It "Script should only contain one function" -TestCases $testCase { 49 | param($file) 50 | $file.fullname | Should Exist 51 | $contents = Get-Content -Path $file.fullname -ErrorAction Stop 52 | $describes = [Management.Automation.Language.Parser]::ParseInput($contents, [ref]$null, [ref]$null) 53 | $test = $describes.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) 54 | $test.Count | Should Be 1 55 | } 56 | 57 | It " should match function name" -TestCases $testCase { 58 | param($file) 59 | $file.fullname | Should Exist 60 | $contents = Get-Content -Path $file.fullname -ErrorAction Stop 61 | $describes = [Management.Automation.Language.Parser]::ParseInput($contents, [ref]$null, [ref]$null) 62 | $test = $describes.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) 63 | $test[0].name | Should Be $file.basename 64 | } 65 | 66 | # Templating for future tests. 67 | # It " should have help block" { 68 | # param($file) 69 | # $file.fullname | Should -FileContentMatch '<#' 70 | # $file.fullname | Should -FileContentMatch '#>' 71 | # } 72 | # It " should have a SYNOPSIS section in the help block" { 73 | # param($file) 74 | # $file.fullname | Should -FileContentMatch '.SYNOPSIS' 75 | # } 76 | # It " should have a DESCRIPTION section in the help block" { 77 | # param($file) 78 | # $file.fullname | Should -FileContentMatch '.DESCRIPTION' 79 | # } 80 | # It " should have a EXAMPLE section in the help block" { 81 | # param($file) 82 | # $file.fullname | Should -FileContentMatch '.EXAMPLE' 83 | # } 84 | # 85 | # It " should be an advanced function" { 86 | # param($file) 87 | # $file.fullname | Should -FileContentMatch 'function' 88 | # $file.fullname | Should -FileContentMatch 'cmdletbinding' 89 | # $file.fullname | Should -FileContentMatch 'param' 90 | # } 91 | # It " should contain Write-Verbose blocks" { 92 | # $file.fullname | Should -FileContentMatch 'Write-Verbose' 93 | # } 94 | 95 | 96 | } 97 | 98 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | os: WMF 5 3 | skip_commits: 4 | files: 5 | - README.md 6 | only_commits: 7 | files: 8 | - PSGallery/ 9 | - Config/ 10 | pull_requests: 11 | do_not_increment_build_number: true 12 | build_script: 13 | - ps: | 14 | Install-PackageProvider -Name NuGet -Force | Out-Null 15 | find-module -Repository PSGallery -Name PowerShellGet | Out-Null 16 | Install-Module -Name PowerShellGet -Force -Repository PSGallery | Out-Null 17 | find-module -Repository PSGallery -Name PSScriptAnalyzer | Out-Null 18 | Install-Module -Name PSScriptAnalyzer -Force -Repository PSGallery | Out-Null 19 | find-module -Repository PSGallery -Name Pester | Out-Null 20 | Install-Module -Name Pester -Force -Repository PSGallery | Out-Null 21 | find-module -Repository PSGallery -Name PSDeploy | Out-Null 22 | Install-Module -Name PSDeploy -Force -Repository PSGallery | Out-Null 23 | find-module -Repository PSGallery -Name posh-git | Out-Null 24 | Install-Module -Name posh-git -Force -Repository PSGallery | Out-Null 25 | $env:Path += ";$env:ProgramFiles\Git\cmd" 26 | test_script: 27 | - ps: | 28 | write-verbose "Running Pester..." 29 | $pest = Invoke-Pester -passthru -verbose 30 | if ($pest.FailedCount -gt 0) { 31 | throw "$($pest.FailedCount) tests failed." 32 | } 33 | --------------------------------------------------------------------------------