<# .SYNOPSIS Performs Looker Initialzation on the designated remote Linux VM .DESCRIPTION The Deploy-Looker.ps1 script updates the registry with new data generated during the past month and generates a report. .PARAMETER ComputerName Specifies the hostname of the remote Linux VM. .PARAMETER Port (Optinal) Specifies the port of the remote Linux VM, if this parameter is not set, the script will use 22 as default port. .PARAMETER Username Specifies the user name to login to the remote Linux VM. .PARAMETER Password (Optional) Specifies the password to login to the remote Linux VM. Note: if use SSH public key to authencitate, this parameter is ussed to enter passphrase. .PARAMETER KeyfilePath (Optional) Specifies the path to the SSH public key. .PARAMETER SudoPass (Optional) Specifies the password for sudo in the remote Linux VM. .PARAMETER LookerZipFilePath Specifies the path to the Looker Zip File generated by LookerPreInstallationTool. .PARAMETER LookerImageFilePath (Optional) Specifies the path to the Looker Image File for offline initialization. .PARAMETER LookerImageVersionTag (Optional) Specifies the looker image tag for dev/testing purpose. .PARAMETER PassPasspharse (Optional) Specifies the passhparse for password store on Linux VM if you want to upgrade from previously installed Looker and stored password in Linux VM. .PARAMETER OfflineBundleFilePath (Optional) Specifies the path to the offline bundle File for offline initialization. .PARAMETER AutoUpdateFingerprint (Optional) Specifies whether automatically update fingerpirnt when setup SSH connection. .INPUTS None. You cannot pipe objects to Deploy-Looker.ps1. .OUTPUTS None. Deploy-Looker.ps1 does not generate any output. .EXAMPLE PS> .\Deploy-Looker.ps1 -ComputerName 20.3.144.237 -Username uipath -Password Pas$w0rd -LookerZipFilePath C:\install\Insights_Lookerfiles_20220610102005.zip .EXAMPLE PS> .\Deploy-Looker.ps1 -ComputerName 20.3.144.237 -Username uipath -Password Pas$w0rd -LookerZipFilePath C:\install\Insights_Lookerfiles_20220610102005.zip -OfflineBundleFilePath "C:\install\looker_image.tar.zip" #> # Parameters param( [string]$ComputerName = "", [int]$Port = 22, [string]$Username = "", [string]$Password = "", [string]$KeyfilePath = "", [string]$SudoPass = "", [string]$DeployDir = "~", [string]$LookerZipFilePath = "", [string]$LookerImageFilePath = "", [string]$LookerImageVersionTag = "", [string]$PassPasspharse = "", [string]$OfflineBundleFilePath = "", [bool]$AutoUpdateFingerprint = $True ) Function Test-Admin { $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object System.Security.Principal.WindowsPrincipal( $identity ) return $principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::Administrator ) } Function GenerateStrongPassword ([Parameter(Mandatory = $true)][int]$PasswordLength) { if ($PasswordLength -lt 10) { Write-Host -ForegroundColor Red "Password length must be greater than 10. Will set the password length to 10." $PasswordLength = 10 } $newPassword = -join ((48..57) + (65..90) + (97..122) | Get-Random -Count $PasswordLength | ForEach-Object { [char]$_ }) return $newPassword } Function Write-HR() { $HR = "-" * $Host.UI.RawUI.WindowSize.Width Write-Output $HR } Function Get-FileName($FilePath) { return Split-Path $FilePath -leaf } Function Get-MaskedPass($Pass) { if ( $Pass.length -gt 0) { return $Pass.SubString(0, 4) + "*" * 10 } return $Pass } Function Send-File($FilePath, $DeployDir) { if (-Not(Test-Path $FilePath -PathType Leaf)) { Write-Host -ForegroundColor Red "File $FilePath does not exists." exit 1 } $FileName = Get-FileName($FilePath) Write-Output "Uploading $FileName to host $ComputerName`:$DeployDir..." if ($keyfilePath.Length -eq 0) { Set-SCPItem -ComputerName $ComputerName -AcceptKey -Credential $Credentials -Port $Port -Path $FilePath -Destination $DeployDir } else { Set-SCPItem -ComputerName $ComputerName -AcceptKey -Credential $Credentials -KeyFile $keyfilePath -Port $Port -Path $FilePath -Destination $DeployDir } if (-Not $?) { Write-Host -ForegroundColor Red "Failed to upload $FileName to host $ComputerName. Exiting..." exit 1 } } Function Invoke-RemoteCommand($command) { $Result = Invoke-SSHCommand -SessionId $Session.sessionid -Command $Command -ShowStandardOutputStream -Timeout 600 if ($Result.ExitStatus -ne 0) { Write-Host -ForegroundColor Red "Failed to execute command '$Command' on host $ComputerName. Exiting..." Write-Host -ForegroundColor Red $Result.Error exit 1 } } Function Get-RemotePassword($passName) { $command = "pass ls '$passName'; " if ($PassPasspharse.Length -ge 0) { $pwCommand = "export PASSWORD_STORE_GPG_OPTS=`"--pinentry-mode=loopback --passphrase $PassPasspharse`"; " $command = $pwCommand + $command + "unset PASSWORD_STORE_GPG_OPTS" } $Result = Invoke-SSHCommand -SessionId $Session.sessionid -Command $command if ($Result.ExitStatus -eq 0) { $pass = $Result.Output.Trim() if ($Pass.Length -gt 0) { Write-Host "Succesfully get password $passName" return $pass } } # Write-Host -ForegroundColor Red "Failed to get password $passName" return "" } if (-not(Test-Admin)) { Write-Output "User is not running with administrative rights.`nPlease open a PowerShell console as administrator and try again." Exit 2 } $HelpInfo = "Please use the following command to get help. `n`n Get-Help .\Deploy-Looker.ps1`n" if ($ComputerName.Length -eq 0) { Write-Output "The -ComputerName parameter is required." Write-Output $HelpInfo Exit 2 } if ($Username.Length -eq 0) { Write-Output "The -Username parameter is required." Write-Output $HelpInfo Exit 2 } if ($LookerZipFilePath.Length -eq 0) { Write-Output "The -LookerZipFilePath parameter is required." Write-Output $HelpInfo Exit 2 } if (($KeyfilePath.Length -eq 0) -and ($Password.Length -eq 0)) { Write-Output "At least one of the -KeyfilePath parameter or -Password parameter is required." Write-Output $HelpInfo Exit 2 } $ErrorActionPreference = "SilentlyContinue" Stop-Transcript | out-null $CurrentTimestamp = $(get-date -f yyyyMMddhhmmss) $ScriptVersion = "1.0" $sLogName = "Deploy-Looker_$CurrentTimestamp.log" $sLogFile = Join-Path -Path $PSScriptRoot -ChildPath $sLogName Start-Transcript -path $sLogFile Write-Output "`nRunning UiPath Insights Looker Server Deployment Script v$ScriptVersion" Write-HR $MaskedPassword = Get-MaskedPass($Password) $MaskedSudoPass = Get-MaskedPass($SudoPass) $MaskedPassPasspharse = Get-MaskedPass($PassPasspharse) Write-Host "Parameters: ComputerName = $ComputerName Port = $Port Username = $Username Password = $MaskedPassword SudoPass = $MaskedSudoPass DeployDir = $DeployDir KeyfilePath = $KeyfilePath LookerZipFilePath = $LookerZipFilePath LookerImageFilePath = $LookerImageFilePath LookerImageVersionTag = $LookerImageVersionTag PassPasspharse = $MaskedPassPasspharse OfflineBundleFilePath = $OfflineBundleFilePath `n" # Check if our module loaded properly if (-Not (Get-Module -ListAvailable -Name Posh-SSH)) { # install the module automatically Install-Module -Name Posh-SSH -Repository PSGallery -Force } # Includes Import-Module Posh-SSH # Automatically update the fingerprint for the given host. if ($AutoUpdateFingerprint) { Remove-SSHTrustedHost $Computername | Out-Null } if (-Not (Test-Path $LookerZipFilePath -PathType Leaf)) { Write-Host -ForegroundColor Red "Cannot find Looker Zip file." exit 1 } # Create Windows Host Deploy Dir $DeployPath = "$Env:ProgramData\UiPath Insights" New-Item -ItemType Directory -Force -Path $DeployPath | Out-Null Write-Host "Deploy output path: $DeployPath" if ($Password.length -gt 0) { $secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force } else { $secpasswd = new-object System.Security.SecureString } $Credentials = New-Object System.Management.Automation.PSCredential($Username, $secpasswd) if (-Not $?) { Write-Host -ForegroundColor Red "Cannot generate credentials. Exiting..." exit 1 } Write-Host -ForegroundColor Green "`nSetting up secure connection to $ComputerName..." if ($keyfilePath.Length -eq 0) { $Session = New-SSHSession -ComputerName $ComputerName -AcceptKey -Credential $Credentials -Port $Port } else { $Session = New-SSHSession -Computername $Computername -AcceptKey -Credential $Credentials -KeyFile $keyfilePath -Port $Port } if (-Not $?) { Write-Host -ForegroundColor Red "Failed to connect to host $ComputerName. Exiting..." exit 1 } # Prepare Looker Admin Pass and Cert Pass $LookerSecret = "" $certs = Get-Childitem -Path Cert:\LocalMachine\My -DocumentEncryptionCert | Where-Object { $_.Subject -ieq "CN=UiPathLookerEncryptionCertificate" } $hasEncryptionCert = ($certs | Measure-Object).Count if ($hasEncryptionCert -eq 0) { $cert = New-SelfSignedCertificate -Subject "UiPathLookerEncryptionCertificate" -CertStoreLocation "Cert:\LocalMachine\My" -KeyUsage KeyEncipherment, DataEncipherment, KeyAgreement -Type DocumentEncryptionCert } else { $cert = ($certs | Select-Object -First 1) } Write-Host -ForegroundColor Green "`nPreparing Looker Password and Certificate Password..." if (Test-Path $DeployPath\LookerSecret -PathType Leaf) { Write-Output "$DeployPath\LookerSecret file exists. The looker admin password and certificate password stored in this file will be used to initialize looker on host $ComputerName." try { $splits = (Unprotect-CmsMessage -Path "$DeployPath\LookerSecret") -Split "`n" $LookerPassword = ($splits[0] -Split " ")[1].Substring(4) $CertificatePassword = ($splits[1] -Split " ")[1] } catch { Write-Host -ForegroundColor Red "Failed to get password from `$DeployPath\LookerSecret`. Please check if the file is damaged or the UiPathLookerEncryptionCertificate has not been installed for the current user." } } else { Write-Output "Looking up the looker admin password and certificate password stored on host $ComputerName pass store." $LookerPassword = Get-RemotePassword("Insights/looker-password") $CertificatePassword = Get-RemotePassword("Insights/cert-password") # Generate Looker Credentials if (($LookerPassword.Length -eq 0) -Or ($CertificatePassword.Length -eq 0)) { Write-Output "No valid password found on host $ComputerName. Generating Looker credentials..." $LookerPassword = GenerateStrongPassword (20) $CertificatePassword = GenerateStrongPassword (20) } $LookerSecret = "UiPathInsightsLookerAdminPass: 1qW@$LookerPassword`nUiPathInsightsLookerCertificatePass: $CertificatePassword" Write-Output "Writing Looker admin password and certificate password to $DeployPath\LookerSecret file..." # Encrypt and save LookerSecret to deploy path $LookerSecret | Protect-CmsMessage -To $cert.Thumbprint -OutFile $DeployPath\LookerSecret } if ($DeployDir -ne "~") { Write-Host -ForegroundColor Green "`nPreparing deploy directory..." Write-Output "Deploy directory: $DeployDir..." Write-Output "Note:" Write-Output " - The ownership of $DeployDir will be transferred to $Username. This is required to allow file uploads and script executions." Write-Output " - If this is not your first deployment, ensure that the same deploy directory as in previous deployments is being used." $Command = "echo `"$SUDO_PASSWORD`" | sudo -S mkdir -p $DeployDir;" $Command = $Command + "echo `"$SUDO_PASSWORD`" | sudo -S chown -R $Username $DeployDir" Invoke-RemoteCommand($Command) } Write-Host -ForegroundColor Green "`nUploading Looker Initialization files to $ComputerName..." Send-File -FilePath $LookerZipFilePath -DeployDir $DeployDir if ($LookerImageFilePath.length -gt 0) { Send-File -FilePath $LookerImageFilePath -DeployDir $DeployDir } if ($OfflineBundleFilePath.length -gt 0) { Send-File -FilePath $OfflineBundleFilePath -DeployDir $DeployDir } $LookerfileZipFileName = Get-FileName($LookerZipFilePath) $Command = "command -v unzip &> /dev/null || { echo 'unzip not found. Installing...'; echo `"$SUDO_PASSWORD`" | sudo -S sudo yum install -y unzip; };" $Command = $Command + "cd $DeployDir;" $Command = $Command + "echo `"$SUDO_PASSWORD`" | sudo -S unzip -o $LookerfileZipFileName" Write-Host -ForegroundColor Green "`nExtracting files from Insights_Lookerfiles Zip file..." Invoke-RemoteCommand($Command) Write-Host -ForegroundColor Green "`nRunning looker-initialization script..." $Command = "" if ($LookerImageVersionTag.Length -gt 0) { $Command = $Command + "export CONTAINER_REGISTRY='insightsdevacr.azurecr.io'; export LOOKER_VERSION_TAG='$LookerImageVersionTag'; " } $Command = $Command + "bash $DeployDir/insights/looker-initialization.sh -l $LookerPassword -c $CertificatePassword -y" if ($SudoPass.length -gt 0) { $Command = $Command + " -s $SudoPass" } if ($LookerImageFilePath.length -gt 0) { $LookerImageFileName = Get-FileName($LookerImageFilePath) $Command = $Command + " -i $DeployDir/$LookerImageFileName" } if ($OfflineBundleFilePath.length -gt 0) { $OfflineBundleFileName = Get-FileName($OfflineBundleFilePath) $Command = $Command + " -o $DeployDir/$OfflineBundleFileName" } Invoke-RemoteCommand($Command) # Remove the session Remove-SSHSession -Name $Session | Out-Null Write-Host -ForegroundColor Green "`nDownloading looker.json file..." if (Test-Path $DeployPath\looker.json -PathType Leaf ) { Write-Output "$DeployPath\looker.json exists. Rename it to $DeployPath\looker_backup_$CurrentTimestamp.json" Rename-Item -Path $DeployPath\looker.json -NewName $DeployPath\looker_backup_$CurrentTimestamp.json } $RemoteLookerJsonFilePath = "$DeployDir/insights/looker.json" if ($keyfilePath.Length -eq 0) { Get-SCPItem -ComputerName $ComputerName -AcceptKey -Credential $Credentials -Path $RemoteLookerJsonFilePath -PathType File -Destination $DeployPath } else { Get-SCPItem -ComputerName $ComputerName -AcceptKey -Credential $Credentials -KeyFile $keyfilePath -Port $Port -Path $RemoteLookerJsonFilePath -PathType File -Destination $DeployPath } if (-Not $?) { Write-Host -ForegroundColor Red "Failed to download looker.json. You can manually downlad it using the following command`n scp $Username@${ComputerName}:'${RemoteLookerJsonFilePath}' '$DeployPath'" exit 1 } Write-Output "Saved the file to $DeployPath\looker.json" Write-Host -ForegroundColor Green "`nUiPath Insights Looker Server deployed succesfully!" Write-Host -ForegroundColor Green "`nPreview of the looker.json file" Write-HR Get-Content "$DeployPath\looker.json" Start-Process $DeployPath Write-HR Stop-Transcript