function Install-SDKRelease { <# .SYNOPSIS Install a release of SDK with some set of feature packages, where they exist #> [CmdletBinding(DefaultParameterSetName = 'VersionAndFeatures')] param ( [Parameter(ParameterSetName = 'VersionAndFeatures')] [ArgumentCompleter({ $manifestFolder = (Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath "Manifests") if (!(Test-Path -Path $manifestFolder)) { $manifestFolder = (Join-Path -Path $PSScriptRoot -ChildPath "Manifests") } $files = Get-ChildItem -Path (Join-Path $manifestFolder "*.json") $fileNames = $files | Foreach-Object { return ([System.IO.Path]::GetFileNameWithoutExtension($_) -split '\.')[0..1] -join '.' } | Sort-Object | Get-Unique return $filenames | Foreach-Object { $_ } })][string]$Version = ((Get-SavedInstallVersions) -split "`n")[0], [Parameter(ParameterSetName = 'VersionAndFeatures')] [ArgumentCompleter({ $manifestFolder = (Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath "Manifests") if (!(Test-Path -Path $manifestFolder)) { $manifestFolder = (Join-Path -Path $PSScriptRoot -ChildPath "Manifests") } $files = Get-ChildItem -Path (Join-Path $manifestFolder "*.json") $fileNames = $files | Foreach-Object { return ([System.IO.Path]::GetFileNameWithoutExtension($_) -split '\.')[2] } | Sort-Object | Get-Unique return $filenames | Foreach-Object { $_ } })][string[]]$Features = ((Get-SavedInstallVersions) -split "`n")[1], [Parameter(ParameterSetName = 'Latest')] [switch]$Latest ) $logLead = Get-LogLeadName $tiers = 0..4 if ($null -ne (Get-Command -Name Set-EnvironmentVariable -ErrorAction Ignore)) { Set-AlkamiConfiguration -Configuration StartServicesOnInstall -Off } $transcriptFilename = "Alkami.SDK.InstallRelease.$(Get-Date -Format "yyyyMMddhhmm").txt" $transcriptPath = Join-Path -Path (Get-OrbLogsPath) -ChildPath $transcriptFilename Start-Transcript -Path $transcriptPath # Ensure hosts file entries exist for later Add-OrbHostEntries # Install/Upgrade Alkami.SDKRelease.Manifests package $chocoSplat = @( "upgrade" "Alkami.SDKRelease.Manifests" "-i" "-y" "--no-progress" "--limit-output" ) Invoke-CallOperatorWithPathAndParameters -Path Choco -Arguments $chocoSplat *>&1 | Out-Default -Transcript | Out-Null try { $allPackages, $removePackages = Get-FeatureSets Write-Verbose (ConvertTo-Json $allPackages) $packagesToUpgrade, $newPackagesToInstall, $packagesToRemove = Get-LocalPackages -Packages $allPackages Write-Host "$logLead : Found [$($packagesToUpgrade.Count)] existing packages to upgrade" foreach ($packageToUpgrade in $packagesToUpgrade) { "Upgrade $($packageToUpgrade.Tier) -> $($packageToUpgrade.id) $($packageToUpgrade.Version)" | Out-Default -Transcript | Out-Null } Write-Host "$logLead : Found [$($newPackagesToInstall.Count)] new packages to install" foreach ($packageToInstall in $newPackagesToInstall) { "Install $($packageToInstall.Tier) -> $($packageToInstall.id) $($packageToInstall)." | Out-Default -Transcript | Out-Null } if ($packagesToRemove.Count -gt 0) { Write-Host "$logLead : Found [$($packagesToRemove.Count)] existing packages to remove" foreach ($package in $packagesToRemove) { "Remove $package" | Out-Default -Transcript | Out-Null } } else { Write-Host "No packages marked for deletion" } $packagesToInstall = @{} foreach ($tier in $tiers) { #Write-host "$logLead : Tier $tier Installs" $installs = @() foreach ($package in $packagesToUpgrade) { if ($package.Tier -eq $tier) { $installs += $package } } foreach ($package in $newPackagesToInstall) { if ($package.Tier -eq $tier) { $installs += $package } } $packagesToInstall[$tier] = $installs } Write-Host "$logLead : Setting Alkami services recovery to take no action" Set-SDKServiceRecovery -ServiceName 'All' -Action 'TakeNoAction' Write-Host "$logLead : Stopping IIS and only services to be upgraded" # Only stop services we are going to be installing so we do as little interruption as possible # Only has to be packages that are being upgraded, the ones being installed don't affect anything foreach ($package in $packagesToUpgrade) { $serviceInfo = (Get-ServiceInfoByCIMFragment -Fragment $package.Id) if ($null -eq $serviceInfo) { # This isn't a service, so we don't care continue } $serviceName = $serviceInfo.Name if (![string]::IsNullOrWhiteSpace($serviceName)) { #Stop-AlkamiService -ServiceName $serviceName #Write-Host "Package Id: " $package.Id #Write-Host "ServiceName: " $serviceName $service = Get-Service -Name $serviceName | Select-Object -First 1 $processes = @(Get-ProcessFromService $service) if ($processes.Count -ne 0) { $process = ($processes | Select-Object -First 1) "Stopping Process: $($process.Name) | $($process.id)" | Out-Default -Transcript | Out-Null #Stop-ProcessIfFound $process.Name Stop-Process $process.Id -force | Out-Null } else { "No running processes found for $($service.Name)" | Out-Default -Transcript | Out-Null } } if ($null -eq $package.ComponentType) { # because of legacy installers, remove this package's service registration to allow for downgrade experiences Invoke-SCExe @('delete', $serviceName) } } if ($packagesToRemove.Count -gt 0) { # remove packages we are uninstalling foreach ($package in $packagesToRemove) { Write-Verbose "Package to remove: $package" $serviceName = (Get-ServiceInfoByCIMFragment -Fragment $package).Name if (![string]::IsNullOrWhiteSpace($serviceName)) { Stop-AlkamiService -ServiceName $serviceName } } } Stop-IISOnly #Stop Radium if installed $serviceName = Get-ServiceInfoByCIMFragment -QueryFragment (Join-Path (Get-OrbPath) 'Radium') if (-not [string]::IsNullOrWhiteSpace($serviceName.Name)) { Stop-AlkamiService -ServiceName $serviceName.Name } #Stop Nag if installed $serviceName = Get-ServiceInfoByCIMFragment -QueryFragment (Join-Path (Get-OrbPath) 'Nag') if (-not [string]::IsNullOrWhiteSpace($serviceName.Name)) { Stop-AlkamiService -ServiceName $serviceName.Name } # We are installing ORB, so we are always going to start it back up $startIIS = $true if ($packagesToRemove.Count -gt 0) { Write-Host "$logLead : Removing packages now" # remove packages we are uninstalling foreach ($package in $packagesToRemove) { "Completely removed package [$package]" | Out-Default -Transcript | Out-Null choco uninstall $package -n -f -i } } $chocoPath = Get-ChocolateyInstallPath Write-Host "$logLead : Installing packages now" "Dumping `$newPackagesToInstall to Transcript" | Out-Default -Transcript | Out-Null (ConvertTo-Json $newPackagesToInstall) | Out-Default -Transcript | Out-Null "Dumping `$packagesToUpgrade to Transcript" | Out-Default -Transcript | Out-Null (ConvertTo-Json $packagesToUpgrade) | Out-Default -Transcript | Out-Null "Dumping `$packagesToRemove to Transcript" | Out-Default -Transcript | Out-Null (ConvertTo-Json $packagesToRemove) | Out-Default -Transcript | Out-Null "Dumping `$tiers to Transcript" | Out-Default -Transcript | Out-Null (ConvertTo-Json $tiers) | Out-Default -Transcript | Out-Null foreach ($tier in $tiers) { if ($tier -eq 1) { Write-Host "Force removing AlkamiModules that just got installed" Get-Module Alkami* | Remove-Module -Force Import-Module Alkami.PowerShell.Database # ensure that database runners can get run, or show us an error message #$tiers[0].Where({$_.ComponentType -eq 'SREModule' -and $_.Id -notmatch "\.SRE\."}).Id | Foreach-Object { Import-Module $_ } Write-Host "$logLead : Starting Redis" Start-Service -Name "redis-master18620" Start-Service -Name "redis-slave18621" Write-Warning "$logLead : If the following two commands (Import-Module WebAdministration, Load-Assembly) fail and stop the installation, please close and reopen your powershell session and try again." Write-Warning "$logLead : Alkami developers occasionally encounter this on new machine installs, and are working to resolve the issue." Import-Module WebAdministration try { Add-Type -AssemblyName "Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" } catch { # Just in case we have IIS Express and IIS loaded (dev machines) [System.Reflection.Assembly]::LoadFile("C:\Windows\system32\inetsrv\Microsoft.Web.Administration.dll") } Assert-PlatformDeveloperKitComponentsInstalled # Before we install anything after the basics, we need to initialize the databases if they haven't been. We should have already delivered the database component. if ($null -eq (Get-EnvironmentVariable -Name "Alkami_SDK_Initialization_DatabaseStandup")) { Write-Host "Ensure default AlkamiDatabases are available and have been configured" $connectionString = Get-MasterConnectionString # names are hard-coded especially Initialize-AlkamiDatabase $connectionString 'AlkamiMaster' Initialize-AlkamiDatabase $connectionString 'DeveloperDynamic' # TODO: Incredibly brittle. Will need to be refactored # Easiest refactor is to restore a masterdb in the initialize functions above C:\programdata\Alkami\Alkami\MachineSetup\DatabaseCore\Migrate.exe -a C:\programdata\Alkami\Alkami\MachineSetup\DatabaseCore\Alkami.Tools.MasterDatabaseMigration.dll --connectionString $connectionString -provider SqlServer2008 -tag alkamimaster -context sdk Write-Host "Setting tenant" Import-DeveloperDynamicTenant $connectionString Set-EnvironmentVariable -Name "Alkami_SDK_Initialization_DatabaseStandup" -Value (Get-Date) -StoreName Machine } # Before we install anything after the basics, we need to initialize the websites and webapps if they haven't been. We should have already delivered the required orb file components. Write-Host "$logLead : Creating web sites and applications now" Install-SDKIISComponents # You have to create the sites before you can run the migrations because of how the database users get setup in the database # It's a pain, I know, but yay out of order operations ... Write-host "$logLead : Running migrations" Invoke-SDKAlkamiMigrations } $packages = $packagesToInstall[$tier] if (Test-IsCollectionNullOrEmpty $packages) { Write-Host "$logLead : PackagesToInstall was empty" continue } else { Write-Host "$logLead : Installing Tier $tier packages with count $($packages.count)" } $potentialServicePaths = @() $parallelInstall = @() # We have to check $allPackages because they may already be installed foreach ($package in $packages) { $packageFolder = Join-Path -Path (Join-Path -Path $chocoPath -ChildPath "lib") -ChildPath $package.Id $potentialServicePaths += $packageFolder } ($potentialServicePaths -Join "`n") | Out-Default -Transcript | Out-Null foreach ($package in $packages) { $packageFolder = Join-Path -Path (Join-Path -Path $chocoPath -ChildPath "lib") -ChildPath $package.Id $runScripts = "-y" if ($package.HasManifest -and $null -ne $package.ComponentType) { $runScripts = "--skip-scripts" $parallelInstall += $packageFolder } $runQuietly = "--no-progress" $sayVeryLittle = "--limit-output" $ignoreDependencies = "-i" if ($package.Id -in @("redis-64")) { $ignoreDependencies = " " } $version = "$($package.Version)" $packageVersion = "[$($package.Version)]" $chocoSplat = @( "upgrade" $package.Id $ignoreDependencies $runScripts $runQuietly $sayVeryLittle ) if ($package.VersionCanTakeAny) { $version = "" $packageVersion = "the latest available" # Write-Host "choco upgrade $($package.Id) -i $runScripts" # choco upgrade $package.Id -i $runScripts #Removed -f } else { # Write-Host "choco upgrade $($package.Id) --version $version -i $runScripts" # choco upgrade $package.Id --version $version -i $runScripts #Removed -f $chocoSplat += "--version=$version" } Write-Host "Downloading$(if (!$package.HasManifest){" and installing"}) $($package.id) - This may take a moment, even if things seem frozen." # https://stackoverflow.com/questions/38523369/write-host-vs-write-information-in-powershell-5 # Out-Default -Transcript <-- magic Invoke-CallOperatorWithPathAndParameters -Path Choco -Arguments $chocoSplat *>&1 | Out-Default -Transcript | Out-Null "Installed package [$($package.Id)] version to $packageVersion" | Out-Default -Transcript | Out-Null } if (!(Test-IsCollectionNullOrEmpty $parallelInstall)) { Write-Host "Running parallel installs. This is going to look like things are locked up. They aren't. Promise." Invoke-Parallel -Objects $parallelInstall -ReturnObjects -Script { param ($innerPackagePath) $path = Join-Path -Path (Join-Path -Path $innerPackagePath -ChildPath tools) -ChildPath "chocolateyInstall.ps1" if (Test-Path -Path $path) { Write-Host "Calling $path" & $path } } } $servicesInstalled = Invoke-Parallel -Objects $potentialServicePaths -ReturnObjects -Script { param ($innerPackagePath) $cimService = Get-ServiceInfoByCIMFragment -QueryFragment $innerPackagePath if ($null -ne $cimService) { return $cimService.Name } } if ($tier -lt 3) { foreach ($serviceName in $servicesInstalled) { Write-Host "$logLead : Start-AlkamiService -ServiceName $serviceName" Start-AlkamiService -ServiceName $serviceName } } else { Invoke-Parallel -Objects $servicesInstalled -ReturnObjects -Script { param ($serviceName) Start-AlkamiService -ServiceName $serviceName } } } # We've already done the database migrations, but we need to ensure the database users got setup for componentized web services # Ensure logins are setup Invoke-DatabaseConfigurationAlkamiMasterTask Invoke-DatabaseConfigurationAlkamiTenantTask if ($startIIS) { Remove-DotNetTemporaryFiles Start-IISAndServices # restarts all of the services that were not upgraded/installed but stopped by installing Alkami.SRE.MigrationUtility } Write-Host "$logLead : Setting Alkami services back to recover" Set-SDKServiceRecovery -ServiceName 'All' -Action 'recovery' Set-SDKServiceStartupType -ServiceName 'All' -StartupType 'Automatic' } finally { Stop-Transcript Write-Host "`n`n`tTranscript log saved to $transcriptPath`n`n" } }