diff --git a/BC.SaasHelper.ps1 b/BC.SaasHelper.ps1 index a57e9c5ab..cab731afb 100644 --- a/BC.SaasHelper.ps1 +++ b/BC.SaasHelper.ps1 @@ -7,6 +7,7 @@ . (Join-Path $PSScriptRoot "Saas\Get-BcEnvironmentUsedStorage.ps1") . (Join-Path $PSScriptRoot "Saas\Get-BcEnvironmentUpdateWindow") . (Join-Path $PSScriptRoot "Saas\Get-BcEnvironmentInstalledExtensions.ps1") +. (Join-Path $PSScriptRoot "Saas\Get-BcEnvironmentSessions.ps1") . (Join-Path $PSScriptRoot "Saas\Get-BcNotificationRecipients.ps1") . (Join-Path $PSScriptRoot "Saas\Get-BcEnvironmentPublishedApps.ps1") . (Join-Path $PSScriptRoot "Saas\Get-BcEnvironmentScheduledUpgrade.ps1") @@ -16,10 +17,12 @@ . (Join-Path $PSScriptRoot "Saas\New-BcNotificationRecipient.ps1") . (Join-Path $PSScriptRoot "Saas\Publish-PerTenantExtensionApps.ps1") . (Join-Path $PSScriptRoot "Saas\Remove-BcEnvironment.ps1") +. (Join-Path $PSScriptRoot "Saas\Remove-BcEnvironmentSession.ps1") . (Join-Path $PSScriptRoot "Saas\Rename-BcEnvironment.ps1") . (Join-Path $PSScriptRoot "Saas\Reschedule-BcEnvironmentUpgrade.ps1") +. (Join-Path $PSScriptRoot "Saas\Restart-BcEnvironment.ps1") . (Join-Path $PSScriptRoot "Saas\Restore-BcEnvironment.ps1") . (Join-Path $PSScriptRoot "Saas\Set-BcEnvironmentApplicationInsightsKey.ps1") . (Join-Path $PSScriptRoot "Saas\Set-BcEnvironmentUpdateWindow.ps1") . (Join-Path $PSScriptRoot "Saas\Wait-BcEnvironmentReady.ps1") -. (Join-Path $PSScriptRoot "Saas\Download-BcEnvironmentInstalledExtensionToFolder.ps1") \ No newline at end of file +. (Join-Path $PSScriptRoot "Saas\Download-BcEnvironmentInstalledExtensionToFolder.ps1") diff --git a/BcContainerHelper.psd1 b/BcContainerHelper.psd1 index 938d7a3ef..8a1bc63f1 100644 --- a/BcContainerHelper.psd1 +++ b/BcContainerHelper.psd1 @@ -112,7 +112,8 @@ FunctionsToExport = 'Add-FontsToBcContainer', 'Add-GitToAlProjectFolder', 'Get-BcEnvironmentAvailableRestorePeriods', 'Get-BcEnvironmentDatabaseExportHistory', 'Get-BcEnvironmentInstalledExtensions', 'Download-BcEnvironmentInstalledExtensionToFolder', - 'Get-BcEnvironmentOperations', 'Get-BcEnvironmentPublishedApps', + 'Get-BcEnvironmentOperations', 'Get-BcEnvironmentPublishedApps', + 'Get-BcEnvironmentSessions', 'Get-BcEnvironments', 'Get-BcEnvironmentScheduledUpgrade', 'Get-BcEnvironmentUpdateWindow', 'Get-BcEnvironmentUsedStorage', 'Get-BcNotificationRecipients', 'Get-BcNuGetPackage', @@ -154,6 +155,7 @@ FunctionsToExport = 'Add-FontsToBcContainer', 'Add-GitToAlProjectFolder', 'Remove-BcCompilerFolder', 'Remove-BcContainer', 'Remove-BcContainerSession', 'Remove-BcContainerTenant', 'Remove-BcDatabase', 'Remove-BcEnvironment', + 'Remove-BcEnvironmentSession', 'Remove-CompanyInBcContainer', 'Remove-ConfigPackageInBcContainer', 'Remove-DesktopShortcut', 'Rename-BcEnvironment', 'Renew-BcAuthContext', 'Renew-LetsEncryptCertificate', @@ -162,6 +164,7 @@ FunctionsToExport = 'Add-FontsToBcContainer', 'Add-GitToAlProjectFolder', 'Resolve-DependenciesFromAzureFeed', 'Restart-BcContainer', 'Restart-BcContainerServiceTier', 'Restore-BcDatabaseFromArtifacts', 'Restore-BcEnvironment', 'Restore-DatabasesInBcContainer', + 'Restart-BcEnvironment', 'Run-AlCops', 'Run-AlPipeline', 'Run-AlValidation', 'Run-BCPTTestsInBcContainer', 'Run-ConnectionTestToBcContainer', 'Run-TestsInBcContainer', 'Set-BcContainerFeatureKeys', diff --git a/Misc/Set-BcContainerFeatureKeys.ps1 b/Misc/Set-BcContainerFeatureKeys.ps1 index 57686760d..100cbf6fa 100644 --- a/Misc/Set-BcContainerFeatureKeys.ps1 +++ b/Misc/Set-BcContainerFeatureKeys.ps1 @@ -1,4 +1,4 @@ -<# +<# .Synopsis Set Feature Keys in container .Description @@ -9,8 +9,11 @@ Tenant in which you want to set feature keys .Parameter featureKeys Hashtable of featureKeys you want to set + .Parameter EnableInCompany + Enables/disables features for given company name. No data update is initiated. .Example Set-BcContainerFeatureKeys -containerName test2 -featureKeys @{"EmailHandlingImprovements" = "None"} + Set-BcContainerFeatureKeys -containerName test2 -featureKeys @{"EmailHandlingImprovements" = "None"} -EnableInCompany 'CRONUS International Ltd.' #> function Set-BcContainerFeatureKeys { Param ( @@ -19,14 +22,16 @@ function Set-BcContainerFeatureKeys { [Parameter(Mandatory=$false)] [string] $tenant = "*", [Parameter(Mandatory=$true)] - [hashtable] $featureKeys + [hashtable] $featureKeys, + [Parameter(Mandatory=$false)] + [string] $EnableInCompany ) $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @("featureKeys") try { - + if ($featureKeys.Keys.Count -ne 0) { - Invoke-ScriptInBCContainer -containerName $containerName -ScriptBlock { Param([string] $tenant, [hashtable] $featureKeys) + Invoke-ScriptInBCContainer -containerName $containerName -ScriptBlock { Param([string] $tenant, [hashtable] $featureKeys, $EnableInCompany) $customConfigFile = Join-Path (Get-Item "C:\Program Files\Microsoft Dynamics NAV\*\Service").FullName "CustomSettings.config" [xml]$customConfig = [System.IO.File]::ReadAllText($customConfigFile) $databaseServer = $customConfig.SelectSingleNode("//appSettings/add[@key='DatabaseServer']").Value @@ -55,6 +60,13 @@ try { $databases | % { $databaseName = $_ Write-Host "Setting feature keys on database: $databaseName" + # Just information about 'mode' of Feature Key update + if ([String]::IsNullOrEmpty($EnableInCompany)) { + Write-Host "Setting feature keys globally, but not for any company" -ForegroundColor Yellow + } + else { + Write-Host "Setting feature keys globally and for company "$EnableInCompany -ForegroundColor Yellow + } $featureKeys.Keys | % { $featureKey = $_ $enabledStr = $featureKeys[$featureKey] @@ -68,23 +80,39 @@ try { $enabled = -1 Write-Host "WARNING: Unknown value ($enabledStr) for feature key $featureKey" } - if ($enabled -ne -1) { + + # Test if feature which has to be updated is available in table "Feature Data Update Status$63ca2fa4-4f03-4f2b-a480-172fef340d3f" + $FeatureExistsInDestination = Invoke-Sqlcmd -Database $databaseName -Query $("SELECT COUNT(*) FROM [dbo].[Feature Data Update Status"+'$'+"63ca2fa4-4f03-4f2b-a480-172fef340d3f] where [Feature Key] = '$featureKey'") + + if(($FeatureExistsInDestination[0].ToString()) -eq "0") { + Write-host "Feature $featureKey doesn't exist in database - Failure" + } + + # Feature key is updated just in case that status is correct and respective feature is available in table "Feature Data Update Status$63ca2fa4-4f03-4f2b-a480-172fef340d3f" + if (($enabled -ne -1) -and ($FeatureExistsInDestination[0].ToString() -ne "0")) { try { - #Create new record in table of feature setup table [Tenant Feature Key] in case it is missing + #Create new record in table "Tenant Feature Key" in case it is missing $SQLRecord = Invoke-Sqlcmd -Database $databaseName -Query "SELECT * FROM [dbo].[Tenant Feature Key] where ID = '$featureKey'" - if ([String]::IsNullOrEmpty($SQLRecord)) - { - Write-host "Creating record for feature ID '$featureKey'" + if ([String]::IsNullOrEmpty($SQLRecord)) { + Write-host "Creating record for feature $featureKey" $SQLcolumns = "ID, Enabled" $SQLvalues = "'$featureKey',0" - Invoke-Sqlcmd -Database $databaseName -Query "INSERT INTO [CRONUS].[dbo].[Tenant Feature Key] ($SQLcolumns) VALUES ($SQLvalues)" -Verbose + Invoke-Sqlcmd -Database $databaseName -Query "INSERT INTO [dbo].[Tenant Feature Key] ($SQLcolumns) VALUES ($SQLvalues)" -Verbose } Write-Host -NoNewline "Setting feature key $featureKey to $enabledStr - " $result = Invoke-Sqlcmd -Database $databaseName -Query "UPDATE [dbo].[Tenant Feature Key] set Enabled = $enabled where ID = '$featureKey';Select @@ROWCOUNT" if ($result[0] -eq "1") { Write-Host " Success" + # Update record in table "Feature Data Update Status$63ca2fa4-4f03-4f2b-a480-172fef340d3f" if it is requested for particular company + if (![String]::IsNullOrEmpty($EnableInCompany)) { + Write-Host -NoNewline "Setting feature key $featureKey to $enabledStr for Company $EnableInCompany - " + $result = Invoke-Sqlcmd -Database $databaseName -Query $("UPDATE [dbo].[Feature Data Update Status"+'$'+"63ca2fa4-4f03-4f2b-a480-172fef340d3f] set [Feature Status] = $enabled where [Feature Key] = '$featureKey' AND [Company Name] = '$EnableInCompany';Select @@ROWCOUNT") + if ($result[0] -eq "1") { + Write-Host " Success" + } + } } - else { + if ($result[0] -ne "1") { throw } } @@ -95,7 +123,7 @@ try { } } } - } -argumentList $tenant, $featureKeys + } -argumentList $tenant, $featureKeys, $EnableInCompany } } catch { diff --git a/Saas/Get-BcEnvironmentSessions.ps1 b/Saas/Get-BcEnvironmentSessions.ps1 new file mode 100644 index 000000000..f2acb88fa --- /dev/null +++ b/Saas/Get-BcEnvironmentSessions.ps1 @@ -0,0 +1,55 @@ +<# + .Synopsis + Function for retrieving a list of current sessions for given environment from an online Business Central tenant + .Description + Function for retrieving a list of current sessions for given environment from an online Business Central tenant + Wrapper for https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/administration/administration-center-api_session_management#get-active-sessions + .Parameter bcAuthContext + Authorization Context created by New-BcAuthContext. + .Parameter applicationFamily + Application Family in which the environment is located. Default is BusinessCentral. + .Parameter environment + Name of the environment + .Parameter apiVersion + API version. Default is v2.21. + .Example + $authContext = New-BcAuthContext -includeDeviceLogin + Get-BcEnvironmentSessions -bcAuthContext $authContext -environment "MySandbox" +#> +function Get-BcEnvironmentSessions { + Param( + [Parameter(Mandatory = $true)] + [Hashtable] $bcAuthContext, + [string] $applicationFamily = "BusinessCentral", + [Parameter(Mandatory = $true)] + [string] $environment = "", + [string] $apiversion = "v2.21" + ) + + $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @() + try { + $bcAuthContext, $headers, $endPointURL = Create-SaasUrl -bcAuthContext $bcAuthContext -endPoint "sessions" -environment $environment -applicationFamily $applicationFamily -apiVersion $apiVersion + try { + $Result = (Invoke-RestMethod -Method Get -UseBasicParsing -Uri $endPointURL -Headers $headers) + if ($Result.PSObject.Properties.Name -eq 'Value') { + $Result.Value + } + else { + $Result + } + } + catch { + Write-Host $_ + throw (GetExtendedErrorMessage $_) + } + } + catch { + TrackException -telemetryScope $telemetryScope -errorRecord $_ + throw + } + finally { + TrackTrace -telemetryScope $telemetryScope + } +} + +Export-ModuleMember -Function Get-BcEnvironmentSessions diff --git a/Saas/Remove-BcEnvironmentSession.ps1 b/Saas/Remove-BcEnvironmentSession.ps1 new file mode 100644 index 000000000..4e59aba6c --- /dev/null +++ b/Saas/Remove-BcEnvironmentSession.ps1 @@ -0,0 +1,61 @@ +<# + .Synopsis + Function for stopping and deletion of session from environment (from an online Business Central tenant) + .Description + Function for stop and deletion of session from environment (from an online Business Central tenant) + Wrapper for https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/administration/administration-center-api_session_management#stop-and-delete-a-session + .Parameter SessionID + Session ID of session to be stopped and deleted. + .Parameter bcAuthContext + Authorization Context created by New-BcAuthContext. + .Parameter applicationFamily + Application Family in which the environment is located. Default is BusinessCentral. + .Parameter environment + Name of the environment + .Parameter apiVersion + API version. Default is v2.21. + .Example + $authContext = New-BcAuthContext -includeDeviceLogin + Remove-BcEnvironmentSession -bcAuthContext $authContext -environment "MySandbox" +#> +function Remove-BcEnvironmentSession { + Param( + [Parameter(Mandatory = $true)] + [Hashtable] $bcAuthContext, + [Parameter(Mandatory = $true)] + [string] $sessionID, + [string] $applicationFamily = "BusinessCentral", + [Parameter(Mandatory = $true)] + [string] $environment = "", + [string] $apiversion = "v2.21" + ) + + $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @() + try { + $bcAuthContext, $headers, $endPointURL = Create-SaasUrl -bcAuthContext $bcAuthContext -endPoint "sessions/$sessionID" -environment $environment -applicationFamily $applicationFamily -apiVersion $apiVersion + + Write-Host "Submitting session deletion request for session ID $sessionID in $environment" + + try { + $Result = (Invoke-RestMethod -Method Delete -UseBasicParsing -Uri $endPointURL -Headers $headers) + + } + catch { + Write-Host $_ + throw (GetExtendedErrorMessage $_) + } + Write-Host "Session deletion request submitted" + + } + + + catch { + TrackException -telemetryScope $telemetryScope -errorRecord $_ + throw + } + finally { + TrackTrace -telemetryScope $telemetryScope + } +} + +Export-ModuleMember -Function Remove-BcEnvironmentSession diff --git a/Saas/Restart-BcEnvironment.ps1 b/Saas/Restart-BcEnvironment.ps1 new file mode 100644 index 000000000..cee452997 --- /dev/null +++ b/Saas/Restart-BcEnvironment.ps1 @@ -0,0 +1,81 @@ +<# + .Synopsis + Function for restarting a Business Central online environment + .Description + Function for restarting a Business Central online environment + .Parameter bcAuthContext + Authorization Context created by New-BcAuthContext. + .Parameter applicationFamily + Application Family in which the environment is located. Default is BusinessCentral. + .Parameter environment + Name of the environment for restarting + .Parameter apiVersion + API version. Default is v2.21. + .Parameter doNotWait + Include this switch if you don't want to wait for completion of the environment + .Example + $authContext = New-BcAuthContext -includeDeviceLogin + Restart-BcEnvironment -bcAuthContext $authContext -environment 'MySandbox' +#> +function Restart-BcEnvironment { + Param( + [Parameter(Mandatory = $true)] + [Hashtable] $bcAuthContext, + [Parameter(Mandatory = $true)] + [string] $environment, + [Parameter(Mandatory = $false)] + [string] $applicationFamily = "BusinessCentral", + [Parameter(Mandatory = $false)] + [string] $apiversion = "v2.21", + [Parameter(Mandatory = $false)] + [switch] $doNotWait + ) + + $telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @() + try { + $bcAuthContext = Renew-BcAuthContext -bcAuthContext $bcAuthContext + Wait-BcEnvironmentsReady -environments @($environment) -bcAuthContext $bcAuthContext -apiVersion $apiVersion -applicationFamily $applicationFamily + + $bcEnvironments = Get-BcEnvironments -bcAuthContext $bcAuthContext -applicationFamily $applicationFamily -apiVersion $apiVersion + $bcEnvironment = $bcEnvironments | Where-Object { $_.name -eq $environment } + if (!($bcEnvironment)) { + throw "No environment named $environment exists" + } + + + $bcAuthContext, $headers, $endPointURL = Create-SaasUrl -bcAuthContext $bcAuthContext -endPoint "restart" -environment $environment -applicationFamily $applicationFamily -apiVersion $apiVersion + + Write-Host "Submitting environment restart request for $environment" + + try { + $environmentResult = (Invoke-RestMethod -Method POST -Uri $endPointURL -Headers $headers -ContentType 'application/json') + } + catch { + throw (GetExtendedErrorMessage $_) + } + Write-Host "Restart environment request submitted" + + if (!$doNotWait) { + Write-Host -NoNewline "Restarting." + do { + Start-Sleep -Seconds 2 + Write-Host -NoNewline "." + $bcAuthContext = Renew-BcAuthContext -bcAuthContext $bcAuthContext + $Operation = (Get-BcOperations -bcAuthContext $bcAuthContext -apiVersion $apiVersion -applicationFamily $applicationFamily | Where-Object { ($_.productFamily -eq $applicationFamily) -and ($_.type -eq $environmentResult.type) -and ($_.id -eq $environmentResult.id) }) + } while ($Operation.status -in "queued", "scheduled", "running") + Write-Host $Operation.status + if ($Operation.status -eq "failed") { + throw "Could not restart environment with error: $($Operation.errorMessage)" + } + } + } + catch { + TrackException -telemetryScope $telemetryScope -errorRecord $_ + throw + } + finally { + TrackTrace -telemetryScope $telemetryScope + } +} + +Export-ModuleMember -Function Restart-BcEnvironment