diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ee6b638 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,50 @@ +# Changelog + +All notable changes to the [Alteryx deploy](https://github.com/Akaizoku/alteryx-deploy) project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.1.0](https://github.com/Akaizoku/alteryx-deploy/releases/1.1.0) - 2021-11-21 + +### Added + +- `Invoke-StartAlteryx` now checks for release version to ensure compatibility +- Check connectivity to licensing system +- Disable Alteryx license action +- Disable Alteryx license(s) during uninstallation +- License key parameter + +### Changed + +- Disabled InstallAware logging by default to improve performances +- Improved documentation + +### Fixed + +- Fix conflict with version parameter and PowerShell modules versions +- Fix `Get-AlteryxUtility` function call following refactoring of [PSAYX](https://github.com/Akaizoku/PSAYX) + +## [1.0.0](https://github.com/Akaizoku/alteryx-deploy/releases/1.0.0) - 2021-09-20 + +Minimum viable product (MVP) release for the Alteryx automated deployment utility. + +### Added + +The following functions have been added: + +- Install-Alteryx +- Invoke-ActivateAlteryx +- Invoke-BackupAlteryx +- Invoke-RestartAlteryx +- Invoke-RestoreAlteryx +- Invoke-StartAlteryx +- Invoke-StopAlteryx +- Show-Configuration +- Uninstall-Alteryx +- Update-Alteryx + +The following files have been added: + +- LICENSE +- README.md diff --git a/Deploy-Alteryx.ps1 b/Deploy-Alteryx.ps1 index 62f45da..bc4d8e1 100644 --- a/Deploy-Alteryx.ps1 +++ b/Deploy-Alteryx.ps1 @@ -3,35 +3,38 @@ <# .SYNOPSIS - Setup Alteryx + Deploy Alteryx .DESCRIPTION - Setup and configure Alteryx + Deploy and configure Alteryx .PARAMETER Action The action parameter corresponds to the operation to perform. - Eight options are available: - - backup: backup the Alteryx application database - - configure: configure the Alteryx application - - install: install the Alteryx application - - restart: restart the Alteryx application - - restore: restore a backup of the Alteryx application database - - show: display the script configuration - - start: start the Alteryx application - - stop: stop the Alteryx application - - uninstall: uninstall the Alteryx application - - upgrade: upgrade the Alteryx application + + Eleven options are available: + + - activate: activate the Alteryx application license + - backup: backup the Alteryx application database + - deactivate: deactivate the Alteryx application license + - install: install the Alteryx application + - restart: restart the Alteryx application + - restore: restore a backup of the Alteryx application database + - show: display the script configuration + - start: start the Alteryx application + - stop: stop the Alteryx application + - uninstall: uninstall the Alteryx application + - upgrade: upgrade the Alteryx application .PARAMETER Unattended The unattended switch define if the script should run in silent mode without any user interaction. .NOTES - File name: Deploy-Alteryx.psm1 + File name: Deploy-Alteryx.ps1 Author: Florian Carrier Creation date: 2021-06-13 - Last modified: 2021-09-10 + Last modified: 2021-11-15 Dependencies: - PowerShell Tool Kit (PSTK) - - Alteryx PowerShell Module (PSAYX) + - Alteryx PowerShell Module (PSAYX) .LINK https://github.com/Akaizoku/alteryx-deploy @@ -57,6 +60,7 @@ Param ( [ValidateSet ( "activate", "backup", + "deactivate", "install", "restart", "restore", @@ -66,13 +70,14 @@ Param ( "uninstall", "upgrade" )] - [String] + [System.String] $Action, [Parameter ( Position = 2, Mandatory = $false, HelpMessage = "Version parameter overwrite" )] + [ValidateNotNullOrEmpty ()] [System.String] $Version, [Parameter ( @@ -80,7 +85,8 @@ Param ( Mandatory = $false, HelpMessage = "Database backup path" )] - [String] + [ValidateNotNullOrEmpty ()] + [System.String] $BackupPath, [Parameter ( Position = 4, @@ -91,8 +97,21 @@ Param ( "Designer", "Server" )] + [ValidateNotNullOrEmpty ()] [System.String] $Product = "Server", + [Parameter ( + Position = 5, + Mandatory = $false, + HelpMessage = "License key(s)" + )] + [ValidatePattern ("^\w{4}-\w{4}-\w{4}-\w{4}-\w{4}-\w{4}-\w{4}-\w{4}(\s\w{4}-\w{4}-\w{4}-\w{4}-\w{4}-\w{4}-\w{4}-\w{4})*$")] + [Alias ( + "Keys", + "Serial" + )] + [System.String[]] + $LicenseKey, [Parameter ( HelpMessage = "Run script in non-interactive mode" )] @@ -102,14 +121,13 @@ Param ( Begin { # ---------------------------------------------------------------------------- - # Global preferences + # * Global preferences # ---------------------------------------------------------------------------- $ErrorActionPreference = "Stop" - $DebugPreference = "Continue" - Set-StrictMode -Version Latest + $ProgressPreference = "Continue" # ---------------------------------------------------------------------------- - # Global variables + # * Global variables # ---------------------------------------------------------------------------- # General $ISOTimeStamp = Get-Date -Format "yyyyMMdd_HHmmss" @@ -121,28 +139,34 @@ Begin { $CustomProperties = "custom.ini" # ---------------------------------------------------------------------------- - # Modules + # * Modules # ---------------------------------------------------------------------------- - # List all required modules - $Modules = @("PSTK", "PSAYX") - foreach ($Module in $Modules) { - try { - # Check if module is installed - Import-Module -Name "$Module" -ErrorAction "Stop" -Force - Write-Log -Type "CHECK" -Object "The $Module module was successfully loaded." - } catch { - # Otherwise check if package is available locally - try { - Import-Module -Name (Join-Path -Path $LibDirectory -ChildPath "$Module") -ErrorAction "Stop" -Force - Write-Log -Type "CHECK" -Object "The $Module module was successfully loaded from the library directory." - } catch { - Throw "The $Module library could not be loaded. Make sure it has been made available on the machine or manually put it in the ""$LibDirectory"" directory" - } - } - } + # Dependencies + $Modules = [Ordered]@{ + "PSTK" = "1.2.4" + "PSAYX" = "1.0.1" + } + # Load modules + foreach ($Module in $Modules.GetEnumerator()) { + try { + # Check if package is available locally + Import-Module -Name (Join-Path -Path $LibDirectory -ChildPath $Module.Name) -MinimumVersion $Module.Value -ErrorAction "Stop" -Force + $ModuleVersion = (Get-Module -Name $Module.Name).Version + Write-Log -Type "CHECK" -Object "The $($Module.Name) module (v$ModuleVersion) was successfully loaded from the library directory." + } catch { + try { + # Otherwise check if module is installed + Import-Module -Name $Module.Name -MinimumVersion $Module.Value -ErrorAction "Stop" -Force + $ModuleVersion = (Get-Module -Name $Module.Name).Version + Write-Log -Type "CHECK" -Object "The $($Module.Name) module (v$ModuleVersion) was successfully loaded." + } catch { + Throw "The $($Module.Name) module (v$($Module.Value)) could not be loaded. Make sure it has been installed on the machine or packaged in the ""$LibDirectory"" directory" + } + } + } # ---------------------------------------------------------------------------- - # Script configuration + # * Script configuration # ---------------------------------------------------------------------------- # General settings $Properties = Get-Properties -File $DefaultProperties -Directory $ConfDirectory -Custom $CustomProperties @@ -159,7 +183,7 @@ Begin { Write-Log -Type "DEBUG" -Message $PSCmdlet.MyInvocation.Line # ------------------------------------------------------------------------------ - # Checks + # * Checks # ------------------------------------------------------------------------------ # Ensure shell is running as 64 bit process if ([Environment]::Is64BitProcess -eq $false) { @@ -167,7 +191,7 @@ Begin { } # ---------------------------------------------------------------------------- - # Functions + # * Functions # ---------------------------------------------------------------------------- # Load PowerShell functions $Functions = Get-ChildItem -Path $Properties.PSDirectory @@ -178,7 +202,7 @@ Begin { } # ---------------------------------------------------------------------------- - # Variables + # * Variables # ---------------------------------------------------------------------------- # (Re)load environment variables # Write-Log -Type "DEBUG" -Message "Load environment variables" @@ -188,7 +212,7 @@ Begin { # } # ---------------------------------------------------------------------------- - # Options + # * Options # ---------------------------------------------------------------------------- # Installation properties $ValidateSet = @( @@ -206,22 +230,29 @@ Begin { if ($PSBoundParameters.ContainsKey("BackupPath")) { $Properties.Add("BackupPath", $BackupPath) } + if ($PSBoundParameters.ContainsKey("LicenseKey")) { + if ($LicenseKey.Count -eq 1 -And $LicenseKey -match "\s") { + $LicenseKey = $LicenseKey.Split(" ") + } + $Properties.Add("LicenseKey", @($LicenseKey)) + } } Process { # Check operation to perform switch ($Action) { - "activate" { Invoke-ActivateAlteryx -Properties $Properties -Unattended:$Unattended } - "backup" { Invoke-BackupAlteryx -Properties $Properties -Unattended:$Unattended } - "install" { Install-Alteryx -Properties $Properties -InstallationProperties $InstallationProperties -Unattended:$Unattended } - "restart" { Invoke-RestartAlteryx -Properties $Properties -Unattended:$Unattended } - "restore" { Invoke-RestoreAlteryx -Properties $Properties -Unattended:$Unattended } - "show" { Show-Configuration -Properties $Properties -InstallationProperties $InstallationProperties } - "start" { Invoke-StartAlteryx -Properties $Properties -Unattended:$Unattended } - "stop" { Invoke-StopAlteryx -Properties $Properties -Unattended:$Unattended } - "uninstall" { Uninstall-Alteryx -Properties $Properties -InstallationProperties $InstallationProperties -Unattended:$Unattended } - "upgrade" { Update-Alteryx -Properties $Properties -InstallationProperties $InstallationProperties -Unattended:$Unattended } - default { Write-Log -Type "ERROR" -Message """$Action"" operation is not supported" -ExitCode 1 } + "activate" { Invoke-ActivateAlteryx -Properties $Properties -Unattended:$Unattended } + "backup" { Invoke-BackupAlteryx -Properties $Properties -Unattended:$Unattended } + "deactivate" { Invoke-DeactivateAlteryx -Properties $Properties -Unattended:$Unattended } + "install" { Install-Alteryx -Properties $Properties -InstallationProperties $InstallationProperties -Unattended:$Unattended } + "restart" { Invoke-RestartAlteryx -Properties $Properties -Unattended:$Unattended } + "restore" { Invoke-RestoreAlteryx -Properties $Properties -Unattended:$Unattended } + "show" { Show-Configuration -Properties $Properties -InstallationProperties $InstallationProperties } + "start" { Invoke-StartAlteryx -Properties $Properties -Unattended:$Unattended } + "stop" { Invoke-StopAlteryx -Properties $Properties -Unattended:$Unattended } + "uninstall" { Uninstall-Alteryx -Properties $Properties -InstallationProperties $InstallationProperties -Unattended:$Unattended } + "upgrade" { Update-Alteryx -Properties $Properties -InstallationProperties $InstallationProperties -Unattended:$Unattended } + default { Write-Log -Type "ERROR" -Message """$Action"" operation is not supported" -ExitCode 1 } } } diff --git a/README.md b/README.md index 2c68b83..94cb4f0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,408 @@ # Alteryx Deploy -Alteryx automated deployment utility +`alteryx-deploy` is a small PowerShell utility for the automation of the deployment and maintenance of Alteryx. + +## Table of contents + +1. [Usage](#usage) +2. [Pre-requisites](#pre-requisites) + 1. [Permissions](#permissions) + 2. [PowerShell version](#powershell-version) + 3. [PowerShell Modules](#powershell-modules) + 4. [Alteryx](#alteryx) +3. [Configuration](#configuration) + 1. [Script configuration](#script-configuration) + 2. [Installation configuration](#installation-configuration) +4. [Parameters](#parameters) + 1. [Mandatory](#mandatory) + 1. [Action](#action) + 2. [Optional](#optional) + 1. [Version](#version) + 2. [Backup Path](#backup-path) + 3. [Product](#product) + 4. [License Key](#license-key) + 5. [Unattended](#unattended) + 6. [WhatIf](#whatif) + 7. [Debug](#debug) +5. [Process](#process) + 1. [Installation](#installation) + 2. [Upgrade](#upgrade) + 3. [Uninstallation](#uninstallation) + 4. [Activation](#activation) + 5. [Deactivation](#deactivation) + 6. [Backup](#backup) + 7. [Restore](#restore) + 8. [Start](#start) + 9. [Stop](#stop) + 10. [Restart](#restart) +6. [Logs](#logs) +7. [Dependencies](#dependencies) +8. [Compatibility](#compatibility) +9. [Known issues](#known-issues) + 1. [Access to the cloud file is denied](#access-to-the-cloud-file-is-denied) + +## Usage + +1. Check the `default.ini` configuration file located under the `conf` folder; +2. If needed, add custom configuration to the `custom.ini` configuration file in the same configuration folder; +3. Update the installation configuration in the file `install.ini`; +4. Run the `Deploy-Alteryx.ps1` script with the corresponding action parameter; + - activate: activate the Alteryx application license + - backup: backup the Alteryx application database + - deactivate: deactivate the Alteryx application license + - install: install the Alteryx application + - restart: restart the Alteryx application + - restore: restore a backup of the Alteryx application database + - show: display the script configuration + - start: start the Alteryx application + - stop: stop the Alteryx application + - uninstall: uninstall the Alteryx application + - upgrade: upgrade the Alteryx application +5. Check the logs. + +## Pre-requisites + +### Permissions + +This script requires administrator rights to be run. + +### PowerShell version + +This script requires [PowerShell version 5.0](https://docs.microsoft.com/en-us/powershell/scripting/windows-powershell/whats-new/what-s-new-in-windows-powershell-50) or later to be run. + +### PowerShell Modules + +This script makes use of functions from the PowerShell Tool Kit ([PSTK]) module and Alteryx PowerShell ([PSAYX]) module as described in the [dependencies section](#dependencies). + +The modules must be [installed](https://docs.microsoft.com/en-us/powershell/module/powershellget/install-module) on the local machine, or placed in the `lib` folder at the root of the script directory. + +Example script structure with embedded dependencies: + +```cmd +.alteryx-deploy ++---conf ++---lib +| +---PSAYX +| \---PSTK ++---powershell +\---res +``` + +### Alteryx + +Alteryx installation files must be made available in the source directory (default `D:\Sources`). See the [compatibility section](#compatibility) for more information about the supported versions. + +You can download them from . + +Please refer to [Alteryx system requirements](https://help.alteryx.com/20213/server/system-requirements) for minimum machine requirements. + +## Configuration + +### Script configuration + +The default configuration of the utility is stored into `default.ini`. This file should not be amended. All custom configuration must be made in the `custom.ini` file. Any customisation done in that file will override the default values. + +Below is an example of custom configuration file: + +```ini +[Paths] +# Sources directory +SrcDirectory = D:\Alteryx\Sources +# Alteryx installation directory +InstallationPath = D:\Alteryx\Server +# Backup directory +BackupDirectory = D:\Alteryx\Server\backup +# Data packages installation directory +DataPackagesPath = D:\Alteryx\Data +``` + +### Installation configuration + +To configure which products should be installed, edit the `install.ini` configuration file located in the `conf` directory. + +Below is an example of installation configuration file: + +```ini +[Installation] +Server = true +PredictiveTools = true +IntelligenceSuite = true +DataPackages = false +``` + +## Parameters + +In addition to the configuration files presented above, parameters will define the operation performed during the execution. + +### Mandatory + +This section lists the mandatory parameters that must be specified to run the script. + +#### Action + +The `Action` parameter corresponds to the operation to perform. + +Eleven options are available: + +- activate: activate the Alteryx application license +- backup: backup the Alteryx application database +- deactivate: deactivate the Alteryx application license +- install: install the Alteryx application +- restart: restart the Alteryx application +- restore: restore a backup of the Alteryx application database +- show: display the script configuration +- start: start the Alteryx application +- stop: stop the Alteryx application +- uninstall: uninstall the Alteryx application +- upgrade: upgrade the Alteryx application + +### Optional + +This section lists the optional parameters that can be specified at runtime. + +#### Version + +The `Version` parameter is optional and enables the user to overwrite the version set in the configuration files. + +#### Backup Path + +The `BackupPath` parameter is optional and enables the user to specify a backup file to use for a database restore. + +#### Product + +The `Product` parameter is optional and enables the user to define if Alteryx Designer should be installed instead of the default Alteryx Server. + +#### License Key + +The `LicenseKey` parameter is optional and enables the user to specify license keys at runtime. + +#### Unattended + +The `Unattended` switch enables the user to specify that the script should run without any interaction. + +If this switch is not used, the setup wizard from Alteryx will be displayed and the user will need to go through the installation steps manually. + +#### WhatIf + +The `WhatIf` switch enables the user to test a command without executing the actions of the command. This means that changes are not applied but processing steps are simply displayed. + +#### Debug + +The `Debug` standard switch enable the display of additional log information. + +A lot of useful debug messages have been defined. Should you wish to enable them, it is recommended to make use of the [`$DebugPreference`](https://docs.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Core/About/about_Preference_Variables#debugpreference) variable. + +```powershell +$DebugPreference = "Continue" +``` + +## Process + +Below are the execution steps of the `.\Deploy-Alteryx.ps1` script. + +**Remarks**: + +1. The execution steps will vary depending on the configuration of the scripts. +2. The steps described below correspond to a complete and successfull execution of the script. + +### Installation + +Below are the steps to install the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "install" +``` + +1. Install Alteryx Server (or Designer if specified with the `-Product` parameter; +2. Install Predictive Tools (if enabled); +3. Install Intelligence Suite (if enabled); +4. Install Data packages (if enabled). + +### Upgrade + +Below are the steps to upgrade the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "upgrade" +``` + +1. Backup Alteryx database and configuration files; +2. Upgrade Alteryx Server (or Designer if specified with the `-Product` parameter; +3. Install Predictive Tools (if enabled); +4. Install Intelligence Suite (if enabled); +5. Install Data packages (if enabled); +6. Check installation status and rollback if errors occurred. + +### Uninstallation + +Below are the steps to uninstall the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "uninstall" +``` + +1. Uninstall Alteryx Server (or Designer if specified with the `-Product` parameter. + +**Warnings:** + +1. The uninstallation process requires the original installation executable file. +2. The uninstallation of Alteryx Server does also remove all dependencies such as Precdictive Tools, Intelligence Suite, etc. + +### Activation + +Below are the steps to activate (license) the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "activate" +``` + +1. Check licensing system connectivity (); +2. Check license file path; +3. Activate Alteryx licenses. + +### Deactivation + +Below are the steps to deactivate (license) the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "deactivate" +``` + +1. Check licensing system connectivity (); +2. Check license file path; +3. Deactivate Alteryx licenses. + +### Backup + +Below are the steps to backup the Alteryx application database. + +```powershell +.\Deploy-Alteryx.ps1 -Action "backup" +``` + +1. Check Alteryx Service status and stop it if it is running; +2. Create database dump; +3. Create copy of application configuration files; +4. Backup controller token; +5. Compress all backup files; +6. Restart Alteryx Service (if it was running previously). + +### Restore + +Below are the steps to restore the Alteryx application database. + +```powershell +.\Deploy-Alteryx.ps1 -Action "restore" +``` + +1. Check Alteryx Service status and stop it if it is running; +2. Check backup path; + - If backup file is an archive (.ZIP), extract files from archive. + - If backup path is a directory, select most recent backup file (using [last write time](https://docs.microsoft.com/en-us/dotnet/api/system.io.filesysteminfo.lastwritetime)). +3. Restore MongoDB database; +4. Restore application configuration files; +5. Restore controller token; +6. Reset storage keys; +7. Restart Alteryx Service (if it was running previously). + +### Start + +Below are the steps to start the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "start" +``` + +1. Check Alteryx Service status; +2. If it is not already running, start Alteryx Service; +3. Check if the service started properly. + +### Stop + +Below are the steps to stop the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "stop" +``` + +1. Check Alteryx Service status; +2. If it is not already stopped, stop Alteryx Service; +3. Check if the service stopped properly. + +### Restart + +Below are the steps to restart the Alteryx application. + +```powershell +.\Deploy-Alteryx.ps1 -Action "restart" +``` + +1. Stop Alteryx Service; +2. Start Alteryx Service. + +## Logs + +Transcript log files are generated in the log directory of the script. + +- The naming convention is as follows: `_-Alteryx.log` +- The format of the log is: `\t\t` + +Additional log files are generated during the installation, upgrade, or uninstallation of Alteryx as an XML file in teh same log directory. + +- The naming convention is as follows: `_.log` + +Below is an example of a successful installation log: + +```log +2021-11-15 10:09:36 INFO Installation of Alteryx Server 2021.3.3.63061 +2021-11-15 10:09:36 INFO Installing Alteryx Server +2021-11-15 10:34:14 CHECK Alteryx Server installed successfully +2021-11-15 10:34:14 WARN Do not forget to configure system settings +2021-11-15 10:34:14 INFO Installing Predictive Tools +2021-11-15 10:36:45 CHECK Predictive Tools installed successfully +2021-11-15 10:36:45 INFO Installing Intelligence Suite +2021-11-15 10:49:36 CHECK Intelligence Suite installed successfully +2021-11-15 10:49:36 INFO Activating Alteryx license +2021-11-15 10:49:36 INFO Checking licensing system connectivity +2021-11-15 10:49:54 CHECK 3 licenses were successfully activated +2021-11-15 10:49:54 CHECK Alteryx Server 2021.3.3.63061 installed successfully +``` ## Dependencies -This module depends on the usage of functions provided by two modules: +This module depends on the usage of functions provided by two PowerShell modules: + +1. PowerShell Tool Kit ([PSTK]) module (version 1.2.5) +2. Alteryx PowerShell ([PSAYX]) module (version 1.0.1) + +## Compatibility + +Below are listed the compatible versions for each of the release. + +Only the first version supported is listed. Later releases should also be compatible as long as no breaking change has been introduced. Please refer to the [Alteryx release notes](https://help.alteryx.com/release-notes) for more information. + +| `alteryx-deploy` | Alteryx | PowerShell | [PSTK] | [PSAYX] | +| ---------------- | -------- | ---------- | ------ | ------- | +| [1.0.0] | [2021.3] | 5.0 | 1.2.4 | 1.0.0 | +| [1.1.0] | [2021.3] | 5.0 | 1.2.5 | 1.0.1 | + +## Known issues + +### Access to the cloud file is denied + +This error can occur during the backup of the application when [OneDrive](https://docs.microsoft.com/en-us/onedrive/one-drive-sync) is installed and is synchronising the temporary backup files while the script attempts to remove them (see [PowerShell issue #9246](https://github.com/PowerShell/PowerShell/issues/9246) for more information). + +This has no impact on the backup process and only affects temporary files used to generate the backup archive. + +> 2021-11-21 02:12:55 INFO Remove staging backup folder +> +> 2021-11-21 02:12:55 ERROR Access to the cloud file is denied + +It can be prevented by configuring a temporary directory (`TempDirectory`) that is not synchronised by OneDrive. Temporary files can also be removed manually. -1. PowerShell Tool Kit (PSTK) module -2. Alteryx PowerShell (PSAYX) module + +[PSTK]: https://www.powershellgallery.com/packages/PSTK +[PSAYX]: https://www.powershellgallery.com/packages/PSAYX +[1.0.0]:https://github.com/Akaizoku/alteryx-deploy/releases/1.0.0 +[1.1.0]:https://github.com/Akaizoku/alteryx-deploy/releases/1.1.0 +[2021.3]:https://help.alteryx.com/release-notes/server/server-20213-release-notes diff --git a/conf/default.ini b/conf/default.ini index bb32719..4d0e2f0 100644 --- a/conf/default.ini +++ b/conf/default.ini @@ -35,13 +35,17 @@ InstallationOptions = install.ini # Installation language Language = English # Version -Version = 2021.3.2.54175 +Version = 2021.3.3.63061 # SSL/TLS EnableSSL = false +# InstallAware log +InstallAwareLog = false [License] +# Licensing system URL +LicensingURL = whitelist.alteryx.com # License file -LicenseFile = +LicenseFile = # License email LicenseEmail = diff --git a/conf/install.ini b/conf/install.ini index 049fdce..770a44a 100644 --- a/conf/install.ini +++ b/conf/install.ini @@ -2,4 +2,4 @@ Server = true PredictiveTools = true IntelligenceSuite = true -DataPackages = true \ No newline at end of file +DataPackages = false \ No newline at end of file diff --git a/powershell/Install-Alteryx.ps1 b/powershell/Install-Alteryx.ps1 index 08f088a..382f6d5 100644 --- a/powershell/Install-Alteryx.ps1 +++ b/powershell/Install-Alteryx.ps1 @@ -16,7 +16,7 @@ function Install-Alteryx { File name: Install-Alteryx.ps1 Author: Florian Carrier Creation date: 2021-07-05 - Last modified: 2021-09-14 + Last modified: 2021-11-20 .LINK https://www.powershellgallery.com/packages/PSAYX @@ -82,21 +82,25 @@ function Install-Alteryx { # Update file version number $ServerFileName = Set-Tags -String $ServerInstaller -Tags (Resolve-Tags -Tags $Tags -Prefix "<" -Suffix ">") $ServerPath = Join-Path -Path $Properties.SrcDirectory -ChildPath $ServerFileName - if (Test-Path -Path $ServerPath) { - Write-Log -Type "INFO" -Message "Installing Alteryx $($InstallationProperties.Product)" - if ($PSCmdlet.ShouldProcess($ServerPath, "Install")) { - $ServerLog = Join-Path -Path $Properties.LogDirectory -ChildPath "${ISOTimeStamp}_${ServerFileName}.log" - $ServerInstall = Install-AlteryxServer -Path $ServerPath -InstallDirectory $Properties.InstallationPath -Log $ServerLog -Serial $Properties.LicenseEmail -Language $Properties.Language -AllUsers -Unattended:$Unattended + Write-Log -Type "INFO" -Message "Installing Alteryx $($InstallationProperties.Product)" + if ($PSCmdlet.ShouldProcess($ServerPath, "Install")) { + if (Test-Path -Path $ServerPath) { + if ($Properties.InstallAwareLog -eq $true) { + $InstallAwareLog = Join-Path -Path $Properties.LogDirectory -ChildPath "${ISOTimeStamp}_${ServerFileName}.log" + $ServerInstall = Install-AlteryxServer -Path $ServerPath -InstallDirectory $Properties.InstallationPath -Log $InstallAwareLog -Serial $Properties.LicenseEmail -Language $Properties.Language -AllUsers -Unattended:$Unattended + } else { + $ServerInstall = Install-AlteryxServer -Path $ServerPath -InstallDirectory $Properties.InstallationPath -Serial $Properties.LicenseEmail -Language $Properties.Language -AllUsers -Unattended:$Unattended + } Write-Log -Type "DEBUG" -Message $ServerInstall if ($ServerInstall.ExitCode -eq 0) { Write-Log -Type "CHECK" -Message "Alteryx $($InstallationProperties.Product) installed successfully" } else { Write-Log -Type "ERROR" -Message "An error occured during the installation" -ExitCode $ServerInstall.ExitCode } + } else { + Write-Log -Type "ERROR" -Message "Path not found $ServerPath" + Write-Log -Type "ERROR" -Message "Alteryx $($InstallationProperties.Product) installation file could not be located" -ExitCode 1 } - } else { - Write-Log -Type "ERROR" -Message "Path not found $ServerPath" - Write-Log -Type "ERROR" -Message "Alteryx $($InstallationProperties.Product) installation file could not be located" -ExitCode 1 } # Configuration if ($InstallationProperties.Product -eq "Server") { @@ -104,6 +108,7 @@ function Install-Alteryx { Write-Log -Type "WARN" -Message "Do not forget to configure system settings" } else { # TODO start system settings + Write-Log -Type "WARN" -Message "Do not forget to configure system settings" } } } @@ -120,9 +125,9 @@ function Install-Alteryx { $RPath = Join-Path -Path $Properties.SrcDirectory -ChildPath $RFileName } # Check source file - if (Test-Path -Path $RPath) { - Write-Log -Type "INFO" -Message "Installing Predictive Tools" - if ($PSCmdlet.ShouldProcess($RPath, "Install")) { + Write-Log -Type "INFO" -Message "Installing Predictive Tools" + if ($PSCmdlet.ShouldProcess($RPath, "Install")) { + if (Test-Path -Path $RPath) { $RCommand = (@("&", $RPath, $Arguments) -join " ").Trim() Write-Log -Type "DEBUG" -Message $RCommand $RInstall = Start-Process -FilePath $RPath -ArgumentList $Arguments -Verb "RunAs" -PassThru -Wait @@ -132,10 +137,10 @@ function Install-Alteryx { } else { Write-Log -Type "ERROR" -Message "An error occured during the installation" -ExitCode $RInstall.ExitCode } + } else { + Write-Log -Type "ERROR" -Message "Path not found $RPath" + Write-Log -Type "ERROR" -Message "Predictive Tools installation file could not be located" -ExitCode 1 } - } else { - Write-Log -Type "ERROR" -Message "Path not found $RPath" - Write-Log -Type "ERROR" -Message "Predictive Tools installation file could not be located" -ExitCode 1 } } # ------------------------------------------------------------------------------ @@ -145,9 +150,9 @@ function Install-Alteryx { # Update file version number $AISFileName = Set-Tags -String $AISInstaller -Tags (Resolve-Tags -Tags $Tags -Prefix "<" -Suffix ">") $AISPath = Join-Path -Path $Properties.SrcDirectory -ChildPath $AISFileName - if (Test-Path -Path $AISPath) { - Write-Log -Type "INFO" -Message "Installing Intelligence Suite" - if ($PSCmdlet.ShouldProcess($AISPath, "Install")) { + Write-Log -Type "INFO" -Message "Installing Intelligence Suite" + if ($PSCmdlet.ShouldProcess($AISPath, "Install")) { + if (Test-Path -Path $AISPath) { $AISCommand = (@("&", $AISPath, $Arguments) -join " ").Trim() Write-Log -Type "DEBUG" -Message $AISCommand $AISInstall = Start-Process -FilePath $AISPath -ArgumentList $Arguments -Verb "RunAs" -PassThru -Wait @@ -157,10 +162,10 @@ function Install-Alteryx { } else { Write-Log -Type "ERROR" -Message "An error occured during the installation" -ExitCode $AISInstall.ExitCode } + } else { + Write-Log -Type "ERROR" -Message "Path not found $AISPath" + Write-Log -Type "ERROR" -Message "Alteryx Intelligence Suite installation file could not be located" -ExitCode 1 } - } else { - Write-Log -Type "ERROR" -Message "Path not found $AISPath" - Write-Log -Type "ERROR" -Message "Alteryx Intelligence Suite installation file could not be located" -ExitCode 1 } } # ------------------------------------------------------------------------------ @@ -213,7 +218,7 @@ function Install-Alteryx { # ------------------------------------------------------------------------------ # Licensing # ------------------------------------------------------------------------------ - Invoke-ActivateAlteryx -Properties $Properties -Unattended:$Unattended + Invoke-ActivateAlteryx -Properties $Properties -Unattended:$Unattended } End { Write-Log -Type "CHECK" -Message "Alteryx $($InstallationProperties.Product) $($Properties.Version) installed successfully" diff --git a/powershell/Invoke-ActivateAlteryx.ps1 b/powershell/Invoke-ActivateAlteryx.ps1 index 687fc09..836ffd2 100644 --- a/powershell/Invoke-ActivateAlteryx.ps1 +++ b/powershell/Invoke-ActivateAlteryx.ps1 @@ -16,7 +16,7 @@ function Invoke-ActivateAlteryx { File name: Invoke-ActivateAlteryx.ps1 Author: Florian Carrier Creation date: 2021-07-05 - Last modified: 2021-08-30 + Last modified: 2021-11-21 .LINK https://www.powershellgallery.com/packages/PSAYX @@ -46,54 +46,63 @@ function Invoke-ActivateAlteryx { # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # License utility - $LicenseUtility = Get-AlteryxLicenseUtility -Path (Join-Path -Path $Properties.InstallationPath -ChildPath "bin") + $LicenseUtility = Get-AlteryxUtility -Utility "License" -Path $Properties.InstallationPath } Process { - Write-Log -Type "INFO" -Message "Activating Alteryx licenses" + Write-Log -Type "INFO" -Message "Activating Alteryx" if ($PSCmdlet.ShouldProcess("Alteryx", "Activate")) { - # Check license key(s) - if (Test-Object -Path $Properties.LicenseFile -NotFound) { - Write-Log -Type "ERROR" -Message "License file path not found $($Properties.LicenseFile)" -ExitCode 1 - } - $Keys = Get-Content -Path $Properties.LicenseFile - if ($Keys.Count -gt 1) { - $Success = "$($Keys.Count) licenses were successfully activated" - $Failure = "Licenses could not be activated" - $Keys = $Keys -join " " - } elseif ($Keys.Count -eq 1) { - $Success = "$($Keys.Count) license was successfully activated" - $Failure = "License could not be activated" - } else { - Write-Log -Type "ERROR" -Message "No license key was provided" -ExitCode 1 - } - # Check email address - if ($null -eq $Properties.LicenseEmail) { - if ($Unattended) { - Write-Log -Type "ERROR" -Message "No email address provided for license activation" - Write-Log -Type "WARN" -Message "Retrieving email address associated with current session through Windows Active Directory" - $Email = Get-ADUser -Identity $env:UserName -Properties "mail" | Select-Object -ExpandProperty "mail" - Write-Log -Type "DEBUG" -Message $Email + # Check licensing system connectivity + Write-Log -Type "INFO" -Message "Checking licensing system connectivity" + if ((Test-HTTPStatus -URI $Properties.LicensingURL) -eq $true) { + # Check license key(s) + if (-Not (Find-Key -Hashtable $Properties -Key "LicenseKey")) { + # Read keys from license file + if (Test-Object -Path $Properties.LicenseFile -NotFound) { + Write-Log -Type "ERROR" -Message "License file path not found $($Properties.LicenseFile)" -ExitCode 1 + } + $Properties.LicenseKey = @(Get-Content -Path $Properties.LicenseFile) + } + # Count keys + if ($Properties.LicenseKey.Count -gt 1) { + $Success = "$($Properties.LicenseKey.Count) licenses were successfully activated" + $Failure = "Licenses could not be activated" + $Properties.LicenseKey = $Properties.LicenseKey -join " " + } elseif ($Properties.LicenseKey.Count -eq 1) { + $Success = "$($Properties.LicenseKey.Count) license was successfully activated" + $Failure = "License could not be activated" } else { - # Prompt for email address and validate format - do { - $Email = Read-Host -Prompt "Please enter the email address for license activation" - } until ($Email -as [System.Net.Mail.MailAddress]) + Write-Log -Type "ERROR" -Message "No license key was provided" -ExitCode 1 + } + # Check email address + if ($null -eq $Properties.LicenseEmail) { + if ($Unattended) { + Write-Log -Type "ERROR" -Message "No email address provided for license activation" + Write-Log -Type "WARN" -Message "Retrieving email address associated with current session through Windows Active Directory" + $Email = Get-ADUser -Identity $env:UserName -Properties "mail" | Select-Object -ExpandProperty "mail" + Write-Log -Type "DEBUG" -Message $Email + } else { + # Prompt for email address and validate format + do { + $Email = Read-Host -Prompt "Please enter the email address for license activation" + } until ($Email -as [System.Net.Mail.MailAddress]) + } + } else { + $Email = $Properties.LicenseEmail + } + # Call license utility + $Activation = Add-AlteryxLicense -Path $LicenseUtility -Key $Properties.LicenseKey -Email $Email + # Check activation status + if (Select-String -InputObject $Activation -Pattern "License(s) successfully activated." -SimpleMatch -Quiet) { + Write-Log -Type "CHECK" -Message $Success + } else { + # Output error and stop script + Write-Log -Type "ERROR" -Message $Activation + Write-Log -Type "ERROR" -Message $Failure -ExitCode 1 } } else { - $Email = $Properties.LicenseEmail - } - # Call license utility - $Activation = Add-AlteryxLicense -Path $LicenseUtility -Key $Keys -Email $Email - # Check activation status - if (Select-String -InputObject $Activation -Pattern "License(s) successfully activated." -SimpleMatch -Quiet) { - Write-Log -Type "CHECK" -Message $Success - } else { - # Output error and stop script - Write-Log -Type "ERROR" -Message $Activation - Write-Log -Type "ERROR" -Message $Failure -ExitCode 1 + Write-Log -Type "ERROR" -Message "Unable to reach licensing system" + Write-Log -Type "WARN" -Message "Skipping license activation" } - } else { - Write-Log -Type "CHECK" -Message $Success } } } \ No newline at end of file diff --git a/powershell/Invoke-BackupAlteryx.ps1 b/powershell/Invoke-BackupAlteryx.ps1 index 797a6ca..ff77cd8 100644 --- a/powershell/Invoke-BackupAlteryx.ps1 +++ b/powershell/Invoke-BackupAlteryx.ps1 @@ -10,7 +10,7 @@ function Invoke-BackupAlteryx { File name: Invoke-BackupAlteryx.ps1 Author: Florian Carrier Creation date: 2021-08-26 - Last modified: 2021-09-10 + Last modified: 2021-11-21 #> [CmdletBinding ( SupportsShouldProcess = $true @@ -64,16 +64,18 @@ function Invoke-BackupAlteryx { # ---------------------------------------------------------------------------- # Check Alteryx service status Write-Log -Type "INFO" -Message "Check Alteryx Service status" - $Service = "AlteryxService" - if (Test-Service -Name $Service) { - $WindowsService = Get-Service -Name $Service - Write-Log -Type "DEBUG" -Message $Service - $ServiceStatus = $WindowsService.Status - if ($ServiceStatus -eq "Running") { - Invoke-StopAlteryx -Properties $Properties -Unattended:$Unattended + if ($PSCmdlet.ShouldProcess("Alteryx Service", "Stop")) { + $Service = "AlteryxService" + if (Test-Service -Name $Service) { + $WindowsService = Get-Service -Name $Service + Write-Log -Type "DEBUG" -Message $Service + $ServiceStatus = $WindowsService.Status + if ($ServiceStatus -eq "Running") { + Invoke-StopAlteryx -Properties $Properties -Unattended:$Unattended + } + } else { + Write-Log -Type "ERROR" -Message "Alteryx Service ($Service) could not be found" -ExitCode 1 } - } else { - Write-Log -Type "ERROR" -Message "Alteryx Service ($Service) could not be found" -ExitCode 1 } # ---------------------------------------------------------------------------- # Create database dump @@ -100,15 +102,16 @@ function Invoke-BackupAlteryx { # Backup configuration files if ($Backup.Configuration -eq $true) { Write-Log -Type "INFO" -Message "Backup configuration files" - $ProgramData = Join-Path -Path ([Environment]::GetEnvironmentVariable("ProgramData")) -ChildPath "Alteryx" - foreach ($ConfigurationFile in $ConfigurationFiles.GetEnumerator()) { - $FilePath = Join-Path -Path $ProgramData -ChildPath $ConfigurationFile.Value - if (Test-Object -Path $FilePath) { - if ($PSCmdlet.ShouldProcess($FilePath, "Back-up")) { + if ($PSCmdlet.ShouldProcess("Configuration files", "Back-up")) { + $ProgramData = Join-Path -Path ([Environment]::GetEnvironmentVariable("ProgramData")) -ChildPath "Alteryx" + foreach ($ConfigurationFile in $ConfigurationFiles.GetEnumerator()) { + $FilePath = Join-Path -Path $ProgramData -ChildPath $ConfigurationFile.Value + if (Test-Object -Path $FilePath) { Copy-Object -Path $FilePath -Destination $TempBackupPath -Force + } else { + Write-Log -Type "WARN" -Message "$($ConfigurationFile.Name) configuration file could not be found" + Write-Log -Type "DEBUG" -Message $FilePath } - } else { - Write-Log -Type "WARN" -Message "$($ConfigurationFile.Name) configuration file could not be found ($FilePath)" } } } @@ -127,15 +130,20 @@ function Invoke-BackupAlteryx { # ---------------------------------------------------------------------------- # Compress backup Write-Log -Type "INFO" -Message "Compress backup files" - Compress-Archive -Path "$TempBackupPath\*" -DestinationPath $BackupPath -CompressionLevel "Optimal" -WhatIf:$WhatIfPreference - Write-Log -Type "DEBUG" -Message $BackupPath - if (Test-Object -Path $BackupPath -NotFound) { - Write-Log -Type "ERROR" -Message "Backup files compression failed" -ErrorCode 1 - } else { - # Remove staging folder - Write-Log -Type "INFO" -Message "Remove staging backup folder" - if ($PSCmdlet.ShouldProcess($TempBackupPath, "Remove")) { - Remove-Object -Path $TempBackupPath -Type "Folder" + if ($PSCmdlet.ShouldProcess("Compress", "Back-up")) { + if (Test-Object -Path $Properties.BackupDirectory -NotFound) { + New-Item -Path $Properties.BackupDirectory -ItemType Directory -Force | Out-Null + } + Compress-Archive -Path "$TempBackupPath\*" -DestinationPath $BackupPath -CompressionLevel "Optimal" -WhatIf:$WhatIfPreference + Write-Log -Type "DEBUG" -Message $BackupPath + if (Test-Object -Path $BackupPath -NotFound) { + Write-Log -Type "ERROR" -Message "Backup files compression failed" -ErrorCode 1 + } else { + # Remove staging folder + Write-Log -Type "INFO" -Message "Remove staging backup folder" + if ($PSCmdlet.ShouldProcess($TempBackupPath, "Remove")) { + Remove-Object -Path $TempBackupPath -Type "Folder" + } } } # ---------------------------------------------------------------------------- diff --git a/powershell/Invoke-DeactivateAlteryx.ps1 b/powershell/Invoke-DeactivateAlteryx.ps1 new file mode 100644 index 0000000..fcd51a1 --- /dev/null +++ b/powershell/Invoke-DeactivateAlteryx.ps1 @@ -0,0 +1,125 @@ +function Invoke-DeactivateAlteryx { + <# + .SYNOPSIS + Deactivate Alteryx license + + .DESCRIPTION + Deactivate one or more license keys for Alteryx + + .PARAMETER Properties + The properties parameter corresponds to the configuration of the application. + + .PARAMETER Unattended + The unattended switch specifies if the script should run in non-interactive mode. + + .NOTES + File name: Invoke-DeactivateAlteryx.ps1 + Author: Florian Carrier + Creation date: 2021-11-20 + Last modified: 2021-11-21 + + .LINK + https://www.powershellgallery.com/packages/PSAYX + + .LINK + https://help.alteryx.com/current/product-activation-and-licensing/use-command-line-options + #> + [CmdletBinding ( + SupportsShouldProcess = $true + )] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Properties" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Properties, + [Parameter ( + HelpMessage = "Switch to remove all license keys" + )] + [Switch] + $All, + [Parameter ( + HelpMessage = "Non-interactive mode" + )] + [Switch] + $Unattended + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # License utility + $LicenseUtility = Get-AlteryxUtility -Utility "License" -Path $Properties.InstallationPath + } + Process { + Write-Log -Type "INFO" -Message "Deactivating Alteryx" + if ($PSCmdlet.ShouldProcess("Alteryx", "Deactivate")) { + # Check licensing system connectivity + Write-Log -Type "INFO" -Message "Checking licensing system connectivity" + if ((Test-HTTPStatus -URI $Properties.LicensingURL) -eq $true) { + # Deactivate license + if ($All) { + # Remove all license keys + $Deactivation = Remove-AlteryxLicense -Path $LicenseUtility + # Check deactivation status + if (Select-String -InputObject $Dectivation -Pattern "License(s) successfully removed." -SimpleMatch -Quiet) { + Write-Log -Type "CHECK" -Message "All licenses were successfully deactivated" + } else { + # Output error + Write-Log -Type "DEBUG" -Message $Deactivation + } + } else { + # Check license key(s) + if (-Not (Find-Key -Hashtable $Properties -Key "LicenseKey")) { + # Read keys from license file + if (Test-Object -Path $Properties.LicenseFile -NotFound) { + Write-Log -Type "ERROR" -Message "License file path not found $($Properties.LicenseFile)" -ExitCode 1 + } + $Properties.LicenseKey = @(Get-Content -Path $Properties.LicenseFile) + } + if ($Properties.LicenseKey.Count -eq 0) { + Write-Log -Type "ERROR" -Message "No license key was provided" -ExitCode 1 + } + # Deactivate each key + $Count = 0 + foreach ($Key in $Properties.LicenseKey) { + Write-Log -Type "INFO" -Message "Deactivating license key $Key" + $Dectivation = Remove-AlteryxLicense -Path $LicenseUtility -Key $Key + # Check deactivation status + if (Select-String -InputObject $Dectivation -Pattern "License(s) successfully removed." -SimpleMatch -Quiet) { + $Count += 1 + } else { + # Output error + Write-Log -Type "DEBUG" -Message $Deactivation + } + } + # Check outcome + if ($Properties.LicenseKey.Count -gt 1) { + $Success = "$($Properties.LicenseKey.Count) licenses were successfully deactivated" + $ErrorCount = $Properties.LicenseKey.Count - $Count + if ($ErrorCount -eq $Properties.LicenseKey.Count) { + $Failure = "None of the licenses could not be deactivated" + } else { + $Failure = "$ErrorCount out of $($Properties.LicenseKey.Count) licenses could not be deactivated" + } + } elseif ($Properties.LicenseKey.Count -eq 1) { + $Success = "$($Properties.LicenseKey.Count) license was successfully deactivated" + $Failure = "License could not be deactivated" + } + if ($Count -eq $Properties.LicenseKey.Count) { + Write-Log -Type "CHECK" -Message $Success + } elseif ($ErrorCount -eq $Properties.LicenseKey.Count) { + Write-Log -Type "ERROR" -Message $Failure + } else { + Write-Log -Type "WARN" -Message $Failure + } + } + } else { + Write-Log -Type "ERROR" -Message "Unable to reach licensing system" + Write-Log -Type "WARN" -Message "Skipping license deactivation" + } + } + } +} \ No newline at end of file diff --git a/powershell/Invoke-RestoreAlteryx.ps1 b/powershell/Invoke-RestoreAlteryx.ps1 index 2bf8a08..9b5fe13 100644 --- a/powershell/Invoke-RestoreAlteryx.ps1 +++ b/powershell/Invoke-RestoreAlteryx.ps1 @@ -10,7 +10,7 @@ function Invoke-RestoreAlteryx { File name: Invoke-RestoreAlteryx.ps1 Author: Florian Carrier Creation date: 2021-08-26 - Last modified: 2021-08-31 + Last modified: 2021-11-21 Comment: User configuration files are out of scope of this procedure: - %APPDATA%\Alteryx\Engine\UserConnections.xml - %APPDATA%\Alteryx\Engine\UserAlias.xml @@ -67,79 +67,87 @@ function Invoke-RestoreAlteryx { } Process { Write-Log -Type "CHECK" -Message "Start $RestoreType restore of Alteryx Server" - # ---------------------------------------------------------------------------- - # Check Alteryx service status - Write-Log -Type "INFO" -Message "Check Alteryx Service status" - $Service = "AlteryxService" - if (Test-Service -Name $Service) { - $WindowsService = Get-Service -Name $Service - Write-Log -Type "DEBUG" -Message $Service - $ServiceStatus = $WindowsService.Status - if ($ServiceStatus -eq "Running") { - Invoke-StopAlteryx -Properties $Properties + if ($PSCmdlet.ShouldProcess("Alteryx Service", "Stop")) { + # ---------------------------------------------------------------------------- + # Check Alteryx service status + Write-Log -Type "INFO" -Message "Check Alteryx Service status" + $Service = "AlteryxService" + if (Test-Service -Name $Service) { + $WindowsService = Get-Service -Name $Service + Write-Log -Type "DEBUG" -Message $Service + $ServiceStatus = $WindowsService.Status + if ($ServiceStatus -eq "Running") { + Invoke-StopAlteryx -Properties $Properties -Unattended:$Unattended + } + } else { + Write-Log -Type "ERROR" -Message "Alteryx Service ($Service) could not be found" -ExitCode 1 } - } else { - Write-Log -Type "ERROR" -Message "Alteryx Service ($Service) could not be found" -ExitCode 1 } # ---------------------------------------------------------------------------- # Check source backup path - if ($null -eq (Get-KeyValue -Hashtable $Properties -Key "BackupPath" -Silent)) { - $SourcePath = $Properties.BackupDirectory - } else { - $SourcePath = $Properties.BackupPath - } - if (Test-Object -Path $SourcePath) { - if ($SourcePath -is [System.IO.FileInfo]) { - if ([System.IO.Path]::GetExtension($SourcePath) -eq ".zip") { + if ($PSCmdlet.ShouldProcess("Backup files", "Retrieve")) { + if ($null -eq (Get-KeyValue -Hashtable $Properties -Key "BackupPath" -Silent)) { + $SourcePath = $Properties.BackupDirectory + } else { + $SourcePath = $Properties.BackupPath + } + if (Test-Object -Path $SourcePath) { + if ($SourcePath -is [System.IO.FileInfo]) { + if ([System.IO.Path]::GetExtension($SourcePath) -eq ".zip") { + # Extract archive file + Write-Log -Type "INFO" -Message "Extract backup file contents" + $BackupPath = Join-Path -Path $Properties.TempDirectory -ChildPath ([System.IO.Path]::GetFileNameWithoutExtension($SourcePath)) + Expand-Archive -Path $SourcePath -DestinationPath $BackupPath -Force -WhatIf:$WhatIfPreference + $Staging = $true + } else { + Write-Log -Type "ERROR" -Message "Only ZIP or uncompressed files are supported" -ExitCode 1 + } + } else { + # Select most recent backup in the directory + Write-Log -Type "WARN" -Message "No backup file was specified" + Write-Log -Type "DEBUG" -Message $SourcePath + Write-Log -Type "INFO" -Message "Retrieving most recent backup" + $BackupFile = (Get-Object -Path $SourcePath -ChildItem -Type "File" -Filter "*.zip" | Sort-Object -Descending -Property "LastWriteTime" | Select-Object -First 1).FullName + Write-Log -Type "DEBUG" -Message $BackupFile # Extract archive file Write-Log -Type "INFO" -Message "Extract backup file contents" - $BackupPath = Join-Path -Path ([System.IO.Path]::GetDirectoryName($SourcePath)) -ChildPath ([System.IO.Path]::GetFileNameWithoutExtension($SourcePath)) - Expand-Archive -Path $SourcePath -DestinationPath $BackupPath -Force -WhatIf:$WhatIfPreference + $BackupPath = Join-Path -Path $Properties.TempDirectory -ChildPath ([System.IO.Path]::GetFileNameWithoutExtension($BackupFile)) + Expand-Archive -Path $BackupFile -DestinationPath $BackupPath -Force -WhatIf:$WhatIfPreference $Staging = $true - } else { - Write-Log -Type "ERROR" -Message "Only ZIP or uncompressed files are supported" -ExitCode 1 } } else { - # Select most recent backup in the directory - Write-Log -Type "WARN" -Message "No backup file was specified" - Write-Log -Type "DEBUG" -Message $SourcePath - Write-Log -Type "INFO" -Message "Retrieving most recent backup" - $BackupFile = (Get-Object -Path $SourcePath -ChildItem -Type "File" -Filter "*.zip" | Sort-Object -Descending -Property "LastWriteTime" | Select-Object -First 1).FullName - Write-Log -Type "DEBUG" -Message $BackupFile - # Extract archive file - Write-Log -Type "INFO" -Message "Extract backup file contents" - $BackupPath = Join-Path -Path ([System.IO.Path]::GetDirectoryName($BackupFile)) -ChildPath ([System.IO.Path]::GetFileNameWithoutExtension($BackupFile)) - Expand-Archive -Path $BackupFile -DestinationPath $BackupPath -Force -WhatIf:$WhatIfPreference - $Staging = $true + Write-Log -Type "ERROR" -Message "No database backup could be found" -ExitCode 1 } - } else { - Write-Log -Type "ERROR" -Message "No database backup could be found" -ExitCode 1 } # ---------------------------------------------------------------------------- # Restore database if ($Restore.Database -eq $true) { Write-Log -Type "INFO" -Message "Restore MongoDB database from backup" - $MongoDBPath = Join-Path -Path $BackupPath -ChildPath "MongoDB" - $DatabaseRestore = Restore-AlteryxDatabase -SourcePath $MongoDBPath -TargetPath $TargetPath -ServicePath $ServicePath - if ($DatabaseRestore -match "failed") { - Write-Log -Type "ERROR" -Message $DatabaseRestore - Write-Log -Type "ERROR" -Message "Database restore failed" -ExitCode 1 - } else { - Write-Log -Type "DEBUG" -Message $DatabaseRestore + if ($PSCmdlet.ShouldProcess("MongoDB", "Restore")) { + $MongoDBPath = Join-Path -Path $BackupPath -ChildPath "MongoDB" + $DatabaseRestore = Restore-AlteryxDatabase -SourcePath $MongoDBPath -TargetPath $TargetPath -ServicePath $ServicePath + if ($DatabaseRestore -match "failed") { + Write-Log -Type "ERROR" -Message $DatabaseRestore + Write-Log -Type "ERROR" -Message "Database restore failed" -ExitCode 1 + } else { + Write-Log -Type "DEBUG" -Message $DatabaseRestore + } } } # ---------------------------------------------------------------------------- # Restore configuration files if ($Restore.Configuration -eq $true) { Write-Log -Type "INFO" -Message "Restore configuration files" - $BackupConfigurationFiles = Get-Object -Path $BackupPath -ChildItem -Filter "*.xml" - foreach ($ConfigurationFile in $ConfigurationFiles.GetEnumerator()) { - $BackupConfigurationFile = $BackupConfigurationFiles | Where-Object -Property "BaseName" -EQ -Value $ConfigurationFile.Name - if ($null -ne $BackupConfigurationFile) { - # Overwrite existing file - Copy-Object -Path $BackupConfigurationFile.FullName -Destination $ConfigurationFile.Value -Force - } else { - Write-Log -Type "WARN" -Message "$($ConfigurationFile.Name) backup configuration file could not be found" + if ($PSCmdlet.ShouldProcess("Configuration files", "Restore")) { + $BackupConfigurationFiles = Get-Object -Path $BackupPath -ChildItem -Filter "*.xml" + foreach ($ConfigurationFile in $ConfigurationFiles.GetEnumerator()) { + $BackupConfigurationFile = $BackupConfigurationFiles | Where-Object -Property "BaseName" -EQ -Value $ConfigurationFile.Name + if ($null -ne $BackupConfigurationFile) { + # Overwrite existing file + Copy-Object -Path $BackupConfigurationFile.FullName -Destination $ConfigurationFile.Value -Force + } else { + Write-Log -Type "WARN" -Message "$($ConfigurationFile.Name) backup configuration file could not be found" + } } } } @@ -147,15 +155,17 @@ function Invoke-RestoreAlteryx { # Set controller token if ($Restore.Token -eq $true) { Write-Log -Type "INFO" -Message "Restore controller token" - $TokenFile = Join-Path -Path $BackupPath -ChildPath "ControllerToken.txt" - if (Test-Object -Path $TokenFile) { - $ControllerToken = Get-Content -Path $TokenFile -Raw - $SetToken = Set-AlteryxServerSecret -Secret $ControllerToken.Trim() -Path $ServicePath - if ($SetToken -match "failed") { - Write-Log -Type "ERROR" -Message $SetToken - Write-Log -Type "ERROR" -Message "Controller token update failed" - } else { - Write-Log -Type "DEBUG" -Message $SetToken + if ($PSCmdlet.ShouldProcess("Controller token", "Restore")) { + $TokenFile = Join-Path -Path $BackupPath -ChildPath "ControllerToken.txt" + if (Test-Object -Path $TokenFile) { + $ControllerToken = Get-Content -Path $TokenFile -Raw + $SetToken = Set-AlteryxServerSecret -Secret $ControllerToken.Trim() -Path $ServicePath + if ($SetToken -match "failed") { + Write-Log -Type "ERROR" -Message $SetToken + Write-Log -Type "ERROR" -Message "Controller token update failed" + } else { + Write-Log -Type "DEBUG" -Message $SetToken + } } } } @@ -173,22 +183,24 @@ function Invoke-RestoreAlteryx { # Reset storage key if ($Restore.Configuration -eq $true) { Write-Log -Type "INFO" -Message "Restore storage key" - $XPath = "SystemSettings/Controller/StorageKeysEncrypted" - # Retrieve backup storage keys value - $BackUpRunTimeSettings = Get-Object -Path $BackupPath -ChildItem -Filter "RunTimeSettings.xml" - if ($null -ne $BackUpRunTimeSettings) { - $BackupXML = New-Object -TypeName "System.XML.XMLDocument" - $BackupXML.Load($BackUpRunTimeSettings.FullName) - $BackupXMLNode = Select-XMLNode -XML $BackupXML -XPath $XPath - Write-Log -Type "DEBUG" -Message $BackupXMLNode.InnerText - # Update configuration file - $RunTimeSettingsXML = New-Object -TypeName "System.XML.XMLDocument" - $RunTimeSettingsXML.Load($ConfigurationFiles.RunTimeSettings) - $NewXMLNode = Select-XMLNode -XML $RunTimeSettingsXML -XPath $XPath - $NewXMLNode.InnerText = $BackupXMLNode.InnerText - $RunTimeSettingsXML.Save($ConfigurationFiles.RunTimeSettings) - } else { - Write-Log -Type "WARN" -Message "RunTimeSettings.xml backup configuration file could not be located" + if ($PSCmdlet.ShouldProcess("Storage Key", "Restore")) { + $XPath = "SystemSettings/Controller/StorageKeysEncrypted" + # Retrieve backup storage keys value + $BackUpRunTimeSettings = Get-Object -Path $BackupPath -ChildItem -Filter "RunTimeSettings.xml" + if ($null -ne $BackUpRunTimeSettings) { + $BackupXML = New-Object -TypeName "System.XML.XMLDocument" + $BackupXML.Load($BackUpRunTimeSettings.FullName) + $BackupXMLNode = Select-XMLNode -XML $BackupXML -XPath $XPath + Write-Log -Type "DEBUG" -Message $BackupXMLNode.InnerText + # Update configuration file + $RunTimeSettingsXML = New-Object -TypeName "System.XML.XMLDocument" + $RunTimeSettingsXML.Load($ConfigurationFiles.RunTimeSettings) + $NewXMLNode = Select-XMLNode -XML $RunTimeSettingsXML -XPath $XPath + $NewXMLNode.InnerText = $BackupXMLNode.InnerText + $RunTimeSettingsXML.Save($ConfigurationFiles.RunTimeSettings) + } else { + Write-Log -Type "WARN" -Message "RunTimeSettings.xml backup configuration file could not be located" + } } } # ---------------------------------------------------------------------------- @@ -198,8 +210,10 @@ function Invoke-RestoreAlteryx { Remove-Object -Path $BackupPath -Type "Folder" } # Restart service if it was running before - if ($ServiceStatus -eq "Running") { - Invoke-StartAlteryx -Properties $Properties + if ($PSCmdlet.ShouldProcess("Alteryx Service", "Restart")) { + if ($ServiceStatus -eq "Running") { + Invoke-StartAlteryx -Properties $Properties -Unattended:$Unattended + } } } End { diff --git a/powershell/Invoke-StartAlteryx.ps1 b/powershell/Invoke-StartAlteryx.ps1 index 7505268..7bff17d 100644 --- a/powershell/Invoke-StartAlteryx.ps1 +++ b/powershell/Invoke-StartAlteryx.ps1 @@ -10,7 +10,7 @@ function Invoke-StartAlteryx { File name: Invoke-StartAlteryx.ps1 Author: Florian Carrier Creation date: 2021-07-08 - Last modified: 2021-07-30 + Last modified: 2021-11-21 #> [CmdletBinding ( SupportsShouldProcess = $true @@ -36,17 +36,17 @@ function Invoke-StartAlteryx { # Variables $ServiceName = "AlteryxService" # Retrieve Alteryx Service utility path - $AlteryxService = Get-AlteryxServerProcess -Process "Service" -InstallDirectory $Properties.InstallationPath + $AlteryxService = Get-AlteryxUtility -Utility "Service" -Path $Properties.InstallationPath } Process { Write-Log -Type "INFO" -Message "Starting Alteryx Service" - # Check service status - $WindowsService = Get-Service -Name $ServiceName - Write-Log -Type "DEBUG" -Message $WindowsService - if ($WindowsService.Status -eq "Running") { - Write-Log -Type "WARN" -Message "Alteryx Service ($ServiceName) is already running" - } else { - if ($PSCmdlet.ShouldProcess("Alteryx Service", "Start")) { + if ($PSCmdlet.ShouldProcess("Alteryx Service", "Start")) { + # Check service status + $WindowsService = Get-Service -Name $ServiceName + Write-Log -Type "DEBUG" -Message $WindowsService + if ($WindowsService.Status -eq "Running") { + Write-Log -Type "WARN" -Message "Alteryx Service ($ServiceName) is already running" + } else { if ($Unattended -eq $false) { $Confirm = Confirm-Prompt -Prompt "Do you want to start the Alteryx Service?" } @@ -55,8 +55,13 @@ function Invoke-StartAlteryx { $Process = Start-Process -FilePath $AlteryxService -ArgumentList "start" -Verb "RunAs" -PassThru -Wait Write-Log -Type "DEBUG" -Message $Process # Check process outcome - # TODO check why exit code is 2 - if ($Process.ExitCode -eq 2) { + if (Compare-Version -Version $Properties.Version -Operator "ge" -Reference "2021.4.1") { + $ExpectedExitCode = 0 + } else { + # Do not ask + $ExpectedExitCode = 2 + } + if ($Process.ExitCode -eq $ExpectedExitCode) { # Wait for service to start while ((Get-Service -Name $ServiceName).Status -eq "StartPending") { Write-Log -Type "INFO" -Message "Alteryx Service is starting..." diff --git a/powershell/Invoke-StopAlteryx.ps1 b/powershell/Invoke-StopAlteryx.ps1 index f425e82..503236e 100644 --- a/powershell/Invoke-StopAlteryx.ps1 +++ b/powershell/Invoke-StopAlteryx.ps1 @@ -10,7 +10,7 @@ function Invoke-StopAlteryx { File name: Invoke-StopAlteryx.ps1 Author: Florian Carrier Creation date: 2021-07-08 - Last modified: 2021-08-30 + Last modified: 2021-11-20 #> [CmdletBinding ( SupportsShouldProcess = $true @@ -36,7 +36,7 @@ function Invoke-StopAlteryx { # Variables $ServiceName = "AlteryxService" # Retrieve Alteryx Service utility path - $AlteryxService = Get-AlteryxServerProcess -Process "Service" -InstallDirectory $Properties.InstallationPath + $AlteryxService = Get-AlteryxUtility -Utility "Service" -Path $Properties.InstallationPath } Process { Write-Log -Type "INFO" -Message "Stopping Alteryx Service" diff --git a/powershell/Uninstall-Alteryx.ps1 b/powershell/Uninstall-Alteryx.ps1 index 3e811d2..1100a2c 100644 --- a/powershell/Uninstall-Alteryx.ps1 +++ b/powershell/Uninstall-Alteryx.ps1 @@ -16,7 +16,7 @@ function Uninstall-Alteryx { File name: Uninstall-Alteryx.ps1 Author: Florian Carrier Creation date: 2021-07-08 - Last modified: 2021-09-14 + Last modified: 2021-11-21 .LINK https://www.powershellgallery.com/packages/PSAYX @@ -70,24 +70,30 @@ function Uninstall-Alteryx { # Alteryx Server # ------------------------------------------------------------------------------ # TODO check if Alteryx is installed + # Deactivate license keys + Invoke-DeactivateAlteryx -Properties $Properties -All -Unattended:$Unattended # Update file version number $ServerFileName = Set-Tags -String $ServerInstaller -Tags (Resolve-Tags -Tags $Tags -Prefix "<" -Suffix ">") $ServerPath = Join-Path -Path $Properties.SrcDirectory -ChildPath $ServerFileName - if (Test-Path -Path $ServerPath) { - Write-Log -Type "INFO" -Message "Uninstalling Alteryx $($InstallationProperties.Product)" - if ($PSCmdlet.ShouldProcess($ServerPath, "Uninstall")) { - $ServerLog = Join-Path -Path $Properties.LogDirectory -ChildPath "${ISOTimeStamp}_${ServerFileName}.log" - $ServerUninstall = Uninstall-AlteryxServer -Path $ServerPath -Log $ServerLog -Unattended:$Unattended + Write-Log -Type "INFO" -Message "Uninstalling Alteryx $($InstallationProperties.Product)" + if ($PSCmdlet.ShouldProcess($ServerPath, "Uninstall")) { + if (Test-Path -Path $ServerPath) { + if ($Properties.InstallAwareLog -eq $true) { + $InstallAwareLog = Join-Path -Path $Properties.LogDirectory -ChildPath "${ISOTimeStamp}_${ServerFileName}.log" + $ServerUninstall = Uninstall-AlteryxServer -Path $ServerPath -Log $InstallAwareLog -Unattended:$Unattended + } else { + $ServerUninstall = Uninstall-AlteryxServer -Path $ServerPath -Unattended:$Unattended + } Write-Log -Type "DEBUG" -Message $ServerUninstall if ($ServerUninstall.ExitCode -eq 0) { Write-Log -Type "CHECK" -Message "Alteryx Server uninstalled successfully" } else { Write-Log -Type "ERROR" -Message "An error occured during the uninstallation" -ExitCode $ServerUninstall.ExitCode } + } else { + Write-Log -Type "ERROR" -Message "Path not found $ServerPath" + Write-Log -Type "ERROR" -Message "Alteryx $($InstallationProperties.Product) executable file could not be located" -ExitCode 1 } - } else { - Write-Log -Type "ERROR" -Message "Path not found $ServerPath" - Write-Log -Type "ERROR" -Message "Alteryx $($InstallationProperties.Product) executable file could not be located" -ExitCode 1 } # TODO enable uninstall of standalone components Write-Log -Type "CHECK" -Message "Uninstallation of Alteryx $($InstallationProperties.Product) $Version successfull" diff --git a/powershell/Update-Alteryx.ps1 b/powershell/Update-Alteryx.ps1 index 0cf905a..a1e53c8 100644 --- a/powershell/Update-Alteryx.ps1 +++ b/powershell/Update-Alteryx.ps1 @@ -10,7 +10,7 @@ function Update-Alteryx { File name: Update-Alteryx.ps1 Author: Florian Carrier Creation date: 2021-09-02 - Last modified: 2021-09-10 + Last modified: 2021-11-20 #> [CmdletBinding ( SupportsShouldProcess = $true @@ -42,8 +42,9 @@ function Update-Alteryx { # Get global preference vrariables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Retrieve Alteryx Service utility path - $AlteryxService = Get-AlteryxServerProcess -Process "Service" -InstallDirectory $Properties.InstallationPath - # Define error counter + $AlteryxService = Get-AlteryxUtility -Utility "Service" -Path $Properties.InstallationPath + # Clear error pipeline + $Error.Clear() } Process { Write-Log -Type "CHECK" -Object "Starting Alteryx Server upgrade to $($Properties.Version)" @@ -59,7 +60,7 @@ function Update-Alteryx { Install-Alteryx -Properties $Properties -InstallationProperties $InstallationProperties -Unattended:$Unattended # Check for errors if ($Error.Count -gt 0) { - Write-Log -Type "ERROR" -Object "Upgrade process failed with $($Errors.Count) errors" + Write-Log -Type "ERROR" -Object "Upgrade process failed with $($Error.Count) errors" Write-Log -Type "WARN" -Object "Restoring previous version ($BackupVersion)" # Overwrite target version $Properties.Version = $BackupVersion