mirror of
https://github.com/Icinga/icinga-powershell-framework.git
synced 2025-12-20 23:00:35 -05:00
Merge pull request #389 from Icinga:feature/developer_tools
Feature: Adds developer tools for easier start and management Adds additional Cmdlets to get working on new Icinga for Windows components and publishing of modules including validation alot easier. `New-IcingaForWindowsComponent`: Creates an entire new module structure depending on input values, to get a base skeleton for further development ready `Publish-IcingaForWindowsComponent`: Properly adds documentation, possible plugin configuration and dependencies of files to the manifest file, ensuring the integrity of the module `Test-IcingaForWindowsComponent`: Allows to test your module and checks for code styling issues and errors as well as testing the module itself, if it can be imported, printing errors in case of problems `Open-IcingaForWindowsComponentInEditor`: Allows to open a certain Icinga for Windows component in an editor to get started a lot easier. Only supports `Visual Studio Code` at the moment
This commit is contained in:
commit
aa4750e92a
17 changed files with 1201 additions and 339 deletions
|
|
@ -24,6 +24,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
|
|||
### Enhancements
|
||||
|
||||
* [#383](https://github.com/Icinga/icinga-powershell-framework/pull/383) Moves the components REST-Api [icinga-powershell-restapi](https://icinga.com/docs/icinga-for-windows/latest/restapi/doc/01-Introduction/) and API-Checks [icinga-powershell-apichecks](https://icinga.com/docs/icinga-for-windows/latest/apichecks/doc/01-Introduction/) directly into the Framework
|
||||
* [#389](https://github.com/Icinga/icinga-powershell-framework/pull/389) Adds developer tools for easier start and management of development custom extensions for Icinga for Windows
|
||||
* [#392](https://github.com/Icinga/icinga-powershell-framework/pull/392) Adds support to read logs from Windows EventLog while using `Read-IcingaAgentLogFile`
|
||||
* [#393](https://github.com/Icinga/icinga-powershell-framework/pull/393) Adds generic reader function `Read-IcingaWindowsEventLog`, allowing to read any EventLog as stream on the console and adds in addition `Read-IcingaForWindowsLog` for reading Icinga for Windows specific logs
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ Once a daemon is registered, you can also unregister them from Icinga for Window
|
|||
|
||||
```powershell
|
||||
Register-IcingaBackgroundDaemon `
|
||||
-BackgroundDaemon 'Start-IcingaServiceCheckDaemon';
|
||||
-Command 'Start-IcingaServiceCheckDaemon';
|
||||
```
|
||||
|
||||
### Show Background Daemons
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ At first, we need to register this daemon:
|
|||
|
||||
```powershell
|
||||
Register-IcingaBackgroundDaemon `
|
||||
-BackgroundDaemon 'Start-IcingaServiceCheckDaemon';
|
||||
-Command 'Start-IcingaServiceCheckDaemon';
|
||||
```
|
||||
|
||||
## Manage Service Checks
|
||||
|
|
|
|||
96
doc/900-Developer-Guide/00-General.md
Normal file
96
doc/900-Developer-Guide/00-General.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Developer Guide - General Information
|
||||
|
||||
This guide will introduce you on how to write custom PowerShell modules (described as Icinga for Windows components) and how certain aspects of the architecture work.
|
||||
|
||||
## PowerShell Module Architecture
|
||||
|
||||
Each single PowerShell module has to be installed inside a module directory of Windows. You can view a list of current available locations by using `$Env:PSModulePath`. By default, we are going to use the directory `C:\Program Files\WindowsPowerShell\Modules`.
|
||||
|
||||
### Folder Structure
|
||||
|
||||
To create a new module, you can create a custom folder within the PowerShell module folder. This folder is the namespace of your module and is required for later creating the `RootModule` and `Manifest`.
|
||||
|
||||
Within your module folder, you are free to create as many sub-directories as you want and place script and module files there, which are shipped and used by your module.
|
||||
|
||||
### Manifest And RootModule
|
||||
|
||||
To provide all basic information, you will require to create at least a `Manifest` file, which has the file ending `.psd1`. The name of the file has to match the folder name you choose as namespace for your module in the previous section.
|
||||
|
||||
Our `RootModule` is using the file ending `.psm1` and can use the same name as your folder, but is not required to, as long as a valid `.psd1` file is present. Within our manifest, we can define the path on where the `.psm1` can be found.
|
||||
|
||||
### Nested Modules
|
||||
|
||||
While writing your own module, you will add additional code and possible different files to your project. By adding additional `.psm1` files for easier loading of functions, we can use the `NestedModules` attribute within our `.psd1` file, to add them to our known module list.
|
||||
|
||||
Please note that it is only required to use the relative path, starting with `.\` to use the root directory of your module as base.
|
||||
|
||||
Lets assume we have the following file structure:
|
||||
|
||||
```text
|
||||
module
|
||||
|_ plugin.psd1
|
||||
|_ plugin.psm1
|
||||
|_ provider
|
||||
|_ custom_provider.psm1
|
||||
|_ plugin
|
||||
|_ custom_plugin.psm1
|
||||
```
|
||||
|
||||
In this case, our `NestedModules` variable within our `.psd1` file requires the following values
|
||||
|
||||
```powershell
|
||||
NestedModules = @(
|
||||
'.\provider\custom_provider.psm1',
|
||||
'.\provider\custom_plugin.psm1'
|
||||
)
|
||||
```
|
||||
|
||||
## Using Icinga for Windows Dev Tools
|
||||
|
||||
Maintaining the entire structure above seems to be complicated at the beginning, especially when considering to update the `NestedModules` section whenever you make changes. To mitigate this, Icinga for Windows provides a bunch of Cmdlets to help with the process
|
||||
|
||||
### Create New Components
|
||||
|
||||
To create new components, you can use the command `New-IcingaForWindowsComponent`. It will create a new PowerShell module inside the same module directory, were you installed the Framework itself.
|
||||
|
||||
The command ships with a bunch of configurations to modify the created `.psd1` in addition, with a different author, copyright, and so on. the most important arguments how ever are `Name` and `ComponentType`.
|
||||
|
||||
| Argument | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| Name | String | The name of your Icinga for Windows component. This will create a new module in the following syntax: `icinga-powershell-{name}` |
|
||||
| ComponentType | String | The type of component you want to create for Icinga for Windows with different base-modules and code available to get started quickly. Available types: `plugins`, `apiendpoint`, `daemon`, `library` |
|
||||
| OpenInEditor | Switch | Will directly open the module after creation inside an editor for editing |
|
||||
|
||||
### Publish/Update Components
|
||||
|
||||
Once you have started to write your own code, you can use the Cmdlet `Publish-IcingaForWindowsComponent` to update the `NestedModules` attribute inside the `.psd1` file automatically, including the documentation in case the module is of type plugin.
|
||||
|
||||
In addition, you ca create a `.zip` file for this module which can be integrated directly into the [Repository Manager](..\120-Repository-Manager\01-Add-Repositories.md). By default, created `.zip` files will be created in your home folder, the path can how ever be changed while executing the command.
|
||||
|
||||
| Argument | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| Name | String | The name of your Icinga for Windows component to update information from |
|
||||
| ReleasePackagePath | String | The path on where the `.zip` file will be created in. Defaults to the current users home folder |
|
||||
| CreateReleasePackage | Switch | This will toggle the `.zip` file creation of the specified package |
|
||||
|
||||
### Testing Your Component
|
||||
|
||||
In order to validate if your module can be loaded and is working properly, you can use the command `Test-IcingaForWindowsComponent`. In addition to an import check, it will also validate the code styling and give you an overview if and how many issues there are with your code.
|
||||
|
||||
By default, only a summary of possible issues is added to the output, you can how ever use an argument flag to print a list of possible found issues, allowing you to resolve them more easily.
|
||||
|
||||
| Argument | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| Name | String | The name of your Icinga for Windows component to test |
|
||||
| ShowIssues | Switch | Prints a list of all possible found issues into the console |
|
||||
|
||||
### Open Components
|
||||
|
||||
A quick and easy way for opening components inside an editor is to use the command `Open-IcingaForWindowsComponentInEditor`. You simply require to specify the name of the component and the editor is opening.
|
||||
|
||||
At the moment, only [Visual Studio Code](https://code.visualstudio.com/) is supported. More editors will follow in the future.
|
||||
|
||||
| Argument | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| Name | String | The name of your Icinga for Windows component to open |
|
||||
| Editor | String | Allows to specify, which editor the component should be opened with. Supported values: `code` |
|
||||
|
|
@ -26,11 +26,12 @@ For performance metrics you can provide a `Unit` to ensure your graphing is disp
|
|||
| --- | --- | --- | --- |
|
||||
| Name | String | * | The unique name of each check within a plugin. Will be display in the check output. |
|
||||
| Value | Object | * | The value all comparison is done with. In general this should be a `Numeric` or `String` value |
|
||||
| BaseValue | Object | * | A value from which a dynamic percentage result is calculated from, by including the current value. Could for example be the maximum size of a partition |
|
||||
| Unit | Units | | Specify the unit for a value to display graph properly |
|
||||
| Minimum | String | | The minimum value which is displayed on your graphs |
|
||||
| Maximum | String | | The maximum value which is displayed on your graphs |
|
||||
| BaseValue | Object | | Sets a base value for the check which allows to use dynamic `%` usage on thresholds. The base value will calculate the `%` value from the current value, allowing generic `%` monitoring |
|
||||
| ObjectExists | Bool | | If you are using values coming from objects, like Services, you can use this argument to determin if the object itself exist or not. In case it doesn't, you will receive a proper output on the check result |
|
||||
| ObjectExists | Bool | | If you are using values coming from objects, like Services, you can use this argument to determine if the object itself exist or not. In case it doesn't, you will receive a proper output on the check result |
|
||||
| Translation | Hashtable | | In case you want to map values to certain descriptions, you can place a hashtable at this argument which will then map the value to the description on the check result. For example this would apply to service running states |
|
||||
| LabelName | String | | Allows to override the default label name generated based on the `-Name` argument to a custom name. Please ensure to remove any special characters manually, as the name set here is the exact name for the label |
|
||||
| NoPerfData | Switch | | Disables Performance Data output for this check object |
|
||||
|
|
@ -78,22 +79,22 @@ $IcingaCheck.WarnOutOfRange(10).CritOutOfRange(20) | Out-Null
|
|||
|
||||
### Functions
|
||||
|
||||
For most parts it is recommended to use the `OutOfRange` functions for `warning` and `critical` checks as the user is able to dynamicly set the range with the arguments of the plugins. For string values the `Like` and `Match` functions should be used.
|
||||
For most parts it is recommended to use the `OutOfRange` functions for `warning` and `critical` checks as the user is able to dynamically set the range with the arguments of the plugins. For string values the `Like` and `Match` functions should be used.
|
||||
|
||||
#### Recommended functions
|
||||
|
||||
| Function | Parameters | Description | Example |
|
||||
| --- | --- | --- | --- |
|
||||
| WarnOutOfRange | Warning | This will make use of the Icinga Threshhold handling, like `10`, `~:10`, `@10:20` and properly return the correct ok / warning state of the plugin | $IcingaCheck.WarnOutOfRange(10) | Out-Null |
|
||||
| CritOutOfRange | Critial | This will make use of the Icinga Threshhold handling, like `10`, `~:10`, `@10:20` and properly return the correct ok / critical state of the plugin | $IcingaCheck.CritOutOfRange(10) | Out-Null |
|
||||
| WarnOutOfRange | Warning | This will make use of the Icinga Threshold handling, like `10`, `~:10`, `@10:20` and properly return the correct ok / warning state of the plugin | $IcingaCheck.WarnOutOfRange(10) | Out-Null |
|
||||
| CritOutOfRange | Critical | This will make use of the Icinga Threshold handling, like `10`, `~:10`, `@10:20` and properly return the correct ok / critical state of the plugin | $IcingaCheck.CritOutOfRange(10) | Out-Null |
|
||||
| WarnIfLike | Warning | Will return warning in case the input is `like` the value | $IcingaCheck.WarnIfLike('\*running\*') |
|
||||
| WarnIfNotLike | Warning | Will return warning in case the input is `not like` the value | $IcingaCheck.WarnIfNotLike('\*running\*') |
|
||||
| WarnIfMatch | Warning | Will return warning in case the input is `matching` the value | $IcingaCheck.WarnIfMatch('running') |
|
||||
| WarnIfNotMatch | Warning | Will return warning in case the input is `not matching` the value | $IcingaCheck.WarnIfNotMatch('running') |
|
||||
| CritIfLike | Critial | Will return critical in case the input is `like` the value | $IcingaCheck.CritIfLike('\*running\*') |
|
||||
| CritIfNotLike | Critial | Will return critical in case the input is `not like` the value | $IcingaCheck.CritIfNotLike('\*running\*') |
|
||||
| CritIfMatch | Critial | Will return critical in case the input is `matching` the value | $IcingaCheck.CritIfMatch('running') |
|
||||
| CritIfNotMatch | Critial | Will return critical in case the input is `not matching` the value | $IcingaCheck.CritIfNotMatch('running') |
|
||||
| CritIfLike | Critical | Will return critical in case the input is `like` the value | $IcingaCheck.CritIfLike('\*running\*') |
|
||||
| CritIfNotLike | Critical | Will return critical in case the input is `not like` the value | $IcingaCheck.CritIfNotLike('\*running\*') |
|
||||
| CritIfMatch | Critical | Will return critical in case the input is `matching` the value | $IcingaCheck.CritIfMatch('running') |
|
||||
| CritIfNotMatch | Critical | Will return critical in case the input is `not matching` the value | $IcingaCheck.CritIfNotMatch('running') |
|
||||
|
||||
#### All other functions
|
||||
|
||||
|
|
@ -107,10 +108,10 @@ For most parts it is recommended to use the `OutOfRange` functions for `warning`
|
|||
| WarnIfGreaterEqualThan | Warning | Will return warning in case the input is `greater or equal` than the value | $IcingaCheck.WarnIfGreaterEqualThan(10) |
|
||||
| CritIfBetweenAndEqual | Min, Max | Will return critical in case the input is `between or equal` the `min` and `max` value | $IcingaCheck.CritIfBetweenAndEqual(10, 20) |
|
||||
| CritIfBetween | Min, Max | Will return critical in case the input is between the `min` and `max` value | $IcingaCheck.CritIfBetween(10, 20) |
|
||||
| CritIfLowerThan | Critial | Will return critical in case the input is `lower` than the value | $IcingaCheck.CritIfLowerThan(10) |
|
||||
| CritIfLowerEqualThan | Critial | Will return critical in case the input is `lower or equal` than the value | $IcingaCheck.CritIfLowerEqualThan(10) |
|
||||
| CritIfGreaterThan | Critial | Will return critical in case the input is `greater` than the value | $IcingaCheck.CritIfGreaterThan(10) |
|
||||
| CritIfGreaterEqualThan | Critial | Will return critical in case the input is `greater or equal` than the value | $IcingaCheck.CritIfGreaterEqualThan(10) |
|
||||
| CritIfLowerThan | Critical | Will return critical in case the input is `lower` than the value | $IcingaCheck.CritIfLowerThan(10) |
|
||||
| CritIfLowerEqualThan | Critical | Will return critical in case the input is `lower or equal` than the value | $IcingaCheck.CritIfLowerEqualThan(10) |
|
||||
| CritIfGreaterThan | Critical | Will return critical in case the input is `greater` than the value | $IcingaCheck.CritIfGreaterThan(10) |
|
||||
| CritIfGreaterEqualThan | Critical | Will return critical in case the input is `greater or equal` than the value | $IcingaCheck.CritIfGreaterEqualThan(10) |
|
||||
|
||||
### Examples
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
Developer Guide: Custom Daemons
|
||||
===
|
||||
# Developer Guide: Custom Daemons
|
||||
|
||||
By installing the PowerShell Framework as service you have the possibility to [register custom daemons](../110-Installation/05-Background-Daemons.md) which are executed in the background. This developer guide article will assist you in creating custom daemons.
|
||||
|
||||
Creating A New Module
|
||||
---
|
||||
## Creating A New Module
|
||||
|
||||
The best approach for creating a custom daemon is by creating an independent module which is installed in your PowerShell modules directly. This will ensure you are not overwriting your custom data with possible framework updates.
|
||||
|
||||
### Developer Tools
|
||||
|
||||
To get started easier, you can run this command to create the new module:
|
||||
|
||||
```powershell
|
||||
New-IcingaForWindowsComponent -Name 'agentservice' -ComponentType 'daemon';
|
||||
```
|
||||
|
||||
If you wish to create the module manually, please read on.
|
||||
|
||||
### Manual Creation
|
||||
|
||||
In this guide, we will assume the name of the module is `icinga-powershell-agentservice`.
|
||||
|
||||
At first we will have to create a new module. Navigate to the PowerShell modules folder the Framework itself is installed to. In this tutorial we will assume the location is
|
||||
|
|
@ -18,16 +28,15 @@ C:\Program Files\WindowsPowerShell\Modules
|
|||
|
||||
Now create a new folder with the name `icinga-powershell-agentservice` and navigate into it.
|
||||
|
||||
As we require a `psm1` file which contains our code, we will create a new file with the name `icinga-powershell-agentservice.psm1`. This will allow the PowerShell autoloader to load the module automaticly.
|
||||
As we require a `psm1` file which contains our code, we will create a new file with the name `icinga-powershell-agentservice.psm1`. This will allow the PowerShell autoloader to load the module automatically.
|
||||
|
||||
**Note:** It could be possible, depending on your [execution policies](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-6), that your module is not loaded properly. If this is the case, you can try to unblock the file by opening a PowerShell and use the `Unblock-File` Cmdelet
|
||||
**Note:** It could be possible, depending on your [execution policies](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-6), that your module is not loaded properly. If this is the case, you can try to unblock the file by opening a PowerShell and use the `Unblock-File` Cmdlet
|
||||
|
||||
```powershell
|
||||
Unblock-File -Path 'C:\Program Files\WindowsPowerShell\Modules\icinga-powershell-agentservice\icinga-powershell-agentservice.psm1'
|
||||
```
|
||||
|
||||
Testing The Module
|
||||
---
|
||||
## Testing The Module
|
||||
|
||||
Once the modules files are created and unblocked, we can start testing if the autoloader is properly working and our module is detected.
|
||||
|
||||
|
|
@ -50,8 +59,7 @@ Import-Module 'C:\Program Files\WindowsPowerShell\Modules\icinga-powershell-agen
|
|||
|
||||
inside your console prompt. After that try again to execute the command `Test-MyIcingaAgentServiceCommand` and check if it works this time. If not, you might check the naming of your module to ensure `folder name` and `.psm1 file name` is identical.
|
||||
|
||||
Create A New function
|
||||
---
|
||||
## Create A New function
|
||||
|
||||
Once everything is working properly we can create our starting function we will later use for [registering our daemon](../110-Installation/05-Background-Daemons.md).
|
||||
|
||||
|
|
@ -66,28 +74,23 @@ function Start-IcingaAgentServiceTest()
|
|||
}
|
||||
```
|
||||
|
||||
Basic Daemon Architecture
|
||||
---
|
||||
## Basic Daemon Architecture
|
||||
|
||||
A basic daemon contains of two parts. At first we require a `ScriptBlock` with our code to execute and second a `new thread` we can create with the Cmdlet `New-IcingaThreadInstance`.
|
||||
A basic daemon consists of two parts. At first we require a function our daemon loader will start, creating a new thread by using `New-IcingaThreadInstance`. In addition, we require another function which will then be executed as our thread call.
|
||||
|
||||
Each daemon must spawn within an own thread to ensure we are not blocking the execution of other deamons and interfere with the framework loader.
|
||||
Each daemon must spawn within an own thread to ensure we are not blocking the execution of other daemons and interfere with the framework loader.
|
||||
|
||||
Writing Our ScriptBlock
|
||||
---
|
||||
## Writing Our Thread Function
|
||||
|
||||
As we start a new thread, we will require at first a variable to contain our actual code we wish to execute inside the thread. This can be done by using `ScriptBlocks`.
|
||||
As we start a new thread, we will require at first to provide some basic details, like our `$IcingaGlobalData` variable.
|
||||
|
||||
At first we will create a variable inside our `Start-IcingaAgentServiceTest` function.
|
||||
At first we will create a new function which our thread is calling. As we intend to add some, we should use the `Add` convention.
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
function Add-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Everything which will be executed inside the thread
|
||||
# belongs here
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -95,48 +98,44 @@ Depending on our daemon, later usage and possible sharing of data between all lo
|
|||
|
||||
Our recommendation is to always do this for every daemon, as later changes might be more complicated and time consuming.
|
||||
|
||||
To be able to parse an argument to your script block, we will use the `param` argument.
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
function Add-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Allow us to parse the framework global data to this thread
|
||||
param($IcingaDaemonData);
|
||||
param (
|
||||
$IcingaDaemonData
|
||||
);
|
||||
|
||||
# Everything which will be executed inside the thread
|
||||
# belongs here
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Now as the basic part is finished, we will require to make our framework libraries aavailable within this thread. To do so, we will initialse the framework with the `Use-Icinga` Cmdlet, do however only import libraries and tell the framework that the wish to utilize it as `daemon`. The last part is important, as this will change the handling for writing console outputs and instead of an `exit` for certain failures the module will log them internally.
|
||||
Now as the basic part is finished, we will require to make our framework libraries available within this thread. To do so, we will initialise the framework with the `Use-Icinga` Cmdlet, do however only import libraries and tell the framework that the wish to utilize it as `daemon`. The last part is important, as this will change the handling for writing console outputs and instead of an `exit` for certain failures the module will log them internally.
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
function Add-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Allow us to parse the framework global data to this thread
|
||||
param($IcingaDaemonData);
|
||||
param (
|
||||
$IcingaDaemonData
|
||||
);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
Use-Icinga -LibOnly -Daemon;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
As we will parse the `global` framework data anyways, we should already make use of it. In this case, we will write the current service state of Icinga 2 into a global `synchronized` hashtable. Before we can do this, we will have to add a new hashtable to our background daemons
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
function Add-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Allow us to parse the framework global data to this thread
|
||||
param($IcingaDaemonData);
|
||||
param (
|
||||
$IcingaDaemonData
|
||||
);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
|
|
@ -156,7 +155,6 @@ function Start-IcingaAgentServiceTest()
|
|||
'ServiceState',
|
||||
[hashtable]::Synchronized(@{})
|
||||
);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -165,12 +163,12 @@ As now our base skeleton for daemon is ready we can start to write the actual pa
|
|||
Because the code is executed as separate thread, we will have to ensure it will run as long as the PowerShell service is being executed. This will be done with a simple `while` loop
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
function Add-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Allow us to parse the framework global data to this thread
|
||||
param($IcingaDaemonData);
|
||||
param (
|
||||
$IcingaDaemonData
|
||||
);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
|
|
@ -191,24 +189,23 @@ function Start-IcingaAgentServiceTest()
|
|||
[hashtable]::Synchronized(@{})
|
||||
);
|
||||
|
||||
# Keep our code excuted as long as the PowerShell service is
|
||||
# Keep our code executed as long as the PowerShell service is
|
||||
# being executed. This is required to ensure we will execute
|
||||
# the code frequently instead of only once
|
||||
while ($TRUE) {
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
*ALWAYS* ensure you add some sort for `sleep` at the end of the `while` loop to allow your CPU some breaks. If you do not do this, you might suffer from high CPU loads. The `sleep duration` interval can depend either on a simple CPU cycle break or by telling the daemon to execute tasks only in certain intervalls. In our case we wish to execute the daemon every `5 seconds`.
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
function Add-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Allow us to parse the framework global data to this thread
|
||||
param($IcingaDaemonData);
|
||||
param (
|
||||
$IcingaDaemonData
|
||||
);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
|
|
@ -229,7 +226,7 @@ function Start-IcingaAgentServiceTest()
|
|||
[hashtable]::Synchronized(@{})
|
||||
);
|
||||
|
||||
# Keep our code excuted as long as the PowerShell service is
|
||||
# Keep our code executed as long as the PowerShell service is
|
||||
# being executed. This is required to ensure we will execute
|
||||
# the code frequently instead of only once
|
||||
while ($TRUE) {
|
||||
|
|
@ -238,19 +235,18 @@ function Start-IcingaAgentServiceTest()
|
|||
# ensure daemon tasks are executed on a certain interval
|
||||
Start-Sleep -Seconds 5;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
This is basicly the foundation of every single daemon you will write. Now we will add the actual task our daemon will execute while it is running. As mentioned before, we will test if our Icinga 2 Agent service is running and restart it if it is stopped. To keep track of the current status and possible errors during restart, we will add additional `synchronized` hashtables to store the `value` of the current service status and possible `restart_error` counts. To count the `restart_error` we will have to initiliase a single variable we name `$RestartErrors` before we enter our `while` loop.
|
||||
This is basically the foundation of every single daemon you will write. Now we will add the actual task our daemon will execute while it is running. As mentioned before, we will test if our Icinga 2 Agent service is running and restart it if it is stopped. To keep track of the current status and possible errors during restart, we will add additional `synchronized` hashtables to store the `value` of the current service status and possible `restart_error` counts. To count the `restart_error` we will have to initialises a single variable we name `$RestartErrors` before we enter our `while` loop.
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
function Add-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Allow us to parse the framework global data to this thread
|
||||
param($IcingaDaemonData);
|
||||
param (
|
||||
$IcingaDaemonData
|
||||
);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
|
|
@ -274,7 +270,7 @@ function Start-IcingaAgentServiceTest()
|
|||
# Initialise our error counter variable
|
||||
[int]$RestartErrors = 0;
|
||||
|
||||
# Keep our code excuted as long as the PowerShell service is
|
||||
# Keep our code executed as long as the PowerShell service is
|
||||
# being executed. This is required to ensure we will execute
|
||||
# the code frequently instead of only once
|
||||
while ($TRUE) {
|
||||
|
|
@ -319,110 +315,34 @@ function Start-IcingaAgentServiceTest()
|
|||
# ensure daemon tasks are executed on a certain interval
|
||||
Start-Sleep -Seconds 5;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Calling Our ScriptBlock
|
||||
---
|
||||
## Calling Our Function
|
||||
|
||||
Once our SciptBlock is completed we only require to call it once our daemon is registered. Do to so, we will use the Cmdlet `New-IcingaThreadInstance`.
|
||||
Once our function is completed we only require to call it once our daemon is registered. Do to so, we will use the Cmdlet `New-IcingaThreadInstance`.
|
||||
|
||||
As arguments we will have to add a unique `name` to use for this thread as well as a `thread pool` we will add add the thread to. In our case we will use the Frameworks default pool. Last but not least we require to parse our `ScriptBlock`, possible `Arguments` and tell the thread to `Start` right after being created. For the arguments we will parse the frameworks `global` `IcingaDaemonData` we also use inside our ScriptBlock to store data in.
|
||||
As arguments we will have to add a unique `name` to use for this thread as well as a `thread pool`, on which the function will be added to. In our case we will use the Frameworks default pool. Last but not least we require to parse possible `Arguments` to our function and tell the thread to `Start` right after being created. For the arguments we will parse the frameworks `global` `IcingaDaemonData` we also use inside our function to store data in.
|
||||
|
||||
The function will be a added at the very bottom of our `Start-IcingaAgentServiceTest` function
|
||||
This call will be added inside the `Start-IcingaAgentServiceTest` we created earlier and didn't touch so far yet.
|
||||
|
||||
```powershell
|
||||
function Start-IcingaAgentServiceTest()
|
||||
{
|
||||
# Our ScriptBlock for the code being executed inside the thread
|
||||
[ScriptBlock]$IcingaServiceTest = {
|
||||
# Allow us to parse the framework global data to this thread
|
||||
param($IcingaDaemonData);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
Use-Icinga -LibOnly -Daemon;
|
||||
|
||||
# Add a synchronized hashtable to the global data background
|
||||
# daemon hashtable to write data to. In addition it will
|
||||
# allow to share data collected from this daemon with others
|
||||
$IcingaDaemonData.BackgroundDaemon.Add(
|
||||
'TestIcingaAgentService',
|
||||
[hashtable]::Synchronized(@{})
|
||||
);
|
||||
# This will add another hashtable to our previous
|
||||
# TestIcingaAgentService hashtable to store actual service
|
||||
# information
|
||||
$IcingaDaemonData.BackgroundDaemon.TestIcingaAgentService.Add(
|
||||
'ServiceState',
|
||||
[hashtable]::Synchronized(@{})
|
||||
);
|
||||
|
||||
# Initialise our error counter variable
|
||||
[int]$RestartErrors = 0;
|
||||
|
||||
# Keep our code excuted as long as the PowerShell service is
|
||||
# being executed. This is required to ensure we will execute
|
||||
# the code frequently instead of only once
|
||||
while ($TRUE) {
|
||||
# Get the current service information. If the service is
|
||||
# not installed, continue silently to return $null
|
||||
$ServiceState = Get-Service 'icinga2' -ErrorAction SilentlyContinue;
|
||||
|
||||
# Only execute our code if the Icinga Agent service is
|
||||
# installed
|
||||
if ($null -ne $ServiceState) {
|
||||
# Add the current service state to our hashtable.
|
||||
Add-IcingaHashtableItem `
|
||||
-Hashtable $IcingaDaemonData.BackgroundDaemon.TestIcingaAgentService.ServiceState `
|
||||
-Key 'value' `
|
||||
-Value $ServiceState.Status `
|
||||
-Override | Out-Null;
|
||||
|
||||
# Restart the service if it is not running
|
||||
if ($ServiceState.Status -ne 'Running') {
|
||||
try {
|
||||
# Try to restart the service
|
||||
Restart-Service 'icinga2' -ErrorAction Stop;
|
||||
|
||||
Add-IcingaHashtableItem `
|
||||
-Hashtable $IcingaDaemonData.BackgroundDaemon.TestIcingaAgentService.ServiceState `
|
||||
-Key 'restart_error' `
|
||||
-Value 0 `
|
||||
-Override | Out-Null;
|
||||
} catch {
|
||||
# Add an error counter in case we failed
|
||||
$RestartErrors += 1;
|
||||
Add-IcingaHashtableItem `
|
||||
-Hashtable $IcingaDaemonData.BackgroundDaemon.TestIcingaAgentService.ServiceState `
|
||||
-Key 'restart_error' `
|
||||
-Value $RestartErrors `
|
||||
-Override | Out-Null;
|
||||
}
|
||||
}
|
||||
}
|
||||
# ALWAYS add some sort of sleep at the end. Either to
|
||||
# break the CPU cycle and give it some break or to
|
||||
# ensure daemon tasks are executed on a certain interval
|
||||
Start-Sleep -Seconds 5;
|
||||
}
|
||||
};
|
||||
|
||||
# Now create a new thread for our ScriptBlock, assign a name and
|
||||
# parse all required arguments to it. Last but not least start it
|
||||
# directly
|
||||
# Now create a new thread and use our previous created
|
||||
# function as command to call it and parse all our
|
||||
# arguments to it
|
||||
New-IcingaThreadInstance `
|
||||
-Name "Icinga_PowerShell_IcingaAgent_StateCheck" `
|
||||
-Name 'Icinga_PowerShell_IcingaAgent_StateCheck' `
|
||||
-ThreadPool $global:IcingaDaemonData.IcingaThreadPool.BackgroundPool `
|
||||
-ScriptBlock $IcingaServiceTest `
|
||||
-Arguments @( $global:IcingaDaemonData ) `
|
||||
-Command 'Add-IcingaAgentServiceTest' `
|
||||
-CmdParameters @{
|
||||
'IcingaDaemonData' = $global:IcingaDaemonData;
|
||||
} `
|
||||
-Start;
|
||||
}
|
||||
```
|
||||
|
||||
Register Our Daemon
|
||||
---
|
||||
## Register Our Daemon
|
||||
|
||||
Now as our daemon is ready we can simply [register it](../110-Installation/05-Background-Daemons.md) by using the Framework commands
|
||||
|
||||
|
|
@ -430,7 +350,7 @@ Now as our daemon is ready we can simply [register it](../110-Installation/05-Ba
|
|||
Register-IcingaBackgroundDaemon -Command 'Start-IcingaAgentServiceTest';
|
||||
```
|
||||
|
||||
Once registered, you will have to restart the PowerShell service itselfs to apply the changes
|
||||
Once registered, you will have to restart the PowerShell service itself to apply the changes
|
||||
|
||||
```powershell
|
||||
Restart-IcingaWindowsService;
|
||||
|
|
@ -438,10 +358,9 @@ Restart-IcingaWindowsService;
|
|||
|
||||
Thats it! Now the daemon is loaded with every start, checking for the Agent state and restart it if it is not running.
|
||||
|
||||
**Note:** In order to restart the Icinga Agent service, the user the PowerShell service is running with requires these kind of priviliges. Otherwise it will throw an error and the error counter will increase
|
||||
**Note:** In order to restart the Icinga Agent service, the user the PowerShell service is running with requires these kind of privileges. Otherwise it will throw an error and the error counter will increase
|
||||
|
||||
Developer Console
|
||||
---
|
||||
## Developer Console
|
||||
|
||||
During development you might want to test the current implementation and check if everything is working as intended. To do so, you require to open a PowerShell terminal as `administrator`. We would recommend to `stop` the PowerShell service in this case to prevent possible daemons writing files to the system and overwriting each others.
|
||||
|
||||
|
|
@ -458,6 +377,6 @@ Once done you will receive back your prompt, however all registered background d
|
|||
$global:IcingaDaemonData.BackgroundDaemon.TestIcingaAgentService.ServiceState['value'];
|
||||
```
|
||||
|
||||
In case your Icinga Agent service is intalled and your daemon is running properly, this should print the current state of the service.
|
||||
In case your Icinga Agent service is installed and your daemon is running properly, this should print the current state of the service.
|
||||
|
||||
Of course for more complex daemons you are able to manipulate data directly or add more detailed debug output.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,18 @@ Additional required files within the `provider` folder can be included by using
|
|||
|
||||
The best approach for creating a custom plugin is by creating an independent module which is installed in your PowerShell modules directly. This will ensure you are not overwriting your custom data with possible framework updates.
|
||||
|
||||
### Developer Tools
|
||||
|
||||
To get started easier, you can run this command to create the new module:
|
||||
|
||||
```powershell
|
||||
New-IcingaForWindowsComponent -Name 'plugintutorial' -ComponentType 'plugins';
|
||||
```
|
||||
|
||||
If you wish to create the module manually, please read on.
|
||||
|
||||
### Manual Creation
|
||||
|
||||
In this guide, we will assume the name of the module is `icinga-powershell-plugintutorial`.
|
||||
|
||||
At first we will have to create a new module. Navigate to the PowerShell modules folder the Framework itself is installed to. In this tutorial we will assume the location is
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Developer Guide: Custom API-Endpoints
|
||||
|
||||
Starting with Icinga [PowerShell Framework v1.1.0](https://icinga.com/docs/windows/latest) plenty of features and functionaliy have been added for shipping data by using a REST-API. This Developer Guide will describe on how to write custom API endpoints by using the [PowerShell Framework v1.1.0](https://icinga.com/docs/windows/latest) and the [Icinga PowerShell REST-Api](https://icinga.com/docs/windows/latest/restapi/doc/01-Introduction/). In this example we will write a custom endpoint to simply provide a file list for a specific folder.
|
||||
Starting with Icinga [PowerShell Framework v1.1.0](https://icinga.com/docs/windows/latest) plenty of features and functionality have been added for shipping data by using a REST-API. This Developer Guide will describe on how to write custom API endpoints by using the [PowerShell Framework v1.1.0](https://icinga.com/docs/windows/latest) and the [Icinga PowerShell REST-Api](https://icinga.com/docs/windows/latest/restapi/doc/01-Introduction/). In this example we will write a custom endpoint to simply provide a file list for a specific folder.
|
||||
|
||||
## File Structure
|
||||
|
||||
|
|
@ -23,6 +23,18 @@ Additional required files within the `lib` folder can be included by using the `
|
|||
|
||||
The best approach for creating a custom API endpoint is by creating an independent module which is installed in your PowerShell modules directly. This will ensure you are not overwriting your custom data with possible other module updates.
|
||||
|
||||
### Developer Tools
|
||||
|
||||
To get started easier, you can run this command to create the new module:
|
||||
|
||||
```powershell
|
||||
New-IcingaForWindowsComponent -Name 'apitutorial' -ComponentType 'apiendpoint';
|
||||
```
|
||||
|
||||
If you wish to create the module manually, please read on.
|
||||
|
||||
### Manual Creation
|
||||
|
||||
In this guide, we will assume the name of the module is `icinga-powershell-apitutorial`.
|
||||
|
||||
At first we will have to create a new module. Navigate to the PowerShell modules folder the Framework itself is installed to. In this tutorial we will assume the location is
|
||||
|
|
@ -35,7 +47,7 @@ Now create a new folder with the name `icinga-powershell-apitutorial` and naviga
|
|||
|
||||
As we require a `psm1` file which contains our code, we will create a new file with the name `icinga-powershell-apitutorial.psm1`. This will allow the PowerShell autoloader to load the module automatically.
|
||||
|
||||
**Note:** It could be possible, depending on your [execution policies](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-6), that your module is not loaded properly. If this is the case, you can try to unblock the file by opening a PowerShell and use the `Unblock-File` Cmdelet
|
||||
**Note:** It could be possible, depending on your [execution policies](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-6), that your module is not loaded properly. If this is the case, you can try to unblock the file by opening a PowerShell and use the `Unblock-File` Cmdlet
|
||||
|
||||
```powershell
|
||||
Unblock-File -Path 'C:\Program Files\WindowsPowerShell\Modules\icinga-powershell-apitutorial\icinga-powershell-apitutorial.psm1'
|
||||
|
|
@ -45,7 +57,7 @@ Unblock-File -Path 'C:\Program Files\WindowsPowerShell\Modules\icinga-powershell
|
|||
|
||||
Once the module files are created and unblocked, we can start testing if the autoloader is properly working and our module is detected.
|
||||
|
||||
For this open the file `icinga-powershell-apitutorial.psm1` in your prefered editor and add the following code snippet
|
||||
For this open the file `icinga-powershell-apitutorial.psm1` in your preferred editor and add the following code snippet
|
||||
|
||||
```powershell
|
||||
function Test-MyIcingaAPITutorialCommand()
|
||||
|
|
@ -83,9 +95,9 @@ function Invoke-IcingaAPITutorialRESTCall()
|
|||
|
||||
### Basic API Architecture
|
||||
|
||||
A developer using the REST-Api integration does not have to worry about anything regarding header fetchting, URL encoding or similar. All data is parsed by the [Icinga PowerShell REST-Api](https://icinga.com/docs/windows/latest/restapi/doc/01-Introduction/) and invoked to our function.
|
||||
A developer using the REST-Api integration does not have to worry about anything regarding header fetching, URL encoding or similar. All data is parsed by the [Icinga PowerShell REST-Api](https://icinga.com/docs/windows/latest/restapi/doc/01-Introduction/) and invoked to our function.
|
||||
|
||||
Our API endpoint will be called by a namespace, refering to our actual function executing the code.
|
||||
Our API endpoint will be called by a namespace, referring to our actual function executing the code.
|
||||
|
||||
### Writing Our Base-Skeleton
|
||||
|
||||
|
|
@ -96,16 +108,17 @@ function Invoke-IcingaAPITutorialRESTCall()
|
|||
{
|
||||
# Create our arguments the REST-Api daemon will use to parse the request
|
||||
param (
|
||||
[Hashtable]$Request = @{},
|
||||
[Hashtable]$Connection = @{},
|
||||
$IcingaGlobals
|
||||
[Hashtable]$Request = @{ },
|
||||
[Hashtable]$Connection = @{ },
|
||||
$IcingaGlobals,
|
||||
[string]$ApiVersion = $null
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Request Argument
|
||||
|
||||
The request argument provides a hashtable with all parsed content of the request to later work with. The following elements are avaialble by default:
|
||||
The request argument provides a hashtable with all parsed content of the request to later work with. The following elements are available by default:
|
||||
|
||||
##### Method
|
||||
|
||||
|
|
@ -113,7 +126,7 @@ The HTTP method being used for the request, like `GET`, `POST`, `DELETE` and so
|
|||
|
||||
##### RequestPath
|
||||
|
||||
The request path is split into two hastable entries: `FullPath` and `PathArray`. This tells you exactly which URL the user specified and allows you to build proper handling for different entry points of your endpoint.
|
||||
The request path is split into two hashtable entries: `FullPath` and `PathArray`. This tells you exactly which URL the user specified and allows you to build proper handling for different entry points of your endpoint.
|
||||
|
||||
For the path array, on index 0 you will always find the `version` and on index 1 `your endpoint alias`. Following this, possible additional path extensions in your module will always start on index 2.
|
||||
|
||||
|
|
@ -176,22 +189,23 @@ This argument is containing the connection details of the client including the T
|
|||
|
||||
#### IcingaGlobals Argument
|
||||
|
||||
This argument contains all global data and content of the REST-Api background dameon. This will then come in handy to share data between API endpoints and to access some global configuration data.
|
||||
This argument contains all global data and content of the REST-Api background daemon. This will then come in handy to share data between API endpoints and to access some global configuration data.
|
||||
|
||||
### Sending Data to the Client
|
||||
|
||||
Now we are basically ready to process data. To do so, we will fetch the current folder content of our PowerShell module with `Get-ChildItem` and send this content to our client. For sending data to the client, we can use `Send-IcingaTCPClientMessage`. This Cmdlet will use a `Message` as `New-IcingaTCPClientRESTMessage` object which itself cotains the `HTTPResponse` and our `ContentBody`. In addition to `Send-IcingaTCPClientMessage` we also have to specify the `Stream` to write to. The stream object is part of our `Connection` argument.
|
||||
Now we are basically ready to process data. To do so, we will fetch the current folder content of our PowerShell module with `Get-ChildItem` and send this content to our client. For sending data to the client, we can use `Send-IcingaTCPClientMessage`. This Cmdlet will use a `Message` as `New-IcingaTCPClientRESTMessage` object which itself contains the `HTTPResponse` and our `ContentBody`. In addition to `Send-IcingaTCPClientMessage` we also have to specify the `Stream` to write to. The stream object is part of our `Connection` argument.
|
||||
|
||||
All content will be send as JSON encoded, so please ensure you are using a datatype which is convertable by `ConvertTo-Json`.
|
||||
All content will be send as JSON encoded, so please ensure you are using a datatype which is convertible by `ConvertTo-Json`.
|
||||
|
||||
```powershell
|
||||
function Invoke-IcingaAPITutorialRESTCall()
|
||||
{
|
||||
# Create our arguments the REST-Api daemon will use to parse the request
|
||||
param (
|
||||
[Hashtable]$Request = @{},
|
||||
[Hashtable]$Connection = @{},
|
||||
$IcingaGlobals
|
||||
[Hashtable]$Request = @{ },
|
||||
[Hashtable]$Connection = @{ },
|
||||
$IcingaGlobals,
|
||||
[string]$ApiVersion = $null
|
||||
);
|
||||
|
||||
# Fetch all file names within our module directory. We filter this to ensure we
|
||||
|
|
@ -212,7 +226,7 @@ function Invoke-IcingaAPITutorialRESTCall()
|
|||
|
||||
Now as we have written a basic function to fetch folder content and to send it back to our client, we will have to `register` our Cmdlet to the endpoint. For this we will open our `icinga-powershell-apitutorial.psm1` and add a `namespace` function which has to follow this naming guideline: `Register-IcingaRESTAPIEndpoint{0}`
|
||||
|
||||
Replace `{0}` with the name you have choosen for your `Invoke-Icinga{0}RESTCall`. Once the REST-Api Daemon is loaded, all functions within this namespace are executed. The function has to return a hashtable with an `Alias` refering to the URL part the user has to enter and a `Command` being executed for this alias.
|
||||
Replace `{0}` with the name you have chosen for your `Invoke-Icinga{0}RESTCall`. Once the REST-Api Daemon is loaded, all functions within this namespace are executed. The function has to return a hashtable with an `Alias` referring to the URL part the user has to enter and a `Command` being executed for this alias.
|
||||
|
||||
```powershell
|
||||
function Register-IcingaRESTAPIEndpointAPITutorial()
|
||||
|
|
@ -266,4 +280,4 @@ https://example.com:5668/v1/apitutorial
|
|||
|
||||
### Conclusion
|
||||
|
||||
This is a basic tutorial on how to write custom API-Endpoints and make them available in your environment. Of course you can now start to filter requests depending on the URL the user added, used headers or other input like the body for example. All data send by the client is accessable to developers for writing their own extensions and modules.
|
||||
This is a basic tutorial on how to write custom API-Endpoints and make them available in your environment. Of course you can now start to filter requests depending on the URL the user added, used headers or other input like the body for example. All data send by the client is accessible to developers for writing their own extensions and modules.
|
||||
|
|
|
|||
257
lib/core/dev/New-IcingaForWindowsComponent.psm1
Normal file
257
lib/core/dev/New-IcingaForWindowsComponent.psm1
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Creates an empty new Icinga for Windows module
|
||||
.DESCRIPTION
|
||||
Creates an empty new Icinga for Windows module
|
||||
.PARAMETER Name
|
||||
The name of the Icinga for Windows component and module
|
||||
.PARAMETER Author
|
||||
The author of the module as string
|
||||
.PARAMETER CompanyName
|
||||
The company this module belongs to as string
|
||||
.PARAMETER Copyright
|
||||
The copyright owner of this module as string
|
||||
.PARAMETER ModuleVersion
|
||||
The version of this module as 3 digit version number, eg '1.0.0'
|
||||
.PARAMETER Description
|
||||
The description of what this module does as string
|
||||
.PARAMETER RequiredModules
|
||||
The required modules this module depends on, as an array of hashtables e.g.
|
||||
@( @{ ModuleName = 'icinga-powershell-framework'; ModuleVersion = '1.7.0' } )
|
||||
.PARAMETER Tags
|
||||
An array of string tags and keywords, for finding this module on the installation list
|
||||
.PARAMETER ProjectUri
|
||||
The url of the project as string
|
||||
.PARAMETER LicenseUri
|
||||
The url to the license as string
|
||||
.PARAMETER ComponentType
|
||||
Defines on how Icinga for Windows will load this module. Valid options are
|
||||
'plugins', 'apiendpoint', 'daemon', 'library'
|
||||
.PARAMETER OpenInEditor
|
||||
Will open the newly created module within the editor
|
||||
#>
|
||||
function New-IcingaForWindowsComponent()
|
||||
{
|
||||
param (
|
||||
[string]$Name,
|
||||
[string]$Author = $env:USERNAME,
|
||||
[string]$CompanyName = '',
|
||||
[string]$Copyright = ([string]::Format('(c) {0} {1} | GPL v2.0', [DateTime]::Now.Year, $env:USERNAME)),
|
||||
[Version]$ModuleVersion = '1.0.0',
|
||||
[string]$Description = '',
|
||||
[array]$RequiredModules = @( @{ ModuleName = 'icinga-powershell-framework'; ModuleVersion = '1.7.0' } ),
|
||||
[string[]]$Tags = $Name,
|
||||
[string]$ProjectUri = '',
|
||||
[string]$LicenseUri = '',
|
||||
[ValidateSet('plugins', 'apiendpoint', 'daemon', 'library')]
|
||||
[string]$ComponentType = 'plugins',
|
||||
[switch]$OpenInEditor = $FALSE
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($Name)) {
|
||||
Write-IcingaConsoleError 'Please specify a name for your new component';
|
||||
return;
|
||||
}
|
||||
|
||||
[string]$ModuleName = [string]::Format('icinga-powershell-{0}', $Name.ToLower());
|
||||
[string]$ModuleRoot = Get-IcingaForWindowsRootPath;
|
||||
[string]$ModuleDir = Join-Path -Path $ModuleRoot -ChildPath $ModuleName;
|
||||
[string]$DaemonFunction = '';
|
||||
[string]$EndpointName = '';
|
||||
|
||||
if (Test-Path $ModuleDir) {
|
||||
Write-IcingaConsoleError 'A component with this name does already exist. Use "Publish-IcingaForWindowsComponent" to apply changes';
|
||||
return;
|
||||
}
|
||||
|
||||
$TextInfo = (Get-Culture).TextInfo;
|
||||
|
||||
New-Item -ItemType Directory -Path $ModuleDir | Out-Null;
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'doc') | Out-Null;
|
||||
New-Item -ItemType File -Path (Join-Path -Path $ModuleDir -ChildPath 'README.md') | Out-Null;
|
||||
New-Item -ItemType File -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psm1', $ModuleName))) | Out-Null;
|
||||
|
||||
switch ($ComponentType) {
|
||||
'plugins' {
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'plugins') | Out-Null;
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'provider') | Out-Null;
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'lib') | Out-Null;
|
||||
|
||||
break;
|
||||
};
|
||||
'apiendpoint' {
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'endpoint') | Out-Null;
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'lib') | Out-Null;
|
||||
|
||||
[string]$RegisterFunction = ([string]::Format('Register-IcingaRESTAPIEndpoint{0}', $TextInfo.ToTitleCase($Name.ToLower())));
|
||||
[string]$RegisterFunctionFile = (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('endpoint\{0}.psm1', $RegisterFunction)));
|
||||
[string]$InvokeFunction = ([string]::Format('Invoke-IcingaForWindowsApiRESTCall{0}', $TextInfo.ToTitleCase($Name.ToLower())));
|
||||
[string]$InvokeFunctionFile = (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('endpoint\{0}.psm1', $InvokeFunction)));
|
||||
|
||||
Set-Content -Path $RegisterFunctionFile -Value ([string]::Format('function {0}()', $RegisterFunction));
|
||||
Add-Content -Path $RegisterFunctionFile -Value '{';
|
||||
Add-Content -Path $RegisterFunctionFile -Value ' # Ensure that we can call our API with a specific endpoint';
|
||||
Add-Content -Path $RegisterFunctionFile -Value ' return @{';
|
||||
Add-Content -Path $RegisterFunctionFile -Value ([string]::Format(" 'Alias' = '{0}';", $Name.ToLower()));
|
||||
Add-Content -Path $RegisterFunctionFile -Value ([string]::Format(" 'Command' = '{0}';", $InvokeFunction));
|
||||
Add-Content -Path $RegisterFunctionFile -Value ' }';
|
||||
Add-Content -Path $RegisterFunctionFile -Value '}';
|
||||
|
||||
Set-Content -Path $InvokeFunctionFile -Value ([string]::Format('function {0}()', $InvokeFunction));
|
||||
Add-Content -Path $InvokeFunctionFile -Value '{';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' # Do not modify the param section';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' param (';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' [Hashtable]$Request = @{},';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' [Hashtable]$Connection = @{},';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' $IcingaGlobals,';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' [string]$ApiVersion = $null';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' );'
|
||||
Add-Content -Path $InvokeFunctionFile -Value '';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' $Global:IcingaDaemonData = $IcingaGlobals;';
|
||||
Add-Content -Path $InvokeFunctionFile -Value '';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' # This is the main function for your API endpoint.';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' # Also check the developer guide for further details: https://icinga.com/docs/icinga-for-windows/latest/doc/900-Developer-Guide/12-Custom-API-Endpoints/';
|
||||
Add-Content -Path $InvokeFunctionFile -Value '';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' # Send a success message once you connect to the endpoint as example';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' Send-IcingaTCPClientMessage -Message (';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' New-IcingaTCPClientRESTMessage `';
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' -HTTPResponse ($IcingaHTTPEnums.HTTPResponseType.Ok) `';
|
||||
Add-Content -Path $InvokeFunctionFile -Value " -ContentBody 'Api endpoint is installed'";
|
||||
Add-Content -Path $InvokeFunctionFile -Value ' ) -Stream $Connection.Stream;';
|
||||
Add-Content -Path $InvokeFunctionFile -Value '}';
|
||||
|
||||
$EndpointName = $Name.ToLower();
|
||||
|
||||
break;
|
||||
};
|
||||
'daemon' {
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'daemon') | Out-Null;
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'lib') | Out-Null;
|
||||
New-Item `
|
||||
-ItemType File `
|
||||
-Path (Join-Path -Path (Join-Path -Path $ModuleDir -ChildPath 'daemon') -ChildPath ([string]::Format('Start-IcingaForWindowsDaemon{0}.psm1', $TextInfo.ToTitleCase($Name.ToLower())))) | Out-Null;
|
||||
|
||||
$DaemonFunction = ([string]::Format('Start-IcingaForWindowsDaemon{0}', $TextInfo.ToTitleCase($Name.ToLower())));
|
||||
$DaemonEntry = ([string]::Format('Add-IcingaIcingaForWindowsDaemonEntry{0}', $TextInfo.ToTitleCase($Name.ToLower())));
|
||||
|
||||
Set-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value ([string]::Format('function {0}()', $DaemonFunction));
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value '{';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value ' # This is the entry point for Icinga for Windows. Use this function for registering your background daemon';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value ' # Also check the developer guide for further details: https://icinga.com/docs/icinga-for-windows/latest/doc/900-Developer-Guide/10-Custom-Daemons/';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value '';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value ' New-IcingaThreadInstance `';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value (
|
||||
[string]::Format(
|
||||
' -Name {1}IcingaForWindows_Daemon_{0}{1} `',
|
||||
$TextInfo.ToTitleCase($Name.ToLower()),
|
||||
"'"
|
||||
)
|
||||
);
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value ' -ThreadPool $IcingaDaemonData.IcingaThreadPool.BackgroundPool `';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value (
|
||||
[string]::Format(
|
||||
' -Command {0}{1}{0} `',
|
||||
"'",
|
||||
$DaemonEntry
|
||||
)
|
||||
);
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value (
|
||||
[string]::Format(
|
||||
' -CmdParameters @{{ {0}IcingaDaemonData{0} = $global:IcingaDaemonData }} `',
|
||||
"'"
|
||||
)
|
||||
);
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value ' -Start;';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonFunction))) -Value '}';
|
||||
|
||||
Set-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ([string]::Format('function {0}()', $DaemonEntry));
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value '{';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' param (';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' $IcingaDaemonData';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' );';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value '';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' # This is your main daemon function. Add your code inside the WHILE() loop which is executed once the daemon is loaded.';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' # Also check the developer guide for further details: https://icinga.com/docs/icinga-for-windows/latest/doc/900-Developer-Guide/10-Custom-Daemons/';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value '';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' Use-Icinga -LibOnly -Daemon;';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value '';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' while ($TRUE) {';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' # Add your daemon code within this loop';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' Start-Sleep -Seconds 1;';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' }';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value '}';
|
||||
|
||||
break;
|
||||
};
|
||||
'library' {
|
||||
New-Item -ItemType Directory -Path (Join-Path -Path $ModuleDir -ChildPath 'lib') | Out-Null;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Copy-ItemSecure `
|
||||
-Path (Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'templates\Manifest.psd1.template') `
|
||||
-Destination (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName))) `
|
||||
-Force | Out-Null;
|
||||
|
||||
Write-IcingaForWindowsComponentManifest -Name $Name -ModuleConfig @{
|
||||
'$MODULENAME$' = ([string]::Format('Windows {0}', $Name));
|
||||
'$GUID$' = (New-Guid);
|
||||
'$ROOTMODULE$' = ([string]::Format('{0}.psm1', $ModuleName));
|
||||
'$AUTHOR$' = $Author;
|
||||
'$COMPANYNAME$' = $CompanyName;
|
||||
'$COPYRIGHT$' = $Copyright;
|
||||
'$MODULEVERSION$' = $ModuleVersion.ToString();
|
||||
'$VMODULEVERSION$' = ([string]::Format('v{0}', $ModuleVersion.ToString()));
|
||||
'$DESCRIPTION$' = $Description;
|
||||
'$REQUIREDMODULES$' = $RequiredModules;
|
||||
'$NESTEDMODULES$' = '';
|
||||
'$TAGS$' = $Tags;
|
||||
'$PROJECTURI$' = $ProjectUri;
|
||||
'$LICENSEURI$' = $LicenseUri;
|
||||
'$COMPONENTTYPE$' = $ComponentType;
|
||||
'$DAEMONFUNCTION$' = $DaemonFunction;
|
||||
'$APIENDPOINT$' = $EndpointName;
|
||||
};
|
||||
|
||||
Set-Content `
|
||||
-Path (Join-Path -Path $ModuleDir -ChildPath 'README.md') `
|
||||
-Value ([string]::Format('# Icinga for Windows - {0}', $Name));
|
||||
|
||||
|
||||
Add-Content `
|
||||
-Path (Join-Path -Path $ModuleDir -ChildPath 'README.md') `
|
||||
-Value '';
|
||||
|
||||
Add-Content `
|
||||
-Path (Join-Path -Path $ModuleDir -ChildPath 'README.md') `
|
||||
-Value ([string]::Format('This is the auto generated readme for the Icinga for Windows Module `icinga-powershell-{0}`', $Name.ToLower()));
|
||||
|
||||
Add-Content `
|
||||
-Path (Join-Path -Path $ModuleDir -ChildPath 'README.md') `
|
||||
-Value '';
|
||||
|
||||
Add-Content `
|
||||
-Path (Join-Path -Path $ModuleDir -ChildPath 'README.md') `
|
||||
-Value 'You can start to modify this readme including writing your PowerShell code. Use "Publish-IcingaForWindowsComponent" to generate Icinga 2/Icinga Director configuration files for plugins, auto-generate your configuration and update your module manifest to include all module files you created into the base module.';
|
||||
|
||||
Add-Content `
|
||||
-Path (Join-Path -Path $ModuleDir -ChildPath 'README.md') `
|
||||
-Value '';
|
||||
|
||||
Copy-ItemSecure `
|
||||
-Path (Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'templates\PSScriptAnalyzerSettings.psd1.template') `
|
||||
-Destination (Join-Path -Path $ModuleDir -ChildPath 'PSScriptAnalyzerSettings.psd1') `
|
||||
-Force | Out-Null;
|
||||
|
||||
Write-IcingaConsoleNotice 'New component "{0}" has been created as module "icinga-powershell-{1}" at location "{2}"' -Objects $Name, $Name.ToLower(), $ModuleDir;
|
||||
Publish-IcingaForWindowsComponent -Name $Name -NoOutput;
|
||||
|
||||
Import-Module $ModuleDir -Force;
|
||||
Import-Module $ModuleDir -Global -Force;
|
||||
|
||||
if ($OpenInEditor) {
|
||||
Open-IcingaForWindowsComponentInEditor -Name $Name;
|
||||
}
|
||||
}
|
||||
63
lib/core/dev/Open-IcingaForWindowsComponentInEditor.psm1
Normal file
63
lib/core/dev/Open-IcingaForWindowsComponentInEditor.psm1
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Opens any Icinga for Windows component in the defined editor
|
||||
.DESCRIPTION
|
||||
Opens any Icinga for Windows component in the defined editor
|
||||
.PARAMETER Name
|
||||
The name of the Icinga for Windows component
|
||||
.PARAMETER Editor
|
||||
Defines which editor should be used
|
||||
.EXAMPLE
|
||||
Open-IcingaForWindowsComponentInEditor -Name 'framework' -Editor 'code';
|
||||
#>
|
||||
function Open-IcingaForWindowsComponentInEditor()
|
||||
{
|
||||
param (
|
||||
[string]$Name,
|
||||
[ValidateSet('code')]
|
||||
[string]$Editor = 'code'
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($Name)) {
|
||||
Write-IcingaConsoleError 'Please specify the name of the component you want to open';
|
||||
return;
|
||||
}
|
||||
|
||||
[string]$ModuleName = [string]::Format('icinga-powershell-{0}', $Name.ToLower());
|
||||
[string]$ModuleRoot = Get-IcingaForWindowsRootPath;
|
||||
[string]$ModuleDir = Join-Path -Path $ModuleRoot -ChildPath $ModuleName;
|
||||
[string]$ModuleManifest = (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName)))
|
||||
|
||||
if ((Test-Path $ModuleDir) -eq $FALSE) {
|
||||
Write-IcingaConsoleError 'A component with the name "{0}" does not exist. Use "New-IcingaForWindowsComponent" to create a new one or verify that the provided name is correct.' -Objects $Name;
|
||||
return;
|
||||
}
|
||||
|
||||
[bool]$EditorInstalled = $FALSE;
|
||||
[string]$EditorName = 'Unspecified';
|
||||
|
||||
switch ($Editor) {
|
||||
'code' {
|
||||
if ($null -ne (Get-Command 'code.cmd' -ErrorAction SilentlyContinue)) {
|
||||
$EditorInstalled = $TRUE;
|
||||
}
|
||||
$EditorName = 'Visual Studio Code';
|
||||
break;
|
||||
};
|
||||
# TODO: Add more editors
|
||||
}
|
||||
|
||||
if ($EditorInstalled -eq $FALSE) {
|
||||
Write-IcingaConsoleError 'Unable to open module "{0}" with {1}. Either the binary was not found or {1} is not installed' -Objects $ModuleName, $EditorName;
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($Editor) {
|
||||
'code' {
|
||||
& code --new-window "$ModuleDir";
|
||||
|
||||
break;
|
||||
}
|
||||
# TODO: Add more editors
|
||||
}
|
||||
}
|
||||
155
lib/core/dev/Publish-IcingaForWindowsComponent.psm1
Normal file
155
lib/core/dev/Publish-IcingaForWindowsComponent.psm1
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Updates an Icinga for Windows component by updating the NestedModules entry
|
||||
of the manifest including documentation and provides an optional .zip file
|
||||
for usage within the Icinga for Windows repositories
|
||||
.DESCRIPTION
|
||||
Updates an Icinga for Windows component by updating the NestedModules entry
|
||||
of the manifest including documentation and provides an optional .zip file
|
||||
for usage within the Icinga for Windows repositories
|
||||
.PARAMETER Name
|
||||
The name of the Icinga for Windows component and module
|
||||
.PARAMETER ReleasePackagePath
|
||||
The path on where the .zip file for release will be created at.
|
||||
Defaults to the current users home folder
|
||||
.PARAMETER NoOutput
|
||||
Use this flag to disable console outputs for the progress, required by other
|
||||
functions using this function
|
||||
.PARAMETER CreateReleasePackage
|
||||
If set, the function will create a .zip file for this specific module which
|
||||
can be used within the Icinga for Windows repository manager.
|
||||
Use -ReleasePackagePath to override the default location on where the
|
||||
package will be created in. Default location is the current users
|
||||
home folder
|
||||
#>
|
||||
function Publish-IcingaForWindowsComponent()
|
||||
{
|
||||
param (
|
||||
[string]$Name,
|
||||
[string]$ReleasePackagePath = '',
|
||||
[switch]$NoOutput = $FALSE,
|
||||
[switch]$CreateReleasePackage = $FALSE
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($Name)) {
|
||||
Write-IcingaConsoleError 'Please specify the name of the component you want to publish';
|
||||
return;
|
||||
}
|
||||
|
||||
[string]$ModuleName = [string]::Format('icinga-powershell-{0}', $Name.ToLower());
|
||||
[string]$ModuleRoot = Get-IcingaForWindowsRootPath;
|
||||
[string]$ModuleDir = Join-Path -Path $ModuleRoot -ChildPath $ModuleName;
|
||||
[string]$ModuleManifest = (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName)))
|
||||
|
||||
if ((Test-Path $ModuleDir) -eq $FALSE) {
|
||||
Write-IcingaConsoleError 'A component with the name "{0}" does not exist. Use "New-IcingaForWindowsComponent" to create a new one or verify that the provided name is correct.' -Objects $Name;
|
||||
return;
|
||||
}
|
||||
|
||||
$ComponentType = '';
|
||||
$ManifestScript = '';
|
||||
$ManifestContent = Get-Content -Path $ModuleManifest;
|
||||
|
||||
foreach ($entry in $ManifestContent) {
|
||||
[string]$LineContent = [string]$entry;
|
||||
if ($LineContent.Contains('#')) {
|
||||
$LineContent = $LineContent.Substring(0, $LineContent.IndexOf('#'));
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrEmpty($LineContent) -Or $LineContent -eq "`r`n" -Or $LineContent -eq "`n" -Or [string]::IsNullOrEmpty($LineContent.Replace(' ', ''))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ManifestScript += $LineContent;
|
||||
$ManifestScript += "`r`n";
|
||||
}
|
||||
|
||||
[ScriptBlock]$ManifestScriptBlock = [ScriptBlock]::Create('return ' + $ManifestScript);
|
||||
$ModuleManifestData = (& $ManifestScriptBlock);
|
||||
$ModuleList = @();
|
||||
$ModuleFiles = Get-ChildItem -Path $ModuleDir -Recurse -Filter '*.psm1';
|
||||
|
||||
foreach ($entry in $ModuleFiles) {
|
||||
if ($entry.Name -eq ([string]::Format('{0}.psm1', $ModuleName))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ModuleList += $entry.FullName.Replace($ModuleDir, '.');
|
||||
}
|
||||
|
||||
if ($NoOutput) {
|
||||
Disable-IcingaFrameworkConsoleOutput;
|
||||
}
|
||||
|
||||
Write-IcingaForWindowsComponentManifest -Name $Name -ModuleList $ModuleList;
|
||||
|
||||
if ($ModuleManifestData.PrivateData.Type -eq 'plugins') {
|
||||
Publish-IcingaPluginConfiguration -ComponentName $Name;
|
||||
Publish-IcingaPluginDocumentation -ModulePath $ModuleDir;
|
||||
}
|
||||
|
||||
if ($CreateReleasePackage) {
|
||||
if ([string]::IsNullOrEmpty($ReleasePackagePath)) {
|
||||
$ReleasePackagePath = Join-Path -Path $ENV:HOMEDRIVE -ChildPath $ENV:HOMEPATH;
|
||||
$ReleasePackagePath = Join-Path -Path $ReleasePackagePath -ChildPath ([string]::Format('ifw_releases\{0}', $ModuleName))
|
||||
}
|
||||
|
||||
if ((Test-Path $ReleasePackagePath) -eq $FALSE) {
|
||||
New-Item -ItemType Directory -Path $ReleasePackagePath | Out-Null;
|
||||
}
|
||||
|
||||
if ((Test-Path $ReleasePackagePath) -eq $FALSE) {
|
||||
Write-IcingaConsoleError 'Failed to create path "{0}" for providing .zip archive for module "{1}"' -Objects $ReleasePackagePath, $ModuleName;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Test-IcingaAddTypeExist 'System.IO.Compression.FileSystem') -eq $FALSE) {
|
||||
Add-Type -Assembly 'System.IO.Compression.FileSystem';
|
||||
}
|
||||
|
||||
[string]$ZipName = [string]::Format(
|
||||
'{0}-{1}.zip', $ModuleName, $ModuleManifestData.PrivateData.Version
|
||||
);
|
||||
|
||||
$ZipFile = Join-Path -Path $ReleasePackagePath -ChildPath $ZipName;
|
||||
|
||||
if (Test-Path $ZipFile) {
|
||||
Remove-ItemSecure -Path $ZipFile -Force | Out-Null;
|
||||
}
|
||||
|
||||
$ReleaseTmpDir = New-IcingaTemporaryDirectory;
|
||||
Copy-ItemSecure -Path $ModuleDir -Destination $ReleaseTmpDir -Recurse -Force | Out-Null;
|
||||
|
||||
$ReleaseTmpContent = Get-ChildItem -Path (Join-Path -Path $ReleaseTmpDir -ChildPath $ModuleName) -Recurse;
|
||||
|
||||
foreach ($entry in $ReleaseTmpContent) {
|
||||
if ((Test-Path $entry.FullName) -eq $FALSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($entry.Name[0] -eq '.') {
|
||||
Remove-ItemSecure -Path $entry.FullName -Recurse -Force | Out-Null;
|
||||
}
|
||||
}
|
||||
|
||||
[System.IO.Compression.ZipFile]::CreateFromDirectory(
|
||||
$ReleaseTmpDir,
|
||||
$ZipFile,
|
||||
[System.IO.Compression.CompressionLevel]::Optimal,
|
||||
$FALSE
|
||||
);
|
||||
|
||||
Remove-ItemSecure -Path $ReleaseTmpDir -Force -Recurse | Out-Null;
|
||||
|
||||
if (Test-Path $ZipFile) {
|
||||
Write-IcingaConsoleNotice 'Published module with version "{0}" at "{1}"' -Objects $ModuleManifestData.PrivateData.Version, $ZipFile;
|
||||
} else {
|
||||
Write-IcingaConsoleError 'Failed to publish module "{0}" at "{1}". Please verify you have enough permissions to access this location and try again' -Objects $ModuleName, $ZipFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Write-IcingaConsoleNotice 'Component "{0}" has been updated as module "icinga-powershell-{1}" at location "{2}" successfully' -Objects $Name, $Name.ToLower(), $ModuleDir;
|
||||
|
||||
Enable-IcingaFrameworkConsoleOutput;
|
||||
}
|
||||
92
lib/core/dev/Test-IcingaForWindowsComponent.psm1
Normal file
92
lib/core/dev/Test-IcingaForWindowsComponent.psm1
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Test an Icinga for Windows component and validate the functionality
|
||||
including the code styling
|
||||
.DESCRIPTION
|
||||
Test an Icinga for Windows component and validate the functionality
|
||||
including the code styling
|
||||
.PARAMETER Name
|
||||
The name of the Icinga for Windows component and module
|
||||
.PARAMETER ShowIssues
|
||||
Prints a list of all code styling issues found within the module
|
||||
for resolving them
|
||||
.EXAMPLE
|
||||
Test-IcingaForWindowsComponent -Name 'framework' -ShowIssues;
|
||||
#>
|
||||
function Test-IcingaForWindowsComponent()
|
||||
{
|
||||
param (
|
||||
[string]$Name,
|
||||
[switch]$ShowIssues = $FALSE
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($Name)) {
|
||||
Write-IcingaConsoleError 'Please specify the name of the component you want to test';
|
||||
return;
|
||||
}
|
||||
|
||||
[string]$ModuleName = [string]::Format('icinga-powershell-{0}', $Name.ToLower());
|
||||
[string]$ModuleRoot = Get-IcingaForWindowsRootPath;
|
||||
[string]$ModuleDir = Join-Path -Path $ModuleRoot -ChildPath $ModuleName;
|
||||
[string]$ScriptAnalyzer = Join-Path -Path $ModuleDir -ChildPath 'PSScriptAnalyzerSettings.psd1';
|
||||
[string]$ModuleManifest = (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName)))
|
||||
|
||||
if ((Test-Path $ModuleDir) -eq $FALSE) {
|
||||
Write-IcingaConsoleError 'A component with the name "{0}" does not exist. Use "New-IcingaForWindowsComponent" to create a new one or verify that the provided name is correct.' -Objects $Name;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($null -ne (Get-Command -Name 'Invoke-ScriptAnalyzer' -ErrorAction SilentlyContinue)) {
|
||||
$ScriptAnalyzerContent = Get-Content -Raw -Path $ScriptAnalyzer;
|
||||
[ScriptBlock]$ScriptAnalyzerScriptBlock = [ScriptBlock]::Create('return ' + $ScriptAnalyzerContent);
|
||||
$ScriptAnalyzerData = (& $ScriptAnalyzerScriptBlock);
|
||||
|
||||
$ScriptAnalyzerData.Add('IncludeRule', $ScriptAnalyzerData.IncludeRules);
|
||||
$ScriptAnalyzerData.Add('ExcludeRule', $ScriptAnalyzerData.ExcludeRules);
|
||||
$ScriptAnalyzerData.Remove('Rules');
|
||||
$ScriptAnalyzerData.Remove('IncludeRules');
|
||||
$ScriptAnalyzerData.Remove('ExcludeRules');
|
||||
|
||||
$Result = Invoke-ScriptAnalyzer -Path $ModuleDir -Recurse -ReportSummary @ScriptAnalyzerData;
|
||||
|
||||
if ($Result.Count -ne 0) {
|
||||
if ($ShowIssues -eq $FALSE) {
|
||||
Write-IcingaConsoleWarning 'Your script analyzer has found issues inside this module. Use "-ShowIssues" to print each of them, allowing you to fix them.';
|
||||
} else {
|
||||
Write-Host ($Result | Out-String);
|
||||
}
|
||||
} else {
|
||||
Write-IcingaConsoleNotice 'Your module does not have any code styling errors';
|
||||
}
|
||||
} else {
|
||||
Write-IcingaConsoleWarning 'The PowerShell ScriptAnalyzer is not installed on this system. Validating the module is not possible.';
|
||||
}
|
||||
|
||||
try {
|
||||
Import-Module -Name $ModuleDir -Force -ErrorAction Stop;
|
||||
|
||||
Write-IcingaConsoleNotice 'Your module was successfully loaded. No errors were detected';
|
||||
}
|
||||
catch {
|
||||
$ErrorScriptName = $_.InvocationInfo.ScriptName;
|
||||
$ErrorScriptLine = $_.InvocationInfo.Line;
|
||||
$PositionMessage = $_.InvocationInfo.PositionMessage;
|
||||
|
||||
if (([string]$_.FullyQualifiedErrorId).Contains('System.IO.FileLoadException')) {
|
||||
$ErrorScriptName = $ModuleManifest;
|
||||
$ErrorScriptLine = 'Check your NestedModule argument to verify that all included modules are present and valid'
|
||||
}
|
||||
|
||||
Write-IcingaConsoleError `
|
||||
-Message 'Failed to import the module "{0}": {1}{2}{2}Module File: {3}{2}Error Line: {4}{2}Position: {5}{2}{2}Full Error: {6}' `
|
||||
-Objects @(
|
||||
$ModuleName,
|
||||
$_.FullyQualifiedErrorId,
|
||||
(New-IcingaNewLine),
|
||||
$ErrorScriptName,
|
||||
$ErrorScriptLine,
|
||||
$PositionMessage,
|
||||
$_.Exception.Message
|
||||
);
|
||||
}
|
||||
}
|
||||
145
lib/core/dev/Write-IcingaForWindowsComponentManifest.psm1
Normal file
145
lib/core/dev/Write-IcingaForWindowsComponentManifest.psm1
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Updates a Icinga for Windows manifest file by updating NestedModules for
|
||||
easier usage
|
||||
.DESCRIPTION
|
||||
Updates a Icinga for Windows manifest file by updating NestedModules for
|
||||
easier usage
|
||||
.PARAMETER Name
|
||||
The name of the Icinga for Windows component to edit
|
||||
.PARAMETER ModuleConfig
|
||||
Configuration parsed as hashtable to update our manifest template with proper data
|
||||
.PARAMETER ModuleList
|
||||
An array of PowerShell module files within module to update the NestedModule entry with
|
||||
#>
|
||||
function Write-IcingaForWindowsComponentManifest()
|
||||
{
|
||||
param (
|
||||
[string]$Name,
|
||||
[hashtable]$ModuleConfig = @{ },
|
||||
[array]$ModuleList = @()
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($Name)) {
|
||||
Write-IcingaConsoleError 'Please specify a name for writing the component manifest';
|
||||
return;
|
||||
}
|
||||
|
||||
$PSDefaultParameterValues = @{ '*:Encoding' = 'utf8' };
|
||||
|
||||
[string]$ModuleName = [string]::Format('icinga-powershell-{0}', $Name.ToLower());
|
||||
[string]$ModuleRoot = Get-IcingaForWindowsRootPath;
|
||||
[string]$ModuleDir = Join-Path -Path $ModuleRoot -ChildPath $ModuleName;
|
||||
[string]$ManifestFileData = Read-IcingaFileSecure -File (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName)));
|
||||
$ContentString = New-Object -TypeName 'System.Text.StringBuilder';
|
||||
|
||||
if ([string]::IsNullOrEmpty($ManifestFileData)) {
|
||||
Write-IcingaConsoleWarning 'The manifest file of module "{0}" could not be loaded' -Objects $ModuleName;
|
||||
return;
|
||||
}
|
||||
|
||||
$ManifestFileData = $ManifestFileData.Substring($ManifestFileData.IndexOf('@'), $ManifestFileData.Length - $ManifestFileData.IndexOf('@'));
|
||||
|
||||
if ($null -ne $ModuleConfig -And $ModuleConfig.Count -ne 0) {
|
||||
foreach ($entry in $ModuleConfig.Keys) {
|
||||
$Value = $ModuleConfig[$entry];
|
||||
|
||||
if ($entry -Like '$TAGS$') {
|
||||
$Value = (ConvertFrom-IcingaArrayToString -Array $Value -AddQuotes -UseSingleQuotes)
|
||||
} elseif ($entry -Like '$REQUIREDMODULES$') {
|
||||
[int]$CurrentIndex = 0;
|
||||
foreach ($module in $Value) {
|
||||
$CurrentIndex += 1;
|
||||
$ContentString.Append('@{ ');
|
||||
|
||||
foreach ($dependency in $module.Keys) {
|
||||
$DependencyValue = $module[$dependency];
|
||||
|
||||
$ContentString.Append([string]::Format("{0} = '{1}'; ", $dependency, $DependencyValue));
|
||||
}
|
||||
$ContentString.Append('}');
|
||||
|
||||
if ($CurrentIndex -ne $Value.Count) {
|
||||
$ContentString.Append(",`r`n ");
|
||||
}
|
||||
}
|
||||
|
||||
$Value = $ContentString.ToString();
|
||||
}
|
||||
|
||||
$ManifestFileData = $ManifestFileData.Replace($entry, $Value);
|
||||
}
|
||||
|
||||
Write-IcingaFileSecure -File (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName))) -Value $ManifestFileData;
|
||||
}
|
||||
|
||||
$ContentString.Clear();
|
||||
|
||||
[array]$ManifestContent = Get-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName)));
|
||||
|
||||
if ($null -eq $ManifestContent -Or $ManifestContent.Count -eq 0) {
|
||||
Write-IcingaConsoleWarning 'The manifest file of module "{0}" could not be loaded for updating NestedModules' -Objects $ModuleName;
|
||||
return;
|
||||
}
|
||||
|
||||
[bool]$UpdateNestedModules = $FALSE;
|
||||
|
||||
foreach ($entry in $ManifestContent) {
|
||||
[string]$ManifestLine = $entry;
|
||||
|
||||
if ($UpdateNestedModules -And $entry -Like '*)*') {
|
||||
$UpdateNestedModules = $FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($UpdateNestedModules) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($entry -Like '*nestedmodules*') {
|
||||
if ($entry -NotLike '*)*') {
|
||||
$UpdateNestedModules = $TRUE;
|
||||
}
|
||||
$ContentString.AppendLine(' NestedModules = @(') | Out-Null;
|
||||
|
||||
if ($ModuleList.Count -ne 0) {
|
||||
[array]$NestedModules = (ConvertFrom-IcingaArrayToString -Array $ModuleList -AddQuotes -UseSingleQuotes).Split(',');
|
||||
[int]$ModuleIndex = 0;
|
||||
foreach ($module in $NestedModules) {
|
||||
if ([string]::IsNullOrEmpty($module)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ModuleIndex += 1;
|
||||
|
||||
if ($ModuleIndex -ne $NestedModules.Count) {
|
||||
if ($ModuleIndex -eq 1) {
|
||||
$ManifestLine = [string]::Format(' {0},', $module);
|
||||
} else {
|
||||
$ManifestLine = [string]::Format(' {0},', $module);
|
||||
}
|
||||
} else {
|
||||
if ($ModuleIndex -eq 1) {
|
||||
$ManifestLine = [string]::Format(' {0}', $module);
|
||||
} else {
|
||||
$ManifestLine = [string]::Format(' {0}', $module);
|
||||
}
|
||||
}
|
||||
|
||||
$ContentString.AppendLine($ManifestLine) | Out-Null;
|
||||
}
|
||||
}
|
||||
|
||||
$ContentString.AppendLine(' )') | Out-Null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrEmpty($ManifestLine.Replace(' ', '')) -Or $ManifestLine -eq "`r`n" -Or $ManifestLine -eq "`n") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ContentString.AppendLine($ManifestLine) | Out-Null;
|
||||
}
|
||||
|
||||
Write-IcingaFileSecure -File (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('{0}.psd1', $ModuleName))) -Value $ContentString.ToString();
|
||||
}
|
||||
|
|
@ -99,6 +99,11 @@ function Publish-IcingaPluginConfiguration()
|
|||
Get-IcingaCheckCommandConfig -CheckName $check -OutDirectory $IcingaConfigDir -FileName $check -IcingaConfig;
|
||||
}
|
||||
|
||||
if ($CheckList.Count -eq 0) {
|
||||
Write-IcingaConsoleNotice 'The module "{0}" is not containing any plugins' -Objects $ComponentName;
|
||||
return;
|
||||
}
|
||||
|
||||
Get-IcingaCheckCommandConfig -CheckName $CheckList -OutDirectory $BasketConfigDir -FileName ([string]::Format('{0}_Bundle', (Get-Culture).TextInfo.ToTitleCase($ComponentName)));
|
||||
Get-IcingaCheckCommandConfig -CheckName $CheckList -OutDirectory $IcingaConfigDir -FileName ([string]::Format('{0}_Bundle', (Get-Culture).TextInfo.ToTitleCase($ComponentName))) -IcingaConfig;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ function Start-IcingaWindowsRESTApi()
|
|||
|
||||
$RootFolder = $PSScriptRoot;
|
||||
|
||||
$global:IcingaDaemonData.IcingaThreadContent.Add('RESTApi', ([hashtable]::Synchronized(@{})));
|
||||
$global:IcingaDaemonData.IcingaThreadContent.Add('RESTApi', ([hashtable]::Synchronized(@{ })));
|
||||
$global:IcingaDaemonData.IcingaThreadPool.Add('IcingaRESTApi', (New-IcingaThreadPool -MaxInstances ($ThreadId + 3)));
|
||||
$global:IcingaDaemonData.IcingaThreadContent.RESTApi.Add('ApiRequests', ([hashtable]::Synchronized(@{})));
|
||||
$global:IcingaDaemonData.IcingaThreadContent.RESTApi.Add('ApiCallThreadAssignment', ([hashtable]::Synchronized(@{})));
|
||||
$global:IcingaDaemonData.IcingaThreadContent.RESTApi.Add('ApiRequests', ([hashtable]::Synchronized(@{ })));
|
||||
$global:IcingaDaemonData.IcingaThreadContent.RESTApi.Add('ApiCallThreadAssignment', ([hashtable]::Synchronized(@{ })));
|
||||
$global:IcingaDaemonData.IcingaThreadContent.RESTApi.Add('TotalThreads', $ConcurrentThreads);
|
||||
$global:IcingaDaemonData.IcingaThreadContent.RESTApi.Add('LastThreadId', 0);
|
||||
|
||||
|
|
|
|||
35
templates/Manifest.psd1.template
Normal file
35
templates/Manifest.psd1.template
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
@{
|
||||
ModuleVersion = '$MODULEVERSION$'
|
||||
RootModule = '$ROOTMODULE$'
|
||||
GUID = '$GUID$'
|
||||
Author = '$AUTHOR$'
|
||||
CompanyName = '$COMPANYNAME$'
|
||||
Copyright = '$COPYRIGHT$'
|
||||
Description = '$DESCRIPTION$'
|
||||
PowerShellVersion = '4.0'
|
||||
RequiredModules = @(
|
||||
$REQUIREDMODULES$
|
||||
)
|
||||
NestedModules = @(
|
||||
$NESTEDMODULES$
|
||||
)
|
||||
FunctionsToExport = @( '*' )
|
||||
CmdletsToExport = @( '*' )
|
||||
VariablesToExport = @( '*' )
|
||||
AliasesToExport = @( '*' )
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
Tags = @( $TAGS$ )
|
||||
LicenseUri = '$LICENSEURI$'
|
||||
ProjectUri = '$PROJECTURI$'
|
||||
ReleaseNotes = ''
|
||||
|
||||
};
|
||||
Version = '$VMODULEVERSION$'
|
||||
Name = '$MODULENAME$';
|
||||
Type = '$COMPONENTTYPE$';
|
||||
Function = '$DAEMONFUNCTION$';
|
||||
Endpoint = '$APIENDPOINT$';
|
||||
}
|
||||
HelpInfoURI = ''
|
||||
}
|
||||
67
templates/PSScriptAnalyzerSettings.psd1.template
Normal file
67
templates/PSScriptAnalyzerSettings.psd1.template
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
@{
|
||||
Severity = @(
|
||||
'Error',
|
||||
'Warning',
|
||||
'Information'
|
||||
);
|
||||
IncludeRules = @(
|
||||
'PSAvoidUsingPositionalParameters',
|
||||
'PSAvoidUsingInternalURLs',
|
||||
'PSAvoidUninitializedVariable',
|
||||
'PSUseApprovedVerbs',
|
||||
'PSAvoidUsingCmdletAliases',
|
||||
'PSAvoidUsingPlainTextForPassword',
|
||||
'PSMissingModuleManifestField',
|
||||
'PSUseDeclaredVarsMoreThanAssignments',
|
||||
'PSAvoidTrailingWhitespace',
|
||||
'PSAvoidUsingDeprecatedManifestFields',
|
||||
'PSUseToExportFieldsInManifest',
|
||||
'PSUseProcessBlockForPipelineCommand',
|
||||
'PSUseConsistentIndentation',
|
||||
'PSUseCompatibleCmdlets',
|
||||
'PSUseConsistentWhitespace',
|
||||
'PSAlignAssignmentStatement',
|
||||
'PSUseCorrectCasing'
|
||||
);
|
||||
ExcludeRules = @(
|
||||
'PSAvoidGlobalVars',
|
||||
'PSUseSingularNouns',
|
||||
'PSAvoidUsingWriteHost'
|
||||
)
|
||||
Rules = @{
|
||||
PSUseCompatibleCmdlets = @{
|
||||
Compatibility = @("4.0")
|
||||
};
|
||||
PSPlaceOpenBrace = @{
|
||||
Enable = $true
|
||||
OnSameLine = $false
|
||||
NewLineAfter = $true
|
||||
IgnoreOneLineBlock = $true
|
||||
};
|
||||
PSPlaceCloseBrace = @{
|
||||
Enable = $true
|
||||
NewLineAfter = $true
|
||||
IgnoreOneLineBlock = $true
|
||||
NoEmptyLineBefore = $true
|
||||
};
|
||||
PSUseConsistentIndentation = @{
|
||||
Enable = $true
|
||||
Kind = 'space'
|
||||
IndentationSize = 4
|
||||
PipelineIndentation = 'IncreaseIndentationForFirstPipeline'
|
||||
};
|
||||
PSUseConsistentWhitespace = @{
|
||||
Enable = $true
|
||||
CheckOpenBrace = $false
|
||||
CheckOpenParen = $true
|
||||
CheckOperator = $false
|
||||
CheckSeparator = $true
|
||||
};
|
||||
PSAlignAssignmentStatement = @{
|
||||
Enable = $true
|
||||
};
|
||||
PSUseCorrectCasing = @{
|
||||
Enable = $true
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue