Guest User

Untitled

a guest
Jun 22nd, 2018
213
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 121.16 KB | None | 0 0
  1. function Icinga2AgentModule {
  2. #
  3. # Setup parameters which can be accessed
  4. # with -<ParamName>
  5. #
  6. param(
  7. # Agent setup
  8. [string]$AgentName,
  9. [string]$Ticket,
  10. [string]$InstallAgentVersion,
  11. [bool]$FetchAgentName = $FALSE,
  12. [bool]$FetchAgentFQDN = $FALSE,
  13. [int]$TransformHostname = 0,
  14.  
  15. # Agent configuration
  16. [int]$AgentListenPort = 5665,
  17. [string]$ParentZone,
  18. [bool]$AcceptConfig = $TRUE,
  19. [bool]$IcingaEnableDebugLog = $FALSE,
  20. [bool]$AgentAddFirewallRule = $FALSE,
  21. [array]$ParentEndpoints,
  22. [array]$EndpointsConfig,
  23. [array]$GlobalZones = @( 'director-global' ),
  24.  
  25. # Agent installation / update
  26. [string]$IcingaServiceUser,
  27. [string]$DownloadUrl = 'https://packages.icinga.com/windows/',
  28. [string]$AgentInstallDirectory,
  29. [bool]$AllowUpdates = $FALSE,
  30. [array]$InstallerHashes,
  31. [bool]$FlushApiDirectory = $FALSE,
  32.  
  33. # Agent signing
  34. [string]$CAServer,
  35. [int]$CAPort = 5665,
  36. [bool]$ForceCertificateGeneration = $FALSE,
  37. [string]$CAFingerprint,
  38.  
  39. # Director communication
  40. [string]$DirectorUrl,
  41. [string]$DirectorUser,
  42. [string]$DirectorPassword,
  43. [string]$DirectorDomain,
  44. [string]$DirectorAuthToken,
  45. [System.Object]$DirectorHostObject,
  46. [bool]$DirectorDeployConfig = $FALSE,
  47.  
  48. # NSClient Installer
  49. [bool]$InstallNSClient = $FALSE,
  50. [bool]$NSClientAddDefaults = $FALSE,
  51. [bool]$NSClientEnableFirewall = $FALSE,
  52. [bool]$NSClientEnableService = $FALSE,
  53. [string]$NSClientDirectory,
  54. [string]$NSClientInstallerPath,
  55.  
  56. # Uninstaller arguments
  57. [bool]$FullUninstallation = $FALSE,
  58. [bool]$RemoveNSClient = $FALSE,
  59.  
  60. #Internal handling
  61. [switch]$RunInstaller = $FALSE,
  62. [switch]$RunUninstaller = $FALSE,
  63. [switch]$IgnoreSSLErrors = $FALSE,
  64. [bool]$DebugMode = $FALSE,
  65. [string]$ModuleLogFile
  66. );
  67.  
  68. #
  69. # Initialise our installer object
  70. # and generate our config objects
  71. #
  72. $installer = New-Object -TypeName PSObject;
  73. $installer | Add-Member -membertype NoteProperty -name 'properties' -value @{}
  74. $installer | Add-Member -membertype NoteProperty -name 'cfg' -value @{
  75. agent_name = $AgentName;
  76. ticket = $Ticket;
  77. agent_version = $InstallAgentVersion;
  78. fetch_agent_name = $FetchAgentName;
  79. fetch_agent_fqdn = $FetchAgentFQDN;
  80. transform_hostname = $TransformHostname;
  81. agent_listen_port = $AgentListenPort;
  82. parent_zone = $ParentZone;
  83. accept_config = $AcceptConfig;
  84. icinga_enable_debug_log = $IcingaEnableDebugLog;
  85. agent_add_firewall_rule = $AgentAddFirewallRule;
  86. parent_endpoints = $ParentEndpoints;
  87. endpoints_config = $EndpointsConfig;
  88. global_zones = $GlobalZones;
  89. icinga_service_user = $IcingaServiceUser;
  90. download_url = $DownloadUrl;
  91. agent_install_directory = $AgentInstallDirectory;
  92. allow_updates = $AllowUpdates;
  93. installer_hashes = $InstallerHashes;
  94. flush_api_directory = $FlushApiDirectory;
  95. ca_server = $CAServer;
  96. ca_port = $CAPort;
  97. force_cert = $ForceCertificateGeneration;
  98. ca_fingerprint = $CAFingerprint;
  99. director_url = $DirectorUrl;
  100. director_user = $DirectorUser;
  101. director_password = $DirectorPassword;
  102. director_domain = $DirectorDomain;
  103. director_auth_token = $DirectorAuthToken;
  104. director_host_object = $DirectorHostObject;
  105. director_deploy_config = $DirectorDeployConfig;
  106. install_nsclient = $InstallNSClient;
  107. nsclient_add_defaults = $NSClientAddDefaults;
  108. nsclient_firewall = $NSClientEnableFirewall;
  109. nsclient_service = $NSClientEnableService;
  110. nsclient_directory = $NSClientDirectory;
  111. nsclient_installer_path = $NSClientInstallerPath;
  112. full_uninstallation = $FullUninstallation;
  113. remove_nsclient = $RemoveNSClient;
  114. ignore_ssl_errors = $IgnoreSSLErrors;
  115. debug_mode = $DebugMode;
  116. module_log_file = $ModuleLogFile;
  117. }
  118.  
  119. #
  120. # Access default script config parameters
  121. # by using this function. These variables
  122. # are set during the initial call of
  123. # the script with the parameters
  124. #
  125. $installer | Add-Member -membertype ScriptMethod -name 'config' -value {
  126. param([string] $key);
  127. return $this.cfg[$key];
  128. }
  129.  
  130. #
  131. # Override the given arguments of the PowerShell script with
  132. # custom values or edited values
  133. #
  134. $installer | Add-Member -membertype ScriptMethod -name 'overrideConfig' -value {
  135. param([string] $key, $value);
  136. $this.cfg[$key] = $value;
  137. }
  138.  
  139. #
  140. # Convert a boolean value $TRUE $FALSE
  141. # to a string value
  142. #
  143. $installer | Add-Member -membertype ScriptMethod -name 'convertBoolToString' -value {
  144. param([bool]$key);
  145. if ($key) {
  146. return 'true';
  147. }
  148. return 'false';
  149. }
  150.  
  151. #
  152. # Convert a boolean value $TRUE $FALSE
  153. # to a int value
  154. #
  155. $installer | Add-Member -membertype ScriptMethod -name 'convertBoolToInt' -value {
  156. param([bool]$key);
  157. if ($key) {
  158. return 1;
  159. }
  160. return 0;
  161. }
  162.  
  163. #
  164. # Global variables can be accessed
  165. # by using this function. Example:
  166. # $this.getProperty('agent_version)
  167. #
  168. $installer | Add-Member -membertype ScriptMethod -name 'getProperty' -value {
  169. param([string] $key);
  170.  
  171. # Initialse some variables first
  172. # will only be called once
  173. if (-Not $this.properties.Get_Item('initialized')) {
  174. $this.init();
  175. }
  176.  
  177. return $this.properties.Get_Item($key);
  178. }
  179.  
  180. #
  181. # Set the value of a global variable
  182. # to ensure later usage. Example
  183. # $this.setProperty('agent_version', '2.4.10')
  184. #
  185. $installer | Add-Member -membertype ScriptMethod -name 'setProperty' -value {
  186. param([string]$key, $value);
  187.  
  188. # Initialse some variables first
  189. # will only be called once
  190. if (-Not $this.properties.Get_Item('initialized')) {
  191. $this.properties.Set_Item('initialized', $TRUE);
  192. $this.init();
  193. }
  194.  
  195. $this.properties.Set_Item($key, $value);
  196. }
  197.  
  198. #
  199. # This function will dump all global
  200. # variables of the script for debugging
  201. # purposes
  202. #
  203. $installer | Add-Member -membertype ScriptMethod -name 'dumpProperties' -value {
  204. [string]$dumpData = $this.properties | Out-String;
  205. write-host $dumpData;
  206. }
  207.  
  208. #
  209. # Dump all configured arguments for easier debugging
  210. #
  211. $installer | Add-Member -membertype ScriptMethod -name 'dumpConfig' -value {
  212. [string]$dumpData = $this.cfg | Out-String;
  213. write-host $dumpData;
  214. }
  215.  
  216. #
  217. # Write all output from consoles to a logfile
  218. #
  219. $installer | Add-Member -membertype ScriptMethod -name 'writeLogFile' -value {
  220. param([string]$severity, [string]$content);
  221.  
  222. # If no logfile is specified, do nothing
  223. if (-Not $this.config('module_log_file')) {
  224. return;
  225. }
  226.  
  227. # Store our logfile into a variable
  228. $logFile = $this.config('module_log_file');
  229.  
  230. # Have we specified a directory to write into or a file already?
  231. try {
  232. # Check if we are a directory or a file
  233. # Will return false for files or non-existing files
  234. $directory = (Get-Item $logFile) -is [System.IO.DirectoryInfo];
  235. } catch {
  236. # Nothing to catch. Simply get rid of error messages from aboves function in case of error
  237. # Will return false anyways on error
  238. }
  239.  
  240. # If we are a directory, add a file we can write to
  241. if ($directory) {
  242. $logFile = Join-Path -Path $logFile -ChildPath 'icinga2agent_psmodule.log';
  243. }
  244.  
  245. # Format a timestamp to get to know the exact date and time. Example: 2017-13-07 22:09:13.263.263
  246. $timestamp = Get-Date -Format "yyyy-dd-MM HH:mm:ss.fff";
  247. $content = [string]::Format('{0} [{1}]: {2}', $timestamp, $severity, $content);
  248.  
  249. # Write the content to our logfile
  250. Add-Content -Path $logFile -Value $content;
  251. }
  252.  
  253. #
  254. # This function will print messages as errors, but add them internally to
  255. # an exception list. These will re-printed at the end to summarize possible
  256. # issues during the run
  257. #
  258. $installer | Add-Member -membertype ScriptMethod -name 'exception' -value {
  259. param([string]$message, [string[]]$args);
  260. [array]$exceptions = $this.getProperty('exception_messages');
  261. if ($exceptions -eq $null) {
  262. $exceptions = @();
  263. }
  264. $exceptions += $message;
  265. $this.setProperty('exception_messages', $exceptions);
  266. write-host 'Fatal:' $message -ForegroundColor red;
  267. $this.writeLogFile('fatal', $message);
  268. }
  269.  
  270. #
  271. # Get the current exit code of the script. Return 0 for no errors and 1 for
  272. # possible errors, including a summary of what went wrong
  273. #
  274. $installer | Add-Member -membertype ScriptMethod -name 'getScriptExitCode' -value {
  275. [array]$exceptions = $this.getProperty('exception_messages');
  276.  
  277. if ($exceptions -eq $null) {
  278. return 0;
  279. }
  280.  
  281. $this.writeLogFile('fatal', '##################################################################');
  282. $message = '######## The script encountered several errors during run ########';
  283. $this.writeLogFile('fatal', $message);
  284. $this.writeLogFile('fatal', '##################################################################');
  285. write-host $message -ForegroundColor red;
  286. foreach ($err in $exceptions) {
  287. write-host 'Fatal:' $err -ForegroundColor red;
  288. $this.writeLogFile('fatal', $err);
  289. }
  290.  
  291. return 1;
  292. }
  293.  
  294. #
  295. # Print the relevant exception
  296. # By reading the relevant info
  297. # from the stack
  298. #
  299. $installer | Add-Member -membertype ScriptMethod -name 'printLastException' -value {
  300. $this.exception($_.Exception.Message);
  301. }
  302.  
  303. #
  304. # this function will print an info message
  305. # or throw an exception, based on the
  306. # provided exitcode
  307. # (0 = ok, anything else => exception)
  308. #
  309. $installer | Add-Member -membertype ScriptMethod -name 'printAndAssertResultBasedOnExitCode' -value {
  310. param([string]$result, [string]$exitcode);
  311. if ($exitcode -ne 0) {
  312. throw $result;
  313. } else {
  314. $this.info($result);
  315. }
  316. }
  317.  
  318. #
  319. # Return an error message with red text
  320. #
  321. $installer | Add-Member -membertype ScriptMethod -name 'error' -value {
  322. param([string] $message, [array] $args);
  323. Write-Host 'Error:' $message -ForegroundColor red;
  324. $this.writeLogFile('error', $message);
  325. }
  326.  
  327. #
  328. # Return a warning message with yellow text
  329. #
  330. $installer | Add-Member -membertype ScriptMethod -name 'warn' -value {
  331. param([string] $message, [array] $args);
  332. Write-Host 'Warning:' $message -ForegroundColor yellow;
  333. $this.writeLogFile('warning', $message);
  334. }
  335.  
  336. #
  337. # Return a info message with green text
  338. #
  339. $installer | Add-Member -membertype ScriptMethod -name 'info' -value {
  340. param([string] $message, [array] $args);
  341. Write-Host 'Notice:' $message -ForegroundColor green;
  342. $this.writeLogFile('info', $message);
  343. }
  344.  
  345. #
  346. # Return a debug message with blue text
  347. # in case debug mode is enabled
  348. #
  349. $installer | Add-Member -membertype ScriptMethod -name 'debug' -value {
  350. param([string] $message, [array] $args);
  351. if ($this.config('debug_mode')) {
  352. Write-Host 'Debug:' $message -ForegroundColor blue;
  353. $this.writeLogFile('debug', $message);
  354. }
  355. }
  356.  
  357. #
  358. # Initialise certain parts of the
  359. # script first
  360. #
  361. $installer | Add-Member -membertype ScriptMethod -name 'init' -value {
  362. $this.setProperty('initialized', $TRUE);
  363. # Set the default config dir
  364. $this.setProperty('config_dir', (Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\etc\icinga2\'));
  365. $this.setProperty('api_dir', (Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\var\lib\icinga2\api'));
  366. $this.setProperty('icinga_ticket', $this.config('ticket'));
  367. $this.setProperty('local_hostname', $this.config('agent_name'));
  368. # Ensure we generate the required configuration content
  369. $this.generateConfigContent();
  370. }
  371.  
  372. #
  373. # We require to run this script as admin. Generate the required function here
  374. # We might run this script from a non-privileged user. Ensure we have admin
  375. # rights first. Otherwise abort the script.
  376. #
  377. $installer | Add-Member -membertype ScriptMethod -name 'isAdmin' -value {
  378. $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent();
  379. $principal = New-Object System.Security.Principal.WindowsPrincipal($identity);
  380.  
  381. if (-not $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
  382. throw 'You require to run this script as administrator.';
  383. return $FALSE;
  384. }
  385. return $TRUE;
  386. }
  387.  
  388. #
  389. # In case we want to define endpoint configuration (address / port)
  390. # we will require to fetch data correctly from a given array
  391. #
  392. $installer | Add-Member -membertype ScriptMethod -name 'getEndpointConfigurationByArrayIndex' -value {
  393. param([int] $currentIndex);
  394.  
  395. # Load the config into a local variable for quicker access
  396. [array]$endpoint_config = $this.config('endpoints_config');
  397.  
  398. # In case no endpoint config is given, we should do nothing
  399. if ($endpoint_config -eq $NULL) {
  400. return '';
  401. }
  402.  
  403. [string]$configArgument = $endpoint_config[$currentIndex];
  404. [string]$config_string = '';
  405. [array]$configObject = '';
  406.  
  407. if ($configArgument -ne '') {
  408. $configObject = $configArgument.Split(';');
  409. } else {
  410. return '';
  411. }
  412.  
  413. # Write the host data from the first array position
  414. if ($configObject[0]) {
  415. $config_string += ' host = "' + $configObject[0] +'"';
  416. }
  417.  
  418. # Write the port data from the second array position
  419. if ($configObject[1]) {
  420. $config_string += "`n"+' port = ' + $configObject[1];
  421. }
  422.  
  423. # Return the host and possible port configuration for this endpoint
  424. return $config_string;
  425. }
  426.  
  427. #
  428. # Build endpoint hosts and objects based
  429. # on configuration
  430. #
  431. $installer | Add-Member -membertype ScriptMethod -name 'generateEndpointNodes' -value {
  432.  
  433. if ($this.config('parent_endpoints')) {
  434. [string]$endpoint_objects = '';
  435. [string]$endpoint_nodes = '';
  436. [int]$endpoint_index = 0;
  437.  
  438. foreach ($endpoint in $this.config('parent_endpoints')) {
  439. $endpoint_objects += 'object Endpoint "' + "$endpoint" +'" {'+"`n";
  440. $endpoint_objects += $this.getEndpointConfigurationByArrayIndex($endpoint_index);
  441. $endpoint_objects += "`n" + '}' + "`n";
  442. $endpoint_nodes += '"' + "$endpoint" + '", ';
  443. $endpoint_index += 1;
  444. }
  445. # Remove the last blank and , from the string
  446. if (-Not $endpoint_nodes.length -eq 0) {
  447. $endpoint_nodes = $endpoint_nodes.Remove($endpoint_nodes.length - 2, 2);
  448. }
  449. $this.setProperty('endpoint_nodes', $endpoint_nodes);
  450. $this.setProperty('endpoint_objects', $endpoint_objects);
  451. $this.setProperty('generate_config', 'true');
  452. } else {
  453. $this.setProperty('generate_config', 'false');
  454. }
  455. }
  456.  
  457. #
  458. # Generate global zones by configuration
  459. #
  460. $installer | Add-Member -membertype ScriptMethod -name 'generateGlobalZones' -value {
  461.  
  462. # Load all configured global zones
  463. [array]$global_zones = $this.config('global_zones');
  464. [string]$zones = '';
  465.  
  466. # In case no zones are given, simply add director-global
  467. if ($global_zones -eq $NULL) {
  468. $this.setProperty('global_zones', $zones);
  469. return;
  470. }
  471.  
  472. # Loop through all given zones and add them to our configuration
  473. foreach ($zone in $global_zones) {
  474. if ($zone -ne '') {
  475. $zones = $zones + 'object Zone "' + $zone + '" {' + "`n" + ' global = true' + "`n" + '}' + "`n";
  476. }
  477. }
  478. $this.setProperty('global_zones', $zones);
  479. }
  480.  
  481. #
  482. # Generate default config values
  483. #
  484. $installer | Add-Member -membertype ScriptMethod -name 'generateConfigContent' -value {
  485. $this.generateEndpointNodes();
  486. $this.generateGlobalZones();
  487. }
  488.  
  489. #
  490. # This function will ensure we create a
  491. # Web Client object we can use entirely
  492. # inside the module to achieve our requirements
  493. #
  494. $installer | Add-Member -membertype ScriptMethod -name 'createWebClientInstance' -value {
  495. param([string]$header, [bool]$directorHeader = $FALSE);
  496.  
  497. [System.Object]$webClient = New-Object System.Net.WebClient;
  498. if ($this.config('director_user') -And $this.config('director_password')) {
  499. [string]$domain = $null;
  500. if ($this.config('director_domain')) {
  501. $domain = $this.config('director_domain');
  502. }
  503. $webClient.Credentials = New-Object System.Net.NetworkCredential($this.config('director_user'), $this.config('director_password'), $domain);
  504. }
  505. $webClient.Headers.add('accept', $header);
  506. if ($directorHeader) {
  507. $webClient.Headers.add('X-Director-Accept', 'text/plain');
  508. }
  509.  
  510. return $webClient;
  511. }
  512.  
  513. #
  514. # Handle HTTP Requests properly to receive proper status codes in return
  515. #
  516. $installer | Add-Member -membertype ScriptMethod -name 'createHTTPRequest' -value {
  517. param([string]$url, [string]$body, [string]$method, [string]$header, [bool]$directorHeader, [bool]$printExceptionMessage);
  518.  
  519. $httpRequest = [System.Net.HttpWebRequest]::Create($url);
  520. $httpRequest.Method = $method;
  521. $httpRequest.Accept = $header;
  522. $httpRequest.ContentType = 'application/json; charset=utf-8';
  523. if ($directorHeader) {
  524. $httpRequest.Headers.Add('X-Director-Accept: text/plain');
  525. }
  526. $httpRequest.TimeOut = 6000;
  527.  
  528. # If we are using self-signed certificates for example, the HTTP request will
  529. # fail caused by the SSL certificate. With this we can allow even faulty
  530. # certificates. This should be used with caution
  531. if ($this.config('ignore_ssl_errors')) {
  532. [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
  533. }
  534.  
  535. if ($this.config('director_user') -And $this.config('director_password')) {
  536. [string]$credentials = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($this.config('director_user') + ':' + $this.config('director_password')));
  537. $httpRequest.Headers.add('Authorization: Basic ' + $credentials);
  538. }
  539.  
  540. # Only send data in case we want to send some data
  541. if ($body -ne '') {
  542. $transmitBytes = [System.Text.Encoding]::UTF8.GetBytes($body);
  543. $httpRequest.ContentLength = $transmitBytes.Length;
  544. [System.IO.Stream]$httpOutput = [System.IO.Stream]$httpRequest.GetRequestStream()
  545. $httpOutput.Write($transmitBytes, 0, $transmitBytes.Length)
  546. $httpOutput.Close()
  547. }
  548.  
  549. try {
  550.  
  551. return $this.readResponseStream($httpRequest.GetResponse());
  552.  
  553. } catch [System.Net.WebException] {
  554. if ($printExceptionMessage) {
  555. # Print an exception message and the possible body in case we received one
  556. # to make troubleshooting easier
  557. [string]$errorResponse = $this.readResponseStream($_.Exception.Response);
  558. $this.error($_.Exception.Message);
  559. if ($errorResponse -ne '') {
  560. $this.error($errorResponse);
  561. }
  562. }
  563.  
  564. $exceptionMessage = $_.Exception.Response;
  565. if ($exceptionMessage.StatusCode) {
  566. return [int][System.Net.HttpStatusCode]$exceptionMessage.StatusCode;
  567. } else {
  568. return 900;
  569. }
  570. }
  571.  
  572. return '';
  573. }
  574.  
  575. #
  576. # Read the content of a response and return it's value as a string
  577. #
  578. $installer | Add-Member -membertype ScriptMethod -name 'readResponseStream' -value {
  579. param([System.Object]$response);
  580.  
  581. if ($response) {
  582. $responseStream = $response.getResponseStream();
  583. $streamReader = New-Object IO.StreamReader($responseStream);
  584. $result = $streamReader.ReadToEnd();
  585. $response.close()
  586. $streamReader.close()
  587.  
  588. return $result;
  589. }
  590.  
  591. $this.exception('Could not retreive response from remote server. Response is null');
  592. return 'No response from remote server';
  593. }
  594.  
  595. #
  596. # Check if the provided result is an HTTP Response code
  597. #
  598. $installer | Add-Member -membertype ScriptMethod -name 'isHTTPResponseCode' -value {
  599. param([string]$httpResult);
  600.  
  601. if ($httpResult.length -eq 3) {
  602. return $TRUE;
  603. }
  604.  
  605. return $FALSE;
  606. }
  607.  
  608. #
  609. # Do we require to update the Agent?
  610. # Might be disabled by user or current version
  611. # is already installed
  612. #
  613. $installer | Add-Member -membertype ScriptMethod -name 'requireAgentUpdate' -value {
  614. if (-Not $this.config('allow_updates') -Or -Not $this.config('agent_version')) {
  615. $this.warn('Icinga 2 Agent update installation disabled.');
  616. return $FALSE;
  617. }
  618.  
  619. if ($this.getProperty('agent_version') -eq $this.config('agent_version')) {
  620. $this.info('Icinga 2 Agent up-to-date. No update required.');
  621. return $FALSE;
  622. }
  623.  
  624. $this.info('Current Icinga 2 Agent Version (' + $this.getProperty('agent_version') + ') is not matching server version (' + $this.config('agent_version') + '). Downloading new version...');
  625.  
  626. return $TRUE;
  627. }
  628.  
  629. #
  630. # We could try to install the Agent from a local directory
  631. #
  632. $installer | Add-Member -membertype ScriptMethod -name 'isDownloadPathLocal' -value {
  633. if ($this.config('download_url') -And (Test-Path ($this.config('download_url')))) {
  634. return $TRUE;
  635. }
  636. return $FALSE;
  637. }
  638.  
  639. #
  640. # Download the Icinga 2 Agent Installer from out defined source
  641. #
  642. $installer | Add-Member -membertype ScriptMethod -name 'downloadInstaller' -value {
  643. if (-Not $this.config('agent_version')) {
  644. return;
  645. }
  646.  
  647. if ($this.isDownloadPathLocal()) {
  648. $this.info('Installing Icinga 2 Agent from local directory');
  649. } else {
  650. $url = $this.config('download_url') + $this.getProperty('install_msi_package');
  651. $this.info('Downloading Icinga 2 Agent Binary from ' + $url + ' ...');
  652.  
  653. Try {
  654. [System.Object]$client = New-Object System.Net.WebClient;
  655. $client.DownloadFile($url, $this.getInstallerPath());
  656.  
  657. if (-Not $this.installerExists()) {
  658. $this.exception('Unable to locate downloaded Icinga 2 Agent installer file from ' + $url + '. Download destination: ' + $this.getInstallerPath());
  659. }
  660. } catch {
  661. $this.exception('Unable to download Icinga 2 Agent from ' + $url + '. Please ensure the link does exist and access is possible. Error: ' + $_.Exception.Message);
  662. }
  663. }
  664. }
  665.  
  666. #
  667. # In case we provide a list of hashes to very against
  668. # we check them to ensure the package we downloaded
  669. # for the Agent installation is allowed to be installed
  670. #
  671. $installer | Add-Member -membertype ScriptMethod -name 'verifyInstallerChecksumAndThrowException' -value {
  672. if (-Not $this.config('installer_hashes')) {
  673. $this.warn("Icinga 2 Agent Installer verification disabled.");
  674. return;
  675. }
  676.  
  677. [string]$installerHash = $this.getInstallerFileHash($this.getInstallerPath());
  678. foreach($hash in $this.config('installer_hashes')) {
  679. if ($hash -eq $installerHash) {
  680. $this.info('Icinga 2 Agent hash verification successfull.');
  681. return;
  682. }
  683. }
  684.  
  685. throw 'Failed to verify against any provided installer hash.';
  686. return;
  687. }
  688.  
  689. #
  690. # Get the SHA1 hash from our uninstaller file
  691. # Own function required because Get-FileHash is not
  692. # supported by PowerShell Version 2
  693. #
  694. $installer | Add-Member -membertype ScriptMethod -name 'getInstallerFileHash' -value {
  695. param([string]$filename);
  696.  
  697. [System.Object]$fileInput = New-Object System.IO.FileStream($filename,[System.IO.FileMode]::Open);
  698. [System.Object]$hash = New-Object System.Text.StringBuilder;
  699. [System.Security.Cryptography.HashAlgorithm]::Create('SHA1').ComputeHash($fileInput) |
  700. ForEach-Object {
  701. [Void]$hash.Append($_.ToString("x2"));
  702. }
  703. $fileInput.Close();
  704. return $hash.ToString().ToUpper();
  705. }
  706.  
  707. #
  708. # Returns the full path to our installer package
  709. #
  710. $installer | Add-Member -membertype ScriptMethod -name 'getInstallerPath' -value {
  711. if (-Not $this.config('download_url') -Or -Not $this.getProperty('install_msi_package')) {
  712. return '';
  713. }
  714. $installerPath = Join-Path -Path $this.config('download_url') -ChildPath $this.getProperty('install_msi_package')
  715. if ($this.isDownloadPathLocal()) {
  716. if (Test-Path $installerPath) {
  717. return $installerPath;
  718. } else {
  719. $this.exception('Failed to locate local Icinga 2 Agent installer at ' + $installerPath);
  720. return '';
  721. }
  722. } else {
  723. return (Join-Path -Path $Env:temp -ChildPath $this.getProperty('install_msi_package'));
  724. }
  725. }
  726.  
  727. #
  728. # Verify that the installer package we downloaded
  729. # does exist in first place
  730. #
  731. $installer | Add-Member -membertype ScriptMethod -name 'installerExists' -value {
  732. if ($this.getInstallerPath() -And (Test-Path $this.getInstallerPath())) {
  733. return $TRUE;
  734. }
  735. return $FALSE;
  736. }
  737.  
  738. #
  739. # Get all arguments for the Icinga 2 Agent installer package
  740. #
  741. $installer | Add-Member -membertype ScriptMethod -name 'getIcingaAgentInstallerArguments' -value {
  742. # Initialise some basic variables
  743. [string]$arguments = '';
  744. [string]$installerLocation = '';
  745.  
  746. # By default, install the Icinga 2 Agent again in the pre-installed directory
  747. # before the update. Will only apply during updates / downgrades of the Agent
  748. if ($this.getProperty('cur_install_dir')) {
  749. # In case we perform an architecture change, we should use the new default location as source in case
  750. # we have installed the Agent into Program Files (x86) for example but are now using a x64 Agent
  751. # which should be installed into Program Files instead
  752. if ($this.getProperty('agent_architecture_change') -And $this.getProperty('agent_migration_target')) {
  753. $installerLocation = [string]::Format(' INSTALL_ROOT="{0}"', $this.getProperty('agent_migration_target'));
  754. } else {
  755. $installerLocation = [string]::Format(' INSTALL_ROOT="{0}"', $this.getProperty('cur_install_dir'));
  756. }
  757. }
  758.  
  759. # However, if we specified a custom directory over the argument, always use that
  760. # one as installer target directory
  761. if ($this.config('agent_install_directory')) {
  762. $installerLocation = [string]::Format(' INSTALL_ROOT="{0}"', $this.config('agent_install_directory'));
  763. $this.setProperty('cur_install_dir', $this.config('agent_install_directory'));
  764. }
  765.  
  766. $arguments += $installerLocation;
  767.  
  768. return $arguments;
  769. }
  770.  
  771. #
  772. # Do we require to migrate data from previous Icinga 2 Agent Directory
  773. #
  774. $installer | Add-Member -membertype ScriptMethod -name 'checkForIcingaMigrationRequirement' -value {
  775. if ($this.getProperty('cur_install_dir')) {
  776. [string]$installDir = $this.getProperty('cur_install_dir');
  777. # Just in case we installed an x86 Agent on the System, we will require to migrate to x64 on a x64 system.
  778. if (${Env:ProgramFiles(x86)} -And $installDir.contains(${Env:ProgramFiles(x86)}) -And $this.getProperty('system_architecture') -eq 'x86_64') {
  779. [string]$migrationPath = $installDir.Replace(${Env:ProgramFiles(x86)}, ${Env:ProgramFiles});
  780. $this.setProperty('agent_architecture_change', $TRUE);
  781. $this.setProperty('require_migration', $TRUE);
  782. $this.setProperty('agent_migration_source', $installDir);
  783. $this.setProperty('agent_migration_target', $migrationPath);
  784. $this.setProperty('cur_install_dir', $migrationPath);
  785. $this.warn('Detected architecture change. Current installed Agent version is x86, while new installed version will be x64. Possible data will be migrated.');
  786. } else {
  787. $this.setProperty('agent_migration_source', $this.getProperty('cur_install_dir'));
  788. }
  789. }
  790.  
  791. if ($this.config('agent_install_directory')) {
  792. [string]$currentInstallDir = $this.cutLastSlashFromDirectoryPath($this.getProperty('cur_install_dir'));
  793. [string]$intendedInstallDir = $this.cutLastSlashFromDirectoryPath($this.config('agent_install_directory'));
  794.  
  795. if ($currentInstallDir -ne $intendedInstallDir) {
  796. $this.setProperty('agent_migration_target', $this.config('agent_install_directory'));
  797. $this.setProperty('require_migration', $TRUE);
  798. }
  799. }
  800. }
  801.  
  802. #
  803. # To ensure we handle path strings correctly, we always require to cut the last \
  804. #
  805. $installer | Add-Member -membertype ScriptMethod -name 'cutLastSlashFromDirectoryPath' -value {
  806. param([string]$path);
  807.  
  808. if (-Not $path -Or $path -eq '') {
  809. return $path;
  810. }
  811.  
  812. if ($path[$path.Length - 1] -eq '\') {
  813. $path = $path.Substring(0, $path.Length - 1);
  814. }
  815.  
  816. return $path;
  817. }
  818.  
  819. #
  820. # Install the Icinga 2 agent from the provided installation package
  821. #
  822. $installer | Add-Member -membertype ScriptMethod -name 'installAgent' -value {
  823. $this.downloadInstaller();
  824. if (-Not $this.installerExists()) {
  825. $this.exception('Failed to setup Icinga 2 Agent. Installer package not found.');
  826. return;
  827. }
  828. $this.verifyInstallerChecksumAndThrowException();
  829. $this.info('Installing Icinga 2 Agent...');
  830.  
  831. # Start the installer process
  832. $result = $this.startProcess('MsiExec.exe', $TRUE, [string]::Format('/quiet /i "{0}" {1}', $this.getInstallerPath(), $this.getIcingaAgentInstallerArguments()));
  833.  
  834. # Exit Code 0 means the Agent was installed successfully
  835. # Otherwise we require to throw an error
  836. if ($result.Get_Item('exitcode') -ne 0) {
  837. $this.exception('Failed to install Icinga 2 Agent. ' + $result.Get_Item('message'));
  838. } else {
  839. $this.info('Icinga 2 Agent installed.');
  840. }
  841.  
  842. $this.setProperty('require_restart', 'true');
  843. }
  844.  
  845. #
  846. # Updates the Agent in case allowed and required.
  847. # Removes previous version of Icinga 2 Agent first
  848. #
  849. $installer | Add-Member -membertype ScriptMethod -name 'updateAgent' -value {
  850. $this.downloadInstaller();
  851. if (-Not $this.installerExists()) {
  852. $this.exception('Failed to update Icinga 2 Agent. Installer package not found.');
  853. return;
  854. }
  855. $this.verifyInstallerChecksumAndThrowException()
  856. if (-Not $this.getProperty('uninstall_id')) {
  857. $this.exception('Failed to update Icinga 2 Agent. Uninstaller is not specified.');
  858. return;
  859. }
  860.  
  861. $this.info('Removing previous Icinga 2 Agent version...');
  862. # Start the uninstaller process
  863. $result = $this.startProcess('MsiExec.exe', $TRUE, $this.getProperty('uninstall_id') +' /q');
  864.  
  865. # Exit Code 0 means the Agent was removed successfully
  866. # Otherwise we require to throw an error
  867. if ($result.Get_Item('exitcode') -ne 0) {
  868. $this.exception('Failed to remove Icinga 2 Agent. ' + $result.Get_Item('message'));
  869. } else {
  870. $this.info('Icinga 2 Agent successfully removed.');
  871. }
  872.  
  873. $this.checkForIcingaMigrationRequirement();
  874. $this.applyPossibleAgentMigration();
  875.  
  876. $this.info('Installing new Icinga 2 Agent version...');
  877. # Start the installer process
  878. $result = $this.startProcess('MsiExec.exe', $TRUE, [string]::Format('/quiet /i "{0}" {1}', $this.getInstallerPath(), $this.getIcingaAgentInstallerArguments()));
  879.  
  880. # Exit Code 0 means the Agent was removed successfully
  881. # Otherwise we require to throw an error
  882. if ($result.Get_Item('exitcode') -ne 0) {
  883. $this.exception('Failed to install new Icinga 2 Agent. ' + $result.Get_Item('message'));
  884. } else {
  885. $this.info('Icinga 2 Agent successfully updated.');
  886. }
  887.  
  888. $this.setProperty('require_restart', 'true');
  889. }
  890.  
  891. #
  892. # Migrate a folder and it's content from a previous Agent installation to
  893. # a new target destination
  894. #
  895. $installer | Add-Member -membertype ScriptMethod -name 'doMigrateIcingaDirectory' -value {
  896. param([string]$sourcePath, [string]$targetPath, [string]$directory);
  897.  
  898. if (Test-Path (Join-Path -Path $sourcePath -ChildPath $directory)) {
  899. [string]$source = Join-Path -Path $sourcePath -ChildPath $directory;
  900. [string]$target = Join-Path -Path $targetPath -ChildPath $directory;
  901. $this.info([string]::Format('Migrating content from "{0}" to "{1}"', $source, $target));
  902. $result = Copy-Item $source $target -Recurse;
  903. }
  904. }
  905.  
  906. #
  907. # Copy a single file from it's source location to our target location
  908. #
  909. $installer | Add-Member -membertype ScriptMethod -name 'doMigrateIcingaFile' -value {
  910. param([string]$sourcePath, [string]$targetPath, [string]$file);
  911. $this.info([string]::Format('Migrating file from "{0}" to "{1}\{2}"', $sourcePath, $targetPath, $_));
  912. Copy-Item $sourcePath $targetPath;
  913. }
  914.  
  915. #
  916. # This function will determine if we require to migrate content from a previous
  917. # Icinga 2 Agent installation to the new location
  918. #
  919. $installer | Add-Member -membertype ScriptMethod -name 'applyPossibleAgentMigration' -value {
  920. if (-Not $this.getProperty('require_migration') -Or $this.getProperty('require_migration') -eq $FALSE) {
  921. $this.info('No migration of Icinga 2 Agent data required.')
  922. return;
  923. }
  924.  
  925. $this.info([string]::Format('Icinga 2 Agent installation location changed from {0} to {1}. Migrating possible content...', $this.getProperty('agent_migration_source'), $this.getProperty('agent_migration_target')));
  926.  
  927. if ($this.getProperty('agent_migration_source') -And (Test-Path ($this.getProperty('agent_migration_source')))) {
  928. # Load Directories and Remove \ at the end of the path if present to ensure we have the same path base
  929. [string]$sourcePath = $this.cutLastSlashFromDirectoryPath($this.getProperty('agent_migration_source'));
  930. [string]$targetPath = $this.cutLastSlashFromDirectoryPath($this.getProperty('agent_migration_target'));
  931.  
  932. # Get all objects within our source root and copy it to our target destination
  933. $result = Get-ChildItem -Path $sourcePath |
  934. ForEach-Object {
  935. if ($_.PSIsContainer) {
  936. $this.doMigrateIcingaDirectory($sourcePath, $targetPath, $_);
  937. } else {
  938. $this.doMigrateIcingaFile($_.FullName, $targetPath, $_);
  939. }
  940. }
  941. $this.info([string]::Format('Migration of source folder applied. Please remove content from previous directory {0} if no longer required.', $sourcePath));
  942. } else {
  943. $this.info('No data for migration found. Setup is clean.');
  944. }
  945. }
  946.  
  947. #
  948. # We might have installed the Icinga 2 Agent
  949. # already. In case we do, get all data to
  950. # ensure we access the Agent correctly
  951. #
  952. $installer | Add-Member -membertype ScriptMethod -name 'isAgentInstalled' -value {
  953. [string]$architecture = '';
  954. if ([IntPtr]::Size -eq 4) {
  955. $architecture = "x86";
  956. $regPath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*';
  957. } else {
  958. $architecture = "x86_64";
  959. $regPath = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*');
  960. }
  961.  
  962. # Try locating current Icinga 2 Agent installation
  963. $localData = Get-ItemProperty $regPath |
  964. .{
  965. process {
  966. if ($_.DisplayName) {
  967. $_;
  968. }
  969. }
  970. } |
  971. Where-Object {
  972. $_.DisplayName -eq 'Icinga 2';
  973. } |
  974. Select-Object -Property InstallLocation, UninstallString, DisplayVersion;
  975.  
  976. if ($localData.UninstallString) {
  977. $this.setProperty('uninstall_id', $localData.UninstallString.Replace("MsiExec.exe ", ""));
  978. }
  979. $this.setProperty('cur_install_dir', $localData.InstallLocation);
  980. $this.setProperty('agent_version', $localData.DisplayVersion);
  981. $this.setProperty('install_msi_package', 'Icinga2-v' + $this.config('agent_version') + '-' + $architecture + '.msi');
  982. $this.setProperty('system_architecture', $architecture);
  983.  
  984. if ($localData.InstallLocation) {
  985. $this.info('Found Icinga 2 Agent version ' + $localData.DisplayVersion + ' installed at ' + $localData.InstallLocation);
  986. return $TRUE;
  987. } else {
  988. $this.warn('Icinga 2 Agent does not seem to be installed on the system');
  989. # Set Default value for install dir
  990. $this.setProperty('cur_install_dir', (Join-Path $Env:ProgramFiles -ChildPath 'ICINGA2'));
  991. }
  992. return $FALSE;
  993. }
  994.  
  995. #
  996. # Ensure we are able to install a firewall rule for the Icinga 2 Agent,
  997. # allowing masters and satellites to connect to our local agent
  998. #
  999. $installer | Add-Member -membertype ScriptMethod -name 'installIcingaAgentFirewallRule' -value {
  1000. if ($this.config('agent_add_firewall_rule') -eq $FALSE) {
  1001. $this.warn('Icinga 2 Agent Firewall Rule will not be installed.');
  1002. return;
  1003. }
  1004.  
  1005. $this.info('Trying to install Icinga 2 Agent Firewall Rule for port ' + $this.config('agent_listen_port'));
  1006.  
  1007. $result = $this.startProcess('netsh', $FALSE, 'advfirewall firewall show rule name="Icinga 2 Agent Inbound by PS-Module"');
  1008. if ($result.Get_Item('exitcode') -eq 0) {
  1009. # Firewall rule is already defined -> delete it and add it again
  1010.  
  1011. $this.info('Icinga 2 Agent Firewall Rule already installed. Trying to remove it to add it again...');
  1012. $result = $this.startProcess('netsh', $TRUE, 'advfirewall firewall delete rule name="Icinga 2 Agent Inbound by PS-Module"');
  1013.  
  1014. if ($result.Get_Item('exitcode') -ne 0) {
  1015. $this.error('Failed to remove Icinga 2 Agent Firewall rule before adding it again: ' + $result.Get_Item('message'));
  1016. return;
  1017. } else {
  1018. $this.info('Icinga 2 Agent Firewall Rule has been removed. Re-Adding now...');
  1019. }
  1020. }
  1021.  
  1022. [string]$argument = 'advfirewall firewall add rule'
  1023. $argument = $argument + ' dir=in action=allow program="' + $this.getInstallPath() + 'sbin\icinga2.exe"';
  1024. $argument = $argument + ' name="Icinga 2 Agent Inbound by PS-Module"';
  1025. $argument = $argument + ' description="Inbound Firewall Rule to allow Icinga 2 masters/satellites to connect to the Icinga 2 Agent installed on this system."';
  1026. $argument = $argument + ' enable=yes';
  1027. $argument = $argument + ' remoteip=any';
  1028. $argument = $argument + ' localip=any';
  1029. $argument = $argument + ' localport=' + $this.config('agent_listen_port');
  1030. $argument = $argument + ' protocol=tcp';
  1031.  
  1032. $result = $this.startProcess('netsh', $FALSE, $argument);
  1033. if ($result.Get_Item('exitcode') -ne 0) {
  1034. # Firewall rule was not added -> print error
  1035. $this.error('Failed to install Icinga 2 Agent Firewall: ' + $result.Get_Item('message'));
  1036. return;
  1037. }
  1038.  
  1039. $this.info('Icinga 2 Agent Firewall Rule successfully installed for port ' + $this.config('agent_listen_port'));
  1040. }
  1041.  
  1042. #
  1043. # Get the default path or our custom path for the NSClient++
  1044. #
  1045. $installer | Add-Member -membertype ScriptMethod -name 'getNSClientDefaultExecutablePath' -value {
  1046.  
  1047. if ($this.config('nsclient_directory')) {
  1048. return (Join-Path -Path $this.config('nsclient_directory') -ChildPath 'nscp.exe');
  1049. }
  1050.  
  1051. if (Test-Path ('C:\Program Files\NSClient++\nscp.exe')) {
  1052. return 'C:\Program Files\NSClient++\nscp.exe';
  1053. }
  1054.  
  1055. if (Test-Path ('C:\Program Files (x86)\NSClient++\nscp.exe')) {
  1056. return 'C:\Program Files (x86)\NSClient++\nscp.exe';
  1057. }
  1058.  
  1059. return '';
  1060. }
  1061.  
  1062. #
  1063. # In case have the Agent already installed
  1064. # We might use a different installation path
  1065. # for the Agent. This function will return
  1066. # the correct, valid installation path
  1067. #
  1068. $installer | Add-Member -membertype ScriptMethod -name 'getInstallPath' -value {
  1069. [string]$agentPath = '';
  1070. if ($this.getProperty('cur_install_dir')) {
  1071. $agentPath = $this.getProperty('cur_install_dir');
  1072. }
  1073. return $agentPath;
  1074. }
  1075.  
  1076. #
  1077. # In case we installed the agent freshly we
  1078. # require to change configuration once we
  1079. # would like to use the Director properly
  1080. # This function will simply do a backup
  1081. # of the icinga2.conf, ensuring we can
  1082. # use them later again
  1083. #
  1084. $installer | Add-Member -membertype ScriptMethod -name 'backupDefaultConfig' -value {
  1085. [string]$configFile = Join-Path -Path $this.getProperty('config_dir') -ChildPath 'icinga2.conf';
  1086. [string]$configBackupFile = $configFile + 'director.bak';
  1087.  
  1088. # Check if a config and backup file already exists
  1089. # Only procceed with backup of the current config if no backup was found
  1090. if (Test-Path $configFile) {
  1091. if (-Not (Test-Path $configBackupFile)) {
  1092. Rename-Item $configFile $configBackupFile;
  1093. $this.info('Icinga 2 configuration backup successfull');
  1094. } else {
  1095. $this.warn('Default icinga2.conf backup detected. Skipping backup');
  1096. }
  1097. }
  1098. }
  1099.  
  1100. #
  1101. # Allow us to restart the Icinga 2 Agent
  1102. #
  1103. $installer | Add-Member -membertype ScriptMethod -name 'cleanupAgentInstaller' -value {
  1104. if (-Not ($this.isDownloadPathLocal())) {
  1105. if ($this.getInstallerPath() -And (Test-Path $this.getInstallerPath())) {
  1106. $this.info('Removing downloaded Icinga 2 Agent installer');
  1107. Remove-Item $this.getInstallerPath() | out-null;
  1108. }
  1109. }
  1110. }
  1111.  
  1112. #
  1113. # Get Api directory if Icinga 2
  1114. #
  1115. $installer | Add-Member -membertype ScriptMethod -name 'getApiDirectory' -value {
  1116. return $this.getProperty('api_dir');
  1117. }
  1118.  
  1119. #
  1120. # Should we remove the Api directory content
  1121. # from the Agent? Can be defined by setting the
  1122. # -RemoveApiDirectory argument of the function builder
  1123. #
  1124. $installer | Add-Member -membertype ScriptMethod -name 'shouldFlushIcingaApiDirectory' -value {
  1125. return $this.config('flush_api_directory');
  1126. }
  1127.  
  1128. #
  1129. # Flush all content from the Icinga 2 Agent
  1130. # Api directory, but keep the folder structure
  1131. #
  1132. $installer | Add-Member -membertype ScriptMethod -name 'flushIcingaApiDirectory' -value {
  1133. if ((Test-Path $this.getApiDirectory()) -And $this.shouldFlushIcingaApiDirectory()) {
  1134. $this.info('Flushing content of ' + $this.getApiDirectory());
  1135. [System.Object]$folder = New-Object -ComObject Scripting.FileSystemObject;
  1136. $folder.DeleteFolder($this.getApiDirectory());
  1137. }
  1138. }
  1139.  
  1140. #
  1141. # Modify the user the Icinga services is running with
  1142. #
  1143. $installer | Add-Member -membertype ScriptMethod -name 'modifyIcingaServiceUser' -value {
  1144.  
  1145. # If no user is specified -> do nothing
  1146. if ($this.config('icinga_service_user') -eq '') {
  1147. return;
  1148. }
  1149.  
  1150. [System.Object]$currentUser = Get-WMIObject win32_service -Filter "Name='icinga2'";
  1151. [string]$credentials = $this.config('icinga_service_user');
  1152. [string]$newUser = '';
  1153. [string]$password = 'dummy';
  1154.  
  1155. if ($currentUser -eq $null) {
  1156. $this.warn('Unable to modify Icinga service user: Service not found.');
  1157. return;
  1158. }
  1159.  
  1160. # Check if we defined user name and password (':' cannot appear within a username)
  1161. # If so split them into seperate variables, otherwise simply use the string as user
  1162. if ($credentials.Contains(':')) {
  1163. [int]$delimeter = $credentials.IndexOf(':');
  1164. $newUser = $credentials.Substring(0, $delimeter);
  1165. $password = $credentials.Substring($delimeter + 1, $credentials.Length - 1 - $delimeter);
  1166. } else {
  1167. $newUser = $credentials;
  1168. }
  1169.  
  1170. # If the user's are identical -> do nothing
  1171. if ($currentUser.StartName -eq $newUser) {
  1172. $this.info('Icinga user was not modified. Source and target service user are identical.');
  1173. return;
  1174. }
  1175.  
  1176. # Try to update the service name and return an error in case of a failure
  1177. # In the error case we do not have to deal with cleanup, as no change was made anyway
  1178. $this.info('Updating Icinga 2 service user to ' + $newUser);
  1179. $result = $this.startProcess('sc.exe', $TRUE, 'config icinga2 obj="' + $newUser + '" ' + 'password=' + $password);
  1180.  
  1181. if ($result.Get_Item('exitcode') -ne 0) {
  1182. $this.error($result.Get_Item('message'));
  1183. return;
  1184. }
  1185.  
  1186. # Just write the success message
  1187. $this.info($result.Get_Item('message'));
  1188.  
  1189. # Try to restart the service
  1190. $result = $this.restartService('icinga2');
  1191.  
  1192. # In case of an error try to rollback to the previous assigned user of the service
  1193. # If this fails aswell, set the user to 'LocalSystem' and restart the service to
  1194. # ensure that the agent is atleast running and collecting some data.
  1195. # Of course we throw plenty of errors to notify the user about problems
  1196. if ($result.Get_Item('exitcode') -ne 0) {
  1197. $this.error($result.Get_Item('message'));
  1198. $this.info('Reseting user to previous working user ' + $currentUser.StartName);
  1199. $result = $this.startProcess('sc.exe', $TRUE, 'config icinga2 obj="' + $currentUser.StartName + '" ' + 'password=' + $password);
  1200. $result = $this.restartService('icinga2');
  1201. if ($result.Get_Item('exitcode') -ne 0) {
  1202. $this.error('Failed to reset Icinga 2 service user to the previous user ' + $currentUser.StartName + '. Setting user to "LocalSystem" now to ensure the service integrity');
  1203. $result = $this.startProcess('sc.exe', $TRUE, 'config icinga2 obj="LocalSystem" password=dummy');
  1204. $this.info($result.Get_Item('message'));
  1205. $result = $this.restartService('icinga2');
  1206. if ($result.Get_Item('exitcode') -eq 0) {
  1207. $this.info('Reseting Icinga 2 service user to "LocalSystem" successfull.');
  1208. return;
  1209. } else {
  1210. $this.error('Failed to rollback Icinga 2 service user to "LocalSystem": ' + $result.Get_Item('message'));
  1211. return;
  1212. }
  1213. }
  1214. }
  1215.  
  1216. $this.info('Icinga 2 service is running');
  1217. }
  1218.  
  1219. #
  1220. # Function to make restart of services easier
  1221. #
  1222. $installer | Add-Member -membertype ScriptMethod -name 'restartService' -value {
  1223. param([string]$service);
  1224.  
  1225. $this.info('Restarting service ' + $service + '...');
  1226.  
  1227. # Stop the current service
  1228. $result = $this.startProcess("sc.exe", $TRUE, "stop $service");
  1229.  
  1230. # Wait until the service is stopped
  1231. $serviceResult = $this.waitForServiceToReachState($service, 'Stopped');
  1232.  
  1233. # Start the service again
  1234. $result = $this.startProcess("sc.exe", $TRUE, "start $service");
  1235.  
  1236. # Wait until the service is started
  1237. if ($this.waitForServiceToReachState($service, 'Running') -eq $FALSE) {
  1238. $result.Set_Item('message', 'Failed to restart service ' + $service + '.');
  1239. $result.Set_Item('exitcode', '1');
  1240. }
  1241.  
  1242. return $result;
  1243. }
  1244.  
  1245. #
  1246. # This function will wait for a specific service until it reaches
  1247. # the defined state. Will break after 20 seconds with an error message
  1248. #
  1249. $installer | Add-Member -membertype ScriptMethod -name 'waitForServiceToReachState' -value {
  1250. param([string]$service, [string]$state);
  1251.  
  1252. [int]$counter = 0;
  1253.  
  1254. # Wait until the service reached the desired state
  1255. while ($TRUE) {
  1256.  
  1257. # Get the current state of the service
  1258. $serviceState = (Get-WMIObject win32_service -Filter "Name='$service'").State;
  1259. if ($serviceState -eq $state) {
  1260. break;
  1261. }
  1262.  
  1263. # Sleep a little to prevent crushing the CPU
  1264. Start-Sleep -Milliseconds 100;
  1265. $counter += 1;
  1266.  
  1267. # After 20 seconds break with an error. It look's like the service does not respond
  1268. if ($counter -gt 200) {
  1269. $this.error('Timeout reached while waiting for ' + $service + ' to reach state ' + $state + '. Service is not responding.');
  1270. return $FALSE;
  1271. }
  1272. }
  1273.  
  1274. # Wait one second and check the status again to ensure it remains within it's state
  1275. Start-Sleep -Seconds 1;
  1276.  
  1277. if ($state -ne (Get-WMIObject win32_service -Filter "Name='$service'").State) {
  1278. return $FALSE;
  1279. }
  1280.  
  1281. return $TRUE;
  1282. }
  1283.  
  1284. #
  1285. # Function to start processes and wait for their exit
  1286. # Will return a dictionary with results (message, error, exitcode)
  1287. #
  1288. $installer | Add-Member -membertype ScriptMethod -name 'startProcess' -value {
  1289. param([string]$executable, [bool]$flushNewLines, [string]$arguments);
  1290.  
  1291. $processData = New-Object System.Diagnostics.ProcessStartInfo;
  1292. $processData.FileName = $executable;
  1293. $processData.RedirectStandardError = $true;
  1294. $processData.RedirectStandardOutput = $true;
  1295. $processData.UseShellExecute = $false;
  1296. $processData.Arguments = $arguments;
  1297. $process = New-Object System.Diagnostics.Process;
  1298. $process.StartInfo = $processData;
  1299. $process.Start() | Out-Null;
  1300. $stdout = $process.StandardOutput.ReadToEnd();
  1301. $stderr = $process.StandardError.ReadToEnd();
  1302. $process.WaitForExit();
  1303.  
  1304. if ($flushNewLines) {
  1305. $stdout = $stdout.Replace("`n", '').Replace("`r", '');
  1306. $stderr = $stderr.Replace("`n", '').Replace("`r", '');
  1307. } else {
  1308. if ($stdout.Contains("`n")) {
  1309. $stdout = $stdout.Substring(0, $stdout.LastIndexOf("`n"));
  1310. }
  1311. }
  1312.  
  1313. $result = @{};
  1314. $result.Add('message', $stdout);
  1315. $result.Add('error', $stderr);
  1316. $result.Add('exitcode', $process.ExitCode);
  1317.  
  1318. return $result;
  1319. }
  1320.  
  1321. #
  1322. # Restart the Icinga 2 service and get the
  1323. # result if the restart failed or everything
  1324. # worked as expected
  1325. #
  1326. $installer | Add-Member -membertype ScriptMethod -name 'restartAgent' -value {
  1327. $result = $this.restartService('icinga2');
  1328.  
  1329. if ($result.Get_Item('exitcode') -eq 0) {
  1330. $this.info('Icinga 2 Agent successfully restarted.');
  1331. $this.setProperty('require_restart', '');
  1332. } else {
  1333. $this.error($result.Get_Item('message'));
  1334. }
  1335. }
  1336.  
  1337. $installer | Add-Member -membertype ScriptMethod -name 'generateIcingaConfiguration' -value {
  1338. if ($this.getProperty('generate_config') -eq 'true') {
  1339.  
  1340. $this.checkConfigInputParametersAndThrowException();
  1341.  
  1342. [string]$icingaCurrentConfig = '';
  1343. if (Test-Path $this.getIcingaConfigFile()) {
  1344. $icingaCurrentConfig = [System.IO.File]::ReadAllText($this.getIcingaConfigFile());
  1345. }
  1346.  
  1347. [string]$icingaNewConfig =
  1348. '/**
  1349. * Icinga 2 Config - Proposed by Icinga 2 PowerShell Module
  1350. */
  1351.  
  1352. /* Define our includes to run the agent properly. */
  1353. include "constants.conf"
  1354. include <itl>
  1355. include <plugins>
  1356. include <nscp>
  1357. include <windows-plugins>
  1358.  
  1359. /* Define our block required to enable or disable Icinga 2 debug log
  1360. * Enable or disable it by using the PowerShell Module with
  1361. * argument -IcingaEnableDebugLog or by switching
  1362. * PowerShellIcinga2EnableDebug to true or false manually.
  1363. * true: Debug log is active
  1364. * false: Debug log is deactivated
  1365. * IMPORTANT: ";" after true or false has to remain to allow the
  1366. * PowerShell Module to switch this feature on or off.
  1367. */
  1368. const PowerShellIcinga2EnableDebug = false;
  1369. if (PowerShellIcinga2EnableDebug) {
  1370. object FileLogger "debug-file" {
  1371. severity = "debug"
  1372. path = LocalStateDir + "/log/icinga2/debug.log"
  1373. }
  1374. }
  1375.  
  1376. /* Try to define a constant for our NSClient++ installation
  1377. * IMPORTANT: If the NSClient++ is installed newly to the system, the
  1378. * Icinga Service has to be restarted in order to set this variable
  1379. * correctly. If the NSClient++ is installed over the PowerShell Module,
  1380. * the Icinga 2 Service is restarted automaticly.
  1381. */
  1382. if (!globals.contains("NscpPath")) {
  1383. NscpPath = dirname(msi_get_component_path("{5C45463A-4AE9-4325-96DB-6E239C034F93}"))
  1384. }
  1385.  
  1386. /* Enable our default main logger feature to write log output. */
  1387. object FileLogger "main-log" {
  1388. severity = "information"
  1389. path = LocalStateDir + "/log/icinga2/icinga2.log"
  1390. }
  1391.  
  1392. /* All informations required to correctly connect to our parent Icinga 2 nodes. */
  1393. object Endpoint "' + $this.getProperty('local_hostname') + '" {}
  1394. ' + $this.getProperty('endpoint_objects') + '
  1395. /* Define the zone and its containing endpoints we should communicate with. */
  1396. object Zone "' + $this.config('parent_zone') + '" {
  1397. endpoints = [ ' + $this.getProperty('endpoint_nodes') +' ]
  1398. }
  1399.  
  1400. /* All of our global zones, check commands and other configuration are synced into.
  1401. * Director global zone must be defined in case the Icinga Director is beeing used.
  1402. * Default value for this is "director-global".
  1403. * All additional zones can be configured with -GlobalZones argument.
  1404. * IMPORTANT: If -GlobalZones argument is used, the Icinga Director global zones has
  1405. * to be defined as well within the argument array.
  1406. */
  1407. ' + $this.getProperty('global_zones') + '
  1408. /* Define a zone for our current agent and set our parent zone for proper communication. */
  1409. object Zone "' + $this.getProperty('local_hostname') + '" {
  1410. parent = "' + $this.config('parent_zone') + '"
  1411. endpoints = [ "' + $this.getProperty('local_hostname') + '" ]
  1412. }
  1413.  
  1414. /* Configure all settings we require for our API listener to properly work.
  1415. * This will include the certificates, if we accept configurations which
  1416. * can be changed with argument -AcceptConfig and the bind informations.
  1417. * The bind_port can be modified with argument -AgentListenPort.
  1418. */
  1419. object ApiListener "api" {
  1420. cert_path = SysconfDir + "/icinga2/pki/' + $this.getProperty('local_hostname') + '.crt"
  1421. key_path = SysconfDir + "/icinga2/pki/' + $this.getProperty('local_hostname') + '.key"
  1422. ca_path = SysconfDir + "/icinga2/pki/ca.crt"
  1423. accept_commands = true
  1424. accept_config = ' + $this.convertBoolToString($this.config('accept_config')) + '
  1425. bind_host = "::"
  1426. bind_port = ' + [int]$this.config('agent_listen_port') + '
  1427. }';
  1428.  
  1429. $this.setProperty('new_icinga_config', $icingaNewConfig);
  1430. $this.setProperty('old_icinga_config', $icingaCurrentConfig);
  1431. }
  1432. }
  1433.  
  1434. #
  1435. # Generate a hash for old and new config
  1436. # and determine if the configuration has changed
  1437. #
  1438. $installer | Add-Member -membertype ScriptMethod -name 'hasConfigChanged' -value {
  1439.  
  1440. if ($this.getProperty('generate_config') -eq 'false') {
  1441. return $FALSE;
  1442. }
  1443. if (-Not $this.getProperty('new_icinga_config')) {
  1444. throw 'New Icinga 2 configuration not generated. Please call "generateIcingaConfiguration" before.';
  1445. }
  1446.  
  1447. [string]$oldConfigHash = $this.getHashFromString($this.getProperty('old_icinga_config'));
  1448. [string]$newConfigHash = $this.getHashFromString($this.getProperty('new_icinga_config'));
  1449.  
  1450. $this.debug('Old Config Hash: "' + $oldConfigHash + '" New Hash: "' + $newConfigHash + '"');
  1451.  
  1452. if ($oldConfigHash -eq $newConfigHash) {
  1453. return $FALSE;
  1454. }
  1455.  
  1456. return $TRUE;
  1457. }
  1458.  
  1459. #
  1460. # Generate a SHA1 Hash from a provided string
  1461. #
  1462. $installer | Add-Member -membertype ScriptMethod -name 'getHashFromString' -value {
  1463. param([string]$text);
  1464. [System.Object]$algorithm = New-Object System.Security.Cryptography.SHA1Managed;
  1465. $hash = [System.Text.Encoding]::UTF8.GetBytes($text);
  1466. $hashInBytes = $algorithm.ComputeHash($hash);
  1467. [string]$result = '';
  1468. foreach($byte in $hashInBytes) {
  1469. $result += $byte.ToString();
  1470. }
  1471. return $result;
  1472. }
  1473.  
  1474. #
  1475. # Return the path to the Icinga 2 config file
  1476. #
  1477. $installer | Add-Member -membertype ScriptMethod -name 'getIcingaConfigFile' -value {
  1478. return (Join-Path -Path $this.getProperty('config_dir') -ChildPath 'icinga2.conf');
  1479. }
  1480.  
  1481. #
  1482. # Create Icinga 2 configuration file based
  1483. # on Director settings
  1484. #
  1485. $installer | Add-Member -membertype ScriptMethod -name 'writeConfig' -value {
  1486. param([string]$configData);
  1487.  
  1488. if (-Not (Test-Path $this.getProperty('config_dir'))) {
  1489. $this.warn('Unable to write Icinga 2 configuration. The required directory was not found. Possibly the Icinga 2 Agent is not installed.');
  1490. return;
  1491. }
  1492.  
  1493. # Write new configuration to file
  1494. $this.info('Writing icinga2.conf to ' + $this.getProperty('config_dir'));
  1495. [System.IO.File]::WriteAllText($this.getIcingaConfigFile(), $configData);
  1496. $this.setProperty('require_restart', 'true');
  1497. }
  1498.  
  1499. #
  1500. # Write old coniguration again
  1501. # just in case we received errors
  1502. #
  1503. $installer | Add-Member -membertype ScriptMethod -name 'rollbackConfig' -value {
  1504. # Write new configuration to file
  1505. $this.info('Rolling back previous icinga2.conf to ' + $this.getProperty('config_dir'));
  1506. [System.IO.File]::WriteAllText($this.getIcingaConfigFile(), $this.getProperty('old_icinga_config'));
  1507. $this.setProperty('require_restart', 'true');
  1508. }
  1509.  
  1510. #
  1511. # Provide a result of an operation (string) and
  1512. # the intended match value. In case every was
  1513. # ok, the function will return an info message
  1514. # with the result. Otherwise it will thrown an
  1515. # exception
  1516. #
  1517. $installer | Add-Member -membertype ScriptMethod -name 'printResultOkOrException' -value {
  1518. param([string]$result, [string]$expected);
  1519. if ($result -And $expected) {
  1520. if (-Not ($result -Like $expected)) {
  1521. throw $result;
  1522. } else {
  1523. $this.info($result);
  1524. }
  1525. } elseif ($result) {
  1526. $this.info($result);
  1527. }
  1528. }
  1529.  
  1530. #
  1531. # Generate the Icinga 2 SSL certificate to ensure the communication between the
  1532. # Agent and the Master can be established in first place
  1533. #
  1534. $installer | Add-Member -membertype ScriptMethod -name 'generateCertificates' -value {
  1535.  
  1536. if ($this.getProperty('local_hostname') -And $this.config('ca_server') -And $this.getProperty('icinga_ticket')) {
  1537. [string]$icingaPkiDir = Join-Path -Path $this.getProperty('config_dir') -ChildPath 'pki\';
  1538. [string]$icingaBinary = Join-Path -Path $this.getInstallPath() -ChildPath 'sbin\icinga2.exe';
  1539. [string]$agentName = $this.getProperty('local_hostname');
  1540.  
  1541. if (-Not (Test-Path $icingaBinary)) {
  1542. $this.warn('Unable to generate Icinga 2 certificates. Icinga 2 executable not found. It looks like the Icinga 2 Agent is not installed.');
  1543. return;
  1544. }
  1545.  
  1546. # Generate the certificate
  1547. $this.info('Generating Icinga 2 certificates');
  1548.  
  1549. $result = $this.startProcess($icingaBinary, $FALSE, 'pki new-cert --cn ' + $this.getProperty('local_hostname') + ' --key ' + $icingaPkiDir + $agentName + '.key --cert ' + $icingaPkiDir + $agentName + '.crt');
  1550. if ($result.Get_Item('exitcode') -ne 0) {
  1551. throw $result.Get_Item('message');
  1552. }
  1553. $this.info($result.Get_Item('message'));
  1554.  
  1555. # Save Certificate
  1556. $this.info("Storing Icinga 2 certificates");
  1557. $result = $this.startProcess($icingaBinary, $FALSE, 'pki save-cert --key ' + $icingaPkiDir + $agentName + '.key --trustedcert ' + $icingaPkiDir + 'trusted-master.crt --host ' + $this.config('ca_server'));
  1558. if ($result.Get_Item('exitcode') -ne 0) {
  1559. throw $result.Get_Item('message');
  1560. }
  1561. $this.info($result.Get_Item('message'));
  1562.  
  1563. # Validate if set against a given fingerprint for the CA
  1564. if (-Not $this.validateCertificate($icingaPkiDir + 'trusted-master.crt')) {
  1565. throw 'Failed to validate against CA authority';
  1566. }
  1567.  
  1568. # Request certificate
  1569. $this.info("Requesting Icinga 2 certificates");
  1570. $result = $this.startProcess($icingaBinary, $FALSE, 'pki request --host ' + $this.config('ca_server') + ' --port ' + $this.config('ca_port') + ' --ticket ' + $this.getProperty('icinga_ticket') + ' --key ' + $icingaPkiDir + $agentName + '.key --cert ' + $icingaPkiDir + $agentName + '.crt --trustedcert ' + $icingaPkiDir + 'trusted-master.crt --ca ' + $icingaPkiDir + 'ca.crt');
  1571. if ($result.Get_Item('exitcode') -ne 0) {
  1572. if ($this.getProperty('agent_name_change')) {
  1573. $this.exception('You have changed the naming of the Agent (upper / lower case) and therefor your certificates are no longer valid. Certificate generation failed because of a possible wrong ticket. Please ensure to set the "hostname" within the Icinga 2 configuration correctly and re-run this script.');
  1574. }
  1575. throw $result.Get_Item('message');
  1576. }
  1577. $this.info($result.Get_Item('message'));
  1578.  
  1579. # Rename the certificates to apply possible upper / lower case naming chanes
  1580. # which is not done by Windows by default
  1581. Move-Item (Join-Path -Path $icingaPkiDir -ChildPath ($agentName + '.key')) (Join-Path -Path $icingaPkiDir -ChildPath ($agentName + '.key'))
  1582. Move-Item (Join-Path -Path $icingaPkiDir -ChildPath ($agentName + '.crt')) (Join-Path -Path $icingaPkiDir -ChildPath ($agentName + '.crt'))
  1583.  
  1584. $this.setProperty('require_restart', 'true');
  1585. } else {
  1586. $this.info('Skipping certificate generation. One or more of the following arguments is not set: -AgentName <name> -CAServer <server> -Ticket <ticket>');
  1587. }
  1588. }
  1589.  
  1590. #
  1591. # Validate against a given fingerprint if we are connected to the correct CA
  1592. #
  1593. $installer | Add-Member -membertype ScriptMethod -name 'validateCertificate' -value {
  1594. param([string] $certificate);
  1595.  
  1596. [System.Object]$certFingerprint = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2;
  1597. $certFingerprint.Import($certificate);
  1598. $this.info('Certificate fingerprint: ' + $certFingerprint.Thumbprint);
  1599.  
  1600. if ($this.config('ca_fingerprint')) {
  1601. if (-Not ($this.config('ca_fingerprint') -eq $certFingerprint.Thumbprint)) {
  1602. $this.error('CA fingerprint does not match! Expected: ' + $this.config('ca_fingerprint') + ', given: ' + $certFingerprint.Thumbprint);
  1603. return $FALSE;
  1604. } else {
  1605. $this.info('CA fingerprint validation successfull');
  1606. return $TRUE;
  1607. }
  1608. }
  1609.  
  1610. $this.warn('CA fingerprint validation disabled');
  1611. return $TRUE;
  1612. }
  1613.  
  1614. #
  1615. # Check the Icinga install directory and determine
  1616. # if the certificates are both available for the
  1617. # Agent. If not, return FALSE
  1618. #
  1619. $installer | Add-Member -membertype ScriptMethod -name 'hasCertificates' -value {
  1620. [string]$icingaPkiDir = Join-Path -Path $this.getProperty('config_dir') -ChildPath 'pki';
  1621. [string]$agentName = $this.getProperty('local_hostname');
  1622. [bool]$filesExist = $FALSE;
  1623. # First check if the files in generell exist
  1624. if (
  1625. ((Test-Path ((Join-Path -Path $icingaPkiDir -ChildPath $agentName) + '.key'))) `
  1626. -And (Test-Path ((Join-Path -Path $icingaPkiDir -ChildPath $agentName) + '.crt')) `
  1627. -And (Test-Path (Join-Path -Path $icingaPkiDir -ChildPath 'ca.crt'))
  1628. ) {
  1629. $filesExist = $TRUE;
  1630. }
  1631.  
  1632. # In case they do, check if the characters (upper / lowercase) are matching as well
  1633. if ($filesExist -eq $TRUE) {
  1634.  
  1635. [string]$hostCRT = $agentName + '.crt';
  1636. [string]$hostKEY = $agentName + '.key';
  1637.  
  1638. # Get all files inside your PKIU directory
  1639. $certificates = Get-ChildItem -Path $icingaPkiDir;
  1640. # Now loop each file and match their name with our hostname
  1641. foreach ($cert in $certificates) {
  1642. if($cert.Name.toLower() -eq $hostCRT.toLower() -Or $cert.Name.toLower() -eq $hostKEY.toLower()) {
  1643. $file = $cert.Name.Replace('.key', '').Replace('.crt', '');
  1644. if (-Not ($file -clike $agentName)) {
  1645. $this.warn([string]::Format('Certificate file {0} is not matching the hostname {1}. Certificate generation is required.', $cert.Name, $agentName));
  1646. $this.setProperty('agent_name_change', $true);
  1647. return $FALSE;
  1648. }
  1649. }
  1650. }
  1651. }
  1652.  
  1653. return $filesExist;
  1654. }
  1655.  
  1656. #
  1657. # Have we passed an argument to force
  1658. # the creation of the certificates?
  1659. #
  1660. $installer | Add-Member -membertype ScriptMethod -name 'forceCertificateGeneration' -value {
  1661. return $this.config('force_cert');
  1662. }
  1663.  
  1664. #
  1665. # Is the current Agent the version
  1666. # we would like to install?
  1667. #
  1668. $installer | Add-Member -membertype ScriptMethod -name 'isAgentUpToDate' -value {
  1669. if ($this.canInstallAgent() -And $this.getProperty('agent_version') -eq $this.config('agent_version')) {
  1670. return $TRUE;
  1671. }
  1672.  
  1673. return $FALSE;
  1674. }
  1675.  
  1676. #
  1677. # Print a message telling us the installed
  1678. # and intended version of the Agent
  1679. #
  1680. $installer | Add-Member -membertype ScriptMethod -name 'printAgentUpdateMessage' -value {
  1681. $this.info('Current Icinga 2 Agent Version (' + $this.getProperty('agent_version') + ') is not matching intended version (' + $this.config('agent_version') + '). Downloading new version...');
  1682. }
  1683.  
  1684. #
  1685. # Do we allow Agent updates / downgrades?
  1686. #
  1687. $installer | Add-Member -membertype ScriptMethod -name 'allowAgentUpdates' -value {
  1688. return $this.config('allow_updates');
  1689. }
  1690.  
  1691. #
  1692. # Have we specified a version to install the Agent?
  1693. #
  1694. $installer | Add-Member -membertype ScriptMethod -name 'canInstallAgent' -value {
  1695. if ($this.config('download_url') -And $this.config('agent_version')) {
  1696. return $TRUE;
  1697. }
  1698.  
  1699. if (-Not $this.config('download_url') -And -Not $this.config('agent_version')) {
  1700. $this.warn('Icinga 2 Agent will not be installed. Arguments -DownloadUrl and -InstallAgentVersion both not defined.');
  1701. return $FALSE;
  1702. }
  1703.  
  1704. if (-Not $this.config('agent_version')) {
  1705. $this.warn('Icinga 2 Agent will not be installed. Argument -InstallAgentVersion is not defined.');
  1706. return $FALSE;
  1707. }
  1708.  
  1709. if (-Not $this.config('download_url')) {
  1710. $this.warn('Icinga 2 Agent will not be installed. Argument -DownloadUrl is not defined.');
  1711. return $FALSE;
  1712. }
  1713.  
  1714. return $FALSE;
  1715. }
  1716.  
  1717. #
  1718. # Check if all required arguments for writing a valid
  1719. # configuration are set
  1720. #
  1721. $installer | Add-Member -membertype ScriptMethod -name 'checkConfigInputParametersAndThrowException' -value {
  1722. if (-Not $this.getProperty('local_hostname')) {
  1723. throw 'Argument -AgentName <name> required for config generation.';
  1724. }
  1725. if (-Not $this.config('parent_zone')) {
  1726. throw 'Argument -ParentZone <name> required for config generation.';
  1727. }
  1728. if (-Not $this.getProperty('endpoint_nodes') -Or -Not $this.getProperty('endpoint_objects')) {
  1729. throw 'Argument -Endpoints <name> requires atleast one defined endpoint.';
  1730. }
  1731. }
  1732.  
  1733. #
  1734. # Execute a check with Icinga2 daemon -C
  1735. # to ensure the configuration is valid
  1736. #
  1737. $installer | Add-Member -membertype ScriptMethod -name 'isIcingaConfigValid' -value {
  1738. param([bool] $checkInternal = $TRUE);
  1739. if (-Not $this.config('parent_zone') -And $checkInternal) {
  1740. throw 'Parent Zone not defined. Please specify it with -ParentZone <name>';
  1741. }
  1742. $icingaBinary = Join-Path -Path $this.getInstallPath() -ChildPath 'sbin\icinga2.exe';
  1743.  
  1744. if (Test-Path $icingaBinary) {
  1745. $result = $this.startProcess($icingaBinary, $FALSE, 'daemon -C');
  1746. if ($result.Get_Item('exitcode') -ne 0) {
  1747. $this.error($result.Get_Item('message'));
  1748. return $FALSE;
  1749. }
  1750. } else {
  1751. $this.warn('Icinga 2 config validation not possible. Icinga 2 executable not found. Possibly the Agent is not installed.');
  1752. }
  1753. return $TRUE;
  1754. }
  1755.  
  1756. #
  1757. # Returns true or false, depending
  1758. # if any changes were made requiring
  1759. # the Icinga 2 Agent to become restarted
  1760. #
  1761. $installer | Add-Member -membertype ScriptMethod -name 'madeChanges' -value {
  1762. return $this.getProperty('require_restart');
  1763. }
  1764.  
  1765. #
  1766. # Apply possible configuration changes to
  1767. # our Icinga 2 Agent
  1768. #
  1769. $installer | Add-Member -membertype ScriptMethod -name 'applyPossibleConfigChanges' -value {
  1770. if ($this.hasConfigChanged() -And $this.getProperty('generate_config') -eq 'true') {
  1771. $this.backupDefaultConfig();
  1772. $this.writeConfig($this.getProperty('new_icinga_config'));
  1773.  
  1774. $this.flushIcingaApiDirectory();
  1775.  
  1776. # Check if the config is valid and rollback otherwise
  1777. if (-Not $this.isIcingaConfigValid()) {
  1778. $this.error('Icinga 2 config validation failed. Rolling back to previous version.');
  1779. if (-Not $this.hasCertificates()) {
  1780. $this.error('Icinga 2 certificates not found. Please generate the certificates over this module or add them manually.');
  1781. }
  1782. $this.rollbackConfig();
  1783. if ($this.isIcingaConfigValid($FALSE)) {
  1784. $this.info('Rollback of Icinga 2 configuration successfull.');
  1785. } else {
  1786. throw 'Icinga 2 config rollback failed. Please check the icinga2.log';
  1787. }
  1788. } else {
  1789. $this.info('Icinga 2 configuration check successfull.');
  1790. }
  1791. } else {
  1792. $this.info('icinga2.conf did not change or required parameters not set. Nothing to do');
  1793. }
  1794. }
  1795.  
  1796. #
  1797. # Enable or disable the Icinga 2 debug log
  1798. #
  1799. $installer | Add-Member -membertype ScriptMethod -name 'switchIcingaDebugLog' -value {
  1800. # In case the config is not valid -> do nothing
  1801. if (-Not $this.isIcingaConfigValid($FALSE)) {
  1802. throw 'Unable to process Icinga 2 debug configuration. The icinga2.conf is corrupt! Please check the icinga2.log';
  1803. }
  1804.  
  1805. # If there is no config file defined -> do nothing
  1806. if (-Not (Test-Path $this.getIcingaConfigFile())) {
  1807. return;
  1808. }
  1809.  
  1810. [string]$icingaCurrentConfig = [System.IO.File]::ReadAllText($this.getIcingaConfigFile());
  1811. [string]$newIcingaConfig = '';
  1812.  
  1813. if ($this.config('icinga_enable_debug_log')) {
  1814. $this.info('Trying to enable debug log for Icinga 2...');
  1815. if ($icingaCurrentConfig.Contains('const PowerShellIcinga2EnableDebug = false;')) {
  1816. $newIcingaConfig = $icingaCurrentConfig.Replace('const PowerShellIcinga2EnableDebug = false;', 'const PowerShellIcinga2EnableDebug = true;');
  1817. $this.info('Icinga 2 debug log has been enabled');
  1818. } else {
  1819. $this.info('Icinga 2 debug log is already enabled or configuration not found');
  1820. }
  1821. } else {
  1822. $this.info('Trying to disable debug log for Icinga 2...');
  1823. if ($icingaCurrentConfig.Contains('const PowerShellIcinga2EnableDebug = true;')) {
  1824. $newIcingaConfig = $icingaCurrentConfig.Replace('const PowerShellIcinga2EnableDebug = true;', 'const PowerShellIcinga2EnableDebug = false;');
  1825. $this.info('Icinga 2 debug log has been disabled');
  1826. } else {
  1827. $this.info('Icinga 2 debug log is not enabled or configuration not found');
  1828. }
  1829. }
  1830.  
  1831. # In case we made a modification to the configuration -> write it
  1832. if ($newIcingaConfig -ne '') {
  1833. $this.writeConfig($newIcingaConfig);
  1834. # Validate the config if it is valid
  1835. if (-Not $this.isIcingaConfigValid($FALSE)) {
  1836. # if not write the old configuration again
  1837. $this.writeConfig($icingaCurrentConfig);
  1838. if (-Not $this.isIcingaConfigValid($FALSE)) {
  1839. throw 'Critical exception: Something went wrong while processing debug configuration. The Icinga 2 config is corrupt! Please check the icinga2.log';
  1840. }
  1841. }
  1842. }
  1843. }
  1844.  
  1845. #
  1846. # Ensure we get the hostname or FQDN
  1847. # from the PowerShell to make things more
  1848. # easier
  1849. #
  1850. $installer | Add-Member -membertype ScriptMethod -name 'fetchHostnameOrFQDN' -value {
  1851. if ($this.config('fetch_agent_fqdn') -And (Get-WmiObject win32_computersystem).Domain) {
  1852. [string]$hostname = (Get-WmiObject win32_computersystem).DNSHostName + '.' + (Get-WmiObject win32_computersystem).Domain;
  1853. $this.setProperty('local_hostname', $hostname);
  1854. $this.info('Setting internal Agent Name to ' + $this.getProperty('local_hostname'));
  1855. } elseif ($this.config('fetch_agent_name')) {
  1856. [string]$hostname = (Get-WmiObject win32_computersystem).DNSHostName;
  1857. $this.setProperty('local_hostname', $hostname);
  1858. $this.info('Setting internal Agent Name to ' + $this.getProperty('local_hostname'));
  1859. }
  1860.  
  1861. # Add additional variables to our config for more user-friendly usage
  1862. [string]$host_fqdn = (Get-WmiObject win32_computersystem).DNSHostName + '.' + (Get-WmiObject win32_computersystem).Domain;
  1863. [string]$hostname = (Get-WmiObject win32_computersystem).DNSHostName;
  1864.  
  1865. $this.setProperty('fqdn', $host_fqdn);
  1866. $this.setProperty('hostname', $hostname);
  1867.  
  1868. if (-Not $this.getProperty('local_hostname')) {
  1869. $this.warn('You have not specified an Agent Name or turned on to auto fetch this information.');
  1870. }
  1871. }
  1872.  
  1873. #
  1874. # Retreive the current IP-Address of the Host
  1875. #
  1876. $installer | Add-Member -membertype ScriptMethod -name 'fetchHostIPAddress' -value {
  1877.  
  1878. # First try to lookup the IP by the FQDN
  1879. if ($this.doLookupIPAddressesForHostname($this.getProperty('fqdn'))) {
  1880. return;
  1881. }
  1882.  
  1883. # Now take a look for the given hostname
  1884. if ($this.doLookupIPAddressesForHostname($this.getProperty('hostname'))) {
  1885. return;
  1886. }
  1887.  
  1888. # If still nothing is found, look on the entire host
  1889. if ($this.doLookupIPAddressesForHostname("")) {
  1890. return;
  1891. }
  1892.  
  1893. $this.exception('Failed to lookup any IP-Address for this host');
  1894. }
  1895.  
  1896. #
  1897. # This function will try to locate the IPv4 address used
  1898. # for communicating with the network
  1899. #
  1900. $installer | Add-Member -membertype ScriptMethod -name 'lookupPrimaryIPv4Address' -value {
  1901. # First execute nslookup for your FQDN and hostname to check if this
  1902. # host is registered and receive it's IP address
  1903. [System.Collections.Hashtable]$fqdnLookup = $this.startProcess('nslookup.exe', $TRUE, $this.getProperty('fqdn'));
  1904. [System.Collections.Hashtable]$hostnameLookup = $this.startProcess('nslookup.exe', $TRUE, $this.getProperty('hostname'));
  1905.  
  1906. # Now get the message of our result we should work with (nslookup output)
  1907. [string]$fqdnLookup = $fqdnLookup.Get_Item('message');
  1908. [string]$hostnameLookup = $hostnameLookup.Get_Item('message');
  1909. # Get our basic IP first
  1910. [string]$usedIP = $this.getProperty('ipaddress');
  1911.  
  1912. # First try to lookup the basic address. If it is not contained, look further
  1913. if ($this.isIPv4AddressInsideLookup($fqdnLookup, $hostnameLookup, $usedIP) -eq $FALSE) {
  1914. [int]$ipCount = $this.getProperty('ipv4_count');
  1915. [bool]$found = $FALSE;
  1916. # Loop through all found IPv4 IP's and try to locate the correct one
  1917. for ($index = 0; $index -lt $ipCount; $index++) {
  1918. $usedIP = $this.getProperty('ipaddress[' + $index + ']');
  1919. if ($this.isIPv4AddressInsideLookup($fqdnLookup, $hostnameLookup, $usedIP)) {
  1920. # Swap IP values once we found a match and exit this loop
  1921. $this.setProperty('ipaddress[' + $index + ']', $this.getProperty('ipaddress'));
  1922. $this.setProperty('ipaddress', $usedIP);
  1923. $found = $TRUE;
  1924. break;
  1925. }
  1926. }
  1927.  
  1928. if ($found -eq $FALSE) {
  1929. $this.warn([string]::Format('Failed to lookup primary IP for this host. Unable to match nslookup against any IPv4 addresses on this system. Using {0} as default now. Access it with &ipaddress& for all JSON requests.', $this.getProperty('ipaddress')));
  1930. return;
  1931. }
  1932. }
  1933.  
  1934. $this.info([string]::Format('Setting IP {0} as primary IP for this host for all requests. Access it with &ipaddress& for all JSON requests.', $usedIP));
  1935. }
  1936.  
  1937. #
  1938. # Check if inside our lookup the IP-Address is found
  1939. #
  1940. $installer | Add-Member -membertype ScriptMethod -name 'isIPv4AddressInsideLookup' -value {
  1941. param([string]$fqdnLookup, [string]$hostnameLookup, [string]$ipv4Address);
  1942.  
  1943. if ($fqdnLookup.Contains($ipv4Address) -Or $hostnameLookup.Contains($ipv4Address)) {
  1944. return $TRUE;
  1945. }
  1946.  
  1947. return $FALSE;
  1948. }
  1949.  
  1950. #
  1951. # Add all found IP-Addresses to our property array
  1952. #
  1953. $installer | Add-Member -membertype ScriptMethod -name 'doLookupIPAddressesForHostname' -value {
  1954. param([string]$hostname);
  1955.  
  1956. $this.info('Trying to fetch Host IP-Address for hostname: ' + $hostname);
  1957. try {
  1958. [array]$ipAddressArray = [Net.DNS]::GetHostEntry($hostname).AddressList;
  1959. $this.addHostIPAddressToProperties($ipAddressArray);
  1960. return $TRUE;
  1961. } catch {
  1962. # Write an error in case something went wrong
  1963. $this.warn('Failed to lookup IP-Address with DNS-Lookup for ' + $hostname + ': ' + $_.Exception.Message);
  1964. }
  1965. return $FALSE;
  1966. }
  1967.  
  1968. #
  1969. # Add all found IP-Addresses to our property array
  1970. #
  1971. $installer | Add-Member -membertype ScriptMethod -name 'addHostIPAddressToProperties' -value {
  1972. param($ipArray);
  1973.  
  1974. [int]$ipV4Index = 0;
  1975. [int]$ipV6Index = 0;
  1976.  
  1977. foreach ($address in $ipArray) {
  1978. # Split config attributes for IPv4 and IPv6 into different values
  1979. if ($address.AddressFamily -eq 'InterNetwork') { #IPv4
  1980. # If the first entry of our default ipaddress is empty -> add it
  1981. if ($this.getProperty('ipaddress') -eq $null) {
  1982. $this.setProperty('ipaddress', $address);
  1983. }
  1984. # Now add the IP's with an array like construct
  1985. $this.setProperty('ipaddress[' + $ipV4Index + ']', $address);
  1986. $ipV4Index += 1;
  1987. } else { #IPv6
  1988. # If the first entry of our default ipaddress is empty -> add it
  1989. if ($this.getProperty('ipaddressV6') -eq $null) {
  1990. $this.setProperty('ipaddressV6', $address);
  1991. }
  1992. # Now add the IP's with an array like construct
  1993. $this.setProperty('ipaddressV6[' + $ipV6Index + ']', $address);
  1994. $ipV6Index += 1;
  1995. }
  1996. }
  1997. $this.setProperty('ipv4_count', $ipV4Index);
  1998. $this.setProperty('ipv6_count', $ipV6Index);
  1999. }
  2000.  
  2001. #
  2002. # Transform the hostname to upper or lower case if required
  2003. # 0: Do nothing (default)
  2004. # 1: Transform to lower case
  2005. # 2: Transform to upper case
  2006. #
  2007. $installer | Add-Member -MemberType ScriptMethod -name 'doTransformHostname' -Value {
  2008. [string]$hostname = $this.getProperty('local_hostname');
  2009. [int]$type = $this.config('transform_hostname');
  2010. switch ($type) {
  2011. 1 { $hostname = $hostname.ToLower(); }
  2012. 2 { $hostname = $hostname.ToUpper(); }
  2013. Default {} # Do nothing by default
  2014. }
  2015.  
  2016. if ($hostname -cne $this.getProperty('local_hostname')) {
  2017. $this.info('Transforming Agent Name to ' + $hostname);
  2018. }
  2019.  
  2020. $this.setProperty('local_hostname', $hostname);
  2021. }
  2022.  
  2023. #
  2024. # Allow the replacing of placeholders within a JSON-String
  2025. #
  2026. $installer | Add-Member -MemberType ScriptMethod -name 'doReplaceJSONPlaceholders' -Value {
  2027. param([string]$jsonString);
  2028.  
  2029. # Replace the encoded & with the original symbol at first
  2030. $jsonString = $jsonString.Replace('\u0026', '&');
  2031.  
  2032. # &hostname& => hostname
  2033. $jsonString = $jsonString.Replace('&hostname&', $this.getProperty('hostname'));
  2034.  
  2035. # &hostname.lowerCase& => hostname to lower
  2036. $jsonString = $jsonString.Replace('&hostname.lowerCase&', $this.getProperty('hostname').ToLower());
  2037.  
  2038. # &hostname.upperCase& => hostname to upper
  2039. $jsonString = $jsonString.Replace('&hostname.upperCase&', $this.getProperty('hostname').ToUpper());
  2040.  
  2041. # &fqdn& => fqdn
  2042. $jsonString = $jsonString.Replace('&fqdn&', $this.getProperty('fqdn'));
  2043.  
  2044. # &fqdn.lowerCase& => fqdn to lower
  2045. $jsonString = $jsonString.Replace('&fqdn.lowerCase&', $this.getProperty('fqdn').ToLower());
  2046.  
  2047. # &fqdn.upperCase& => fqdn to upper
  2048. $jsonString = $jsonString.Replace('&fqdn.upperCase&', $this.getProperty('fqdn').ToUpper());
  2049.  
  2050. # hostname_placeholder => current hostname (either FQDN, hostname, with plain, upper or lower case)
  2051. $jsonString = $jsonString.Replace('&hostname_placeholder&', $this.getProperty('local_hostname'));
  2052.  
  2053. # Try to replace our IP-Address
  2054. if ($jsonString.Contains('&ipaddressV6')) {
  2055. $jsonString = $this.doReplaceJSONIPAddress($jsonString, 'ipaddressV6');
  2056. } elseif ($jsonString.Contains('&ipaddress')) {
  2057. $jsonString = $this.doReplaceJSONIPAddress($jsonString, 'ipaddress');
  2058. }
  2059.  
  2060. # Encode the & again to receive a proper JSON
  2061. $jsonString = $jsonString.Replace('&', '\u0026');
  2062.  
  2063. return $jsonString;
  2064. }
  2065.  
  2066. #
  2067. # Allow the replacing of added IPv4 and IPv6 address
  2068. #
  2069. $installer | Add-Member -MemberType ScriptMethod -name 'doReplaceJSONIPAddress' -Value {
  2070. param([string]$jsonString, [string]$ipType);
  2071.  
  2072. # Add our & delimeter to begin with
  2073. [string]$ipSearchPattern = '&' + $ipType;
  2074.  
  2075. # Now locate the string and cut everything away until only our & tag for the string shall be remaining, including the array placeholder
  2076. [string]$ipAddressEnd = $jsonString.Substring($jsonString.IndexOf($ipSearchPattern) + $ipType.Length + 1, $jsonString.Length - $jsonString.IndexOf($ipSearchPattern) - $ipType.Length - 1);
  2077. # Ensure we still got an ending &, otherwise throw an error
  2078. if ($ipAddressEnd.Contains('&')) {
  2079. # Now cut everything until the first & we found
  2080. $ipAddressEnd = $ipAddressEnd.Substring(0, $ipAddressEnd.IndexOf('&'));
  2081. # Build together our IP-Address string, which could be for example ipaddress[1]
  2082. [string]$ipAddressString = $ipType + $ipAddressEnd;
  2083.  
  2084. # Now replace this finding with our config attribute
  2085. $jsonString = $jsonString.Replace('&' + $ipAddressString + '&', $this.getProperty($ipAddressString));
  2086. } else {
  2087. # If something goes wrong we require to notify our user
  2088. $this.error('Failed to replace IP-Address placeholder. Invalid format for IP-Type ' + $ipType);
  2089. }
  2090.  
  2091. # Return our new JSON-String
  2092. return $jsonString;
  2093. }
  2094.  
  2095. #
  2096. # Check if the local host key is still valid
  2097. #
  2098. $installer | Add-Member -membertype ScriptMethod -name 'isHostAPIKeyValid' -value {
  2099.  
  2100. # If no API key is yet defined, we will require to fetch one
  2101. if (-Not $this.getProperty('director_host_token')) {
  2102. return $FALSE;
  2103. }
  2104.  
  2105. # Check against the powershell-parameter URL if our host API key is valid
  2106. # If we receive content -> everything is ok
  2107. # If we receive any 4xx code, propably the API Key is invalid and we require to fetch a new one
  2108. [string]$url = $this.config('director_url') + 'self-service/powershell-parameters?key=' + $this.getProperty('director_host_token');
  2109. [string]$response = $this.createHTTPRequest($url, '', 'POST', 'application/json', $TRUE, $FALSE);
  2110. if ($this.isHTTPResponseCode($response)) {
  2111. if ($response[0] -eq '4') {
  2112. $this.info('Target host is already present inside Icinga Director without API-Key. Re-Creating key...');
  2113. return $FALSE;
  2114. }
  2115. }
  2116.  
  2117. $this.info('Host API-Key validation successfull.');
  2118. return $TRUE;
  2119. }
  2120.  
  2121. #
  2122. # This function will allow us to create a
  2123. # host object directly inside the Icinga Director
  2124. # with a provided JSON string
  2125. #
  2126. $installer | Add-Member -membertype ScriptMethod -name 'createHostInsideIcingaDirector' -value {
  2127.  
  2128. if ($this.config('director_url') -And $this.getProperty('local_hostname')) {
  2129. if ($this.config('director_auth_token')) {
  2130. if ($this.requireIcingaDirectorAPIVersion('1.4.0', '[Function::createHostInsideIcingaDirector]')) {
  2131.  
  2132. # Check if our API Host-Key is present and valid
  2133. if ($this.isHostAPIKeyValid()) {
  2134. return;
  2135. }
  2136.  
  2137. # If not, try to create the host and fetch the API key
  2138. [string]$apiKey = $this.config('director_auth_token');
  2139. [string]$url = $this.config('director_url') + 'self-service/register-host?name=' + $this.getProperty('local_hostname') + '&key=' + $apiKey;
  2140. [string]$json = '';
  2141. # If no JSON Object is defined (should be default), we shall create one
  2142. if (-Not $this.config('director_host_object')) {
  2143. [string]$hostname = $this.getProperty('local_hostname');
  2144. $json = '{ "address": "' + $hostname + '", "display_name": "' + $hostname + '" }';
  2145. } else {
  2146. # Otherwise use the specified one and replace the host object placeholders
  2147. $json = $this.doReplaceJSONPlaceholders($this.config('director_host_object'));
  2148. }
  2149.  
  2150. $this.info('Creating host ' + $this.getProperty('local_hostname') + ' over API token inside Icinga Director.');
  2151.  
  2152. [string]$httpResponse = $this.createHTTPRequest($url, $json, 'POST', 'application/json', $TRUE, $TRUE);
  2153.  
  2154. if ($this.isHTTPResponseCode($httpResponse) -eq $FALSE) {
  2155. $this.setProperty('director_host_token', $httpResponse);
  2156. $this.writeHostAPIKeyToDisk();
  2157. } else {
  2158. if ($httpResponse -eq '400') {
  2159. $this.warn('Received response 400 from Icinga Director. Possibly you tried to re-create the host ' + $this.getProperty('local_hostname') + '. In case the host already exists, please remove the Host-Api-Key inside the Icinga Director and try again.');
  2160. } else {
  2161. $this.warn('Failed to create host. Response code ' + $httpResponse);
  2162. }
  2163. }
  2164. }
  2165. } elseif ($this.config('director_host_object')) {
  2166. # Setup the url we need to call
  2167. [string]$url = $this.config('director_url') + 'host';
  2168. # Replace the host object placeholders
  2169. [string]$host_object_json = $this.doReplaceJSONPlaceholders($this.config('director_host_object'));
  2170. # Create the host object inside the director
  2171. [string]$httpResponse = $this.createHTTPRequest($url, $host_object_json, 'PUT', 'application/json', $FALSE, $FALSE);
  2172.  
  2173. if ($this.isHTTPResponseCode($httpResponse) -eq $FALSE) {
  2174. $this.info('Placed query for creating host ' + $this.getProperty('local_hostname') + ' inside Icinga Director. Config: ' + $httpResponse);
  2175. } else {
  2176. if ($httpResponse -eq '422') {
  2177. $this.warn('Failed to create host ' + $this.getProperty('local_hostname') + ' inside Icinga Director. The host seems to already exist.');
  2178. } else {
  2179. $this.error('Failed to create host ' + $this.getProperty('local_hostname') + ' inside Icinga Director. Error response ' + $httpResponse);
  2180. }
  2181. }
  2182. # Shall we deploy the config for the generated host?
  2183. if ($this.config('director_deploy_config')) {
  2184. $url = $this.config('director_url') + 'config/deploy';
  2185. [string]$httpResponse = $this.createHTTPRequest($url, '', 'POST', 'application/json', $FALSE, $TRUE);
  2186. $this.info('Deploying configuration from Icinga Director to Icinga. Result: ' + $httpResponse);
  2187. }
  2188. }
  2189. }
  2190. }
  2191.  
  2192. #
  2193. # Write Host API-Key for future usage
  2194. #
  2195. $installer | Add-Member -membertype ScriptMethod -name 'writeHostAPIKeyToDisk' -value {
  2196. if (Test-Path ($this.getProperty('config_dir'))) {
  2197. [string]$apiFile = Join-Path -Path $this.getProperty('config_dir') -ChildPath 'icingadirector.token';
  2198. $this.info('Writing host API-Key "' + $this.getProperty('director_host_token') + '" to "' + $apiFile + '"');
  2199. [System.IO.File]::WriteAllText($apiFile, $this.getProperty('director_host_token'));
  2200. }
  2201. }
  2202.  
  2203. #
  2204. # Read Host API-Key from disk for usage
  2205. #
  2206. $installer | Add-Member -membertype ScriptMethod -name 'readHostAPIKeyFromDisk' -value {
  2207. [string]$apiFile = Join-Path -Path $this.getProperty('config_dir') -ChildPath 'icingadirector.token';
  2208. if (Test-Path ($apiFile)) {
  2209. [string]$hostToken = [System.IO.File]::ReadAllText($apiFile);
  2210. $this.setProperty('director_host_token', $hostToken);
  2211. $this.info('Reading host api token ' + $hostToken + ' from ' + $apiFile);
  2212. } else {
  2213. $this.setProperty('director_host_token', '');
  2214. }
  2215. }
  2216.  
  2217. #
  2218. # Get the API Version from the Icinga Director. In case we are using
  2219. # an older Version of the Director, we wont get this version
  2220. #
  2221. $installer | Add-Member -membertype ScriptMethod -name 'getIcingaDirectorVersion' -value {
  2222. if ($this.config('director_url')) {
  2223. # Do a legacy call to the Icinga Director and get a JSON-Value
  2224. # Older versions of the Director do not support plain/text and
  2225. # would result in making this request quite useless
  2226.  
  2227. [string]$url = $this.config('director_url') + 'self-service/api-version';
  2228. [string]$versionString = $this.createHTTPRequest($url, '', 'POST', 'application/json', $FALSE, $FALSE);
  2229.  
  2230. if ($this.isHTTPResponseCode($versionString) -eq $FALSE) {
  2231. # Remove all characters we do not need inside the string
  2232. [string]$versionString = $versionString.Replace('"', '').Replace("`r", '').Replace("`n", '');
  2233. [array]$version = $versionString.Split('.');
  2234. $this.setProperty('icinga_director_api_version', $versionString);
  2235. return;
  2236. } else {
  2237. $this.warn('You seem to use an older Version of the Icinga Director, as no API version could be retreived.');
  2238. $this.setProperty('icinga_director_api_version', '0.0.0');
  2239. return;
  2240. }
  2241. }
  2242. $this.setProperty('icinga_director_api_version', 'false');
  2243. }
  2244.  
  2245. #
  2246. # Match the Icinga Director API Version against a provided string
  2247. #
  2248. $installer | Add-Member -membertype ScriptMethod -name 'requireIcingaDirectorAPIVersion' -value {
  2249. param([string]$version, [string]$functionName);
  2250.  
  2251. # Director URL not specified
  2252. if ($this.getProperty('icinga_director_api_version') -eq 'false') {
  2253. return $FALSE;
  2254. }
  2255.  
  2256. if ($this.getProperty('icinga_director_api_version') -eq '0.0.0') {
  2257. $this.error('The feature ' + $functionName + ' requires Icinga Director API-Version ' + $version + '. Your Icinga Director version does not support the API.');
  2258. return $FALSE;
  2259. }
  2260.  
  2261. [bool]$versionValid = $TRUE;
  2262. [array]$requiredVersion = $version.Split('.');
  2263. $currentVersion = $this.getProperty('icinga_director_api_version');
  2264.  
  2265. if ($requiredVersion[0] -gt $currentVersion[0]) {
  2266. $versionValid = $FALSE;
  2267. }
  2268.  
  2269. if ($requiredVersion[1] -gt $currentVersion[2]) {
  2270. $versionValid = $FALSE;
  2271. }
  2272.  
  2273. if ($requiredVersion[1] -ge $currentVersion[2] -And $requiredVersion[2] -gt $currentVersion[4]) {
  2274. $versionValid = $FALSE;
  2275. }
  2276.  
  2277. if ($versionValid -eq $FALSE) {
  2278. $this.error('The feature ' + $functionName + ' requires Icinga Director API-Version ' + $version + '. Got version ' + $currentVersion[0] + '.' + $currentVersion[2] + '.' + $currentVersion[4]);
  2279. return $FALSE;
  2280. }
  2281.  
  2282. return $TRUE;
  2283. }
  2284.  
  2285. #
  2286. # This function will convert a [hashtable] or [array] object to string
  2287. # with function ConvertTo-Json for argument -DirectorHostObject.
  2288. # It will however only process those if the PowerShell Version is 3
  2289. # and above, because Version 2 is not providing the required
  2290. # functionality. In that case the module will throw an exception
  2291. #
  2292. $installer | Add-Member -membertype ScriptMethod -name 'convertDirectorHostObjectArgument' -value {
  2293.  
  2294. # First add the value to an object we can work with
  2295. [System.Object]$json = $this.config('director_host_object');
  2296.  
  2297. # Prevent processing of empty data
  2298. if ($json -eq $null -Or $json -eq '') {
  2299. return;
  2300. }
  2301.  
  2302. # In case the argument is already a string -> nothing to do
  2303. if ($json.GetType() -eq [string]) {
  2304. # Do nothing
  2305. return;
  2306. } elseif ($json.GetType() -eq [hashtable] -Or $json.GetType() -eq [array]) {
  2307. # Check which PowerShell Version we are using and throw an error in case our Version does not support the argument
  2308. if ($PSVersionTable.PSVersion.Major -lt 3) {
  2309. [string]$errorMessage = 'You are trying to pass an object of type [hashtable] or [array] for argument "-DirectorHostObject", but are using ' +
  2310. 'PowerShell Version 2 or lower. Passing hashtables through this argument is possible, but it requires to be ' +
  2311. 'converted with function ConvertTo-Json, which is available on PowerShell Version 3 and above only. ' +
  2312. 'You can still process JSON-Values with this module, even on PowerShell Version 2, but you will have to pass the ' +
  2313. 'JSON as string instead of an object. This module will now exit with an error code. For further details, please ' +
  2314. 'read the documentation for the "-DirectorHostObject" argument. ' +
  2315. 'Documentation: https://github.com/Icinga/icinga2-powershell-module/blob/master/doc/10-Basic-Arguments.md';
  2316. $this.exception($errorMessage);
  2317. throw 'PowerShell Version exception.';
  2318. }
  2319.  
  2320. # If our PowerShell Version is supporting the function, convert it to a valid string
  2321. $this.overrideConfig('director_host_object', (ConvertTo-Json -Compress $json));
  2322. }
  2323. }
  2324.  
  2325. #
  2326. # This function will fetch all arguments configured inside the Icinga Director
  2327. # to allow an entire auto configuration of the Icinga 2 Agent
  2328. #
  2329. $installer | Add-Member -membertype ScriptMethod -name 'fetchArgumentsFromIcingaDirector' -value {
  2330. param([bool]$globalConfig);
  2331.  
  2332. # By default we will use the Host-Api-Key stored on the disk (if written on)
  2333. [string]$key = $this.getProperty('director_host_token');
  2334.  
  2335. # In case we are not having the Host-Api-Key already, use the value from the argument
  2336. if ($globalConfig -eq $TRUE) {
  2337. $key = $this.config('director_auth_token');
  2338. }
  2339.  
  2340. # If no key is specified, we are not having set one and should leave this function
  2341. if ($key -eq '') {
  2342. return;
  2343. }
  2344.  
  2345. if ($this.requireIcingaDirectorAPIVersion('1.4.0', '[Function::fetchArgumentsFromIcingaDirector]')) {
  2346. [string]$url = $this.config('director_url') + 'self-service/powershell-parameters?key=' + $key;
  2347. [string]$argumentString = $this.createHTTPRequest($url, '', 'POST', 'application/json', $TRUE, $FALSE);
  2348.  
  2349. if ($this.isHTTPResponseCode($argumentString) -eq $FALSE) {
  2350. # First split the entire result based in new-lines into an array
  2351. [array]$arguments = $argumentString.Split("`n");
  2352.  
  2353. # Now loop all elements and construct a dictionary for all values
  2354. foreach ($item in $arguments) {
  2355. if ($item.Contains(':')) {
  2356. [int]$argumentPos = $item.IndexOf(":");
  2357. [string]$argument = $item.Substring(0, $argumentPos)
  2358. [string]$value = $item.Substring($argumentPos + 2, $item.Length - 2 - $argumentPos);
  2359. $value = $value.Replace("`r", '');
  2360. $value = $value.Replace("`n", '');
  2361.  
  2362. if ($value.Contains( '!')) {
  2363. [array]$valueArray = $value.Split('!');
  2364. $this.overrideConfig($argument, $valueArray);
  2365. } else {
  2366. if ($value.toLower() -eq 'true') {
  2367. $this.overrideConfig($argument, $TRUE);
  2368. } elseif ($value.toLower() -eq 'false') {
  2369. $this.overrideConfig($argument, $FALSE);
  2370. } else {
  2371. $this.overrideConfig($argument, $value);
  2372. }
  2373. }
  2374. }
  2375. }
  2376. } else {
  2377. $this.error('Received ' + $argumentString + ' from Icinga Director. Possibly your API token is no longer valid or the object does not exist.');
  2378. }
  2379. # Ensure we generate the required configuration content
  2380. $this.generateConfigContent();
  2381. }
  2382. }
  2383.  
  2384. #
  2385. # This function will communicate directly with
  2386. # the Icinga Director and ensuring that we get
  2387. # some of the possible required informations
  2388. #
  2389. $installer | Add-Member -membertype ScriptMethod -name 'fetchTicketFromIcingaDirector' -value {
  2390.  
  2391. if ($this.getProperty('director_host_token')) {
  2392. if ($this.requireIcingaDirectorAPIVersion('1.4.0', '[Function::fetchTicketFromIcingaDirector]')) {
  2393. [string]$url = $this.config('director_url') + 'self-service/ticket?key=' + $this.getProperty('director_host_token');
  2394. [string]$httpResponse = $this.createHTTPRequest($url, '', 'POST', 'application/json', $TRUE, $TRUE);
  2395. if ($this.isHTTPResponseCode($httpResponse) -eq $FALSE) {
  2396. $this.setProperty('icinga_ticket', $httpResponse);
  2397. } else {
  2398. $this.error('Failed to fetch Ticket from Icinga Director. Error response ' + $httpResponse);
  2399. }
  2400. }
  2401. } else {
  2402. if ($this.config('director_url') -And $this.getProperty('local_hostname')) {
  2403. [string]$url = $this.config('director_url') + 'host/ticket?name=' + $this.getProperty('local_hostname');
  2404. [string]$httpResponse = $this.createHTTPRequest($url, '', 'POST', 'application/json', $FALSE, $TRUE);
  2405.  
  2406. if ($this.isHTTPResponseCode($httpResponse) -eq $FALSE) {
  2407. # Lookup all " inside the return string
  2408. $quotes = Select-String -InputObject $httpResponse -Pattern '"' -AllMatches;
  2409.  
  2410. # If we only got two ", we should have received a valid ticket
  2411. # Otherwise we need to throw an error
  2412. if ($quotes.Matches.Count -ne 2) {
  2413. throw 'Failed to fetch ticket for host ' + $this.getProperty('local_hostname') +'. Got ' + $httpResponse + ' as ticket.';
  2414. } else {
  2415. $httpResponse = $httpResponse.subString(1, $httpResponse.length - 3);
  2416. $this.info('Fetched ticket ' + $httpResponse + ' for host ' + $this.getProperty('local_hostname') + '.');
  2417. $this.setProperty('icinga_ticket', $httpResponse);
  2418. }
  2419. } else {
  2420. $this.error('Failed to fetch Ticket from Icinga Director. Error response ' + $httpResponse);
  2421. }
  2422. }
  2423. }
  2424. }
  2425.  
  2426. #
  2427. # Check if NSClient is installed on the system
  2428. #
  2429. $installer | Add-Member -membertype ScriptMethod -name 'isNSClientInstalled' -value {
  2430. $nsclient = Get-WmiObject -Class Win32_Product |
  2431. Where-Object {
  2432. $_.Name -match 'NSClient*';
  2433. }
  2434.  
  2435. if ($nsclient -eq $null) {
  2436. return $FALSE;
  2437. }
  2438.  
  2439. return $TRUE;
  2440. }
  2441.  
  2442. #
  2443. # Shall we install the NSClient as well on the system?
  2444. # All possible actions are handeled here
  2445. #
  2446. $installer | Add-Member -membertype ScriptMethod -name 'installNSClient' -value {
  2447.  
  2448. if ($this.config('install_nsclient')) {
  2449.  
  2450. [string]$installerPath = $this.getNSClientInstallerPath();
  2451. $this.info('Trying to install and configure NSClient++ from ' + $installerPath);
  2452.  
  2453. # First check if the package does exist
  2454. if (Test-Path ($installerPath)) {
  2455.  
  2456. if ($this.isNSClientInstalled() -eq $FALSE) {
  2457. # Get all required arguments for installing the NSClient unattended
  2458. [string]$NSClientArguments = $this.getNSClientInstallerArguments();
  2459.  
  2460. # Start the installer process
  2461. $result = $this.startProcess('MsiExec.exe', $TRUE, '/quiet /i "' + $installerPath + '" ' + $NSClientArguments);
  2462.  
  2463. # Exit Code 0 means the NSClient was installed successfully
  2464. # Otherwise we require to throw an error
  2465. if ($result.Get_Item('exitcode') -ne 0) {
  2466. $this.exception('Failed to install NSClient++. ' + $result.Get_Item('message'));
  2467. } else {
  2468. $this.info('NSClient++ successfully installed.');
  2469.  
  2470. # To tell Icinga 2 we installed the NSClient and to make
  2471. # the NSCPPath variable available, we require to restart Icinga 2
  2472. $this.setProperty('require_restart', 'true');
  2473. }
  2474. } else {
  2475. $this.info('NSClient++ is already installed on the system.');
  2476. }
  2477.  
  2478. # If defined remove the Firewall Rule to secure the system
  2479. # By default the NSClient is only called from the Icinga 2 Agent locally
  2480. $this.removeNSClientFirewallRule();
  2481. # Remove the service if we only call the NSClient locally
  2482. $this.removeNSClientService();
  2483. # Add the default NSClient config if we want to do more
  2484. $this.addNSClientDefaultConfig();
  2485. } else {
  2486. $this.error('Failed to locate NSClient++ Installer at ' + $installerPath);
  2487. }
  2488. } else {
  2489. $this.info('NSClient++ will not be installed on the system.');
  2490. }
  2491. }
  2492.  
  2493. #
  2494. # Determine the location of the NSClient installer
  2495. # By default we are using the shipped NSClient from the Icinga 2 Agent
  2496. #
  2497. $installer | Add-Member -membertype ScriptMethod -name 'getNSClientInstallerPath' -value {
  2498.  
  2499. if ($this.config('nsclient_installer_path') -ne '') {
  2500.  
  2501. # Check of the installer is a local path
  2502. # If so, use this as installer source
  2503. if (Test-Path ($this.config('nsclient_installer_path'))) {
  2504. return $this.config('nsclient_installer_path');
  2505. }
  2506.  
  2507. $this.info('Trying to download NSClient++ from ' + $this.config('nsclient_installer_path'));
  2508. [System.Object]$client = New-Object System.Net.WebClient;
  2509. $client.DownloadFile($this.config('nsclient_installer_path'), (Join-Path -Path $Env:temp -ChildPath 'NSCP.msi'));
  2510.  
  2511. return (Join-Path -Path $Env:temp -ChildPath 'NSCP.msi');
  2512. } else {
  2513. # Icinga is shipping a NSClient Version after installation
  2514. # Install this version if defined
  2515. return (Join-Path -Path $this.getInstallPath() -ChildPath 'sbin\NSCP.msi');
  2516. }
  2517.  
  2518. return '';
  2519. }
  2520.  
  2521. #
  2522. # If we only want to use the NSClient++ to be called from the Icinga 2 Agent
  2523. # we do not require an open Firewall Rule to allow traffic.
  2524. #
  2525. $installer | Add-Member -membertype ScriptMethod -name 'getNSClientInstallerArguments' -value {
  2526. [string]$NSClientArguments = '';
  2527.  
  2528. if ($this.config('nsclient_directory')) {
  2529. $NSClientArguments += ' INSTALLLOCATION=' + $this.config('nsclient_directory');
  2530. }
  2531.  
  2532. return $NSClientArguments;
  2533. }
  2534.  
  2535. #
  2536. # If we only want to use the NSClient++ to be called from the Icinga 2 Agent
  2537. # we do not require an open Firewall Rule to allow traffic.
  2538. #
  2539. $installer | Add-Member -membertype ScriptMethod -name 'removeNSClientFirewallRule' -value {
  2540. if ($this.config('nsclient_firewall') -eq $FALSE) {
  2541.  
  2542. $result = $this.startProcess('netsh', $FALSE, 'advfirewall firewall show rule name="NSClient++ Monitoring Agent"');
  2543. if ($result.Get_Item('exitcode') -ne 0) {
  2544. # Firewall rule was not found. Nothing to do
  2545. $this.info('NSClient++ Firewall Rule is not installed');
  2546. return;
  2547. }
  2548.  
  2549. $this.info('Trying to remove NSClient++ Firewall Rule...');
  2550.  
  2551. $result = $this.startProcess('netsh', $TRUE, 'advfirewall firewall delete rule name="NSClient++ Monitoring Agent"');
  2552.  
  2553. if ($result.Get_Item('exitcode') -ne 0) {
  2554. $this.error('Failed to remove NSClient++ Firewall rule: ' + $result.Get_Item('message'));
  2555. } else {
  2556. $this.info('NSClient++ Firewall Rule has been successfully removed');
  2557. }
  2558. }
  2559. }
  2560.  
  2561. #
  2562. # If we only want to use the NSClient++ to be called from the Icinga 2 Agent
  2563. # we do not require a running NSClient++ Service
  2564. #
  2565. $installer | Add-Member -membertype ScriptMethod -name 'removeNSClientService' -value {
  2566. if ($this.config('nsclient_service') -eq $FALSE) {
  2567. $NSClientService = Get-WmiObject -Class Win32_Service -Filter "Name='nscp'";
  2568. if ($NSClientService -ne $null) {
  2569. $this.info('Trying to remove NSClient++ service...');
  2570. # Before we remove the service, stop it (to prevent ghosts)
  2571. Stop-Service 'nscp';
  2572. # Now remove it
  2573. $result = $NSClientService.delete();
  2574. if ($result.ReturnValue -eq 0) {
  2575. $this.info('NSClient++ Service has been removed');
  2576. } else {
  2577. $this.error('Failed to remove NSClient++ service');
  2578. }
  2579. } else {
  2580. $this.info('NSClient++ Service is not installed')
  2581. }
  2582. }
  2583. }
  2584.  
  2585. #
  2586. # In case we want to do more with the NSClient, we can auto-generate
  2587. # all NSClient++ config attributes
  2588. #
  2589. $installer | Add-Member -membertype ScriptMethod -name 'addNSClientDefaultConfig' -value {
  2590. if ($this.config('nsclient_add_defaults')) {
  2591. [string]$NSClientBinary = $this.getNSClientDefaultExecutablePath();
  2592.  
  2593. if ($NSClientBinary -eq '') {
  2594. $this.error('Unable to generate NSClient++ default config. Executable nscp.exe could not be found ' +
  2595. 'on default locations or the specified custom location. If you installed the NSClient on a ' +
  2596. 'custom location, please specify the path with -NSClientDirectory');
  2597. return;
  2598. }
  2599.  
  2600. if (Test-Path ($NSClientBinary)) {
  2601. $this.info('Generating all default NSClient++ config values');
  2602. $result = $this.startProcess($NSClientBinary, $TRUE, 'settings --generate --add-defaults --load-all');
  2603. if ($result.Get_Item('exitcode') -ne 0) {
  2604. $this.error($result.Get_Item('message'));
  2605. }
  2606. } else {
  2607. $this.error('Failed to generate NSClient++ defaults config. Path to executable is not valid: ' + $NSClientBinary);
  2608. }
  2609. }
  2610. }
  2611.  
  2612. #
  2613. # Deprecated function
  2614. #
  2615. $installer | Add-Member -membertype ScriptMethod -name 'installIcinga2Agent' -value {
  2616. $this.warn('The function "installIcinga2Agent" is deprecated and will be removed soon. Please use "install" instead.')
  2617. return $this.install();
  2618. }
  2619. $installer | Add-Member -membertype ScriptMethod -name 'installMonitoringComponents' -value {
  2620. $this.warn('The function "installMonitoringComponents" is deprecated and will be removed soon. Please use "install" instead.')
  2621. return $this.install();
  2622. }
  2623.  
  2624. #
  2625. # This function will try to load all
  2626. # data from the system and setup the
  2627. # entire Agent without user interaction
  2628. # including download and update if
  2629. # specified. Returnd 0 or 1 as exit code
  2630. #
  2631. $installer | Add-Member -membertype ScriptMethod -name 'install' -value {
  2632. try {
  2633. if (-Not $this.isAdmin()) {
  2634. return 1;
  2635. }
  2636.  
  2637. # Write an output to the logfile only, ensuring we always get a proper 'start entry' for the user
  2638. $this.info('Started script run...');
  2639. # Get the current API-Version from the Icinga Director
  2640. $this.getIcingaDirectorVersion();
  2641. # Convert our DirectorHostObject argument from Object to String if required
  2642. $this.convertDirectorHostObjectArgument();
  2643. # Read arguments for auto config from the Icinga Director
  2644. # At first only with our public key for global config attributes
  2645. $this.fetchArgumentsFromIcingaDirector($TRUE);
  2646. # Read the Host-API Key in case it exists
  2647. $this.readHostAPIKeyFromDisk();
  2648. # Get host name or FQDN if required
  2649. $this.fetchHostnameOrFQDN();
  2650. # Get IP-Address of host
  2651. $this.fetchHostIPAddress();
  2652. # Try to locate the primary IP Address
  2653. $this.lookupPrimaryIPv4Address();
  2654. # Transform the hostname if required
  2655. $this.doTransformHostname();
  2656. # Try to create a host object inside the Icinga Director
  2657. $this.createHostInsideIcingaDirector();
  2658. # Load the configuration again, but this time with our
  2659. # Host key to fetch additional informations like endpoints
  2660. $this.fetchArgumentsFromIcingaDirector($FALSE);
  2661. # First check if we should get some parameters from the Icinga Director
  2662. $this.fetchTicketFromIcingaDirector();
  2663.  
  2664. # Try to locate the current
  2665. # Installation data from the Agent
  2666. if ($this.isAgentInstalled()) {
  2667. if (-Not $this.isAgentUpToDate()) {
  2668. if ($this.allowAgentUpdates()) {
  2669. $this.printAgentUpdateMessage();
  2670. $this.updateAgent();
  2671. $this.cleanupAgentInstaller();
  2672. }
  2673. } else {
  2674. $this.info('Icinga 2 Agent is up-to-date. Nothing to do.');
  2675. }
  2676. } else {
  2677. if ($this.canInstallAgent()) {
  2678. $this.installAgent();
  2679. $this.cleanupAgentInstaller();
  2680. # In case we have an API key assigned, write it to disk
  2681. $this.writeHostAPIKeyToDisk();
  2682. } else {
  2683. $this.warn('Icinga 2 Agent is not installed and not allowed of beeing installed.');
  2684. }
  2685. }
  2686.  
  2687. if (-Not $this.hasCertificates() -Or $this.forceCertificateGeneration()) {
  2688. $this.generateCertificates();
  2689. } else {
  2690. $this.info('Icinga 2 certificates already exist. Nothing to do.');
  2691. }
  2692.  
  2693. $this.generateIcingaConfiguration();
  2694. $this.applyPossibleConfigChanges();
  2695. $this.switchIcingaDebugLog();
  2696. $this.installIcingaAgentFirewallRule();
  2697. $this.installNSClient();
  2698.  
  2699. if ($this.madeChanges()) {
  2700. $this.restartAgent();
  2701. } else {
  2702. $this.info('No changes detected.');
  2703. }
  2704.  
  2705. # We modify the service user at the very last to ensure
  2706. # the user we defined for logging in is valid
  2707. $this.modifyIcingaServiceUser();
  2708. return $this.getScriptExitCode();
  2709. } catch {
  2710. $this.printLastException();
  2711. [void]$this.getScriptExitCode();
  2712. return 1;
  2713. }
  2714. }
  2715.  
  2716. #
  2717. # Deprecated function
  2718. #
  2719. $installer | Add-Member -membertype ScriptMethod -name 'uninstallIcinga2Agent' -value {
  2720. $this.warn('The function "uninstallIcinga2Agent" is deprecated and will be removed soon. Please use "uninstall" instead.')
  2721. return $this.uninstall();
  2722. }
  2723. $installer | Add-Member -membertype ScriptMethod -name 'uninstallMonitoringComponents' -value {
  2724. $this.warn('The function "uninstallMonitoringComponents" is deprecated and will be removed soon. Please use "uninstall" instead.')
  2725. return $this.uninstall();
  2726. }
  2727.  
  2728. #
  2729. # Removes the Icinga 2 Agent from the system
  2730. #
  2731. $installer | Add-Member -membertype ScriptMethod -name 'uninstall' -value {
  2732. $this.info('Trying to locate Icinga 2 Agent...');
  2733.  
  2734. if ($this.isAgentInstalled()) {
  2735. $this.info('Removing Icinga 2 Agent from the system...');
  2736. $result = $this.startProcess('MsiExec.exe', $TRUE, $this.getProperty('uninstall_id') + ' /q');
  2737.  
  2738. if ($result.Get_Item('exitcode') -ne 0) {
  2739. $this.error($result.Get_Item('message'));
  2740. return [int]$result.Get_Item('exitcode');
  2741. }
  2742.  
  2743. $this.info('Icinga 2 Agent successfully removed.');
  2744. }
  2745.  
  2746. if ($this.config('full_uninstallation')) {
  2747. $this.info('Flushing Icinga 2 program data directory...');
  2748. if (Test-Path ((Join-Path -Path $Env:ProgramData -ChildPath 'icinga2'))) {
  2749. try {
  2750. [System.Object]$folder = New-Object -ComObject Scripting.FileSystemObject;
  2751. [void]$folder.DeleteFolder((Join-Path -Path $Env:ProgramData -ChildPath 'icinga2'));
  2752. $this.info('Remaining Icinga 2 configuration successfully removed.');
  2753. } catch {
  2754. $this.exception('Failed to delete Icinga 2 Program Data Directory: ' + $_.Exception.Message);
  2755. }
  2756. } else {
  2757. $this.warn('Icinga 2 Agent program directory not present.');
  2758. }
  2759. }
  2760.  
  2761. if ($this.config('remove_nsclient')) {
  2762. $this.info('Trying to remove installed NSClient++...');
  2763.  
  2764. $nsclient = Get-WmiObject -Class Win32_Product |
  2765. Where-Object {
  2766. $_.Name -match 'NSClient*';
  2767. }
  2768.  
  2769. if ($nsclient -ne $null) {
  2770. $this.info('Removing installed NSClient++...');
  2771. [void]$nsclient.Uninstall();
  2772. $this.info('NSClient++ has been successfully removed.');
  2773. } else {
  2774. $this.warn('NSClient++ could not be located on the system. Nothing to remove.');
  2775. }
  2776. }
  2777.  
  2778. return $this.getScriptExitCode();
  2779. }
  2780.  
  2781. # Make the installation / uninstallation of the script easier and shorter
  2782. [int]$installerExitCode = 0;
  2783. [int]$uninstallerExitCode = 0;
  2784. # If flag RunUninstaller is set, do the uninstallation of the components
  2785. if ($RunUninstaller) {
  2786. $uninstallerExitCode = $installer.uninstall();
  2787. }
  2788. # If flag RunInstaller is set, do the installation of the components
  2789. if ($RunInstaller) {
  2790. $installerExitCode = $installer.install();
  2791. }
  2792. if ($RunInstaller -Or $RunUninstaller) {
  2793. if ($installerExitCode -ne 0 -Or $uninstallerExitCode -ne 0) {
  2794. return 1;
  2795. }
  2796. }
  2797.  
  2798. # Otherwise handle everything as before
  2799. return $installer;
  2800. }
  2801.  
  2802. exit Icinga2AgentModule `
  2803. -AgentName 'WhiteRabbit' `
  2804. -Ticket '1bad9499f9429e7d3e8b1a970bab253f38dd1fa5' `
  2805. -ParentZone 'Icinga' `
  2806. -ParentEndpoints 'Icinga' `
  2807. -CAServer 'Icinga' `
  2808. -RunInstaller
Add Comment
Please, Sign In to add comment