diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c42..0000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Enable-TenantsAfterRecoveryOperation.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Enable-TenantsAfterRecoveryOperation.ps1 deleted file mode 100644 index bae3902..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Enable-TenantsAfterRecoveryOperation.ps1 +++ /dev/null @@ -1,418 +0,0 @@ -<# -.SYNOPSIS - Brings tenants back online after required resources are available in a region - -.DESCRIPTION - This script is intended to be run as a background job in Wingtip SaaS app environment scripts. - The script marks tenants as online as their relevant resources become available after a restore or failover. - -.PARAMETER WingtipRecoveryResourceGroup - Resource group that is used to contain recovered resources - -.EXAMPLE - [PS] C:\>.\Enable-TenantsAfterRecoveryOperation.ps1 -WingtipRecoveryResourceGroup "sampleRecoveryResourceGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup, - - [parameter(Mandatory=$false)] - [validateset('restore', 'repatriation')] - [string]$RecoveryOperation ="restore" -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Import-Module "$PSScriptRoot\..\..\..\Common\CatalogAndDatabaseManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\WtpConfig" -Force -# Import-Module "$PSScriptRoot\..\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration - - -# Get the active tenant catalog -$tenantCatalog = Get-Catalog -ResourceGroupName $WingtipRecoveryResourceGroup -WtpUser $wtpUser.Name - -# Mark tenants online as their databases become available -while ($true) -{ - $tenantList = Get-ExtendedTenant -Catalog $tenantCatalog - $tenantCount = (Get-Tenants -Catalog $tenantCatalog).Count - $offlineTenants = @($tenantList).Where({(($_.TenantStatus -ne 'Online') -or ($_.TenantRecoveryState -In 'n/a', 'OnlineInOrigin'))}) - $tenantsInRecovery = @($tenantList).Where({$_.TenantRecoveryState -ne 'OnlineInOrigin'}) - $onlineTenantCount = $tenantCount - ($offlineTenants.Count) - $repatriatedTenantCount = $tenantCount - ($tenantsInRecovery.Count) - - # Exit if all tenants are online after recovery operation - if (!$offlineTenants -and ($RecoveryOperation -eq 'restore')) - { - # Output recovery progress - Write-Output "100% ($onlineTenantCount of $tenantCount)" - break - } - # Exit if all tenants are online in origin after repatriation operation - elseif (!$tenantsInRecovery -and ($RecoveryOperation -eq 'repatriation')) - { - # Output recovery progress - Write-Output "100% ($repatriatedTenantCount of $tenantCount)" - break - } - else - { - # Get list of offline tenant databases and their recovery status - $originTenantDatabases = @() - $restoredTenantDatabases = @() - $originTenantDatabases += Find-AzureRmResource -ResourceGroupNameEquals $wtpUser.ResourceGroupName -ResourceType "Microsoft.sql/servers/databases" -ResourceNameContains "tenants" - $restoredTenantDatabases += Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers/databases" -ResourceNameContains "tenants" - $databaseRecoveryStatuses = Get-ExtendedDatabase -Catalog $tenantCatalog - - # Output recovery progress - if ($RecoveryOperation -eq 'restore') - { - $TenantRecoveryPercentage = [math]::Round($onlineTenantCount/$tenantCount,2) - $TenantRecoveryPercentage = $TenantRecoveryPercentage * 100 - Write-Output "$TenantRecoveryPercentage% ($onlineTenantCount of $tenantCount)" - } - elseif ($RecoveryOperation -eq 'repatriation') - { - $TenantRecoveryPercentage = [math]::Round($repatriatedTenantCount/$tenantCount,2) - $TenantRecoveryPercentage = $TenantRecoveryPercentage * 100 - Write-Output "$TenantRecoveryPercentage% ($repatriatedTenantCount of $tenantCount)" - } - - # Update tenant status based on the status of database - # Note: this job can be sped up by checking the status of tenant databases in multiple background jobs - if ($RecoveryOperation -eq 'repatriation') - { - $relevantTenantList = $tenantsInRecovery - } - else - { - $relevantTenantList = $offlineTenants - } - - foreach ($tenant in $relevantTenantList) - { - $tenantKey = Get-TenantKey $tenant.TenantName - $tenantRecoveryState = $tenant.TenantRecoveryState - $originTenantDatabase = $originTenantDatabases | Where-Object {$_.Name -match $tenant.DatabaseName} - $restoredTenantDatabase = $restoredTenantDatabases | Where-Object {$_.Name -match $tenant.DatabaseName} - $originDatabaseRecoveryStatus = $databaseRecoveryStatuses | Where-Object{(($_.DatabaseName -eq $tenant.DatabaseName) -and ($_.ServerName -notmatch "$($config.RecoveryRoleSuffix)$"))} - $restoredDatabaseRecoveryStatus = $databaseRecoveryStatuses | Where-Object{(($_.DatabaseName -eq $tenant.DatabaseName) -and ($_.ServerName -match "$($config.RecoveryRoleSuffix)$"))} - - if ($originDatabaseRecoveryStatus.RecoveryState -In ('restoring', 'failingOver')) - { - # Update tenant recovery status to 'RestoringTenantData' (if applicable) - if ($tenantRecoveryState -ne 'RestoringTenantData') - { - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startRecovery" -TenantKey $tenantKey - } - } - elseif (($originDatabaseRecoveryStatus.RecoveryState -In ('restored', 'failedOver')) -and ($tenantRecoveryState -In 'RestoringTenantData', 'n/a')) - { - # Update tenant recovery status to 'RestoredTenantData' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endRecovery" -TenantKey $tenantKey - - # Take tenant offline if applicable - if ($tenant.TenantStatus -ne "Offline") - { - Set-TenantOffline -Catalog $tenantCatalog -TenantKey $tenantKey - } - } - elseif ($restoredTenantDatabase -and !$originTenantDatabase) - { - # Mark tenants that are created in the recovery region as 'OnlineInRecovery' - if ($tenantRecoveryState -ne 'OnlineInRecovery') - { - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endRecovery" -TenantKey $tenantKey - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startShardUpdateToRecovery" -TenantKey $tenantKey - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endShardUpdateToRecovery" -TenantKey $tenantKey - } - } - elseif (($originDatabaseRecoveryStatus.RecoveryState -In ('restored', 'failedOver')) -and ($tenantRecoveryState -eq 'RestoredTenantData')) - { - # Take tenant offline if applicable - if ($tenant.TenantStatus -ne "Offline") - { - Set-TenantOffline -Catalog $tenantCatalog -TenantKey $tenantKey - } - - if ($restoredTenantDatabase) - { - # Get tenant resources - $restoredTenantServer = $restoredTenantDatabase.Name.Split('/')[0] - $originTenantServer = $originTenantDatabase.Name.Split('/')[0] - - # Update tenant recovery status to 'UpdatingTenantShardToRecovery' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startShardUpdateToRecovery" -TenantKey $tenantKey - - # Update tenant shard to point to recovered database - $updateComplete = Update-TenantShardInfo ` - -Catalog $tenantCatalog ` - -TenantName $tenant.TenantName ` - -FullyQualifiedTenantServerName "$restoredTenantServer.database.windows.net" ` - -TenantDatabaseName $tenant.DatabaseName - - if ($updateComplete) - { - # Mark tenant online in catalog - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - - # Check if recovery database entry exists in catalog for tenant - $recoveryDatabaseEntry = Get-ExtendedDatabase -Catalog $tenantCatalog -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - if (!$recoveryDatabaseEntry) - { - # Add recovery database entry in catalog - $recoveryDatabase = Get-AzureRmSqlDatabase -ResourceGroupName $WingtipRecoveryResourceGroup -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - Set-ExtendedDatabase -Catalog $tenantCatalog -Database $recoveryDatabase - } - Set-TenantDatabaseRecoveryChecksum -Catalog $tenantCatalog -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - $onlineTenantCount +=1 - - # Update tenant recovery status to 'OnlineInRecovery' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endShardUpdateToRecovery" -TenantKey $tenantKey - } - } - } - elseif (($originDatabaseRecoveryStatus.RecoveryState -In ('restored', 'failedOver')) -and ($tenantRecoveryState -eq 'UpdatingTenantShardToRecovery')) - { - $restoredTenantServer = $restoredTenantDatabase.Name.Split('/')[0] - $originTenantServer = $originTenantDatabase.Name.Split('/')[0] - - # Update tenant shard to point to recovered database - $updateComplete = Update-TenantShardInfo ` - -Catalog $tenantCatalog ` - -TenantName $tenant.TenantName ` - -FullyQualifiedTenantServerName "$restoredTenantServer.database.windows.net" ` - -TenantDatabaseName $tenant.DatabaseName - - if ($updateComplete) - { - # Mark tenant online in catalog - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - - # Check if recovery database entry exists in catalog for tenant - $recoveryDatabaseEntry = Get-ExtendedDatabase -Catalog $tenantCatalog -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - if (!$recoveryDatabaseEntry) - { - # Add recovery database entry in catalog - $recoveryDatabase = Get-AzureRmSqlDatabase -ResourceGroupName $WingtipRecoveryResourceGroup -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - Set-ExtendedDatabase -Catalog $tenantCatalog -Database $recoveryDatabase - } - Set-TenantDatabaseRecoveryChecksum -Catalog $tenantCatalog -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - $onlineTenantCount +=1 - - # Update tenant recovery status to 'OnlineInRecovery' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endShardUpdateToRecovery" -TenantKey $tenantKey - } - } - elseif (($originDatabaseRecoveryStatus.RecoveryState -In ('restored', 'failedOver')) -and ($tenantRecoveryState -eq 'OnlineInRecovery')) - { - # Set tenant online if not already so - if ($tenant.TenantStatus -ne "Online") - { - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - - # Check if recovery database entry exists in catalog for tenant - $recoveryDatabaseEntry = Get-ExtendedDatabase -Catalog $tenantCatalog -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - if (!$recoveryDatabaseEntry) - { - # Add recovery database entry in catalog - $recoveryDatabase = Get-AzureRmSqlDatabase -ResourceGroupName $WingtipRecoveryResourceGroup -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - Set-ExtendedDatabase -Catalog $tenantCatalog -Database $recoveryDatabase - } - Set-TenantDatabaseRecoveryChecksum -Catalog $tenantCatalog -ServerName $restoredTenantServer -DatabaseName $tenant.DatabaseName - $onlineTenantCount += 1 - } - } - elseif (($restoredTenantDatabase) -and ($restoredDatabaseRecoveryStatus.RecoveryState -In 'resetting')) - { - # Update tenant recovery status to 'ResettingTenantToOrigin' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startReset" -TenantKey $tenantKey - } - elseif (($originDatabaseRecoveryStatus.RecoveryState -In 'complete') -and ($tenantRecoveryState -eq 'ResettingTenantToOrigin')) - { - if ($tenant.TenantStatus -ne 'Online') - { - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - $onlineTenantCount +=1 - } - - # Update tenant recovery status to 'OnlineInOrigin' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endReset" -TenantKey $tenantKey - } - elseif (($originDatabaseRecoveryStatus.RecoveryState -In 'complete') -and ($tenantRecoveryState -eq 'OnlineInOrigin')) - { - # Set tenant online if not already so - if ($tenant.TenantStatus -ne "Online") - { - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - $onlineTenantCount += 1 - } - } - elseif (($restoredTenantDatabase) -and ($restoredDatabaseRecoveryStatus.RecoveryState -In 'replicating', 'replicated', 'repatriating')) - { - # Update tenant recovery status to 'RepatriatingTenantData' if applicable - if ($tenantRecoveryState -ne 'RepatriatingTenantData') - { - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startRepatriation" -TenantKey $tenantKey - } - } - elseif (($restoredDatabaseRecoveryStatus.RecoveryState -In 'complete') -and ($tenantRecoveryState -eq 'RepatriatingTenantData')) - { - # Update tenant recovery status to 'RepatriatedTenantData' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endRepatriation" -TenantKey $tenantKey - - # Update tenant recovery status to 'UpdatingTenantShardToOrigin' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startShardUpdateToOrigin" -TenantKey $tenantKey - - # Take tenant offline if applicable - if ($tenant.TenantStatus -ne "Offline") - { - Set-TenantOffline -Catalog $tenantCatalog -TenantKey $tenantKey -ErrorAction SilentlyContinue - } - - # Get tenant resources - $restoredTenantServer = $restoredTenantDatabase.Name.Split('/')[0] - $originTenantServer = $originTenantDatabase.Name.Split('/')[0] - - # Update tenant shard to point to origin database - $updateComplete = Update-TenantShardInfo ` - -Catalog $tenantCatalog ` - -TenantName $tenant.TenantName ` - -FullyQualifiedTenantServerName "$originTenantServer.database.windows.net" ` - -TenantDatabaseName $tenant.DatabaseName - - if ($updateComplete) - { - # Mark tenant online in catalog - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - $onlineTenantCount +=1 - - # Update tenant recovery status to 'OnlineInOrigin' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endShardUpdateToOrigin" -TenantKey $tenantKey - } - } - elseif (($restoredDatabaseRecoveryStatus.RecoveryState -In 'complete') -and ($tenantRecoveryState -eq 'RepatriatedTenantData')) - { - # Update tenant recovery status to 'UpdatingTenantShardToOrigin' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startShardUpdateToOrigin" -TenantKey $tenantKey - - # Take tenant offline if applicable - if ($tenant.TenantStatus -ne "Offline") - { - Set-TenantOffline -Catalog $tenantCatalog -TenantKey $tenantKey -ErrorAction SilentlyContinue - } - - # Update tenant shard to point to recovered database - $updateComplete = Update-TenantShardInfo ` - -Catalog $tenantCatalog ` - -TenantName $tenant.TenantName ` - -FullyQualifiedTenantServerName "$originTenantServer.database.windows.net" ` - -TenantDatabaseName $tenant.DatabaseName - - if ($updateComplete) - { - # Mark tenant online in catalog - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - $onlineTenantCount +=1 - - # Update tenant recovery status to 'OnlineInOrigin' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endShardUpdateToOrigin" -TenantKey $tenantKey - } - } - elseif (($restoredDatabaseRecoveryStatus.RecoveryState -In 'complete') -and ($tenantRecoveryState -eq 'OnlineInRecovery')) - { - # Update tenant recovery status to 'RepatriatingTenantData' if applicable - if ($tenantRecoveryState -ne 'RepatriatingTenantData') - { - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "startRepatriation" -TenantKey $tenantKey - } - } - elseif (($restoredDatabaseRecoveryStatus.RecoveryState -In 'complete') -and ($tenantRecoveryState -eq 'UpdatingTenantShardToOrigin')) - { - $restoredTenantServer = $restoredTenantDatabase.Name.Split('/')[0] - $originTenantServer = $originTenantDatabase.Name.Split('/')[0] - - # Take tenant offline if applicable - if ($tenant.TenantStatus -ne "Offline") - { - Set-TenantOffline -Catalog $tenantCatalog -TenantKey $tenantKey -ErrorAction SilentlyContinue - } - - # Update tenant shard to point to origin database - $updateComplete = Update-TenantShardInfo ` - -Catalog $tenantCatalog ` - -TenantName $tenant.TenantName ` - -FullyQualifiedTenantServerName "$originTenantServer.database.windows.net" ` - -TenantDatabaseName $tenant.DatabaseName - - if ($updateComplete) - { - # Mark tenant online in catalog - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - $onlineTenantCount +=1 - - # Update tenant recovery status to 'OnlineInOrigin' - $tenantState = Update-TenantRecoveryState -Catalog $tenantCatalog -UpdateAction "endShardUpdateToOrigin" -TenantKey $tenantKey - } - } - elseif (($restoredDatabaseRecoveryStatus.RecoveryState -In 'complete') -and ($tenantRecoveryState -eq 'OnlineInOrigin')) - { - # Set tenant online if not already so - if ($tenant.TenantStatus -ne "Online") - { - Set-TenantOnline -Catalog $tenantCatalog -TenantKey $tenantKey - } - } - - if (!$tenantState) - { - Write-Verbose "Tenant was in an invalid initial recovery state when recovery operation attempted: $tenantRecoveryState" - } - - # Output recovery progress - if ($RecoveryOperation -eq 'restore') - { - $TenantRecoveryPercentage = [math]::Round($onlineTenantCount/$tenantCount,2) - $TenantRecoveryPercentage = $TenantRecoveryPercentage * 100 - Write-Output "$TenantRecoveryPercentage% ($onlineTenantCount of $tenantCount)" - } - elseif ($RecoveryOperation -eq 'repatriation') - { - $TenantRecoveryPercentage = [math]::Round($repatriatedTenantCount/$tenantCount,2) - $TenantRecoveryPercentage = $TenantRecoveryPercentage * 100 - Write-Output "$TenantRecoveryPercentage% ($repatriatedTenantCount of $tenantCount)" - } - } - - # Output recovery progress - if ($RecoveryOperation -eq 'restore') - { - $TenantRecoveryPercentage = [math]::Round($onlineTenantCount/$tenantCount,2) - $TenantRecoveryPercentage = $TenantRecoveryPercentage * 100 - Write-Output "$TenantRecoveryPercentage% ($onlineTenantCount of $tenantCount)" - } - elseif ($RecoveryOperation -eq 'repatriation') - { - $TenantRecoveryPercentage = [math]::Round($repatriatedTenantCount/$tenantCount,2) - $TenantRecoveryPercentage = $TenantRecoveryPercentage * 100 - Write-Output "$TenantRecoveryPercentage% ($repatriatedTenantCount of $tenantCount)" - } - } -} \ No newline at end of file diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Failover-TenantDatabasesToOriginalRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Failover-TenantDatabasesToOriginalRegion.ps1 deleted file mode 100644 index 65575f4..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Failover-TenantDatabasesToOriginalRegion.ps1 +++ /dev/null @@ -1,354 +0,0 @@ -<# -.SYNOPSIS - Failover tenant databases that have been replicated to the original region. - -.DESCRIPTION - This script is intended to be run as a background job in the 'Repatriate-IntoOriginalRegion' script that repatriates the Wingtip SaaS app environment (apps, databases, servers e.t.c) into the origin. - The script fails over tenant databases that have previously been geo-replicates into the original Wingtip region - -.PARAMETER WingtipRecoveryResourceGroup - Resource group in the recovery region that contains recovered resources - -.EXAMPLE - [PS] C:\>.\Failover-TenantDatabasesToOriginalRegion.ps1 -WingtipRecoveryResourceGroup "sampleRecoveryResourceGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\Common\AzureSqlAsyncManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Import-Module "$PSScriptRoot\..\..\..\Common\CatalogAndDatabaseManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\Common\AzureSqlAsyncManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\WtpConfig" -Force -# Import-Module "$PSScriptRoot\..\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration - -# Get the tenant catalog in the recovery region -$tenantCatalog = Get-Catalog -ResourceGroupName $WingtipRecoveryResourceGroup -WtpUser $wtpUser.Name - -# Initialize replication variables -$operationQueue = @() -$operationQueueMap = @{} -$failoverCount = 0 - -#---------------------- Helper Functions -------------------------------------------------------------- -<# - .SYNOPSIS - Starts an asynchronous call to failover a tenant database to the origin region - This function returns a task object that can be used to track the status of the operation -#> -function Start-AsynchronousDatabaseFailover -{ - param - ( - [Parameter(Mandatory=$true)] - [Microsoft.Azure.Management.Sql.Fluent.SqlManager]$AzureContext, - - [Parameter(Mandatory=$true)] - [String]$SecondaryTenantServerName, - - [Parameter(Mandatory=$true)] - [String]$TenantDatabaseName - ) - - # Get replication link Id - $replicationObject = Get-AzureRmSqlDatabaseReplicationLink ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -ServerName $SecondaryTenantServerName ` - -DatabaseName $TenantDatabaseName ` - -PartnerResourceGroupName $WingtipRecoveryResourceGroup - - # Issue asynchronous failover operation - if ($replicationObject) - { - $taskObject = Invoke-AzureSQLDatabaseFailoverAsync ` - -AzureContext $AzureContext ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -ServerName $SecondaryTenantServerName ` - -DatabaseName $TenantDatabaseName ` - -ReplicationLinkId "$($replicationObject.LinkId)" - - return $taskObject - } - else - { - return $null - } -} - -<# - .SYNOPSIS - Marks the failover for a tenant database as complete and updates tenant shard after failover is concluded -#> -function Complete-AsynchronousDatabaseFailover -{ - param - ( - [Parameter(Mandatory=$true)] - [String]$FailoverJobId - ) - - $databaseDetails = $operationQueueMap[$FailoverJobId] - if ($databaseDetails) - { - $restoredServerName = $databaseDetails.ServerName - $originServerName = ($restoredServerName -split "$($config.RecoveryRoleSuffix)")[0] - - # Update tenant shard to origin - $shardUpdate = Update-TenantShardInfo -Catalog $tenantCatalog -TenantName $databaseDetails.DatabaseName -FullyQualifiedTenantServerName "$originServerName.database.windows.net" -TenantDatabaseName $databaseDetails.DatabaseName - if ($shardUpdate) - { - # Update recovery state of tenant resources - $tenantDatabaseObject = Get-ExtendedDatabase -Catalog $tenantCatalog -ServerName $restoredServerName -DatabaseName $databaseDetails.DatabaseName - $serverState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "conclude" -ServerName $restoredServerName - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "conclude" -ServerName $restoredServerName -DatabaseName $databaseDetails.DatabaseName - if ($tenantDatabaseObject.ElasticPoolName) - { - $poolState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "conclude" -ServerName $restoredServerName -ElasticPoolName $tenantDatabaseObject.ElasticPoolName - } - - if (!$dbState) - { - Write-Verbose "Could not update recovery state for database: '$restoredServerName/$($databaseDetails.DatabaseName)'" - } - } - else - { - Write-Verbose "Could not update tenant shard to point to origin: '$restoredServerName/$($databaseDetails.DatabaseName)'" - } - } - else - { - Write-Verbose "Could not find database details for recovery job with Id: '$FailoverJobId'" - } -} - -#----------------------------Main script-------------------------------------------------- - -# Get list of databases that were added in the recovery region -$databaseList = Get-ExtendedDatabase -Catalog $tenantCatalog -$recoveryDatabaseList = $databaseList | Where-Object{$_.ServerName -match "$($config.RecoveryRoleSuffix)$"} -$originDatabaseList = $databaseList | Where-Object{$_.ServerName -notmatch "$($config.RecoveryRoleSuffix)$"} -$failoverQueue = @() -$replicatingDatabases = @() - -# Add databases to failover queue (if applicable) -foreach ($database in $recoveryDatabaseList) -{ - $originServerName = ($database.ServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $originDatabase = $originDatabaseList | Where-Object {($_.DatabaseName -eq $database.DatabaseName) -and ($_.ServerName -eq $originServerName)} - - if ($database.RecoveryState -ne 'complete') - { - if (!$originDatabase) - { - $replicatingDatabases += $database - $failoverQueue += $database - } - else - { - # Get replication status of tenant database in origin region - $replicationLink = Get-AzureRmSqlDatabaseReplicationLink ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -ServerName $originServerName ` - -DatabaseName $database.DatabaseName ` - -PartnerResourceGroupName $WingtipRecoveryResourceGroup ` - -PartnerServerName $database.ServerName ` - -ErrorAction SilentlyContinue - - if ($replicationLink.Role -eq 'Secondary') - { - $failoverQueue += $database - } - else - { - # Mark database recovery state as complete - $failoverCount += 1 - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startFailback" -ServerName $database.ServerName -DatabaseName $database.DatabaseName - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "conclude" -ServerName $database.ServerName -DatabaseName $database.DatabaseName - } - } - } -} - -$replicatedDatabaseCount = $failoverQueue.Count + $failoverCount - -if ($replicatedDatabaseCount -eq 0) -{ - Write-Output "100% (0 of 0)" - exit -} -elseif ($replicatedDatabaseCount -eq $failoverCount) -{ - Write-Output "100% ($failoverCount of $replicatedDatabaseCount)" - exit -} - -# Wait for all databases to have replicas before failover -$allReplicasCreated = $false -while (!$allReplicasCreated) -{ - foreach ($database in $failoverQueue) - { - $currServerName = $database.ServerName - $originServerName = ($currServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $recoveryServerName = $originServerName + $config.RecoveryRoleSuffix - - # Get replication status of tenant database in recovery region - $replicationLink = Get-AzureRmSqlDatabaseReplicationLink ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -ServerName $recoveryServerName ` - -DatabaseName $database.DatabaseName ` - -PartnerResourceGroupName $wtpUser.ResourceGroupName ` - -PartnerServerName $originServerName ` - -ErrorAction SilentlyContinue - if (!$replicationLink) - { - $allReplicasCreated = $false - break - } - - $allReplicasCreated = $true - } - Write-Output "waiting for database replicas to be created ..." -} - -# Output recovery progress -$DatabaseRecoveryPercentage = [math]::Round($failoverCount/$replicatedDatabaseCount,2) -$DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 -Write-Output "$DatabaseRecoveryPercentage% ($($failoverCount) of $replicatedDatabaseCount)" - -# Issue a request to failover tenant databases asynchronously -$azureContext = Get-RestAPIContext -while ($true) -{ - # Issue asynchronous call to failover eligible databases - if ($failoverQueue.Count -gt 0) - { - $currentDatabase = $failoverQueue[0] - - $dbProperties = @{ - "ServerName" = $currentDatabase.ServerName - "DatabaseName" = $currentDatabase.DatabaseName - } - $failoverQueue = $failoverQueue -ne $currentDatabase - $originServerName = ($currentDatabase.ServerName -split "$($config.RecoveryRoleSuffix)")[0] - - # Issue asynchronous call to failover databases - $operationObject = Start-AsynchronousDatabaseFailover -AzureContext $azureContext -SecondaryTenantServerName $originServerName -TenantDatabaseName $currentDatabase.DatabaseName - - if ($operationObject.Exception) - { - Write-Verbose $operationObject.Exception.InnerException - - # Mark database failover error - # Note: To make this process more robust, you would likely check the HTTP status code that is returned and respond appropriately. For example, if a 429 | too many requests is received, you will want to pause for the appropriate amount of time - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "markError" -ServerName $currentDatabase.ServerName -DatabaseName $currentDatabase.DatabaseName - - # Retry failover if unsuccessful - $failoverQueue = @($currentDatabase) + $failoverQueue - Start-Sleep 10 - } - elseif (!$operationObject) - { - # Retry failover if unsuccessful - $failoverQueue = @($currentDatabase) + $failoverQueue - Start-Sleep 10 - } - else - { - # Update recovery state of tenant resources - $serverState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startFailback" -ServerName $currentDatabase.ServerName - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startFailback" -ServerName $currentDatabase.ServerName -DatabaseName $currentDatabase.DatabaseName - $poolState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startFailback" -ServerName $currentDatabase.ServerName -ElasticPoolName $currentDatabase.ElasticPoolName - - # Add operation to queue for tracking - $operationId = $operationObject.Id - if (!$operationQueueMap.ContainsKey("$operationId")) - { - $operationQueue += $operationObject - $operationQueueMap.Add("$operationId", $dbProperties) - } - } - } - else - { - # There are no more databases eligible for failover - break - } -} - -# Check on status of database failover operations -while ($operationQueue.Count -gt 0) -{ - foreach($failoverJob in $operationQueue) - { - if (($failoverJob.IsCompleted) -and ($failoverJob.Status -eq 'RanToCompletion')) - { - # Update tenant database recovery state - Complete-AsynchronousDatabaseFailover -FailoverJobId $failoverJob.Id - - # Remove completed job from queue for polling - $operationQueue = $operationQueue -ne $failoverJob - - # Output recovery progress - $failoverCount+= 1 - $DatabaseRecoveryPercentage = [math]::Round($failoverCount/$replicatedDatabaseCount,2) - $DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 - Write-Output "$DatabaseRecoveryPercentage% ($($failoverCount) of $replicatedDatabaseCount)" - } - elseif (($failoverJob.IsCompleted) -and ($failoverJob.Status -eq "Faulted")) - { - # Mark errorState for databases that could not failover - $jobId = $failoverJob.Id - $databaseDetails = $operationQueueMap["$jobId"] - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "markError" -ServerName $databaseDetails.ServerName -DatabaseName $databaseDetails.DatabaseName - - # Remove completed job from queue for polling - $operationQueue = $operationQueue -ne $failoverJob - - # Retry failover for database - $originServerName = ($databaseDetails.ServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $operationObject = Start-AsynchronousDatabaseFailover -AzureContext $azureContext -SecondaryTenantServerName $originServerName -TenantDatabaseName $databaseDetails.DatabaseName - - # Update recovery state of tenant resources - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startFailback" -ServerName $databaseDetails.ServerName -DatabaseName $databaseDetails.DatabaseName - - # Add operation to queue for tracking - $operationId = $operationObject.Id - $dbProperties = @{ - "ServerName" = $databaseDetails.ServerName - "DatabaseName" = $databaseDetails.DatabaseName - } - if (!$operationQueueMap.ContainsKey("$operationId")) - { - $operationQueue += $operationObject - $operationQueueMap.Add("$operationId", $dbProperties) - } - } - } -} - -# Output recovery progress -$DatabaseRecoveryPercentage = [math]::Round($failoverCount/$replicatedDatabaseCount,2) -$DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 -Write-Output "$DatabaseRecoveryPercentage% ($($failoverCount) of $replicatedDatabaseCount)" diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Failover-TenantDatabasesToRecoveryRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Failover-TenantDatabasesToRecoveryRegion.ps1 deleted file mode 100644 index 0f15dde..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Failover-TenantDatabasesToRecoveryRegion.ps1 +++ /dev/null @@ -1,312 +0,0 @@ -<# -.SYNOPSIS - Failover tenant databases into recovery region - -.DESCRIPTION - This script is intended to be run as a background job in the 'Failover-IntoRecoveryRegion' script that fails over the Wingtip SaaS app into a recovery region. - The script uses the geo-replication capability of Azure SQL databases to failover tenant databases. - -.PARAMETER WingtipRecoveryResourceGroup - Resource group that will be used to contain recovered resources - -.EXAMPLE - [PS] C:\>.\Failover-TenantDatabasesToRecoveryRegion.ps1 -WingtipRecoveryResourceGroup "sampleResourceRecoveryGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\Common\AzureSqlAsyncManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Import-Module "$PSScriptRoot\..\..\..\Common\CatalogAndDatabaseManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\Common\AzureSqlAsyncManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\WtpConfig" -Force -# Import-Module "$PSScriptRoot\..\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration - -# Get the tenant catalog in the recovery region -$tenantCatalog = Get-Catalog -ResourceGroupName $WingtipRecoveryResourceGroup -WtpUser $wtpUser.Name - -# Initialize replication variables -$operationQueue = @() -$operationQueueMap = @{} -$failoverCount = 0 - -#---------------------- Helper Functions -------------------------------------------------------------- -<# - .SYNOPSIS - Starts an asynchronous call to failover a tenant database to the origin region - This function returns a task object that can be used to track the status of the operation -#> -function Start-AsynchronousDatabaseFailover -{ - param - ( - [Parameter(Mandatory=$true)] - [Microsoft.Azure.Management.Sql.Fluent.SqlManager]$AzureContext, - - [Parameter(Mandatory=$true)] - [String]$SecondaryTenantServerName, - - [Parameter(Mandatory=$true)] - [String]$TenantDatabaseName - ) - - # Get replication link Id - $replicationObject = Get-AzureRmSqlDatabaseReplicationLink ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -ServerName $SecondaryTenantServerName ` - -DatabaseName $TenantDatabaseName ` - -PartnerResourceGroupName $wtpUser.ResourceGroupName - - # Issue asynchronous failover operation - if ($replicationObject) - { - $taskObject = Invoke-AzureSQLDatabaseFailoverAsync ` - -AzureContext $AzureContext ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -ServerName $SecondaryTenantServerName ` - -DatabaseName $TenantDatabaseName ` - -ReplicationLinkId "$($replicationObject.LinkId)" ` - -AllowDataLoss - - return $taskObject - } - else - { - return $null - } -} - -<# - .SYNOPSIS - Marks the failover for a tenant database as complete and updates tenant shard after failover is concluded -#> -function Complete-AsynchronousDatabaseFailover -{ - param - ( - [Parameter(Mandatory=$true)] - [String]$FailoverJobId - ) - - $databaseDetails = $operationQueueMap[$FailoverJobId] - if ($databaseDetails) - { - $originServerName = $databaseDetails.ServerName - $restoredServerName = $originServerName + $config.RecoveryRoleSuffix - - # Update tenant shard to recovery region - $shardUpdate = Update-TenantShardInfo -Catalog $tenantCatalog -TenantName $databaseDetails.DatabaseName -FullyQualifiedTenantServerName "$restoredServerName.database.windows.net" -TenantDatabaseName $databaseDetails.DatabaseName - if ($shardUpdate) - { - # Update recovery state of tenant resources - $tenantDatabaseObject = Get-ExtendedDatabase -Catalog $tenantCatalog -ServerName $originServerName -DatabaseName $databaseDetails.DatabaseName - $serverState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endFailover" -ServerName $originServerName - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endFailover" -ServerName $originServerName -DatabaseName $databaseDetails.DatabaseName - if ($tenantDatabaseObject.ElasticPoolName) - { - $poolState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endFailover" -ServerName $originServerName -ElasticPoolName $tenantDatabaseObject.ElasticPoolName - } - - if (!$dbState) - { - Write-Verbose "Could not update recovery state for database: '$originServerName/$($databaseDetails.DatabaseName)'" - } - } - else - { - Write-Verbose "Could not update tenant shard to point to recovery: '$restoredServerName/$($databaseDetails.DatabaseName)'" - } - } - else - { - Write-Verbose "Could not find database details for recovery job with Id: '$FailoverJobId'" - } -} - -#----------------------------Main script-------------------------------------------------- - -# Get list of tenant databases -$databaseList = Get-ExtendedDatabase -Catalog $tenantCatalog -$originDatabaseList = $databaseList | Where-Object{$_.ServerName -notmatch "$($config.RecoveryRoleSuffix)$"} -$failoverQueue = @() - -# Add eligible tenant databases to failover queue in priority order -$eligibleTenantList = Get-ExtendedTenant -Catalog $tenantCatalog -SortTenants | Where-Object{$_.TenantRecoveryState -ne 'OnlineInRecovery'} -foreach ($tenant in $eligibleTenantList) -{ - $tenantServerName = $tenant.ServerName.split('.')[0] - $originTenantServerName = ($tenantServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $recoveryTenantServerName = $originTenantServerName + $config.RecoveryRoleSuffix - $originDatabase = $originDatabaseList | Where-Object {($_.DatabaseName -eq $tenant.DatabaseName) -and ($_.ServerName -eq $originTenantServerName)} - - if ($originDatabase.RecoveryState -ne 'failedOver') - { - # Get replication status of tenant database - $replicationLink = Get-AzureRmSqlDatabaseReplicationLink ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -ServerName $recoveryTenantServerName ` - -DatabaseName $tenant.DatabaseName ` - -PartnerResourceGroupName $wtpUser.ResourceGroupName ` - -PartnerServerName $originTenantServerName ` - -ErrorAction Stop - - if (!$replicationLink) - { - throw "Could not find replication link for tenant database: $originTenantServerName/$($tenant.DatabaseName)" - } - elseif ($replicationLink.Role -eq 'Secondary') - { - $failoverQueue += $originDatabase - } - else - { - # Mark tenant database as failed over - $failoverCount += 1 - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endFailover" -ServerName $originTenantServerName -DatabaseName $tenant.DatabaseName - } - } -} -$replicatedDatabaseCount = $failoverQueue.Count - -if ($replicatedDatabaseCount -eq 0) -{ - Write-Output "100% ($failoverCount of $replicatedDatabaseCount)" - exit -} - -# Output recovery progress -$DatabaseRecoveryPercentage = [math]::Round($failoverCount/$replicatedDatabaseCount,2) -$DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 -Write-Output "$DatabaseRecoveryPercentage% ($($failoverCount) of $replicatedDatabaseCount)" - -# Issue a request to failover tenant databases asynchronously -$azureContext = Get-RestAPIContext -while ($true) -{ - $currentDatabase = $failoverQueue[0] - if ($currentDatabase) - { - $failoverQueue = $failoverQueue -ne $currentDatabase - $originServerName = ($currentDatabase.ServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $recoveryServerName = $originServerName + $config.RecoveryRoleSuffix - - # Issue asynchronous failover request - $operationObject = Start-AsynchronousDatabaseFailover -AzureContext $azureContext -SecondaryTenantServerName $recoveryServerName -TenantDatabaseName $currentDatabase.DatabaseName - $databaseDetails = @{ - "ServerName" = $currentDatabase.ServerName - "DatabaseName" = $currentDatabase.DatabaseName - "ServiceObjective" = $currentDatabase.ServiceObjective - "ElasticPoolName" = $currentDatabase.ElasticPoolName - } - - if ($operationObject.Exception) - { - Write-Verbose $operationObject.Exception.InnerException - - # Mark database failover error - # Note: To make this process more robust, you would likely check the HTTP status code that is returned and respond appropriately. For example, if a 429 | too many requests is received, you will want to pause for the appropriate amount of time - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "markError" -ServerName $currentDatabase.ServerName -DatabaseName $currentDatabase.DatabaseName - $failoverQueue = @($currentDatabase) + $failoverQueue - Start-Sleep 10 - } - elseif (!$operationObject) - { - # Retry failover if unsuccessful - $failoverQueue = @($currentDatabase) + $failoverQueue - Start-Sleep 10 - } - else - { - # Update recovery state of tenant resources - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startFailover" -ServerName $currentDatabase.ServerName -DatabaseName $currentDatabase.DatabaseName - - # Add operation to queue for tracking - $operationId = $operationObject.Id - if (!$operationQueueMap.ContainsKey("$operationId")) - { - $operationQueue += $operationObject - $operationQueueMap.Add("$operationId", $databaseDetails) - } - } - } - else - { - # There are no more databases eligible for failover - break - } -} - -# Check on status of database failover operations -while ($operationQueue.Count -gt 0) -{ - foreach($failoverJob in $operationQueue) - { - if (($failoverJob.IsCompleted) -and ($failoverJob.Status -eq 'RanToCompletion')) - { - # Remove completed job from queue for polling - Complete-AsynchronousDatabaseFailover -FailoverJobId $failoverJob.Id - $operationQueue = $operationQueue -ne $failoverJob - $failoverCount+= 1 - - # Output recovery progress - $DatabaseRecoveryPercentage = [math]::Round($failoverCount/$replicatedDatabaseCount,2) - $DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 - Write-Output "$DatabaseRecoveryPercentage% ($($failoverCount) of $replicatedDatabaseCount)" - } - elseif (($failoverJob.IsCompleted) -and ($failoverJob.Status -eq "Faulted")) - { - # Remove completed job from queue for polling - $operationQueue = $operationQueue -ne $failoverJob - $failoverJobId = $failoverJob.Id - $databaseDetails = $operationQueueMap["$failoverJobId"] - - # Mark database failover error - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "markError" -ServerName $databaseDetails.ServerName -DatabaseName $databaseDetails.DatabaseName - - # Retry failover for database - $originServerName = ($databaseDetails.ServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $recoveryServerName = $originServerName + $config.RecoveryRoleSuffix - $operationObject = Start-AsynchronousDatabaseFailover -AzureContext $azureContext -SecondaryTenantServerName $recoveryServerName -TenantDatabaseName $databaseDetails.DatabaseName - - # Update recovery state of tenant resources - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startFailover" -ServerName $databaseDetails.ServerName -DatabaseName $databaseDetails.DatabaseName - - # Add operation to queue for tracking - $operationId = $operationObject.Id - $dbProperties = @{ - "ServerName" = $databaseDetails.ServerName - "DatabaseName" = $databaseDetails.DatabaseName - } - if (!$operationQueueMap.ContainsKey("$operationId")) - { - $operationQueue += $operationObject - $operationQueueMap.Add("$operationId", $dbProperties) - } - } - } -} - -# Output recovery progress -$DatabaseRecoveryPercentage = [math]::Round($failoverCount/$replicatedDatabaseCount,2) -$DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 -Write-Output "$DatabaseRecoveryPercentage% ($($failoverCount) of $replicatedDatabaseCount)" diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-ManagementDatabasesToRecoveryRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-ManagementDatabasesToRecoveryRegion.ps1 deleted file mode 100644 index e69397e..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-ManagementDatabasesToRecoveryRegion.ps1 +++ /dev/null @@ -1,105 +0,0 @@ -<# -.SYNOPSIS - Replicates management databases including the catalog database to recovery region - -.DESCRIPTION - This script is intended to be run as a background job in the DR scripts. - The script replicates tenant databases by using SQL database geo-repliation - -.PARAMETER WingtipRecoveryResourceGroup - Resource group that will be used to contain recovered resources - -.EXAMPLE - [PS] C:\>.\Replicate-ManagementDatabasesToRecoveryRegion.ps1 -WingtipRecoveryResourceGroup "sampleRecoveryResourceGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration -$currentSubscriptionId = Get-SubscriptionId - -# Get the active tenant catalog -$tenantCatalog = Get-Catalog -ResourceGroupName $wtpUser.ResourceGroupName -WtpUser $wtpUser.Name - -# Get the recovery region resource group -$recoveryResourceGroup = Get-AzureRmResourceGroup -Name $WingtipRecoveryResourceGroup - -$sleepInterval = 10 -$pastDeploymentWaitTime = 0 -$originCatalogServerName = $config.CatalogServerNameStem + $wtpUser.Name -$recoveryCatalogServerName = $config.CatalogServerNameStem + $wtpUser.Name + $config.RecoveryRoleSuffix - -# Find any previous database recovery operations to get most current state of recovered resources -# This allows the script to be re-run if an error during deployment -$pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name "DeployCatalogFailoverGroup" -ErrorAction SilentlyContinue 2>$null - -# Wait for past deployment to complete if it's still active -while (($pastDeployment) -and ($pastDeployment.ProvisioningState -NotIn "Succeeded", "Failed", "Canceled")) -{ - # Wait for no more than 5 minutes (300 secs) for previous deployment to complete - if ($pastDeploymentWaitTime -lt 300) - { - Write-Output "Waiting for previous deployment to complete ..." - Start-Sleep $sleepInterval - $pastDeploymentWaitTime += $sleepInterval - $pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name "DeployCatalogFailoverGroup" -ErrorAction SilentlyContinue 2>$null - } - else - { - Stop-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name "DeployCatalogFailoverGroup" -ErrorAction SilentlyContinue 1>$null 2>$null - break - } -} - -# Wait until catalog recovery server has been created before starting catalog database replication -$recoveryCatalogServer = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers" -ResourceNameEquals $recoveryCatalogServerName -while (!$recoveryCatalogServer) -{ - Start-Sleep $sleepInterval - $recoveryCatalogServer = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers" -ResourceNameEquals $recoveryCatalogServerName -} - -$managementDatabaseCount = (Get-AzureRmSqlDatabase -ResourceGroupName $wtpUser.ResourceGroupName -ServerName $originCatalogServerName | Where-Object {$_.DatabaseName -ne 'master'}).Count -$catalogFailoverGroupName = $config.CatalogServerNameStem + "group-" + $wtpUser.Name - -# Output recovery progress -Write-Output "Replicating ... (0 of $managementDatabaseCount complete)" - -# Deploy failover group for catalog server -$deployment = New-AzureRmResourceGroupDeployment ` - -Name "DeployCatalogFailoverGroup" ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -TemplateFile ("$using:scriptPath\RecoveryTemplates\" + $config.FailoverGroupTemplate) ` - -OriginServerName $originCatalogServerName ` - -RecoveryServerName $recoveryCatalogServerName ` - -RecoveryResourceGroupName $WingtipRecoveryResourceGroup ` - -FailoverGroupName $catalogFailoverGroupName ` - -ErrorAction Stop - -# Replicate all management databases in catalog server -$originServer = Get-AzureRmSqlServer -ResourceGroupName $wtpUser.ResourceGroupName -ServerName $originCatalogServerName -$catalogFailoverGroup = Get-AzureRmSqlDatabaseFailoverGroup -ResourceGroupName $wtpUser.ResourceGroupName -ServerName $originCatalogServerName -FailoverGroupName $catalogFailoverGroupName -$managementDatabases = Get-AzureRmSqlDatabase -ServerName $originCatalogServerName -ResourceGroupName $wtpUser.ResourceGroupName | Where-Object {$_.DatabaseName -ne 'master'} -$catalogFailoverGroup = $catalogFailoverGroup | Add-AzureRmSqlDatabaseToFailoverGroup -Database $managementDatabases - -# Output recovery progress -Write-Output "Replicated ($managementDatabaseCount of $managementDatabaseCount)" diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-ServersToRecoveryRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-ServersToRecoveryRegion.ps1 deleted file mode 100644 index 967a793..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-ServersToRecoveryRegion.ps1 +++ /dev/null @@ -1,135 +0,0 @@ -<# -.SYNOPSIS - Replicates tenant servers and management servers into recovery region - -.DESCRIPTION - This script is intended to be run as a background job in DR scripts. - The script creates replica tenant servers and management servers in a recovery region. These servers will serve as hot standbys that can be failed over to in the event of a disaster. - -.PARAMETER WingtipRecoveryResourceGroup - Resource group that will be used to contain recovered resources - -.EXAMPLE - [PS] C:\>.\Replicate-ServersToRecoveryRegion.ps1 -WingtipRecoveryResourceGroup "sampleRecoveryResourceGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration - -# Get the active tenant catalog -$tenantCatalog = Get-Catalog -ResourceGroupName $wtpUser.ResourceGroupName -WtpUser $wtpUser.Name - -# Get the recovery region resource group -$recoveryResourceGroup = Get-AzureRmResourceGroup -Name $WingtipRecoveryResourceGroup - -$serverQueue = @() -[array]$serverConfigurations = @() -$replicatedServers = 0 -$sleepInterval = 10 -$pastDeploymentWaitTime = 0 -$deploymentName = "WingtipSaaSServerReplication" - -# Find any previous server recovery operations to get most current state of recovered resources -# This allows the script to be re-run if an error during deployment -$pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - -# Wait for past deployment to complete if it's still active -while (($pastDeployment) -and ($pastDeployment.ProvisioningState -NotIn "Succeeded", "Failed", "Canceled")) -{ - # Wait for no more than 5 minutes (300 secs) for previous deployment to complete - if ($pastDeploymentWaitTime -lt 300) - { - Write-Output "Waiting for previous deployment to complete ..." - Start-Sleep $sleepInterval - $pastDeploymentWaitTime += $sleepInterval - $pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - } - else - { - Stop-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 1>$null 2>$null - break - } -} - -# Get list of servers to be replicated -$serverList = @() -$serverList += Get-ExtendedServer -Catalog $tenantCatalog | Where-Object {($_.ServerName -NotMatch "$($config.RecoveryRoleSuffix)$")} -$catalogServer = New-Object -TypeName PsObject -Property @{ServerName = $config.CatalogServerNameStem + $wtpUser.Name; RecoveryState = 'n/a'; State = 'created'} -$serverList += $catalogServer -$restoredServers = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers" - -foreach ($server in $serverList) -{ - # Only replicate servers that haven't already been replicated - $serverRecoveryName = $server.ServerName + $config.RecoveryRoleSuffix - if ($restoredServers.Name -notcontains $serverRecoveryName) - { - $serverQueue += $serverRecoveryName - if ($server.ServerName -match $config.CatalogServerNameStem) - { - $adminLogin = $config.CatalogAdminUserName - $adminPassword = $config.CatalogAdminPassword - } - else - { - $adminLogin = $config.TenantAdminUserName - $adminPassword = $config.TenantAdminPassword - } - - [array]$serverConfigurations += @{ - ServerName = "$serverRecoveryName" - Location = "$($recoveryResourceGroup.Location)" - AdminLogin = "$adminLogin" - AdminPassword = "$adminPassword" - } - } - else - { - $replicatedServers += 1 - } -} - -# Output recovery progress -$serverRecoveryPercentage = [math]::Round($replicatedServers/$serverList.length,2) -$serverRecoveryPercentage = $serverRecoveryPercentage * 100 -Write-Output "Deploying ... ($($replicatedServers) of $($serverList.length) complete)" - - -# Replicate tenant servers and firewall rules in them -# Note: In a production scenario you would additionally restore logins and users that existed in the primary server (see: https://docs.microsoft.com/en-us/azure/sql-database/sql-database-disaster-recovery) -if ($serverQueue.Count -gt 0) -{ - $deployment = New-AzureRmResourceGroupDeployment ` - -Name $deploymentName ` - -ResourceGroupName $recoveryResourceGroup.ResourceGroupName ` - -TemplateFile ("$using:scriptPath\RecoveryTemplates\" + $config.TenantServerRestoreBatchTemplate) ` - -ServerConfigurationObjects $serverConfigurations ` - -ErrorAction Stop - - $replicatedServers += $serverQueue.Length -} - -# Output recovery progress -$serverRecoveryPercentage = [math]::Round($replicatedServers/$serverList.length,2) -$serverRecoveryPercentage = $serverRecoveryPercentage * 100 -Write-Output "Deployed ($($replicatedServers) of $($serverList.length))" diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantDatabasesToOriginalRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantDatabasesToOriginalRegion.ps1 deleted file mode 100644 index ba74bf0..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantDatabasesToOriginalRegion.ps1 +++ /dev/null @@ -1,267 +0,0 @@ -<# -.SYNOPSIS - Replicates tenant databases that have been created in the recovery region into the original region. - -.DESCRIPTION - This script is intended to be run as a background job in the 'Repatriate-IntoOriginalRegion' script that repatriates the Wingtip SaaS app environment (apps, databases, servers e.t.c) into the origin. - The script geo-replicates tenant databases tha have been changed in the recovery region into the original Wingtip region - -.PARAMETER WingtipRecoveryResourceGroup - Resource group in the recovery region that contains recovered resources - -.EXAMPLE - [PS] C:\>.\Replicate-TenantDatabasesToOriginalRegion.ps1 -WingtipRecoveryResourceGroup "sampleRecoveryResourceGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\Common\AzureSqlAsyncManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Import-Module "$PSScriptRoot\..\..\..\Common\CatalogAndDatabaseManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\Common\AzureSqlAsyncManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\WtpConfig" -Force -# Import-Module "$PSScriptRoot\..\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration -$currentSubscriptionId = Get-SubscriptionId - - -# Get the tenant catalog in the recovery region -$tenantCatalog = Get-Catalog -ResourceGroupName $WingtipRecoveryResourceGroup -WtpUser $wtpUser.Name - -# Initialize replication variables -$replicationQueue = @() -$operationQueue = @() -$operationQueueMap = @{} -$replicatedDatabaseCount = 0 - -#---------------------- Helper Functions -------------------------------------------------------------- -<# - .SYNOPSIS - Starts an asynchronous call to create a readable secondary replica of a tenant database - This function returns a task object that can be used to track the status of the operation -#> -function Start-AsynchronousDatabaseReplication -{ - param - ( - [Parameter(Mandatory=$true)] - [Microsoft.Azure.Management.Sql.Fluent.SqlManager]$AzureContext, - - [Parameter(Mandatory=$true)] - [object]$TenantDatabase - ) - - # Construct replication parameters - $originServerName = ($TenantDatabase.ServerName -split "$($config.RecoveryRoleSuffix)")[0] - $originServer = Find-AzureRmResource -ResourceGroupNameEquals $wtpUser.ResourceGroupName -ResourceNameEquals $originServerName - $databaseId = "/subscriptions/$currentSubscriptionId/resourceGroups/$WingtipRecoveryResourceGroup/providers/Microsoft.Sql/servers/$($TenantDatabase.ServerName)/databases/$($TenantDatabase.DatabaseName)" - - # Delete existing tenant database - Remove-AzureRmSqlDatabase -ResourceGroupName $wtpUser.ResourceGroupName -ServerName $originServerName -DatabaseName $TenantDatabase.DatabaseName -ErrorAction SilentlyContinue >$null - - # Issue asynchronous replication operation - if ($TenantDatabase.ServiceObjective -eq 'ElasticPool') - { - # Replicate tenant database into an elastic pool - $taskObject = New-AzureSQLDatabaseReplicaAsync ` - -AzureContext $AzureContext ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -Location $originServer.Location ` - -ServerName $originServerName ` - -DatabaseName $TenantDatabase.DatabaseName ` - -SourceDatabaseId $databaseId ` - -ElasticPoolName $TenantDatabase.ElasticPoolName - } - else - { - # Replicate tenant database into a standalone database - $taskObject = New-AzureSQLDatabaseReplicaAsync ` - -AzureContext $AzureContext ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -Location $originServer.Location ` - -ServerName $originServerName ` - -DatabaseName $TenantDatabase.DatabaseName ` - -SourceDatabaseId $databaseId ` - -RequestedServiceObjectiveName $TenantDatabase.ServiceObjective - } - return $taskObject -} - -<# - .SYNOPSIS - Marks a tenant database replication as complete when the database has been successfully replicated -#> -function Complete-AsynchronousDatabaseReplication -{ - param - ( - [Parameter(Mandatory=$true)] - [String]$ReplicationJobId - ) - - $databaseDetails = $operationQueueMap[$ReplicationJobId] - if ($databaseDetails) - { - $restoredServerName = $databaseDetails.ServerName - - # Update tenant database recovery state - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endReplication" -ServerName $restoredServerName -DatabaseName $databaseDetails.DatabaseName - if (!$dbState) - { - Write-Verbose "Could not update recovery state for database: '$originServerName/$($databaseDetails.DatabaseName)'" - } - } - else - { - Write-Verbose "Could not find database details for recovery job with Id: '$ReplicationJobId'" - } - -} - -#----------------------------Main script-------------------------------------------------- - -# Get list of databases that were added in the recovery region -$databaseList = Get-ExtendedDatabase -Catalog $tenantCatalog -$recoveryDatabaseList = $databaseList | Where-Object{$_.ServerName -match "$($config.RecoveryRoleSuffix)$"} -$originDatabaseList = $databaseList | Where-Object{$_.ServerName -notmatch "$($config.RecoveryRoleSuffix)$"} - -foreach ($database in $recoveryDatabaseList) -{ - $recoveryServerName = $database.ServerName - $originServerName = ($recoveryServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $originDatabase = $originDatabaseList | Where-Object {($_.DatabaseName -eq $database.DatabaseName) -and ($_.ServerName -eq $originServerName)} - - # Get replication status of database - $databaseReplicaExists = Get-AzureRmSqlDatabaseReplicationLink ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -ServerName $recoveryServerName ` - -DatabaseName $database.DatabaseName ` - -PartnerResourceGroupName $wtpUser.ResourceGroupName ` - -PartnerServerName $originServerName ` - -ErrorAction SilentlyContinue - - if (!$originDatabase -and !$databaseReplicaExists) - { - $replicationQueue += $database - } - elseif ($database.RecoveryState -NotIn 'replicated', 'failedOver') - { - # Update database recovery state if it has completed replication - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endReplication" -ServerName $recoveryServerName -DatabaseName $database.DatabaseName - } -} -$newDatabaseCount = $replicationQueue.length - -if ($newDatabaseCount -eq 0) -{ - Write-Output "100% (0 of 0)" - exit -} -else -{ - # Output recovery progress - $DatabaseRecoveryPercentage = [math]::Round($replicatedDatabaseCount/$newDatabaseCount,2) - $DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 - Write-Output "$DatabaseRecoveryPercentage% ($($replicatedDatabaseCount) of $newDatabaseCount)" - - # Issue a request to replicate changed tenant databases asynchronously - $azureContext = Get-RestAPIContext - while($true) - { - $currentDatabase = $replicationQueue[0] - - if ($currentDatabase) - { - $replicationQueue = $replicationQueue -ne $currentDatabase - $operationObject = Start-AsynchronousDatabaseReplication -AzureContext $azureContext -TenantDatabase $currentDatabase - $databaseDetails = @{ - "ServerName" = $currentDatabase.ServerName - "DatabaseName" = $currentDatabase.DatabaseName - "ServiceObjective" = $currentDatabase.ServiceObjective - "ElasticPoolName" = $currentDatabase.ElasticPoolName - } - - if ($operationObject.Exception) - { - Write-Verbose $operationObject.Exception.InnerException - - # Mark database failover error - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "markError" -ServerName $currentDatabase.ServerName -DatabaseName $currentDatabase.DatabaseName - } - else - { - # Update database recovery state - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startReplication" -ServerName $currentDatabase.ServerName -DatabaseName $currentDatabase.DatabaseName - - # Add operation to queue for tracking - $operationId = $operationObject.Id - if (!$operationQueueMap.ContainsKey("$operationId")) - { - $operationQueue += $operationObject - $operationQueueMap.Add("$operationId", $databaseDetails) - } - } - } - else - { - # There are no more databases eligible for replication - break - } - } - - # Check on status of database replication operations - while ($operationQueue.Count -gt 0) - { - foreach($replicationJob in $operationQueue) - { - if (($replicationJob.IsCompleted) -and ($replicationJob.Status -eq 'RanToCompletion')) - { - # Update database recovery state - Complete-AsynchronousDatabaseReplication -replicationJobId $replicationJob.Id - - # Remove completed job from queue for polling - $operationQueue = $operationQueue -ne $replicationJob - - # Output recovery progress - $replicatedDatabaseCount+= 1 - $DatabaseRecoveryPercentage = [math]::Round($replicatedDatabaseCount/$newDatabaseCount,2) - $DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 - Write-Output "$DatabaseRecoveryPercentage% ($($replicatedDatabaseCount) of $newDatabaseCount)" - } - elseif (($replicationJob.IsCompleted) -and ($replicationJob.Status -eq "Faulted")) - { - # Mark errorState for databases that have not been replicated - $jobId = $replicationJob.Id - $databaseDetails = $operationQueueMap["$jobId"] - $dbState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "markError" -ServerName $databaseDetails.ServerName -DatabaseName $databaseDetails.DatabaseName - - # Remove completed job from queue for polling - $operationQueue = $operationQueue -ne $replicationJob - } - } - } - - # Output recovery progress - $DatabaseRecoveryPercentage = [math]::Round($replicatedDatabaseCount/$newDatabaseCount,2) - $DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 - Write-Output "$DatabaseRecoveryPercentage% ($($replicatedDatabaseCount) of $newDatabaseCount)" -} diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantDatabasesToRecoveryRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantDatabasesToRecoveryRegion.ps1 deleted file mode 100644 index d31838c..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantDatabasesToRecoveryRegion.ps1 +++ /dev/null @@ -1,297 +0,0 @@ -<# -.SYNOPSIS - Replicates tenant databases to a recovery region. - -.DESCRIPTION - This script is intended to be run as a background job in the 'Deploy-WingtipTicketsReplica' script that replicates the Wingtip SaaS app (apps, databases, servers e.t.c) into a recovery region. - The script geo-replicates tenant databases into a recovery region - -.PARAMETER WingtipRecoveryResourceGroup - Resource group in the recovery region that contains recovered resources - -.PARAMETER MaxConcurrentReplicationOperations - Maximum number of replication operations that can be run concurrently - -.EXAMPLE - [PS] C:\>.\Replicate-TenantDatabasesToRecoveryRegion.ps1 -WingtipRecoveryResourceGroup "sampleRecoveryResourceGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup, - - [parameter(Mandatory=$false)] - [int] $MaxConcurrentReplicationOperations=50 -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\Common\AzureSqlAsyncManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Import-Module "$PSScriptRoot\..\..\..\Common\CatalogAndDatabaseManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\Common\AzureSqlAsyncManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\WtpConfig" -Force -# Import-Module "$PSScriptRoot\..\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration -$sleepInterval = 10 -$currentSubscriptionId = Get-SubscriptionId - -# Get the tenant catalog in the recovery region -$tenantCatalog = Get-Catalog -ResourceGroupName $WingtipRecoveryResourceGroup -WtpUser $wtpUser.Name - -# Initialize replication variables -$replicationQueue = @() -$operationQueue = @() -$operationQueueMap = @{} -$replicatedDatabaseCount = 0 - -#---------------------- Helper Functions -------------------------------------------------------------- -<# - .SYNOPSIS - Starts an asynchronous call to create a readable secondary replica of a tenant database in the recovery region - This function returns a task object that can be used to track the status of the operation -#> -function Start-AsynchronousDatabaseReplication -{ - param - ( - [Parameter(Mandatory=$true)] - [Microsoft.Azure.Management.Sql.Fluent.SqlManager]$AzureContext, - - [Parameter(Mandatory=$true)] - [object]$TenantDatabase - ) - - # Construct replication parameters - $recoveryServerName = $TenantDatabase.ServerName + $config.RecoveryRoleSuffix - $recoveryServer = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceNameEquals $recoveryServerName - $databaseId = "/subscriptions/$currentSubscriptionId/resourceGroups/$($wtpUser.ResourceGroupName)/providers/Microsoft.Sql/servers/$($TenantDatabase.ServerName)/databases/$($TenantDatabase.DatabaseName)" - - # Delete any existing tenant database in the recovery location - Remove-AzureRmSqlDatabase -ResourceGroupName $WingtipRecoveryResourceGroup -ServerName $recoveryServerName -DatabaseName $TenantDatabase.DatabaseName -ErrorAction SilentlyContinue >$null - - # Issue asynchronous replication operation - if ($TenantDatabase.ServiceObjective -eq 'ElasticPool') - { - # Replicate tenant database into an elastic pool - $taskObject = New-AzureSQLDatabaseReplicaAsync ` - -AzureContext $AzureContext ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -Location $recoveryServer.Location ` - -ServerName $recoveryServerName ` - -DatabaseName $TenantDatabase.DatabaseName ` - -SourceDatabaseId $databaseId ` - -ElasticPoolName $TenantDatabase.ElasticPoolName - } - else - { - # Replicate tenant database into a standalone database - $taskObject = New-AzureSQLDatabaseReplicaAsync ` - -AzureContext $AzureContext ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -Location $recoveryServer.Location ` - -ServerName $recoveryServerName ` - -DatabaseName $TenantDatabase.DatabaseName ` - -SourceDatabaseId $databaseId ` - -RequestedServiceObjectiveName $TenantDatabase.ServiceObjective - } - return $taskObject -} - - -#----------------------------Main script-------------------------------------------------- - -# Wait until all elastic pools have been replicated to start replicating databases -# This ensures that all required container resources have been acquired before database replication begins -$tenantPools = Get-ExtendedElasticPool -Catalog $tenantCatalog | Where-Object {$_.ServerName -notmatch "$($config.RecoveryRoleSuffix)$"} -$poolCount = @($tenantPools).Count -$replicatedElasticPools = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers/elasticpools" - -while (@($replicatedElasticPools).Count -lt $poolCount) -{ - Start-Sleep $sleepInterval - $replicatedElasticPools = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers/elasticpools" - Write-Output "waiting for pool(s) to complete deployment ..." -} - -# Find previous replication operations that are not yet complete. -# This allows the script to be re-run if an error occurs during replication -$ongoingDeployments = @() -$operationsLog = Get-AzureRmLog -ResourceGroupName $WingtipRecoveryResourceGroup -StartTime (Get-Date).AddDays(-1) -MaxRecord 200 3>$null | Where-Object {($_.OperationName.Value -eq 'Microsoft.Sql/servers/databases/write')} -$operationsLog = $operationsLog | Group-Object -Property 'CorrelationId' - -# Find all ongoing database recovery operations -foreach ($operationSequence in $operationsLog) -{ - if ($operationSequence.Group.EventName.Value -notcontains "EndRequest") - { - $operation = $operationSequence.Group | Where-Object {$_.EventName.Value -eq "BeginRequest"} - $ongoingDeployments += $operation - } -} - -# Add ongoing deployments to queue of background jobs that will be monitored -if ($ongoingDeployments.Count -gt 0) -{ - foreach ($deployment in $ongoingDeployments) - { - $jobObject = [PSCustomObject]@{ - Id = $deployment.CorrelationId - ResourceId = $deployment.ResourceId - State = $deployment.Status.Value - EventTimestamp = $deployment.EventTimestamp - } - - $tenantServerName = [regex]::match($deployment.ResourceId,'/servers/([\w-]+)/').Groups[1].Value - $tenantDatabaseName = [regex]::match($test,'/databases/([\w-]+)').Groups[1].Value - $databaseDetails = @{ - "ServerName" = $tenantServerName - "DatabaseName" = $tenantDatabaseName - "ServiceObjective" = $null - "ElasticPoolName" = $null - } - - $jobId = $jobObject.Id - if (!$operationQueueMap.ContainsKey("$jobId")) - { - $operationQueue += $jobObject - $operationQueueMap.Add("$jobId", $databaseDetails) - } - } -} - -# Get all tenant databases that have replicated into the recovery region -$replicatedDatabaseInstances = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers/databases" -ResourceNameContains "tenants" - -# Get list of tenant databases to be replicated -$tenantDatabaseList = Get-ExtendedDatabase -Catalog $tenantCatalog | Where-Object{$_.ServerName -notmatch "$($config.RecoveryRoleSuffix)$"} -$tenantDatabaseCount = $tenantDatabaseList.Count -foreach ($database in $tenantDatabaseList) -{ - $currTenantServerName = $database.ServerName - $originTenantServerName = ($currTenantServerName -split "$($config.RecoveryRoleSuffix)$")[0] - $recoveryTenantServerName = $originTenantServerName + $config.RecoveryRoleSuffix - - $replicationLink = Get-AzureRmSqlDatabaseReplicationLink ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -ServerName $recoveryTenantServerName ` - -DatabaseName $database.DatabaseName ` - -PartnerResourceGroupName $wtpUser.ResourceGroupName ` - -PartnerServerName $originTenantServerName ` - -ErrorAction SilentlyContinue - - if (!$replicationLink) - { - $dbProperties = Get-ExtendedDatabase -Catalog $tenantCatalog -ServerName $currTenantServerName -DatabaseName $database.DatabaseName - $replicationQueue += $dbProperties - } - else - { - $replicatedDatabaseCount += 1 - } -} - -# Output recovery progress -$DatabaseRecoveryPercentage = [math]::Round($replicatedDatabaseCount/$tenantDatabaseCount,2) -$DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 -Write-Output "Replicating ... ($($replicatedDatabaseCount) of $tenantDatabaseCount complete)" - -# Issue a request to replicate databases asynchronously till concurrent operation limit is reached -$azureContext = Get-RestAPIContext -while ($operationQueue.Count -le $MaxConcurrentReplicationOperations) -{ - $currentDatabase = $replicationQueue[0] - if ($currentDatabase) - { - $replicationQueue = $replicationQueue -ne $currentDatabase - $operationObject = Start-AsynchronousDatabaseReplication -AzureContext $azureContext -TenantDatabase $currentDatabase - $databaseDetails = @{ - "ServerName" = $currentDatabase.ServerName - "DatabaseName" = $currentDatabase.DatabaseName - "ServiceObjective" = $currentDatabase.ServiceObjective - "ElasticPoolName" = $currentDatabase.ElasticPoolName - } - - # Add operation to queue for tracking - $operationId = $operationObject.Id - if (!$operationQueueMap.ContainsKey("$operationId")) - { - $operationQueue += $operationObject - $operationQueueMap.Add("$operationId", $databaseDetails) - } - } - else - { - # There are no more databases eligible for replication - break - } -} - -# Check on status of database replication operations -while ($operationQueue.Count -gt 0) -{ - foreach($replicationJob in $operationQueue) - { - if (($replicationJob.IsCompleted) -and ($replicationJob.Status -eq 'RanToCompletion')) - { - # Start new replication operation if there are any databases left to replicate - $currentDatabase = $replicationQueue[0] - if ($currentDatabase) - { - $replicationQueue = $replicationQueue -ne $currentDatabase - $operationObject = Start-AsynchronousDatabaseReplication -AzureContext $azureContext -TenantDatabase $currentDatabase - $databaseDetails = @{ - "ServerName" = $currentDatabase.ServerName - "DatabaseName" = $currentDatabase.DatabaseName - "ServiceObjective" = $currentDatabase.ServiceObjective - "ElasticPoolName" = $currentDatabase.ElasticPoolName - } - - # Add operation to queue for tracking - $operationId = $operationObject.Id - if (!$operationQueueMap.ContainsKey("$operationId")) - { - $operationQueue += $operationObject - $operationQueueMap.Add("$operationId", $databaseDetails) - } - } - - # Remove completed job from queue for polling - $operationQueue = $operationQueue -ne $replicationJob - $replicatedDatabaseCount+= 1 - - # Output recovery progress - $DatabaseRecoveryPercentage = [math]::Round($replicatedDatabaseCount/$tenantDatabaseCount,2) - $DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 - Write-Output "Replicating ... ($($replicatedDatabaseCount) of $tenantDatabaseCount complete)" - } - elseif (($replicationJob.IsCompleted) -and ($replicationJob.Status -eq "Faulted")) - { - # Remove completed job from queue for polling - $operationQueue = $operationQueue -ne $replicationJob - $jobId = $replicationJob.Id - $databaseDetails = $operationQueueMap["$jobId"] - - Write-Verbose "Could not replicate database: '$($databaseDetails.ServerName)/$($databaseDetails.DatabaseName)'" - } - } -} - -# Output replication progress -$DatabaseRecoveryPercentage = [math]::Round($replicatedDatabaseCount/$tenantDatabaseCount,2) -$DatabaseRecoveryPercentage = $DatabaseRecoveryPercentage * 100 -Write-Output "Replicated ($($replicatedDatabaseCount) of $tenantDatabaseCount)" diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantElasticPoolsToRecoveryRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantElasticPoolsToRecoveryRegion.ps1 deleted file mode 100644 index 3423950..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Replicate-TenantElasticPoolsToRecoveryRegion.ps1 +++ /dev/null @@ -1,158 +0,0 @@ -<# -.SYNOPSIS - Replicate tenant elastic pools to recovery region - -.DESCRIPTION - This script is intended to be run as a background job in the DR scripts. - The script creates tenant elastic pools that will be used to host tenant databases restored from the original Wingtip location. - -.PARAMETER WingtipRecoveryResourceGroup - Resource group that will be used to contain recovered resources - -.EXAMPLE - [PS] C:\>.\Replicate-TenantElasticPoolsToRecoveryRegion.ps1 -WingtipRecoveryResourceGrou "sampleResourceGroup" -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration - -# Get the active tenant catalog -$tenantCatalog = Get-Catalog -ResourceGroupName $wtpUser.ResourceGroupName -WtpUser $wtpUser.Name - -# Get the recovery region resource group -$recoveryResourceGroup = Get-AzureRmResourceGroup -Name $WingtipRecoveryResourceGroup - -# Get list of tenant elastic pools to be recovered -$tenantElasticPools = @() -$tenantElasticPools += Get-ExtendedElasticPool -Catalog $tenantCatalog | Where-Object {($_.ServerName -NotMatch "$($config.RecoveryRoleSuffix)$")} - -$recoveredPoolCount = 0 -$poolCount = $tenantElasticPools.length -$sleepInterval = 10 -$pastDeploymentWaitTime = 0 -$deploymentName = "TenantElasticPoolReplication" - -# Find any previous elastic pool recovery operations to get most current state of recovered resources -# This allows the script to be re-run if an error during deployment -$pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - -# Wait for past deployment to complete if it's still active -while (($pastDeployment) -and ($pastDeployment.ProvisioningState -NotIn "Succeeded", "Failed", "Canceled")) -{ - # Wait for no more than 5 minutes (300 secs) for previous deployment to complete - if ($pastDeploymentWaitTime -lt 300) - { - Write-Output "Waiting for previous deployment to complete ..." - Start-Sleep $sleepInterval - $pastDeploymentWaitTime += $sleepInterval - $pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - } - else - { - Stop-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 1>$null 2>$null - break - } -} - -# Wait until at least one tenant server has been replicated before starting elastic pool replication -$replicatedServers = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers" -ResourceNameContains "tenants" -while (!$replicatedServers) -{ - Write-Output "waiting for tenant server(s) to complete deployment ..." - Start-Sleep $sleepInterval - $replicatedServers = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers" -ResourceNameContains "tenants" -} - -# Check for elastic pools that have previously been recovered -$replicatedElasticPools = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers/elasticpools" -foreach ($pool in $tenantElasticPools) -{ - $recoveredServerName = $pool.ServerName + $config.RecoveryRoleSuffix - $compoundPoolName = "$($recoveredServerName)/$($pool.ElasticPoolName)" - $pool | Add-Member "CompoundPoolName" $compoundPoolName - - if ($replicatedElasticPools.Name -contains $pool.CompoundPoolName) - { - $recoveredPoolCount += 1 - } -} - -# Output recovery progress -$elasticPoolRecoveryPercentage = [math]::Round($recoveredPoolCount/$poolCount,2) -$elasticPoolRecoveryPercentage = $elasticPoolRecoveryPercentage * 100 -Write-Output "Deploying ... ($recoveredPoolCount of $poolCount complete)" - -# Recover all elastic pools in restored tenant servers -while ($recoveredPoolCount -lt $poolCount) -{ - $poolRecoveryQueue = @() - [array]$elasticPoolConfigurations = @() - $replicatedServers = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers" -ResourceNameContains "tenants" - $replicatedElasticPools = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceType "Microsoft.sql/servers/elasticpools" - - # Generate list of pools that will be recovered in current loop iteration - foreach($elasticPool in $tenantElasticPools) - { - # Add elastic pools that are eligible to be recovered to the pool queue - $recoveredServerName = $elasticPool.ServerName + $config.RecoveryRoleSuffix - - if (($replicatedElasticPools.Name -notcontains $elasticPool.CompoundPoolName) -and ($replicatedServers.Name -contains $recoveredServerName)) - { - $poolRecoveryQueue += $elasticPool - $recoveredServer = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceNameEquals $recoveredServerName - [array]$elasticPoolConfigurations += @{ - ServerName = "$($recoveredServer.Name)" - Location = "$($recoveredServer.Location)" - ElasticPoolName = "$($elasticPool.ElasticPoolName)" - Edition = "$($elasticPool.Edition)" - Dtu = "$($elasticPool.Dtu)" - DatabaseDtuMax = "$($elasticPool.DatabaseDtuMax)" - DatabaseDtuMin = "$($elasticPool.DatabaseDtuMin)" - StorageMB = "$($elasticPool.StorageMB)" - } - } - - # Recover tenant elastic pools in queue - if ($poolRecoveryQueue.Count -gt 0) - { - $deployment = New-AzureRmResourceGroupDeployment ` - -Name $deploymentName ` - -ResourceGroupName $recoveryResourceGroup.ResourceGroupName ` - -TemplateFile ("$using:scriptPath\RecoveryTemplates\" + $config.TenantElasticPoolRestoreBatchTemplate) ` - -PoolConfigurationObjects $elasticPoolConfigurations ` - -ErrorAction Stop - - $recoveredPoolCount += $poolRecoveryQueue.length - } - - # Output recovery progress - $elasticPoolRecoveryPercentage = [math]::Round($recoveredPoolCount/$poolCount,2) - $elasticPoolRecoveryPercentage = $elasticPoolRecoveryPercentage * 100 - Write-Output "Deploying ... ($recoveredPoolCount of $poolCount complete)" - } -} - -# Output recovery progress -$elasticPoolRecoveryPercentage = [math]::Round($recoveredPoolCount/$poolCount,2) -$elasticPoolRecoveryPercentage = $elasticPoolRecoveryPercentage * 100 -Write-Output "Deployed ($recoveredPoolCount of $poolCount)" diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Restore-WingtipSaaSAppToRecoveryRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Restore-WingtipSaaSAppToRecoveryRegion.ps1 deleted file mode 100644 index 0c6a0c1..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Restore-WingtipSaaSAppToRecoveryRegion.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -<# -.SYNOPSIS - Deploys an instance of the Wingtip web application with a traffic manager endpoint to a recovery region - -.DESCRIPTION - This script is intended to be run as a background job in the 'Restore-IntoSecondaryRegion' script that recovers the Wingtip SaaS app environment (apps, databases, servers e.t.c) into a recovery region. - The script creates a web app, and a traffic manager endpoint that will be used in the recovery process. - -.PARAMETER WingtipRecoveryResourceGroup - Resource group that will be used to contain recovered resources - -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration -$recoveryLocation = (Get-AzureRmResourceGroup -ResourceGroupName $WingtipRecoveryResourceGroup).Location - -# Get Wingtip web app if it exists -$recoveryWebAppName = $config.EventsAppNameStem + $recoveryLocation + '-' + $wtpUser.Name -$wingtipRecoveryApp = Find-AzureRmResource ` - -ResourceType "Microsoft.Web/sites" ` - -ResourceGroupNameEquals $WingtipRecoveryResourceGroup ` - -ResourceNameEquals $recoveryWebAppName - -if ($wingtipRecoveryApp) -{ - Write-Output "Deployed" -} -else -{ - Write-Output "Deploying" - $templatePath = "$using:scriptPath\RecoveryTemplates\" + $config.WebApplicationRecoveryTemplate - $catalogRecoveryServerName = $config.CatalogServerNameStem + $wtpUser.Name + $config.RecoveryRoleSuffix - $tenantsRecoveryServerName = $config.TenantServerNameStem + $wtpUser.Name + $config.RecoveryRoleSuffix - $deployment = New-AzureRmResourceGroupDeployment ` - -Name $recoveryWebAppName ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -TemplateFile $templatePath ` - -WtpUser $wtpUser.Name ` - -WtpOriginResourceGroup $wtpUser.ResourceGroupName ` - -RecoverySuffix $config.RecoveryRoleSuffix ` - -CatalogRecoveryServer $catalogRecoveryServerName ` - -TenantsRecoveryServer $tenantsRecoveryServerName ` - -ErrorAction Stop - - Write-Output "Deployed" -} diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Update-TenantResourcesInOriginalRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Update-TenantResourcesInOriginalRegion.ps1 deleted file mode 100644 index 2f44da5..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Update-TenantResourcesInOriginalRegion.ps1 +++ /dev/null @@ -1,169 +0,0 @@ -<# -.SYNOPSIS - Reconfigures servers and elastic pools in the Wingtip primary region to match settings in the recovery region - -.DESCRIPTION - This script is intended to be run as a background job in the 'Repatriate-IntoPrimaryRegion' script that repatriates the Wingtip SaaS app environment (apps, databases, servers e.t.c) from a recovery region back into the primary region. - The script update the configuration of elastic pools in the primary region and additionally creates any pools and servers provisioned in the recovery region that don't exist in the primary region. - -.PARAMETER WingtipRecoveryResourceGroup - Resource group that will be used to contain recovered resources - -.EXAMPLE - [PS] C:\>.\Update-TenantResourcesInPrimaryRegion.ps1 -WingtipRecoveryResourceGroup -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Import-Module "$PSScriptRoot\..\..\..\Common\CatalogAndDatabaseManagement" -Force -# Import-Module "$PSScriptRoot\..\..\..\WtpConfig" -Force -# Import-Module "$PSScriptRoot\..\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration - -# Get the active tenant catalog in the recovery region -$tenantCatalog = Get-Catalog -ResourceGroupName $WingtipRecoveryResourceGroup -WtpUser $wtpUser.Name - -# Find any previous tenant resource update operations to get most current state of recovered resources -# This allows the script to be re-run if an error during deployment -$deploymentName = "ReconfigureTenantResources" -$pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $wtpUser.ResourceGroupName -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - -# Wait for past deployment to complete if it's still active -while (($pastDeployment) -and ($pastDeployment.ProvisioningState -NotIn "Succeeded", "Failed", "Canceled")) -{ - # Wait for no more than 5 minutes (300 secs) for previous deployment to complete - if ($pastDeploymentWaitTime -lt 300) - { - Write-Output "Waiting for previous deployment to complete ..." - Start-Sleep $sleepInterval - $pastDeploymentWaitTime += $sleepInterval - $pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $wtpUser.ResourceGroupName -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - } - else - { - Stop-AzureRmResourceGroupDeployment -ResourceGroupName $wtpUser.ResourceGroupName -Name $deploymentName -ErrorAction SilentlyContinue 1>$null 2>$null - break - } -} - -# Get list of servers and elastic pools that were recovered into the recovery region -$recoveryRegionServers = Get-ExtendedServer -Catalog $tenantCatalog | Where-Object {($_.ServerName -Match "$($config.RecoveryRoleSuffix)$")} -$recoveryRegionElasticPools = Get-ExtendedElasticPool -Catalog $tenantCatalog | Where-Object {($_.ServerName -Match "$($config.RecoveryRoleSuffix)$")} - -# Save server configuration settings -[array]$tenantServerConfigurations = @() -foreach($server in $recoveryRegionServers) -{ - $originServerName = ($server.ServerName -split $config.RecoveryRoleSuffix)[0] - - # Check if server exists in origin region - $originServerExists = Find-AzureRmResource -ResourceGroupNameEquals $wtpUser.ResourceGroupName -ResourceNameEquals $originServerName -ResourceType "Microsoft.Sql/servers" - - if (!$originServerExists) - { - [array]$tenantServerConfigurations += @{ - ServerName = "$originServerName" - AdminLogin = "$($config.TenantAdminUserName)" - AdminPassword = "$($config.TenantAdminPassword)" - } - - $serverState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startReplication" -ServerName $originServerName - } -} - -# Save elastic pool configuration settings. -[array]$tenantElasticPoolConfigurations = @() -foreach($pool in $recoveryRegionElasticPools) -{ - $originServerName = ($pool.ServerName -split $config.RecoveryRoleSuffix)[0] - - # Check if pool exists with correct configuration - $originPoolSynced = $false - $originPoolExists = Find-AzureRmResource -ResourceGroupNameEquals $wtpUser.ResourceGroupName -ResourceNameEquals "$originServerName/$($pool.ElasticPoolName)" - - if ($originPoolExists) - { - $originPool = Get-AzureRmSqlElasticPool -ResourceGroupName $wtpUser.ResourceGroupName -ServerName $originServerName - if - ( - $originPool.Edition -eq $pool.Edition -and - $originPool.Dtu -eq $pool.Dtu -and - $originPool.DatabaseDtuMax -eq $pool.DatabaseDtuMax -and - $originPool.DatabaseDtuMin -eq $pool.DatabaseDtuMin -and - $originPool.StorageMB -eq $pool.StorageMB - ) - { - $originPoolSynced = $true - } - } - - if (!$originPoolSynced) - { - [array]$tenantElasticPoolConfigurations += @{ - ServerName = "$originServerName" - ElasticPoolName = "$($pool.ElasticPoolName)" - Edition = "$($pool.Edition)" - Dtu = "$($pool.Dtu)" - DatabaseDtuMax = "$($pool.DatabaseDtuMax)" - DatabaseDtuMin = "$($pool.DatabaseDtuMin)" - StorageMB = "$($pool.StorageMB)" - } - # Update elastic pool recovery state - $poolState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "startReplication" -ServerName $originServerName -ElasticPoolName $pool.ElasticPoolName - } -} -$tenantResourceCount = $tenantServerConfigurations.Count + $tenantElasticPoolConfigurations.Count - -if ($tenantResourceCount -eq 0) -{ - Write-Output "All resources synced" - exit -} - -# Output recovery progress -Write-Output "0% (0 of $tenantResourceCount)" - -# Reconfigure servers and elastic pools in primary region to match settings in the recovery region -$deployment = New-AzureRmResourceGroupDeployment ` - -Name $deploymentName ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -TemplateFile ("$using:scriptPath\RecoveryTemplates\" + $config.ReconfigureTenantResourcesTemplate) ` - -ServerConfigurationSettings $tenantServerConfigurations ` - -PoolConfigurationSettings $tenantElasticPoolConfigurations ` - -ErrorAction Stop - -# Mark server recovery as complete -foreach($server in $tenantServerConfigurations) -{ - $serverState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endReplication" -ServerName $server.ServerName -} - -# Mark pool recovery as complete -foreach($pool in $tenantElasticPoolConfigurations) -{ - $poolState = Update-TenantResourceRecoveryState -Catalog $tenantCatalog -UpdateAction "endReplication" -ServerName $pool.ServerName -ElasticPoolName $pool.ElasticPoolName -} - -# Output recovery progress -Write-Output "100% ($tenantResourceCount of $tenantResourceCount)" - diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Update-TenantResourcesInRecoveryRegion.ps1 b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Update-TenantResourcesInRecoveryRegion.ps1 deleted file mode 100644 index ae6b104..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryJobs/Update-TenantResourcesInRecoveryRegion.ps1 +++ /dev/null @@ -1,160 +0,0 @@ -<# -.SYNOPSIS - Reconfigures servers and elastic pools in the Wingtip recovery region to match settings in the origin region - -.DESCRIPTION - This script is intended to be run as a background job in the 'Failover-IntoRecoveryRegion' script that fails over tenant databases to the recovery region. - The script reconfigures any existing tenant servers and elastic pools to match their counterparts in the origin region in preparation for a failover operation - -.PARAMETER WingtipRecoveryResourceGroup - Resource group containing Wingtip recovery resources - -.EXAMPLE - [PS] C:\>.\Update-TenantResourcesInRecoveryRegion.ps1 -WingtipRecoveryResourceGroup sampleRecoveryGroup -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [String] $WingtipRecoveryResourceGroup -) - -Import-Module "$using:scriptPath\..\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$using:scriptPath\..\..\WtpConfig" -Force -Import-Module "$using:scriptPath\..\..\UserConfig" -Force - -# Stop execution on error -$ErrorActionPreference = "Stop" - -# Login to Azure subscription -$credentialLoad = Import-AzureRmContext -Path "$env:TEMP\profile.json" -if (!$credentialLoad) -{ - Initialize-Subscription -} - -# Get deployment configuration -$wtpUser = Get-UserConfig -$config = Get-Configuration - -# Get the active tenant catalog in the recovery region -$tenantCatalog = Get-Catalog -ResourceGroupName $wtpUser.ResourceGroupName -WtpUser $wtpUser.Name - -# Find any previous tenant resource update operations to get most current state of recovered resources -# This allows the script to be re-run if an error during deployment -$deploymentName = "ReconfigureTenantResources" -$pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - -# Wait for past deployment to complete if it's still active -while (($pastDeployment) -and ($pastDeployment.ProvisioningState -NotIn "Succeeded", "Failed", "Canceled")) -{ - # Wait for no more than 5 minutes (300 secs) for previous deployment to complete - if ($pastDeploymentWaitTime -lt 300) - { - Write-Output "Waiting for previous deployment to complete ..." - Start-Sleep $sleepInterval - $pastDeploymentWaitTime += $sleepInterval - $pastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 2>$null - } - else - { - Stop-AzureRmResourceGroupDeployment -ResourceGroupName $WingtipRecoveryResourceGroup -Name $deploymentName -ErrorAction SilentlyContinue 1>$null 2>$null - break - } -} - -# Get list of servers and elastic pools in origin region -$originRegionServers = Get-ExtendedServer -Catalog $tenantCatalog | Where-Object {($_.ServerName -NotMatch "$($config.RecoveryRoleSuffix)$")} -$originRegionElasticPools = Get-ExtendedElasticPool -Catalog $tenantCatalog | Where-Object {($_.ServerName -NotMatch "$($config.RecoveryRoleSuffix)$")} - -# Save server configuration settings -[array]$tenantServerConfigurations = @() -foreach($server in $originRegionServers) -{ - $recoveryServerName = $server.ServerName + $config.RecoveryRoleSuffix - - # Check if server exists in recovery region - $recoveryServerExists = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceNameEquals $recoveryServerName -ResourceType "Microsoft.Sql/servers" - - if (!$recoveryServerExists) - { - [array]$tenantServerConfigurations += @{ - ServerName = "$recoveryServerName" - AdminLogin = "$($config.TenantAdminUserName)" - AdminPassword = "$($config.TenantAdminPassword)" - } - } -} - -# Save elastic pool configuration settings. -[array]$tenantElasticPoolConfigurations = @() -foreach($pool in $originRegionElasticPools) -{ - $recoveryServerName = $pool.ServerName + $config.RecoveryRoleSuffix - - # Check if pool exists with correct configuration - $recoveryPoolSynced = $false - $recoveryPoolExists = Find-AzureRmResource -ResourceGroupNameEquals $WingtipRecoveryResourceGroup -ResourceNameEquals "$recoveryServerName/$($pool.ElasticPoolName)" - - if ($recoveryPoolExists) - { - $recoveryPool = Get-AzureRmSqlElasticPool -ResourceGroupName $WingtipRecoveryResourceGroup -ServerName $recoveryServerName - if - ( - $recoveryPool.Edition -eq $pool.Edition -and - $recoveryPool.Dtu -ge $pool.Dtu -and - $recoveryPool.DatabaseDtuMax -ge $pool.DatabaseDtuMax -and - $recoveryPool.DatabaseDtuMin -ge $pool.DatabaseDtuMin -and - $recoveryPool.StorageMB -ge $pool.StorageMB - ) - { - $recoveryPoolSynced = $true - } - } - - if (!$recoveryPoolSynced) - { - [array]$tenantElasticPoolConfigurations += @{ - ServerName = "$recoveryServerName" - ElasticPoolName = "$($pool.ElasticPoolName)" - Edition = "$($pool.Edition)" - Dtu = "$($pool.Dtu)" - DatabaseDtuMax = "$($pool.DatabaseDtuMax)" - DatabaseDtuMin = "$($pool.DatabaseDtuMin)" - StorageMB = "$($pool.StorageMB)" - } - - $poolServerConfiguration = @{ - ServerName = "$recoveryServerName" - AdminLogin = "$($config.TenantAdminUserName)" - AdminPassword = "$($config.TenantAdminPassword)" - } - - if ($tenantServerConfigurations -notcontains $poolServerConfiguration) - { - $tenantServerConfigurations+= $poolServerConfiguration - } - } -} -$tenantResourceCount = $tenantServerConfigurations.Count + $tenantElasticPoolConfigurations.Count - -if ($tenantResourceCount -eq 0) -{ - Write-Output "All resources synced" - exit -} - -# Output recovery progress -Write-Output "0% (0 of $tenantResourceCount)" - -# Reconfigure servers and elastic pools in primary region to match settings in the recovery region -$deployment = New-AzureRmResourceGroupDeployment ` - -Name $deploymentName ` - -ResourceGroupName $WingtipRecoveryResourceGroup ` - -TemplateFile ("$using:scriptPath\RecoveryTemplates\" + $config.ReconfigureTenantResourcesTemplate) ` - -ServerConfigurationSettings $tenantServerConfigurations ` - -PoolConfigurationSettings $tenantElasticPoolConfigurations ` - -ErrorAction Stop - -# Output recovery progress -Write-Output "100% ($tenantResourceCount of $tenantResourceCount)" - diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryTemplates/failovergrouptemplate.json b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryTemplates/failovergrouptemplate.json deleted file mode 100644 index a6450f0..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryTemplates/failovergrouptemplate.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "OriginServerName": { - "type": "String" - }, - "RecoveryServerName": { - "type": "String" - }, - "RecoveryResourceGroupName": { - "type": "String" - }, - "FailovergroupName": { - "type": "String" - } - }, - "resources": [ - { - "name": "[concat(parameters('OriginServerName'), '/', parameters('FailovergroupName'))]", - "type": "Microsoft.Sql/servers/failoverGroups", - "apiVersion": "2015-05-01-preview", - "properties": { - "serverName": "[parameters('OriginServerName')]", - "partnerServers": [ - { - "id": "[resourceId(parameters('RecoveryResourceGroupName'), 'Microsoft.Sql/servers/', parameters('RecoveryServerName'))]" - } - ], - "readWriteEndpoint": { - "failoverPolicy": "Manual" - }, - "readOnlyEndpoint": { - "failoverPolicy": "Disabled" - } - } - } - ] -} \ No newline at end of file diff --git a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryTemplates/tenantdatabasereplicationbatchtemplate.json b/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryTemplates/tenantdatabasereplicationbatchtemplate.json deleted file mode 100644 index 28b27fe..0000000 --- a/Learning Modules/Business Continuity and Disaster Recovery/DR-FailoverToReplica/RecoveryTemplates/tenantdatabasereplicationbatchtemplate.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "DatabaseConfigurationObjects": { "type": "array" } - }, - "resources": [ - { - "apiVersion": "2015-01-01", - "name": "[concat('DatabaseReplication-',parameters('DatabaseConfigurationObjects')[copyIndex()].DatabaseName)]", - "type": "Microsoft.Resources/deployments", - "copy": { - "name": "recoverydatabases", - "count": "[length(parameters('DatabaseConfigurationObjects'))]" - }, - "properties": { - "mode": "Incremental", - "templateLink": { - "uri": "https://wingtipsaas.blob.core.windows.net/templates-dpt/tenantdatabasereplicationtemplate.json", - "contentVersion": "1.0.0.0" - }, - "parameters": { - "serverName": {"value": "[parameters('DatabaseConfigurationObjects')[copyIndex()].ServerName]" }, - "originServerName": {"value": "[parameters('DatabaseConfigurationObjects')[copyIndex()].OriginServerName]" }, - "databaseName": {"value": "[parameters('DatabaseConfigurationObjects')[copyIndex()].DatabaseName]" }, - "failovergroupName": {"value": "[parameters('DatabaseConfigurationObjects')[copyIndex()].FailoverGroupName]"}, - "recoveryResourceGroupName": {"value": "[parameters('DatabaseConfigurationObjects')[copyIndex()].RecoveryResourceGroupName]"} - } - } - } - ] -} diff --git a/Learning Modules/DPT.ps1 b/Learning Modules/DPT.ps1 deleted file mode 100644 index 369d4c5..0000000 --- a/Learning Modules/DPT.ps1 +++ /dev/null @@ -1 +0,0 @@ -# Database Per Tenant \ No newline at end of file diff --git a/Learning Modules/Devops and Support/Demo-DevopsAndSupport.ps1 b/Learning Modules/Devops and Support/Demo-DevopsAndSupport.ps1 deleted file mode 100644 index 253c6a1..0000000 --- a/Learning Modules/Devops and Support/Demo-DevopsAndSupport.ps1 +++ /dev/null @@ -1,136 +0,0 @@ -# Find tenants by name, then open the selected tenant's database in the Azure Portal - -# To make the exploration more interesting, start a load on the databases with scenario 1 and let it run for a few minutes. - -# Duration of the load generation session. Some activity may continue after this time. -$DurationMinutes = 60 - -# This specifies a tenant database to be overloaded in scenario 1. -$SingleTenantDatabaseName = "fabrikamjazzclub" - -# In scenario 2, try entering 'jazz' when prompted to quickly locate Fabrikam Jazz Club. - -$DemoScenario = 1 -<# Select the scenario to run - Scenario - 1 Generate a high intensity load (approx 90 DTU) on a single tenant plas a normal intensity load (40 DTU) on all other tenants - 2 Open a specific tenant's database in the portal plus their public events page -#> - -## ------------------------------------------------------------------------------------------------ - -Import-Module "$PSScriptRoot\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Force -Import-Module "$PSScriptRoot\..\UserConfig" -Force - -# Get Azure credentials if not already logged on, Use -Force to select a different subscription -Initialize-Subscription -NoEcho - -# Get the resource group and user names used when the WTP application was deployed from UserConfig.psm1. -$wtpUser = Get-UserConfig - -### Default state - enter a valid demo scenaro -if ($DemoScenario -eq 0) -{ - Write-Output "Please modify the demo script to select a scenario to run." - exit -} - - -### Generate a high intensity load (approx 90 DTU) on a single tenant plas a normal intensity load (30 DTU) on all other tenants -if ($DemoScenario -eq 1) -{ - # First, stop and remove any prior running jobs - Write-Output "Stopping any prior jobs. This can take a minute or more... " - Remove-Job * -Force - - # Intensity of normal load, roughly approximates to average eDTU loading on the pool - $Intensity = 30 - - # start a new set of load generation jobs for the current databases with the load configuration above - & $PSScriptRoot\..\Utilities\LoadGenerator.ps1 ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -Wtpuser $wtpUser.Name ` - -Intensity $Intensity ` - -DurationMinutes $DurationMinutes ` - -SingleTenant ` - -SingleTenantDatabaseName $SingleTenantDatabaseName - - exit -} - - -### Open a specific tenant's database in the portal, plus their public events page -if ($DemoScenario -eq 2) -{ - $catalog = Get-Catalog ` - -ResourceGroupName $wtpUser.ResourceGroupName ` - -WtpUser $wtpUser.Name - - $tenantNames = @() - - # Get search string from console and find matching tenants - do - { - [string]$searchString = Read-Host "`nTenant name search string" -ErrorAction Stop - - Write-Output "`nLooking for tenants..." - - # Check search string is valid and prevent SQL injection - Test-LegalNameFragment $searchString - - # Find tenants with names that match the search string - $tenantNames += Find-TenantNames -Catalog $catalog -SearchString $searchString - - if(-not $tenantNames) - { - Write-Output "No tenants found matching '$searchString', try again or ctrl-c to exit" - } - - } while (-not $tenantNames) - - # Display matching tenants - $index = 1 - foreach($tenantName in $TenantNames) - { - $tenantName | Add-Member -type NoteProperty -name "Tenant" -value $index - $index++ - } - - # Prompt for selection - Write-Output "Matching tenants: " - $TenantNames | Format-Table Tenant,TenantName -AutoSize - - # Get the tenant selection from console and open database in portal and the corresponding events page - do - { - try - { - [int]$selectedRow = Read-Host "`nEnter the tenant number to open database in portal, 0 to exit" -ErrorAction Stop - - if ($selectedRow -ne 0) - { - $selectedTenantName = $TenantNames[$selectedRow - 1].TenantName - - # Open the events page for the new venue to verify it's working correctly - Start-Process "http://events.wingtip-dpt.$($wtpUser.Name).trafficmanager.net/$(Get-NormalizedTenantName $selectedTenantName)" - - # open the database blade in the portal to review performance - Open-TenantResourcesInPortal ` - -Catalog $catalog ` - -TenantName $selectedTenantName ` - -ResourceTypes ('database') - } - exit - } - catch - { - Write-Output 'Invalid selection.' - } - - } while (1 -eq 1) - - exit -} - -Write-Output "Invalid scenario selected" \ No newline at end of file diff --git a/Learning Modules/ProvisionConfig.psm1 b/Learning Modules/ProvisionConfig.psm1 deleted file mode 100644 index 6451184..0000000 --- a/Learning Modules/ProvisionConfig.psm1 +++ /dev/null @@ -1,21 +0,0 @@ - -<# -.SYNOPSIS - Returns default provisioning config values that will be used by the auto-provisioning WebJobs -#> -function Get-ProvisionConfiguration -{ - $config = @{` - ServerNameStem = 'tenants' - ServerElasticPoolsMax = 2 - ElasticPoolNameStem = 'Pool' - ElasticPoolEdition = 'Standard' - ElasticPoolDtu = 50 - ElasticPoolDatabaseDtuMax = 50 - ElasticPoolDatabaseDtuMin = 0 - ElasticPoolDatabasesMax = 25 - BufferDatabases = 20 - BufferDatabaseNameStem = 'buffer-' - } - return $config -} diff --git a/Learning Modules/UserConfig.psm1 b/Learning Modules/UserConfig.psm1 deleted file mode 100644 index f8df585..0000000 --- a/Learning Modules/UserConfig.psm1 +++ /dev/null @@ -1,29 +0,0 @@ -<# -.SYNOPSIS - Returns the User name and resource group name used during the Wingtip SaaS application deployment. - The values defined here are referenced by the learning module scripts. -#> - - -## Update the two variables below to the values used when the Wingtip SaaS app was deployed. - -$ResourceGroupName = "" # the resource group used when the Wingtip SaaS application was deployed. CASE SENSITIVE -$User = "" # the User value entered when the Wingtip SaaS application was deployed - -## DO NOT CHANGE VALUES BELOW HERE ------------------------------------------------------------- - -function Get-UserConfig { - - $userConfig = @{` - ResourceGroupName = $ResourceGroupName - Name = $User.ToLower() - } - - if ($userConfig.ResourceGroupName -eq "" -or $userConfig.Name -eq "") - { - throw 'UserConfig is not set. Modify $ResourceGroupName and $User in UserConfig.psm1 and try again.' - } - - return $userConfig - -} diff --git a/Learning Modules/Utilities/AzurePairedRegions.txt b/Learning Modules/Utilities/AzurePairedRegions.txt deleted file mode 100644 index a6d2a92..0000000 --- a/Learning Modules/Utilities/AzurePairedRegions.txt +++ /dev/null @@ -1,35 +0,0 @@ -@{ -eastasia='southeastasia'; -southeastasia='eastasia'; -australiaeast='australiasoutheast'; -australiasoutheast='australiaeast'; -canadacentral='canadaeast'; -canadaeast='canadacentral'; -chinanorth='chinaeast'; -chinaeast='chinanorth'; -centralindia='southindia'; -southindia='centralindia'; -japaneast='japanwest'; -japanwest='japaneast'; -koreacentral='koreasouth'; -koreasouth='koreacentral'; -northcentralus='southcentralus'; -southcentralus='northcentralus'; -eastus='westus'; -westus='eastus'; -eastus2='centralus'; -centralus='eastus2'; -westus2='westcentralus'; -westcentralus='westus2'; -northeurope='westeurope'; -westeurope='northeurope'; -brazilsouth='southcentralus'; -usgoviowa='usgovvirginia'; -usgovvirginia='usgovtexas'; -usgovtexas='usgovarizona'; -usgovarizona='usgovtexas'; -ukwest='uksouth'; -uksouth='ukwest'; -germanycentral='germanynortheast'; -germanynortheast='germanycentral'; -} \ No newline at end of file diff --git a/Learning Modules/Utilities/Demo-InvokeSqlCmdOnTenantDatabases.ps1 b/Learning Modules/Utilities/Demo-InvokeSqlCmdOnTenantDatabases.ps1 deleted file mode 100644 index ac4f707..0000000 --- a/Learning Modules/Utilities/Demo-InvokeSqlCmdOnTenantDatabases.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -# Helper script for invoking Apply-SQLCommandToTenantDatabases. -# Crude way to apply a one-time script against tenant dbs in catalog. Use Elastic Jobs for any serious work...! - -# Ensure UserConfig.psm1 is initialized correctly before running this. - -# SQL script to be applied. Uses Invoke-SqlCmd so can include batches with GO statements. -# Script should be idempotent as will retry on error. No results are returned, check dbs for success. -$commandText = " - -- Add script to be deployed here - " - -# query timeout in seconds -$queryTimeout = 60 - -## ------------------------------------------------------------------------------------------------ - -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Force -Import-Module "$PSScriptRoot\..\UserConfig" -Force - -# Get Azure credentials if not already logged on, Use -Force to select a different subscription -Initialize-Subscription -NoEcho - -# Get the resource group and user names used when the WTP application was deployed from UserConfig.psm1. -$wtpUser = Get-UserConfig - -& $PSScriptRoot\Invoke-SqlCmdOnTenantDatabases ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -WtpUser $wtpUser.Name ` - -CommandText $commandText ` - -QueryTimeout $queryTimeout diff --git a/Learning Modules/Utilities/Demo-LoadGenerator.ps1 b/Learning Modules/Utilities/Demo-LoadGenerator.ps1 deleted file mode 100644 index 23420d0..0000000 --- a/Learning Modules/Utilities/Demo-LoadGenerator.ps1 +++ /dev/null @@ -1,151 +0,0 @@ -# Invokes load generation on the tenant databases currently defined in the catalog. - -# Duration of the load generation session. Some activity may continue after this time. -$DurationMinutes = 120 - -# If SingleTenant is enabled (scenario 4), this specifies the tenant database to be overloaded. -# If set to "" a random tenant database is chosen. -$SingleTenantDatabaseName = "contosoconcerthall" - -# If $true, generator will run once. If $false will keep looking for additional tenants and apply load to them -$OneTime = $false - -$DemoScenario = 0 -<# Select the demo scenario to run - Demo Scenario - 0 None - 1 Start a normal intensity load (approx 30 DTU) - 2 Start a load with longer bursts per database - 3 Start a load with higher DTU bursts per database (approx 70 DTU) - 4 Start a high intensity load (approx 95 DTU) on a single tenant plas a normal intensity load on all other tenants - 5 Start an unbalanced load across multiple pools -#> - -## ------------------------------------------------------------------------------------------------ - -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Force -Import-Module "$PSScriptRoot\..\UserConfig" -Force - -# Get Azure credentials if not already logged on, Use -Force to select a different subscription -Initialize-Subscription -NoEcho - -# Get the resource group and user names used when the WTP application was deployed from UserConfig.psm1. -$wtpUser = Get-UserConfig - -### Default state - enter a valid demo scenaro -if ($DemoScenario -eq 0) -{ - Write-Output "Please modify the demo script to select a scenario to run." - exit -} - -### Generate normal intensity load -if ($DemoScenario -eq 1) -{ - # First, stop and remove any prior running jobs - Write-Output "`nStopping any prior jobs. This can take a minute or more... " - Remove-Job * -Force - - # Intensity of load, roughly approximates to average eDTU loading on the pool - $Intensity = 30 - - # start a new set of load generation jobs for the current databases with the load configuration above - & $PSScriptRoot\..\Utilities\LoadGenerator.ps1 ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -Wtpuser $wtpUser.Name ` - -Intensity $Intensity ` - -DurationMinutes $DurationMinutes ` - -OneTime $OneTime - - exit -} - -### Generate load with longer bursts per database -if ($DemoScenario -eq 2) -{ - # First, stop and remove any prior running jobs - Write-Output "`nStopping any prior jobs. This can take a minute or more... " - Remove-Job * -Force - - # Intensity of load, roughly approximates to average eDTU loading on the pool - $Intensity = 30 - - # start a new set of load generation jobs for the current databases with the load configuration above - & $PSScriptRoot\..\Utilities\LoadGenerator.ps1 ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -Wtpuser $wtpUser.Name ` - -Intensity $Intensity ` - -DurationMinutes $DurationMinutes ` - -LongerBursts ` - -OneTime $OneTime - - exit -} - -### Generate load with higher DTU bursts per database -if ($DemoScenario -eq 3) -{ - # First, stop and remove any prior running jobs - Write-Output "`nStopping any prior jobs. This can take a minute or more... " - Remove-Job * -Force - - # Intensity of load, roughly approximates to average eDTU loading on the pool - $Intensity = 70 - - # start a new set of load generation jobs for the current databases with the load configuration above - & $PSScriptRoot\..\Utilities\LoadGenerator.ps1 ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -Wtpuser $wtpUser.Name ` - -Intensity $Intensity ` - -DurationMinutes $DurationMinutes ` - -OneTime $OneTime - - exit -} - -### Generate a high intensity load (approx 95 DTU) on a single tenant plas a normal intensity load (40 DTU) on all other tenants -if ($DemoScenario -eq 4) -{ - # First, stop and remove any prior running jobs - Write-Output "`nStopping any prior jobs. This can take a minute or more... " - Remove-Job * -Force - - # Intensity of load, roughly approximates to average eDTU loading on the pool - $Intensity = 30 - - # start a new set of load generation jobs for the current databases with the load configuration above - & $PSScriptRoot\..\Utilities\LoadGenerator.ps1 ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -Wtpuser $wtpUser.Name ` - -Intensity $Intensity ` - -DurationMinutes $DurationMinutes ` - -SingleTenant ` - -SingleTenantDatabaseName $SingleTenantDatabaseName ` - -OneTime $OneTime - - exit -} - -### Generate unbalanced load (either 30 DTU or 60 DTU) across multiple pools -if ($DemoScenario -eq 5) -{ - # First, stop and remove any prior running jobs - Write-Output "`nStopping any prior jobs. This can take a minute or more... " - Remove-Job * -Force - - # Intensity of load, roughly approximates to average eDTU loading on the pool - $Intensity = 30 - - # start a new set of load generation jobs for the current databases with the load configuration above - & $PSScriptRoot\..\Utilities\LoadGenerator.ps1 ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -Wtpuser $wtpUser.Name ` - -Intensity $Intensity ` - -DurationMinutes $DurationMinutes ` - -Unbalanced ` - -OneTime $OneTime - - exit -} - -Write-Output "Invalid scenario selected" \ No newline at end of file diff --git a/Learning Modules/Utilities/Demo-ResetEventDates.ps1 b/Learning Modules/Utilities/Demo-ResetEventDates.ps1 deleted file mode 100644 index 61f7ea6..0000000 --- a/Learning Modules/Utilities/Demo-ResetEventDates.ps1 +++ /dev/null @@ -1,18 +0,0 @@ -<# Helper script for resetting event dates for all tenants. - The pre-provisioned tenants and the golden tenant database have events pre-defined on predefined dates. Use this script - to reset the event and ticket purchase dates for all tenants. -#> -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Force -Import-Module "$PSScriptRoot\..\UserConfig" -Force - -# Get Azure credentials if not already logged on, Use -Force to select a different subscription -Initialize-Subscription -NoEcho - -# Get the resource group and user names used when the WTP application was deployed from UserConfig.psm1. -$wtpUser = Get-UserConfig - -### Reset event and ticketpurchase dates for all tenants -& $PSScriptRoot\Reset-EventDatesForAllTenants.ps1 ` - -WtpResourceGroupName $wtpUser.ResourceGroupName ` - -WtpUser $wtpUser.Name -#> \ No newline at end of file diff --git a/Learning Modules/Utilities/Demo-TicketGenerator.ps1 b/Learning Modules/Utilities/Demo-TicketGenerator.ps1 deleted file mode 100644 index e076edd..0000000 --- a/Learning Modules/Utilities/Demo-TicketGenerator.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -# Helper script for running the ticket generator. Requires no input other than setting UserConfig.psm1 - -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Import-Module "$PSScriptRoot\..\UserConfig" -Force - -# Get Azure credentials if not already logged on, Use -Force to select a different subscription -Initialize-Subscription -NoEcho - -# Get the resource group and user names used when the WTP application was deployed from UserConfig.psm1. -$wtpUser = Get-UserConfig - -### (Re)generate tickets for all tenants. The last event in each tenant will have no tickets and can be deleted. -& $PSScriptRoot\TicketGenerator2.ps1 ` - -WtpResourceGroupname $WtpUser.ResourceGroupName ` - -WtpUser $WtpUser.Name diff --git a/Learning Modules/Utilities/FictitiousName_02082017_104533.csv b/Learning Modules/Utilities/FictitiousName_02082017_104533.csv deleted file mode 100644 index dd5efec..0000000 --- a/Learning Modules/Utilities/FictitiousName_02082017_104533.csv +++ /dev/null @@ -1,31 +0,0 @@ -#,First Name,Last Name,Language,Gender -1,Lizzie,Mills,English (US),Female -2,Simone,Guy,English (US),Female -3,Margo,Bond,English (US),Female -4,Wilma,Ingram,English (US),Female -5,Clay,Passmore,English (US),Male -6,Joey,Landon,English (US),Male -7,Deana,Maldonado,English (US),Female -8,Gerardo,Trowbridge,English (US),Male -9,Rebekah,Powers,English (US),Female -10,Seth,Reiss,English (US),Male -11,August,Popp,English (US),Male -12,Kevin,Stoker,English (US),Male -13,Michele,Bowers,English (US),Female -14,June,Ruiz,English (US),Female -15,Wendi,Vaughan,English (US),Female -16,Marietta,Tanner,English (US),Female -17,Pauline,Soto,English (US),Female -18,Corrine,Phillips,English (US),Female -19,Teddy,Armenta,English (US),Male -20,Meagan,Cook,English (US),Female -21,Audrey,Little,English (US),Female -22,Gustavo,Wortham,English (US),Male -23,Ashlee,Fry,English (US),Female -24,Manuela,Guzman,English (US),Female -25,Jo,Glass,English (US),Female -26,Tamara,Zimmerman,English (US),Female -27,Angela,Melton,English (US),Female -28,Tracy,Pitcher,English (US),Male -29,Anastasia,Wilson,English (US),Female -30,Ty,Mazza,English (US),Male diff --git a/Learning Modules/Utilities/FictitiousName_02082017_104844.csv b/Learning Modules/Utilities/FictitiousName_02082017_104844.csv deleted file mode 100644 index 930c450..0000000 --- a/Learning Modules/Utilities/FictitiousName_02082017_104844.csv +++ /dev/null @@ -1,36 +0,0 @@ -#,First Name,Last Name,Language,Gender -1,Ernestine,Bartlett,English (US),Female -2,Caitlin,Hodges,English (US),Female -3,Latonya,Finley,English (US),Female -4,Clay,Oswalt,English (US),Male -5,Flora,Sykes,English (US),Female -6,Debbie,Merrill,English (US),Female -7,Laurence,Madigan,English (US),Male -8,Frankie,Keyser,English (US),Male -9,Josefina,Reeves,English (US),Female -10,Gabriela,Gentry,English (US),Female -11,Scot,Paulk,English (US),Male -12,Jesus,McClain,English (US),Male -13,Carmen,Seale,English (US),Male -14,Jerri,Walton,English (US),Female -15,Otis,Bowler,English (US),Male -16,Deidre,Simpson,English (US),Female -17,Vanessa,Tucker,English (US),Female -18,Ted,Theriault,English (US),Male -19,Wilfred,Horst,English (US),Male -20,Thurman,Fredrick,English (US),Male -21,Enrique,Groff,English (US),Male -22,Veronica,Mullins,English (US),Female -23,Nora,Simon,English (US),Female -24,Natalie,Shannon,English (US),Female -25,Antoine,Hatley,English (US),Male -26,Howard,Flanigan,English (US),Male -27,Billy,Horst,English (US),Male -28,Marla,Joseph,English (US),Female -29,Jasmine,Gregory,English (US),Female -30,Christa,Gutierrez,English (US),Female -31,Russell,Lunn,English (US),Male -32,Frankie,Kong,English (US),Male -33,Donald,Giron,English (US),Male -34,Patrick,Townes,English (US),Male -35,Joanne,Cain,English (US),Female diff --git a/Learning Modules/Utilities/FictitiousNames.csv b/Learning Modules/Utilities/FictitiousNames.csv deleted file mode 100644 index ab6eed7..0000000 --- a/Learning Modules/Utilities/FictitiousNames.csv +++ /dev/null @@ -1,1000 +0,0 @@ -1,Deb,Sen,Bengali,Male -2,Ajit,Mazumdar,Bengali,Male -3,Shaan,Dasgupta,Bengali,Male -4,Dhriti,Debnath,Bengali,Female -5,Minaxi,Bandopadhyay,Bengali,Female -6,Sangeeta,Chattopadhyay,Bengali,Female -7,Jaidev,Sen,Bengali,Male -8,Harsha,Dasgupta,Bengali,Male -9,Satyen,Banerjee,Bengali,Male -10,Gautam,Bhuiyan,Bengali,Male -11,Anoop,Das,Bengali,Male -12,Hemchandra,Ganguly,Bengali,Male -13,Deepankar,Bagchi,Bengali,Male -14,Abhijit,Ghatak,Bengali,Male -15,Chandrabali,Debnath,Bengali,Female -16,Debiprasad,Ghatak,Bengali,Male -17,Ananya,Sanyal,Bengali,Female -18,Dilip,Sikdar,Bengali,Male -19,Hiran,Bandopadhyay,Bengali,Male -20,Geet,Ganguly,Bengali,Female -21,Devesh,Chatterjee,Bengali,Male -22,Chandran,Sengupta,Bengali,Male -23,Devadas,Sikdar,Bengali,Male -24,Anmolika,Sikdar,Bengali,Female -25,Banshi,Dutta,Bengali,Male -26,Amar,Dutta,Bengali,Male -27,Ashutosh,Das,Bengali,Male -28,Chanchala,Thakur,Bengali,Female -29,Upasana,Banerjee,Bengali,Female -30,Divyendu,Dasgupta,Bengali,Male -31,Annapoorna,Bhuiyan,Bengali,Female -32,Tapas,Sikdar,Bengali,Male -33,Kanchan,Dutta,Bengali,Female -34,Arpita,Mukherjee,Bengali,Female -35,Kajol,Mazumdar,Bengali,Female -36,Na,Ni,Chinese (PRC),Female -37,Jerry ,Hu,Chinese (PRC),Male -38,Sun,Zhuang,Chinese (PRC),Male -39,Shen,Chen,Chinese (PRC),Male -40,Ming,Lo,Chinese (PRC),Male -41,Fen,Fu,Chinese (PRC),Female -42,An,Chen,Chinese (PRC),Male -43,Chan,Xia,Chinese (PRC),Female -44,Chan,Ang,Chinese (PRC),Female -45,Jie,Yuen,Chinese (PRC),Male -46,Peng,Yao,Chinese (PRC),Male -47,Ming,Ren,Chinese (PRC),Male -48,Chan,Xu,Chinese (PRC),Female -49,Ming,Teng,Chinese (PRC),Male -50,Yi Ze,Xie,Chinese (PRC),Female -51,Hu,Kuo,Chinese (PRC),Male -52,Cai,Shi,Chinese (PRC),Female -53,Cai,Tsay,Chinese (PRC),Female -54,Wen,Pan,Chinese (PRC),Male -55,Luo,Xie,Chinese (PRC),Male -56,Kuan-Yin,Chen,Chinese (PRC),Male -57,Cheng,Yuan,Chinese (PRC),Male -58,Lixue,Lu,Chinese (PRC),Female -59,Hu,Zhuang,Chinese (PRC),Male -60,Bo,Tan,Chinese (PRC),Male -61,Cai,Xu,Chinese (PRC),Female -62,Cai,Xue,Chinese (PRC),Female -63,Shen,Hu,Chinese (PRC),Male -64,Feng,Hu,Chinese (PRC),Male -65,Yeow,Yin,Chinese (PRC),Male -66,Qiang,Liao,Chinese (PRC),Male -67,Lian,Xu,Chinese (PRC),Female -68,Yun,Yu,Chinese (PRC),Female -69,Cai,Hou,Chinese (PRC),Female -70,Qian,Lin,Chinese (PRC),Female -71,Byung-Hyun,Nam,Korean,Male -72,Hyun-Ok,Rim,Korean,Female -73,Seung-In,Seong,Korean,Male -74,Byung-Hyun,Min,Korean,Male -75,Se-Kwon,Park,Korean,Male -76,Hyun-Ok,Koh,Korean,Female -77,Eun-Kyung,Pak,Korean,Female -78,Hwa-Young,Hah,Korean,Female -79,Jae-Joon,Yu,Korean,Male -80,Hyun-Ki,Jo,Korean,Male -81,Duck-Hwan,No,Korean,Male -82,He-Ran,Cho,Korean,Female -83,Ji-min,Bai,Korean,Female -84,Mee-Kyong,Koh,Korean,Female -85,Jin-Kyong,Pak,Korean,Female -86,Dae-Jong,Min,Korean,Male -87,Hyun-Ki,Ha,Korean,Male -88,Ae-Cha,Ra,Korean,Female -89,Choon-Hee,Sohn,Korean,Female -90,Dae-Shik,Yoon,Korean,Male -91,Young-Tae,Jeong,Korean,Male -92,Jae-Gon,Han,Korean,Male -93,Hyo-Sonn,Joo,Korean,Female -94,Hee-Young,Whang,Korean,Female -95,Hyun-Sik,Jin,Korean,Male -96,Hak-Kun,Jo,Korean,Male -97,Hee-Young,Yun,Korean,Female -98,Hyun-Ki,Cho,Korean,Male -99,Ye-eun,Bai,Korean,Female -100,Mi-Cha,Choe,Korean,Female -101,Kang-Hee,Gwak,Korean,Male -102,Tae-Uk,Seo,Korean,Male -103,Jae-Hwa,Yu,Korean,Male -104,Chul-Soon,Jeon,Korean,Male -105,Gi-Suk,Han,Korean,Male -106,Aki,Nakayama,Japanese,Female -107,Ren,Kojima,Japanese,Male -108,Yuudai,Fujii,Japanese,Male -109,Botan,Fujiwara,Japanese,Male -110,Ayaka,Matsumoto,Japanese,Female -111,Ryouta,Matsumoto,Japanese,Male -112,Chikamasa,Shibata,Japanese,Male -113,Yuudai,Hayashi,Japanese,Male -114,Haruka,Ishida,Japanese,Female -115,Botan,Kobayashi,Japanese,Male -116,Miki,Hayashi,Japanese,Female -117,Nanami,Suzuki,Japanese,Female -118,Rina,Tanaka,Japanese,Female -119,Ren,Takahashi,Japanese,Male -120,Ayaka,Hayakawa,Japanese,Female -121,Nana,Sakai,Japanese,Female -122,Miu,Sasaki,Japanese,Female -123,Natsumi,Sato,Japanese,Female -124,Ayano,Okamoto,Japanese,Female -125,Den,Nakayama,Japanese,Male -126,Miki,Ono,Japanese,Female -127,Kazuya,Matsumoto,Japanese,Male -128,Naoto,Sakai,Japanese,Male -129,Ayame,Nakayama,Japanese,Female -130,Daigoro,Fukuda,Japanese,Male -131,Mizuki,Yamamoto,Japanese,Female -132,Yuuka,Kojima,Japanese,Female -133,Riku,Yasuda,Japanese,Male -134,Kazuya,Tanaka,Japanese,Male -135,Daisuke,Ota,Japanese,Male -136,Chinatsu,Sakamoto,Japanese,Female -137,Yuuka,Ito,Japanese,Female -138,Akiko,Wada,Japanese,Female -139,Daitaro,Hayakawa,Japanese,Male -140,Kaito,Maruyama,Japanese,Male -141,Bartosz,Kozowski,Polish,Male -142,Dariusz,Kozowski,Polish,Male -143,Dominik,Dudek,Polish,Male -144,Ludmilla,Szczepanska,Polish,Female -145,Ludmilla,Maciejewska,Polish,Female -146,Fryderyk,Wieczorek,Polish,Male -147,Patryk,Szczepanski,Polish,Male -148,Aleksandra,Kwiatkowska,Polish,Female -149,Karolina,Krawczyk,Polish,Female -150,Korneliusz,Maciejewski,Polish,Male -151,Gabrysz,Pawlak,Polish,Male -152,Wisia,Kozowska,Polish,Female -153,Wiktor,Wisniewski,Polish,Male -154,Ludwik,Wisniewski,Polish,Male -155,Gabrysz,Grabowski,Polish,Male -156,Grzegorz,Wysocki,Polish,Male -157,Luiza,Nowakowska,Polish,Female -158,Izabela,Zawadzka,Polish,Female -159,Klaudia,Piotrowska,Polish,Female -160,Anna,Kwiatkowska,Polish,Female -161,Jakub,Krol,Polish,Male -162,Ksawery,Kalinowska,Polish,Male -163,Mieczysaw,Wieczorek,Polish,Male -164,Ksenia,Pawlak,Polish,Female -165,Dominika,Maciejewska,Polish,Female -166,Hajnrich,Sawicki,Polish,Male -167,Jakob,Szczepanski,Polish,Male -168,Roman,Wisniewski,Polish,Male -169,Patryk,Gorski,Polish,Male -170,Lidia,Duda,Polish,Female -171,Adrianna,Sokolowska,Polish,Female -172,Tucja,Olszewska,Polish,Female -173,Jozef,Walczak,Polish,Male -174,Mikolaj,Zielinski,Polish,Male -175,Karolina,Piotrowska,Polish,Female -176,Bianca,Lo Duca,Italian,Female -177,Diego,Rossi,Italian,Male -178,Enrico,Ricci,Italian,Male -179,Alice,Pugliesi,Italian,Female -180,Enrico,Marchesi,Italian,Male -181,Michele,Lucchese,Italian,Male -182,Luca,Trentino,Italian,Male -183,Paola,Gallo,Italian,Female -184,Marta,Pisani,Italian,Female -185,Raffaele,Napolitani,Italian,Male -186,Simone,Romani,Italian,Male -187,Lina,Longo,Italian,Female -188,Caterina,Lombardi,Italian,Female -189,Marta,Lombardi,Italian,Female -190,Vittoria,De Luca,Italian,Female -191,Anna,Romano,Italian,Female -192,Aldo,Genovesi,Italian,Male -193,Sergio,Iadanza,Italian,Male -194,Dino,Moretti,Italian,Male -195,Nicola,Milano,Italian,Male -196,Marta,Lucchese,Italian,Female -197,Federica,Lucchese,Italian,Female -198,Mia,Padovesi,Italian,Female -199,Massimo,Udinesi,Italian,Male -200,Mattia,Mariano,Italian,Male -201,Antonio,Sagese,Italian,Male -202,Gabriele,Sagese,Italian,Male -203,Caterina,Pinto,Italian,Female -204,Gino,Folliero,Italian,Male -205,Nicolo,Columbo,Italian,Male -206,Francesca,Mazzanti,Italian,Female -207,Sergio,Cattaneo,Italian,Male -208,Luca,Esposito,Italian,Male -209,Jacopo,Beneventi,Italian,Male -210,Sabrina,Sal,Italian,Female -211,Victor,Valmor,Portuguese (Portugal),Male -212,Vitor,Vila Nova,Portuguese (Portugal),Male -213,Mariana,Pereira,Portuguese (Portugal),Female -214,Pedro,Correa,Portuguese (Portugal),Male -215,Diego,Barbosa,Portuguese (Portugal),Male -216,Joao,Pena,Portuguese (Portugal),Male -217,Igor,Lima,Portuguese (Portugal),Male -218,Emillia,Ferreira,Portuguese (Portugal),Female -219,Catalina,Neves,Portuguese (Portugal),Female -220,Leonor,Torres,Portuguese (Portugal),Female -221,Tiago,Alves,Portuguese (Portugal),Male -222,Francisco,Cavalcanti,Portuguese (Portugal),Male -223,Micaela,Lopes,Portuguese (Portugal),Female -224,Elena,Mota,Portuguese (Portugal),Female -225,Avelina,Oliveira,Portuguese (Portugal),Female -226,Sara,Pena,Portuguese (Portugal),Female -227,Camila,Botas,Portuguese (Portugal),Female -228,Paulo,Barros,Portuguese (Portugal),Male -229,Micaela,Cardoso,Portuguese (Portugal),Female -230,Filipa,Pena,Portuguese (Portugal),Female -231,Hugo,Barbosa,Portuguese (Portugal),Male -232,Elena,Rocha,Portuguese (Portugal),Female -233,Livia,Mota,Portuguese (Portugal),Female -234,Maria,Fernandes,Portuguese (Portugal),Female -235,Margarida,Teixeira,Portuguese (Portugal),Female -236,Matilde,Correla,Portuguese (Portugal),Female -237,Hugo,Carvalho,Portuguese (Portugal),Male -238,Kai,Rocha,Portuguese (Portugal),Male -239,Martim,Cunha,Portuguese (Portugal),Male -240,Lara,Rodrigues,Portuguese (Portugal),Female -241,Carolina,Ferreira,Portuguese (Portugal),Female -242,Diogo,Correa,Portuguese (Portugal),Male -243,Andre,Correa,Portuguese (Portugal),Male -244,Gabriel,Alves,Portuguese (Portugal),Male -245,Rafael,Rocha,Portuguese (Portugal),Male -246,Victor,Avdeev,Russian,Male -247,Pavel,Ivanov,Russian,Male -248,Polina,Kornilova,Russian,Female -249,Yegor,Kuznetsov,Russian,Male -250,Viktoriya,Vasilyeva,Russian,Female -251,Kirill,Zubarev,Russian,Male -252,Dmitry,Avdeev,Russian,Male -253,Mariya,Medvedeva,Russian,Female -254,Viktoria,Kornilova,Russian,Female -255,Mihail,Degtyarev,Russian,Male -256,Anna,Popov,Russian,Female -257,Sergei,Sergeyev,Russian,Male -258,Galina,Voronina,Russian,Female -259,Kateryna,Larionova,Russian,Female -260,Yelizaveta,Sorokina,Russian,Female -261,Ilya,Voronkov,Russian,Male -262,Alex,Ignatyev,Russian,Male -263,Grigory,Alexeeva,Russian,Male -264,Alexandra,Maslova,Russian,Female -265,Oksana,Seleznyova,Russian,Female -266,Anna,Shubina,Russian,Female -267,Gregory,Kuznetsov,Russian,Male -268,Dariya,Voronina,Russian,Female -269,Alexandra,Seleznyova,Russian,Female -270,Boris,Evseyev,Russian,Male -271,Polina,Nikolaeva,Russian,Female -272,Olga,Kuzmina,Russian,Female -273,Sophia,Larionova,Russian,Female -274,Alisa,Sazonova,Russian,Female -275,Yelizaveta,Alexandrova,Russian,Female -276,Ilya,Seleznyov,Russian,Male -277,Irina,Sorokina,Russian,Female -278,Svetlana,Kuzmina,Russian,Female -279,Nikolay,Artemiev,Russian,Male -280,Lilith,Pokrovskaya,Russian,Female -281,Remzi,Dogan,Turkish,Male -282,Osman,Aydan,Turkish,Male -283,Huseyin,Elmas,Turkish,Male -284,Adnan,Kalcu,Turkish,Male -285,Dogu,Guler,Turkish,Male -286,Aalker,Dikmen,Turkish,Male -287,Ugur,Gunes,Turkish,Male -288,Fatma,Ozkan,Turkish,Female -289,Veli,Dogan,Turkish,Male -290,Suleyman,Koc,Turkish,Male -291,ozlem,Basturk,Turkish,Female -292,Erdal,Cetinkaya,Turkish,Male -293,Nalan,Polat,Turkish,Female -294,Veli,Basturk,Turkish,Male -295,Serhat,Tunc,Turkish,Male -296,sahin,Acar,Turkish,Male -297,Cetin,Dikmen,Turkish,Male -298,Tulay,Ozbekut,Turkish,Female -299,Vedat,Akbulut,Turkish,Male -300,Berkay,Basturk,Turkish,Male -301,Irmak,Ozkan,Turkish,Female -302,Huseyin,Gunes,Turkish,Male -303,Melih,Guler,Turkish,Male -304,Doruk,Ozbek,Turkish,Male -305,Utku,Sahin,Turkish,Male -306,Can,Tekin,Turkish,Male -307,Tamer,Ozgur,Turkish,Male -308,Guler,Avcar,Turkish,Female -309,Cagla,Saglam,Turkish,Female -310,Tulin,Keskin,Turkish,Female -311,Nilufer,Ozden,Turkish,Female -312,Oya,Dikmen,Turkish,Female -313,Lale,Keskin,Turkish,Female -314,Jale,Dikmen,Turkish,Female -315,Eda,Cetinkaya,Turkish,Female -316,Antonio,Madera,Spanish (Spain),Male -317,Nerea,Flores,Spanish (Spain),Female -318,Melani,Chavez,Spanish (Spain),Female -319,Samuel,Parra,Spanish (Spain),Male -320,Adrian,Feliciano,Spanish (Spain),Male -321,Lucia,Sevilla,Spanish (Spain),Female -322,Nerea,Arellano,Spanish (Spain),Female -323,Izan,Parra,Spanish (Spain),Male -324,Marta,Martinez,Spanish (Spain),Female -325,Iker,Alcala,Spanish (Spain),Male -326,Ruben,Apodaca,Spanish (Spain),Male -327,Julia,Robledo,Spanish (Spain),Female -328,Cecilia,Cortes,Spanish (Spain),Female -329,Miguel,Ontiveros,Spanish (Spain),Male -330,Guillermo,Melgar,Spanish (Spain),Male -331,Nuria,Rios,Spanish (Spain),Female -332,Nerea,Miranda,Spanish (Spain),Female -333,Isabelina,Gracia,Spanish (Spain),Female -334,Lourdes,Chavez,Spanish (Spain),Female -335,Nicolas,Ontiveros,Spanish (Spain),Male -336,Amal,Pelayo,Spanish (Spain),Female -337,Carolina,Duarte,Spanish (Spain),Female -338,Ela,Melgar,Spanish (Spain),Female -339,Fernando,Apodaca,Spanish (Spain),Male -340,Gonzalo,Nino,Spanish (Spain),Male -341,Manuel,Morales,Spanish (Spain),Male -342,Jorge,Nevarez,Spanish (Spain),Male -343,Manuel,Arteaga,Spanish (Spain),Male -344,Laura,Valentin,Spanish (Spain),Female -345,Adrian,Morales,Spanish (Spain),Male -346,Ruben,Tijerina,Spanish (Spain),Male -347,Marc,Quintanilla,Spanish (Spain),Male -348,Sofia,Barrera,Spanish (Spain),Female -349,Melani,Maldonado,Spanish (Spain),Female -350,Loris,Gracia,Spanish (Spain),Female -351,Janos,Szigethy,Hungarian,Male -352,Laszlo,Kardos,Hungarian,Male -353,Amalia,Foldy,Hungarian,Female -354,Julianna,Szarka,Hungarian,Female -355,Katinka,Makay,Hungarian,Female -356,Sandor,Horvath,Hungarian,Male -357,Kolos,Ujvary,Hungarian,Male -358,David,Lovas,Hungarian,Male -359,Lazar,Rozsa,Hungarian,Male -360,Kolos,Nemeth,Hungarian,Male -361,Marianna,Farkas,Hungarian,Female -362,Szabolcs,Erdei,Hungarian,Male -363,Izabella,Farago,Hungarian,Female -364,Angyalka,Sebok,Hungarian,Female -365,Daniel,Gyarmati,Hungarian,Male -366,Katinka,Horvath,Hungarian,Female -367,David,Havasy,Hungarian,Male -368,Jucika,Farkas,Hungarian,Female -369,Julianna,Molnar,Hungarian,Female -370,Endre,Gero,Hungarian,Male -371,Eva,Szarka,Hungarian,Female -372,Lorand,Soltesz,Hungarian,Male -373,Barbara,Gyenes,Hungarian,Female -374,Szabolcs,Soross,Hungarian,Male -375,Marianna,Varga,Hungarian,Female -376,Peter,Bolla,Hungarian,Male -377,Sebo,Varga,Hungarian,Male -378,Fanni,Foldy,Hungarian,Female -379,Odon,Gyarmati,Hungarian,Male -380,Katarina,Budai,Hungarian,Female -381,Rajmund,Kontz,Hungarian,Male -382,Sebo,Danko,Hungarian,Male -383,Izabella,Torma,Hungarian,Female -384,Istvan,Szabo,Hungarian,Male -385,Barbara,Szollossy,Hungarian,Female -386,Philippos,Spanos,Greek,Male -387,Philippos,Mula,Greek,Male -388,Philipp,Kairis,Greek,Male -389,Tonia,Barba,Greek,Female -390,Denise,Ballis,Greek,Female -391,Philippe,Kotas,Greek,Male -392,Alexis,Christos,Greek,Female -393,Irini,Dukas,Greek,Female -394,Rita,Anania,Greek,Female -395,Iris,Baris,Greek,Female -396,Katharyn,Anagnos,Greek,Female -397,Rhea,Doukas,Greek,Female -398,Theodore,Galanos,Greek,Male -399,Jaison,Georgas,Greek,Male -400,Eleni,Barlas,Greek,Female -401,Kate,Andris,Greek,Female -402,Iason,Nicolaou,Greek,Male -403,Eleftheria,Chontos,Greek,Female -404,Philippos,Nicolaou,Greek,Male -405,Alexander,Ganis,Greek,Male -406,Iason,Lampros,Greek,Male -407,Myron,Lambros,Greek,Male -408,Christos,Gekas,Greek,Male -409,Klaasr,Fotopoulos,Greek,Male -410,Eleftheria,Chronis,Greek,Female -411,Tonia,Chaconas,Greek,Female -412,Alexis,Christopoulos,Greek,Female -413,Katerina,Barberis,Greek,Female -414,Theodore,Lampros,Greek,Male -415,Philippos,Galanos,Greek,Male -416,Georgia,Anastos,Greek,Female -417,Eleftheria,Andreas,Greek,Female -418,Iason,Economos,Greek,Male -419,Lydia,Angelopoulos,Greek,Female -420,Stefanos,Georgas,Greek,Male -421,Gustav,Holst,Danish,Male -422,Hannah,Sorensen,Danish,Female -423,Malthe,Bak,Danish,Male -424,Anette,Jeppesen,Danish,Female -425,Anna,Olsen,Danish,Female -426,Nicolaj,Kruse,Danish,Male -427,Christiane,Andersen,Danish,Female -428,William,Hermansen,Danish,Male -429,Gustav,Bruun,Danish,Male -430,Elena,Lauridsen,Danish,Female -431,Bitte,Jespersen,Danish,Female -432,Mathias,Lind,Danish,Male -433,Magnus,Brandt,Danish,Male -434,Birgitte,Olsen,Danish,Female -435,Oscar,Kjeldsen,Danish,Male -436,Bianca,Thomsen,Danish,Female -437,Jasmin,Kjaer,Danish,Female -438,Emil,Bertelsen,Danish,Male -439,Helen,Kjaer,Danish,Female -440,Magnus,Christoffersen,Danish,Male -441,Elise,Nissen,Danish,Female -442,Elise,Nielsen,Danish,Female -443,Gustav,Schou,Danish,Male -444,Birgitte,Madsen,Danish,Female -445,Hansigne,Moller,Danish,Female -446,Markus,Lind,Danish,Male -447,Nicklas,Gregersen,Danish,Male -448,Cristoffer,Thorsen,Danish,Male -449,Valdemar,Danielsen,Danish,Male -450,Elisabeth,Ostergaard,Danish,Female -451,Anders,Klausen,Danish,Male -452,Nicklas,Paulsen,Danish,Male -453,Cristoffer,Toft,Danish,Male -454,Malthe,Nygaard,Danish,Male -455,Birte,Christensen,Danish,Female -456,Ingrid,Ehrle,German (Germany),Female -457,Hans,Graf,German (Germany),Male -458,Helga,Ehrle,German (Germany),Female -459,Maximilian,Wolfe,German (Germany),Male -460,Berend,Anhalt,German (Germany),Male -461,Giselle,Maier,German (Germany),Female -462,Julia,Wolfe,German (Germany),Female -463,Charles,Graf,German (Germany),Male -464,Claus,Wolf,German (Germany),Male -465,Leon,Lange,German (Germany),Male -466,Kasper,Carstens ,German (Germany),Male -467,Emily,Richter,German (Germany),Female -468,Gebhard,Muller,German (Germany),Male -469,Ulla,Fleer,German (Germany),Female -470,Killian,Hartmann,German (Germany),Male -471,Klaus,Kruger,German (Germany),Male -472,Achim,Klein,German (Germany),Male -473,Nele,Faber,German (Germany),Female -474,Brigitte,Endemann,German (Germany),Female -475,Ingrid,Schulz,German (Germany),Female -476,Berend,Cranz,German (Germany),Male -477,Killian,Kramer,German (Germany),Male -478,Helga,Otto,German (Germany),Female -479,Giselle,Martin,German (Germany),Female -480,Jan,Meyer,German (Germany),Male -481,Niklaus,Lange,German (Germany),Male -482,Sofia,Albrecht,German (Germany),Female -483,Hans,Otto,German (Germany),Male -484,Emily,Maier,German (Germany),Female -485,Jana,Kruger,German (Germany),Female -486,Lina,Krause,German (Germany),Female -487,Charles,Braun,German (Germany),Male -488,Klaus,Braun,German (Germany),Male -489,Conrad,Wagner,German (Germany),Male -490,Franz,Albrecht,German (Germany),Male -491,Malorie,Ratte,French,Female -492,David,Doyon,French,Male -493,Alexandre ,Barjavel,French,Male -494,Marcel ,Bellefeuille,French,Male -495,Genevieve ,Panetier,French,Female -496,Nichole ,Rocher,French,Female -497,Philippe,Sorel,French,Male -498,Blaise,Quenneville,French,Male -499,Raymond,Saucier,French,Male -500,Serge,Rivard,French,Male -501,Simone ,Bosse,French,Female -502,Genevieve,Arcouet,French,Female -503,Coralie,Panetier,French,Female -504,Fabrice,Lacroix,French,Male -505,Luc,Pelletier,French,Male -506,Noel,Belisle,French,Male -507,David,Margand,French,Male -508,Gerard,Beaulieu,French,Male -509,Christien,Margand,French,Male -510,Astrid,Langelier,French,Female -511,Lorie ,Bosse,French,Female -512,Simone ,Berthelette,French,Female -513,Lorraine,Labrecque,French,Female -514,Helene,Berthelette,French,Female -515,Serge,L'Angelier,French,Male -516,Coralie,Guibord,French,Female -517,Annette,Duperre,French,Female -518,Helene,Labrecque,French,Female -519,Alain,Levesque,French,Male -520,Luc,Coupart,French,Male -521,Francine ,Dumoulin,French,Female -522,Christian,Rochefort,French,Male -523,Christien,Pellerin,French,Male -524,Rachelle ,Guimond,French,Female -525,Theodore,Bonsaint,French,Male -526,Kirby,Dulaney,English (US),Male -527,Rachel,Fitzgerald,English (US),Female -528,Mack,Caban,English (US),Male -529,Madelyn,Rios,English (US),Female -530,Jarrod,Ferrari,English (US),Male -531,Nettie,Moss,English (US),Female -532,Erwin,McGowan,English (US),Male -533,Corina,Jarvis,English (US),Female -534,Lynnette,Nieves,English (US),Female -535,Donnell,Liddell,English (US),Male -536,Debbie,Waller,English (US),Female -537,Lucia,Lamb,English (US),Female -538,Emilio,Chiu,English (US),Male -539,Valerie,Cruz,English (US),Female -540,Suzette,Marquez,English (US),Female -541,Beatrice,Baird,English (US),Female -542,Ernestine,Beck,English (US),Female -543,Damian,Fusco,English (US),Male -544,Yvonne,Doyle,English (US),Female -545,Rufus,McLeod,English (US),Male -546,Colleen,Lawrence,English (US),Female -547,Kristopher,Seidel,English (US),Male -548,Joe,Mota,English (US),Male -549,Alison,Castro,English (US),Female -550,Toby,Lattimore,English (US),Male -551,Mindy,Mills,English (US),Female -552,Tracy,Lunn,English (US),Male -553,Isabella,French,English (US),Female -554,Pat,Cross,English (US),Female -555,Araceli,Tate,English (US),Female -556,Jacquelyn,Barrett,English (US),Female -557,Angelita,Duke,English (US),Female -558,Anton,Tallent,English (US),Male -559,John,Casanova,English (US),Male -560,Mary,Kent,English (US),Female -561,Flossie,Cantu,English (US),Female -562,Rickie,Donley,English (US),Male -563,Mabel,Tanner,English (US),Female -564,Russell,Bevins,English (US),Male -565,Zachary,Bey,English (US),Male -566,Marcia,Luna,English (US),Female -567,Hester,Powers,English (US),Female -568,Dallas,Gonsalves,English (US),Male -569,Duane,Ahmad,English (US),Male -570,Olivia,Herring,English (US),Female -571,Angelina,Bowers,English (US),Female -572,Brooke,Lamb,English (US),Female -573,Al,Dibble,English (US),Male -574,Nick,Steadman,English (US),Male -575,Ester,Herrera,English (US),Female -576,Deena,Quinn,English (US),Female -577,Drew,Wilke,English (US),Male -578,Max,Earnest,English (US),Male -579,Dennis,Baumgardner,English (US),Male -580,Jillian,Kemp,English (US),Female -581,Austin,Slattery,English (US),Male -582,Warren,Lock,English (US),Male -583,Emilio,Philips,English (US),Male -584,Adeline,Shepard,English (US),Female -585,Anne,Alvarado,English (US),Female -586,Marcel,Reinhart,English (US),Male -587,Elvin,Ballinger,English (US),Male -588,Ignacio,McClain,English (US),Male -589,Stefanie,Douglas,English (US),Female -590,Herbert,Gooding,English (US),Male -591,Twila,McDaniel,English (US),Female -592,Christopher,Pearl,English (US),Male -593,April,Benson,English (US),Female -594,Lana,Dejesus,English (US),Female -595,Leona,Beasley,English (US),Female -596,Ivy,Bowman,English (US),Female -597,Marylou,Cleveland,English (US),Female -598,Madeline,Koch,English (US),Female -599,Florine,Preston,English (US),Female -600,Kellie,Downs,English (US),Female -601,Karina,Craig,English (US),Female -602,Susanne,Finley,English (US),Female -603,Dominic,Wickham,English (US),Male -604,Amber,Hodges,English (US),Female -605,Cristina,Huff,English (US),Female -606,Jaime,Bartlett,English (US),Female -607,Pedro,Fulkerson,English (US),Male -608,Winston,Christmas,English (US),Male -609,Hannah,Morrison,English (US),Female -610,Sophie,Navarro,English (US),Female -611,Junior,Hadden,English (US),Male -612,Florence,Love,English (US),Female -613,Marcia,Pitts,English (US),Female -614,Estella,Campbell,English (US),Female -615,Ronda,Navarro,English (US),Female -616,Carmela,Wilson,English (US),Female -617,Tisha,McLaughlin,English (US),Female -618,Elton,Nutter,English (US),Male -619,Shanna,Hampton,English (US),Female -620,Elliot,Fries,English (US),Male -621,Anton,Thiel,English (US),Male -622,Caleb,Leake,English (US),Male -623,Eddie,Withrow,English (US),Male -624,Aida,Mosley,English (US),Female -625,Spencer,Laflamme,English (US),Male -626,Thaddeus,Gower,English (US),Male -627,Doug,Theriault,English (US),Male -628,Diane,Hebert,English (US),Female -629,Dianne,Reed,English (US),Female -630,Ruth,Landry,English (US),Female -631,Laverne,Quinn,English (US),Female -632,Joann,Perry,English (US),Female -633,Carl,Giron,English (US),Male -634,Sheryl,Gibson,English (US),Female -635,Shawn,Rupert,English (US),Male -636,Tracey,Shepard,English (US),Female -637,Penelope,Goodwin,English (US),Female -638,Richard,Ashmore,English (US),Male -639,Lucile,Branch,English (US),Female -640,Selma,Fleming,English (US),Female -641,Dorothy,Walls,English (US),Female -642,Dorothy,Hoover,English (US),Female -643,Harriett,Pitts,English (US),Female -644,Beverly,Crane,English (US),Female -645,Gale,Riggs,English (US),Female -646,Ruth,Baker,English (US),Female -647,Aimee,Page,English (US),Female -648,Julius,Hyland,English (US),Male -649,Kaye,Becker,English (US),Female -650,Josie,Puckett,English (US),Female -651,Beverly,Bradford,English (US),Female -652,Claudine,Hurley,English (US),Female -653,William,Mouton,English (US),Male -654,Lucille,Buckner,English (US),Female -655,Gerry,Lieberman,English (US),Male -656,Aileen,Delaney,English (US),Female -657,Ina,Jarvis,English (US),Female -658,Angeline,Hammond,English (US),Female -659,Debora,Huber,English (US),Female -660,Kelly,Aguirre,English (US),Female -661,Hollie,Hahn,English (US),Female -662,Mark,Barba,English (US),Male -663,Muriel,Dillon,English (US),Female -664,Nora,Savage,English (US),Female -665,Garrett,Dortch,English (US),Male -666,Jeannie,Russell,English (US),Female -667,Tracie,Bradford,English (US),Female -668,Stacey,Leonard,English (US),Female -669,Chrystal,Welch,English (US),Female -670,Raquel,Mayer,English (US),Female -671,Benjamin,Orton,English (US),Male -672,Maxine,McIntyre,English (US),Female -673,Melva,Mueller,English (US),Female -674,Glenda,Graves,English (US),Female -675,Carlton,Odum,English (US),Male -676,Christal,Nieves,English (US),Female -677,Jessie,Lindsey,English (US),Female -678,Douglas,Book,English (US),Male -679,Justine,Cobb,English (US),Female -680,Chandra,Puckett,English (US),Female -681,Kenya,Guy,English (US),Female -682,Charlotte,Miles,English (US),Female -683,Jill,Kim,English (US),Female -684,Queen,Colon,English (US),Female -685,Adriana,Davis,English (US),Female -686,Juana,Sosa,English (US),Female -687,Berta,Rowland,English (US),Female -688,Kenya,Maldonado,English (US),Female -689,Teresa,Griffith,English (US),Female -690,Cassandra,Gamble,English (US),Female -691,Gertrude,Bridges,English (US),Female -692,Joanne,Solomon,English (US),Female -693,Jami,Barber,English (US),Female -694,Ernie,Angulo,English (US),Male -695,Olive,Phillips,English (US),Female -696,Junior,Segal,English (US),Male -697,Russel,Mallard,English (US),Male -698,Clement,Beaudry,English (US),Male -699,Gwendolyn,Soto,English (US),Female -700,Dolly,Curtis,English (US),Female -701,Pearlie,Guzman,English (US),Female -702,Wilson,Chitwood,English (US),Male -703,Isabella,Wagner,English (US),Female -704,Cassandra,Perez,English (US),Female -705,Sara,Cash,English (US),Female -706,Vanessa,Fox,English (US),Female -707,Lynn,Stout,English (US),Female -708,Irvin,Ketchum,English (US),Male -709,Jerri,Perez,English (US),Female -710,Jami,Stewart,English (US),Female -711,Wendi,McCray,English (US),Female -712,Nathan,Robey,English (US),Male -713,Sammie,Theriault,English (US),Male -714,Mallory,Zamora,English (US),Female -715,Adrian,Ferguson,English (US),Female -716,Gilbert,Miller,English (US),Male -717,Corrine,Shaw,English (US),Female -718,Brad,Burbank,English (US),Male -719,Marissa,Shindell,English (US),Female -720,Leonor,Stout,English (US),Female -721,Ana,McLeod,English (US),Female -722,Sheila,Neal,English (US),Female -723,Freida,Case,English (US),Female -724,Jacquelyn,Calhoun,English (US),Female -725,Joni,Maynard,English (US),Female -726,Coleen,McCarty,English (US),Female -727,Otto,Roussel,English (US),Male -728,Brenda,Lawson,English (US),Female -729,Leroy,Amundson,English (US),Male -730,Neva,Chavez,English (US),Female -731,Roxanne,Barber,English (US),Female -732,Bobbi,Gilmore,English (US),Female -733,Dwight,Phifer,English (US),Male -734,Morgan,Dodds,English (US),Male -735,Julie,Conway,English (US),Female -736,Tabatha,Suarez,English (US),Female -737,Carolina,Berger,English (US),Female -738,Esteban,McCormick,English (US),Male -739,Jan,Shields,English (US),Female -740,Enrique,Rigby,English (US),Male -741,Thurman,Mota,English (US),Male -742,Carly,Henry,English (US),Female -743,Stefan,Cage,English (US),Male -744,Cleo,Morse,English (US),Female -745,Christine,Murray,English (US),Female -746,Brianna,Grant,English (US),Female -747,Mildred,Salazar,English (US),Female -748,Mason,Wortham,English (US),Male -749,Lakeisha,Bowen,English (US),Female -750,Harvey,Jackman,English (US),Male -751,Winston,Breland,English (US),Male -752,Julianne,O'Donnell,English (US),Female -753,Janell,Weaver,English (US),Female -754,Pam,Stout,English (US),Female -755,Gwendolyn,Griffith,English (US),Female -756,Kari,Burns,English (US),Female -757,Crystal,Tate,English (US),Female -758,Terrie,Fowler,English (US),Female -759,Anton,Skeen,English (US),Male -760,Emily,Payne,English (US),Female -761,Debra,Young,English (US),Female -762,Cecelia,Hays,English (US),Female -763,Sondra,Case,English (US),Female -764,Nikki,Hurst,English (US),Female -765,Tisha,Wright,English (US),Female -766,May,Finley,English (US),Female -767,Leona,Merritt,English (US),Female -768,Jayne,Carr,English (US),Female -769,Noah,McNeil,English (US),Male -770,Ernesto,Ramsay,English (US),Male -771,John,Roy,English (US),Female -772,Frances,McLaughlin,English (US),Female -773,Jenny,Prince,English (US),Female -774,Bill,Brigham,English (US),Male -775,Susanne,Bond,English (US),Female -776,Virgie,Goodwin,English (US),Female -777,Rene,Pittman,English (US),Female -778,Katherine,Richmond,English (US),Female -779,Felicia,Mack,English (US),Female -780,Austin,Bracken,English (US),Male -781,Rudy,Rohde,English (US),Male -782,Rosalind,Walton,English (US),Female -783,Sonja,Foreman,English (US),Female -784,Michel,Barela,English (US),Male -785,Guillermo,McCarty,English (US),Male -786,Angelique,Kline,English (US),Female -787,Rosetta,Deleon,English (US),Female -788,Bessie,Mason,English (US),Female -789,Jan,Mathis,English (US),Female -790,Ebony,Dodson,English (US),Female -791,Elvia,Matthews,English (US),Female -792,Ramona,Stone,English (US),Female -793,Louise,Morrow,English (US),Female -794,Francis,Carr,English (US),Female -795,Kitty,Lott,English (US),Female -796,Lynette,Oliver,English (US),Female -797,Hubert,McClure,English (US),Male -798,Noel,Stillman,English (US),Male -799,Kathleen,Jimenez,English (US),Female -800,Ty,Withrow,English (US),Male -801,Jeri,Hunter,English (US),Female -802,Bruno,Pender,English (US),Male -803,Chester,Munger,English (US),Male -804,Tammy,Peck,English (US),Female -805,Omar,Chitwood,English (US),Male -806,Edward,Theriot,English (US),Male -807,Jesus,Macleod,English (US),Male -808,Virgie,Casey,English (US),Female -809,Patsy,Butler,English (US),Female -810,Nadine,Salas,English (US),Female -811,Floyd,Madera,English (US),Male -812,Leola,Baldwin,English (US),Female -813,Melissa,Newman,English (US),Female -814,Patrice,Cantrell,English (US),Female -815,Gerardo,Dabbs,English (US),Male -816,Isabel,England,English (US),Female -817,Kara,Nash,English (US),Female -818,Muriel,Young,English (US),Female -819,Rudy,Rivard,English (US),Male -820,Cliff,Gault,English (US),Male -821,Callie,Weiss,English (US),Female -822,Terri,Crane,English (US),Female -823,Ester,Barber,English (US),Female -824,Blake,Hoag,English (US),Male -825,James,Mann,English (US),Female -826,Vance,Marra,English (US),Male -827,Nadine,Huber,English (US),Female -828,Lorene,Raymond,English (US),Female -829,Julianne,Hubbard,English (US),Female -830,Lenore,Meyers,English (US),Female -831,Michel,Thrash,English (US),Male -832,Christy,Santos,English (US),Female -833,Penny,Hart,English (US),Female -834,Neil,McKenzie,English (US),Male -835,Demetrius,Homer,English (US),Male -836,Guadalupe,Ross,English (US),Female -837,Lamont,Quinlan,English (US),Male -838,Hubert,Lanning,English (US),Male -839,Brock,Nestor,English (US),Male -840,Laurie,Thornton,English (US),Female -841,Harriet,Doyle,English (UK),Female -842,Aaliyah,Faulkner,English (UK),Female -843,Isaac,Ingram,English (UK),Male -844,Harriet,Marsh,English (UK),Female -845,Tegan,Peacock,English (UK),Female -846,Morgan,Adams,English (UK),Male -847,Holly,Curtis,English (UK),Female -848,Leo,Lamb,English (UK),Male -849,Samantha,Wall,English (UK),Female -850,Kiera,Stevens,English (UK),Female -851,Lucas,Adams,English (UK),Male -852,Billy,Donnelly,English (UK),Male -853,Naomi,Williamson,English (UK),Female -854,Libby,Richards,English (UK),Female -855,Oscar,Lucas,English (UK),Male -856,Anthony,Kirby,English (UK),Male -857,Hollie,Wall,English (UK),Female -858,Kian,Campbell,English (UK),Male -859,Rosie,Richards,English (UK),Female -860,Patrick,White,English (UK),Male -861,Jodie,Moss,English (UK),Female -862,Ellie,Skinner,English (UK),Female -863,Aidan,Adams,English (UK),Male -864,Brandan,Power,English (UK),Male -865,Ellie,Storey,English (UK),Female -866,Ellen,Patterson,English (UK),Female -867,Gabriel,Power,English (UK),Male -868,Oscar,Wright,English (UK),Male -869,Sonia,Bryan,English (UK),Female -870,Mary,Sullivan,English (UK),Female -871,Rhys,Hunt,English (UK),Male -872,Sophia,Storey,English (UK),Female -873,Abigail,Reynolds,English (UK),Female -874,Cameron,White,English (UK),Male -875,Elliot,Thompson,English (UK),Male -876,Benjamin,Kosovich,English (Australia),Male -877,Dakota,Mattner,English (Australia),Female -878,Chloe,Alden,English (Australia),Female -879,Charlotte,Akeroyd,English (Australia),Female -880,Archie,Syme,English (Australia),Male -881,Rebecca,Barney,English (Australia),Female -882,Oscar,Dixon,English (Australia),Male -883,Archie,Lamble,English (Australia),Male -884,Mason,Potter,English (Australia),Male -885,Emily,Adermann,English (Australia),Female -886,Chloe,Rosenhain,English (Australia),Female -887,Sebastian,Kolios,English (Australia),Male -888,Evie,Falstein,English (Australia),Female -889,Zoe,Lack,English (Australia),Female -890,Logan,Hopkins,English (Australia),Male -891,Isabella,Prinsep,English (Australia),Female -892,Jayden,Loton,English (Australia),Male -893,Jack,Knaggs,English (Australia),Male -894,Isabella,Whittle,English (Australia),Female -895,Lily,Lack,English (Australia),Female -896,Amelie,Koch,English (Australia),Female -897,Emily,Luffman,English (Australia),Female -898,Amelia,Waring,English (Australia),Female -899,Chelsea,Hinton,English (Australia),Female -900,Sara,Battarbee,English (Australia),Female -901,Sara,Skuthorp,English (Australia),Female -902,Bianca,Cleary,English (Australia),Female -903,Ruby,Simonetti,English (Australia),Female -904,Ryan,Potter,English (Australia),Male -905,Jordan,Keynes,English (Australia),Male -906,Zane,Kolios,English (Australia),Male -907,Benjamin,Kolios,English (Australia),Male -908,Jai,Paget,English (Australia),Male -909,Oscar,Springthorpe,English (Australia),Male -910,Hayden,Onslow,English (Australia),Male -911,Gal,Steinberg,Hebrew,Female -912,Yoni,Moshe,Hebrew,Male -913,Daniel,Shimon,Hebrew,Male -914,Miki,Berkovitz,Hebrew,Male -915,Yoni,Oz,Hebrew,Male -916,Miki,Rabin,Hebrew,Male -917,Adah,Caspit,Hebrew,Female -918,Shai,Moshe,Hebrew,Male -919,Tamar,Oren,Hebrew,Female -920,Zivah,Mozes,Hebrew,Female -921,Shir,Steinberg,Hebrew,Female -922,Sarah,Goldberg,Hebrew,Female -923,Maor,Bar-Ilan,Hebrew,Male -924,Gal,Even-Chen,Hebrew,Female -925,Miki,Rosen,Hebrew,Male -926,Navah,Goldberg,Hebrew,Female -927,Eitan,Barak,Hebrew,Male -928,Navah,Simon,Hebrew,Female -929,Hadar,Silver,Hebrew,Female -930,Hadar,Barak,Hebrew,Male -931,Daniel,Bar-Ilan,Hebrew,Male -932,Miki,Gilboa,Hebrew,Male -933,Guy,Ben-Ze'ev,Hebrew,Male -934,Miki,Shapiro,Hebrew,Male -935,Aviv,Berkovitz,Hebrew,Male -936,Hadassa,Mendelssohn,Hebrew,Female -937,Itai,Barda,Hebrew,Male -938,Hadasah,Oren,Hebrew,Female -939,Avigayil,Ben-Meir,Hebrew,Female -940,Itai,Ben-Tzvi,Hebrew,Male -941,Navah,Mizrachi,Hebrew,Female -942,Hadasa,Rozen,Hebrew,Female -943,Hagai,Glober,Hebrew,Male -944,Navah,Even-Shushan,Hebrew,Female -945,Lian,Ben-Menachem,Hebrew,Female -946,Premwadee,Atitarn,Thai,Female -947,Preecha,Ornlamai,Thai,Male -948,Sunisa,Thanasukolwit,Thai,Female -949,Pongrit,Sukbunsung,Thai,Male -950,Kwanjai,Wattana,Thai,Female -951,Duanphen,Praphasirirat,Thai,Female -952,Sakchai,Maneerattana,Thai,Male -953,Chuan,Wongsawat,Thai,Male -954,Sakchai,Paowsong,Thai,Male -955,Asda,Sriwarunyu,Thai,Male -956,Promporn,Jetatikarn,Thai,Female -957,Budsaba,Praphasirirat,Thai,Female -958,Suttipong,Suppamongkon,Thai,Male -959,Manee,Montri,Thai,Female -960,Busarakham,Charoenkul,Thai,Female -961,Pongrit,Udomprecha,Thai,Male -962,Busarakham,Oraphan,Thai,Female -963,Orapan,Neungluthai,Thai,Female -964,Chantana,Kurusarttra,Thai,Female -965,Ned,Santisakul,Thai,Male -966,Nuntida,Suttikul,Thai,Female -967,Gaandaa,Charoenkul,Thai,Female -968,Chaow,Ornlamai,Thai,Male -969,Prasert,Thumying,Thai,Male -970,Hathai,Aromdee,Thai,Female -971,Areya,Charoenkul,Thai,Female -972,Nattaporn,Kunchai,Thai,Female -973,Chantana,Saowaluk,Thai,Female -974,Chompunut,Kunchai,Thai,Female -975,Pradtana,Jetjirawat,Thai,Female -976,Pramod,Puntasrima,Thai,Male -977,Korrakoj,Kitjakarn,Thai,Female -978,Amporn,Rattanaporn,Thai,Female -979,Nattaporn,Kongpaisarn,Thai,Female -980,Prawit,Suppamongkon,Thai,Male -981,Marco,Paez,Spanish (Mexico),Male -982,Cristobal,Osorio,Spanish (Mexico),Male -983,Javier,Ceballos,Spanish (Mexico),Male -984,Francisca,Madera,Spanish (Mexico),Female -985,Juan,Nevarez,Spanish (Mexico),Male -986,Gerardo,Jaramillo,Spanish (Mexico),Male -987,Diego,Burgos,Spanish (Mexico),Male -988,Gerardo,Segovia,Spanish (Mexico),Male -989,Martina,Hernandes,Spanish (Mexico),Female -990,Monica,Segovia,Spanish (Mexico),Female -991,Manuel,Pelayo,Spanish (Mexico),Male -992,Carlos,Pelayo,Spanish (Mexico),Male -993,Sofia,Paz,Spanish (Mexico),Female -994,David,Romero,Spanish (Mexico),Male -995,Amanda,Barragan,Spanish (Mexico),Female -996,Mara,Gutierrez,Spanish (Mexico),Female -997,Fatima,Tapia,Spanish (Mexico),Female -998,Jimena,Hinojosa,Spanish (Mexico),Female -999,Mara,Bahena,Spanish (Mexico),Female -1000,Monica,Madera,Spanish (Mexico),Female diff --git a/Learning Modules/Utilities/Invoke-SqlCmdOnTenantDatabases.ps1 b/Learning Modules/Utilities/Invoke-SqlCmdOnTenantDatabases.ps1 deleted file mode 100644 index eca82bc..0000000 --- a/Learning Modules/Utilities/Invoke-SqlCmdOnTenantDatabases.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -<# -.SYNOPSIS - Deploys a SQL script against all tenant databases in the catalog sequentially and to the golden tenant database. - The script should be idempotent as it will retry on error, dropped connection etc. - Lightweight solution to fanout deployment. Use Elastic Jobs for a more robust solution. -#> -[cmdletbinding()] -param( -[Parameter(Mandatory=$true)] -[string]$WtpResourceGroupName, - -[Parameter(Mandatory=$true)] -[string]$WtpUser, - -[Parameter(Mandatory=$true)] -[string]$CommandText, - -[int]$QueryTimeout = 60 -) -$WtpUser = $WtpUser.ToLower() - -Import-Module $PSScriptRoot\..\Common\SubscriptionManagement -Force -Import-Module $PSScriptRoot\..\Common\CatalogAndDatabaseManagement -Force - -$config = Get-Configuration - -## Apply script to deployed tenant databases - -$adminUserName = $config.TenantAdminUserName -$adminPassword = $config.TenantAdminPassword - -# Get the catalog for the current user -$catalog = Get-Catalog ` - -ResourceGroupName $WtpResourceGroupName ` - -WtpUser $WtpUser - -# Get all the databases in the catalog shard map -$shards = @() -$shards += Get-Shards -ShardMap $catalog.ShardMap - -foreach ($shard in $Shards) -{ - - Write-Output "Applying script to database '$($shard.Location.Database)' on server '$($shard.Location.Server)'." - $tenantServer = $shard.Location.Server - - Invoke-SqlcmdWithRetry ` - -Username $adminUserName ` - -Password $adminPassword ` - -ServerInstance $tenantServer ` - -Database $shard.Location.Database ` - -ConnectionTimeout 30 ` - -QueryTimeout $QueryTimeout ` - -Query $CommandText - -} - -## Apply script to the golden tenant database on the catalog server so new tenants databases will have the script applied - -$adminUserName = $config.CatalogAdminUserName -$adminPassword = $config.CatalogAdminPassword - -$catalogServer = $catalog.Database.ServerName -$fullyQualifiedCatalogServerName = $catalogServer + ".database.windows.net" -$goldenTenantDatabase = $config.GoldenTenantDatabaseName - - Write-Output "Applying script to database '$goldenTenantDatabase' on server '$catalogServer'." - Invoke-SqlcmdWithRetry ` - -Username $adminUserName ` - -Password $adminPassword ` - -ServerInstance $fullyQualifiedCatalogServerName ` - -Database $goldenTenantDatabase ` - -ConnectionTimeout 30 ` - -QueryTimeout $QueryTimeout ` - -Query $CommandText \ No newline at end of file diff --git a/Learning Modules/Utilities/LoadGenerator.ps1 b/Learning Modules/Utilities/LoadGenerator.ps1 deleted file mode 100644 index d4eb729..0000000 --- a/Learning Modules/Utilities/LoadGenerator.ps1 +++ /dev/null @@ -1,416 +0,0 @@ -[CmdletBinding()] -Param( - # Resource group containing the - [Parameter(Mandatory=$True)] - [string]$WtpResourceGroupName, - - [Parameter(Mandatory=$True)] - [string]$WtpUser, - - # Intensity of load - equates roughly to the workload in DTU applied to each database - [int][validaterange(1,100)] $Intensity = 40, - - # If enabled causes databases in different pools on the same server to be loaded unequally - # Use to demonstrate load balancing databases between pools - [switch]$Unbalanced, - - # Duration of the load generation session in minutes. Due to the way loads are applied, some - # activity may continue after this time. - [int]$DurationMinutes = 120, - - # If enabled, causes a single tenant to have a specific distinct load applied - # Use with SingleTenantIntensity to demonstrate moving a database in or out of a pool - [switch] $SingleTenant, - - # If SingleTenant is enabled, defines the load in DTU applied to an isolated tenant - [int][validateRange(1,100)] $SingleTenantDtu = 95, - - # If singleTenant is enabled, identifes the tenant database. If not specified a random tenant database is chosen - [string]$SingleTenantDatabaseName = "", - - [switch]$LongerBursts, - - # if OneTime switch is used then jobs are submitted and the script stops, othewise it continues to poll for new tenants - [bool]$OneTime = $false -) - -## Configuration - -$WtpUser = $WtpUser.ToLower() - -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Force -Import-Module "$PSScriptRoot\..\Common\CatalogAndDatabaseManagement" -Force - -# Get Azure credentials if not already logged on, Use -Force to select a different subscription -Initialize-Subscription - -$config = Get-Configuration - -$tenantAdminUser = $config.TenantAdminUsername -$tenantAdminPassword = $config.TenantAdminPassword - -## MAIN SCRIPT ------------------------------------------------------------------------------ - -# Get the catalog -$catalog = Get-Catalog -ResourceGroupName $WtpResourceGroupName -WtpUser $WtpUser -$catalogResourceGroupName = $catalog.Database.ResourceGroupName - -# Burst durations are randomized, the following set the min and max duration in seconds -$burstMinDuration = 25 -$burstMaxDuration = 40 - -# boost increases burst duration, increasing the likelihood of overlaps -if ($LongerBursts.IsPresent) {$burstMinDuration = 30; $burstMaxDuration = 52} - -# interval between bursts is randomized, the following set min and max interval in seconds -$intervalMin = 100 -$intervalMax = 360 - -# longer bursts also decreases the interval between bursts, increasing likelihood of overlaps -if ($LongerBursts.IsPresent) {$intervalMin = $intervalMin * 0.9; $intervalMax = $intervalMax * 0.9} - -# DTU burst level is randomized by applying a factor with min and max values -$burstMinFactor = 0.6 -$burstMaxFactor = 1.1 - -# Load factor skews the load on databases for unbalanced pools or intense single tenant usage scenarios. -# Load factor impacts DTU levels and interval between bursts -> interval = interval/loadFactor (low load factor ==> longer intervals) -$highPoolLoadFactor = 1.20 -$lowPoolLoadFactor = 0.50 -# Load factor for single tenant burst mode -$intenseLoadFactor = 15.00 - -# density load factor, decreases the database load for pools with more dbs, allowing more realistic demos with pools with small populations -# impacts interval between bursts [interval = interval + (interval * densityLoadFactor * pool.dbcount)] -# 0 removes the effect, 0.1 will double the typical interval for 10 dbs -$densityLoadFactor = 0.1 - -$jobs = @{} - -## Start job invocation loop - -$start = Get-Date - -$sleepCount = 0 -$sleep = 10 - -$settings = "`nDuration: $DurationMinutes mins, Intensity: $intensity, LongerBursts: $LongerBursts, Unbalanced: $Unbalanced, SingleTenant: $SingleTenant" - -if($SingleTenant) -{ - if ($SingleTenantDatabaseName -ne "") - { - $settings += ", Database: $SingleTenantDatabaseName" - } - $settings += ", DTU: $singleTenantDtu" -} - -Write-Output $settings -Write-Output "`nInvoking a load generation job for each database. Will check for new databases every $sleep seconds for $durationMinutes minutes." -write-output "`nUse Ctrl-C to stop invoking jobs. Already started jobs can be managed as follows:" -Write-Output " Get-Job to view status of all jobs" -Write-Output " Receive-Job -Keep to view output from an individual job" -Write-Output " Stop-Job to stop a job. Use Stop-Job * to stop all jobs (which can take a minute or more)" -Write-Output " Remove-Job to remove a job. Use Remove-Job * to remove all jobs. Use -Force to stop and remove.`n" - -while (1 -eq 1) -{ - $shards = Get-Shards -ShardMap $catalog.ShardMap - - $ServerNames = @() - foreach ($shard in $Shards) - { - $fullyQualifiedServerName = $shard.Location.Server - $serverName = $fullyQualifiedServerName.split('.')[0] - $ServerNames += $serverName - } - - $serverNames = $serverNames| sort | Get-Unique - - # Array that will contain all databases to be targeted - $allDbs = @() - - foreach($serverName in $serverNames) - { - [array]$serverPools = (Get-AzureRmSqlElasticPool -ResourceGroupName $catalogResourceGroupName -ServerName $serverName).ElasticPoolName - $poolNumber = 1 - - foreach($elasticPool in $serverPools) - { - # Set up the relative load level for each pool - if($Unbalanced.IsPresent -and $serverPools.Count -gt 1) - { - # alternating pools on the same server are given high and low loads - if ($poolNumber % 2 -ne 0) - { - $loadFactor = $highPoolLoadFactor - Write-Output ("Pool " + $elasticPool + " on " + $serverName + " has high load factor - " + $loadFactor) - } - else - { - $loadFactor = $lowPoolLoadFactor - Write-Output ("Pool "+ $elasticPool + " on " + $serverName + " has low load factor - " + $loadFactor) - } - } - else - { - # Neutral default for the relative load level for databases in a pool - $loadFactor = 1.0 - } - - $elasticDbs = (Get-AzureRmSqlElasticPoolDatabase -ResourceGroupName $catalogResourceGroupName -ServerName $serverName -ElasticPoolName $elasticPool).DatabaseName - - Foreach($elasticDb in $elasticDbs) - { - # vary the baseline DTU level of each database using a random factor x the input intensity x load factor for the pool - - $burstFactor = Get-Random -Minimum $burstMinFactor -Maximum $burstMaxFactor # randomizes the intensity of each database - $burstDtu = [math]::Ceiling($Intensity * $BurstFactor * $loadFactor) - - - # add db with its pool-based load factor to the list - $dbProperties = @{ServerName=($serverName + ".database.windows.net");DatabaseName=$elasticDb;BurstDtu=$burstDtu;LoadFactor=$loadFactor;ElasticPoolName=$elasticPool;PoolDbCount=$elasticDbs.Count} - $db = New-Object PSObject -Property $dbProperties - - $allDbs += $db - } - - $poolNumber ++ - } - - # Get standalone dbs and add to $allDbs - - $StandaloneDbs = (Get-AzureRmSqlDatabase -ResourceGroupName $catalogResourceGroupName -ServerName $serverName | where {$_.CurrentServiceObjectiveName -ne "ElasticPool"} | where {$_.DatabaseName -ne "master"} ).DatabaseName - Foreach ($standaloneDb in $StandaloneDbs) - { - $burstLevel = Get-Random -Minimum $burstMinFactor -Maximum $burstMaxFactor # randomizes the intensity of each database - $burstDtu = [math]::Ceiling($burstLevel * $Intensity) - - #store db with a neutral load factor - $dbProperties = @{ServerName=($serverName + ".database.windows.net");DatabaseName=$StandaloneDb;BurstDtu=$burstDtu;LoadFactor=1.0;ElasticPoolName="";PoolDbCount=0.0} - $db = New-Object PSObject -Property $dbProperties - - $allDbs += $db - } - } - - if ($SingleTenant.IsPresent -and $SingleTenantDatabaseName -ne "") - { - $SingleTenantDatabaseName = Get-NormalizedTenantname $SingleTenantDatabaseName - - #validate that the name is one of the database names about to be processed - $allDBNames = $allDBs | select -ExpandProperty DatabaseName - - if (-not ($allDBNames -contains $SingleTenantDatabaseName)) - { - throw "The Single Tenant Database Name '$SingleTenantDatabaseName' was not found. Check the spelling and try again." - } - } - - # spawn jobs to spin up load on each database in $allDbs - # note there are limits to using PS jobs at scale; this should only be used for small scale demonstrations - - # Set the end time for all jobs - $endTime = [DateTime]::Now.AddMinutes($DurationMinutes) - - $scriptPath= $PSScriptRoot - - # Script block for job that executes the load generation stored procedure on each database - $scriptBlock = ` - { - param($server,$dbName,$AdminUser,$AdminPassword,$DurationMinutes,$intervalMin,$intervalMax,$burstMinDuration,$burstMaxDuration,$baseDtu,$loadFactor,$densityLoadFactor,$poolDbCount) - - Import-Module "$using:scriptPath\..\Common\CatalogAndDatabaseManagement" -Force - - Write-Output ("Database " + $dbName + "/" + $server + " Load factor: " + $loadFactor + " Density weighting: " + ($densityLoadFactor*$poolDbCount)) - - $endTime = [DateTime]::Now.AddMinutes($DurationMinutes) - - $firstTime = $true - - While ([DateTime]::Now -lt $endTime) - { - # add variable delay before execution, this staggers bursts - # load factor is applied to reduce interval for high or intense loads, and increase interval for low loads - # density load factor extends interval for higher density pools to reduce overloading - if($firstTime) - { - $snooze = [math]::ceiling((Get-Random -Minimum 0 -Maximum ($intervalMax - $intervalMin)) / $loadFactor) - $snooze = $snooze + ($snooze * $densityLoadFactor * $poolDbCount) - $firstTime = $false - } - else - { - $snooze = [math]::ceiling((Get-Random -Minimum $intervalMin -Maximum $intervalMax) / $loadFactor) - $snooze = $snooze + ($snooze * $densityLoadFactor * $poolDbCount) - } - Write-Output ("Snoozing for " + $snooze + " seconds") - Start-Sleep $snooze - - # vary each burst to add realism to the workload - - # vary burst duration - $burstDuration = Get-Random -Minimum $burstMinDuration -Maximum $burstMaxDuration - - # Increase burst duration based on load factor. Has marginal effect on low loadfactor databases. - $burstDuration += ($loadFactor * 2) - - # vary DTU - $dtuVariance = Get-Random -Minimum 0.9 -Maximum 1.1 - $burstDtu = [Math]::ceiling($baseDtu * $dtuVariance) - - # ensure burst DTU doesn't exceed 100 - if($burstDtu -gt 100) - { - $burstDtu = 100 - } - - # configure and submit the SQL script to run the load generator - $sqlScript = "EXEC sp_CpuLoadGenerator @duration_seconds = " + $burstDuration + ", @dtu_to_simulate = " + $burstDtu - try - { - Invoke-SqlAzureWithRetry -ServerInstance $server ` - -Database $dbName ` - -Username $AdminUser ` - -Password $AdminPassword ` - -Query $sqlscript ` - -QueryTimeout 36000 - } - catch - { - write-error $_.Exception.Message - Write-Output ("Error connecting to tenant database " + $dbName + "/" + $server) - } - - [string]$message = $([DateTime]::Now) - Write-Output ( $message + " Starting load: " + $burstDtu + " DTUs for " + $burstDuration + " seconds") - - # exit loop if end time exceeded - if ([DateTime]::Now -gt $endTime) - { - break; - } - } - } - - # Start a job for each database. Each job runs for the specified session duration and triggers load periodically. - # The base-line load level for each db is set by the entry in $allDbs. Burst duration, interval and DTU are randomized - # slightly within each job to create a more realistic workload - - $randomTenantIndex = 0 - - if ($SingleTenant -and $SingleTenantDatabaseName -eq "") - { - $randomTenantIndex = Get-Random -Minimum 1 -Maximum ($allDbs.Count + 1) - } - - $i = 1 - - foreach ($db in $allDBs) - { - # skip further processing if job is already started for this database - if ($jobs.ContainsKey($db.DatabaseName)) - { - continue - } - - if($sleeping -eq $true) - { - $sleeping = $false - # emit next job output on a new line - Write-Output "`n" - } - - # Customize the load applied for each database - if ($SingleTenant) - { - if ($i -eq $randomTenantIndex) - { - # this is the randomly selected database, so use the single-tenant factors - $burstDtu = $SingleTenantDtu - $loadFactor = $intenseLoadFactor - } - elseif ($randomTenantIndex -eq 0 -and $SingleTenantDatabaseName -eq $db.DatabaseName) - { - # this is the named database, so use the single-tenant factors - $burstDtu = $SingleTenantDtu - $loadFactor = $intenseLoadFactor - } - else - { - # use per-db computed factors - $burstDtu = $db.BurstDtu - $loadFactor = $db.LoadFactor - } - } - else - { - # use per-db computed factors - $burstDtu = $db.BurstDtu - $loadFactor = $db.LoadFactor - } - - $poolDbCount = $db.PoolDbCount - - $i ++ - - $outputText = " Starting load with load factor $loadFactor with baseline DTU $burstDtu" - if ($db.ElasticPoolName -ne "") - { - $outputText += " in pool " + $db.ElasticPoolName - } - else - { - $outputText += " standalone" - } - - if ($LongerBursts.IsPresent) - { - $outputText += " [BOOSTED]" - } - - # start the load generation job for the current database - - $job = Start-Job ` - -ScriptBlock $scriptBlock ` - -Name $db.DatabaseName ` - -ArgumentList $(` - $db.ServerName,$db.DatabaseName,` - $TenantAdminUser,$TenantAdminPassword,` - $DurationMinutes,$intervalMin,$intervalMax,` - $burstMinDuration,$burstMaxDuration,$burstDtu,` - $loadFactor,$densityLoadFactor,$poolDbCount) - - # add job to dictionary of currently running jobs - $jobs += @{$job.Name = $job} - - $outputText = ("Job $($job.Id) $($Job.Name) $outputText") - write-output $outputText - } - - $now = Get-Date - $runtime = ($now - $start).TotalMinutes - - if ($runtime -ge $DurationMinutes -or $OneTime) - { - Write-Output "`n`nLoad generation session stopping after $runtime minutes" - exit - } - - $sleepCount ++ - - # displays rows of dots to show it's still working. A dot every 10 seconds, a new row every 10 minutes - if ($sleepCount -ge 60) - { - write-host "." - $sleepCount = 0 - } - else - { - write-host -NoNewline "." - } - - $sleeping = $true - - Sleep $sleep -} \ No newline at end of file diff --git a/Learning Modules/Utilities/Remove-RestoredTenant.ps1 b/Learning Modules/Utilities/Remove-RestoredTenant.ps1 deleted file mode 100644 index 2a01201..0000000 --- a/Learning Modules/Utilities/Remove-RestoredTenant.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -<# -.SYNOPSIS - Removes a previously restored tenant entry with _old suffix. -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [string]$WtpResourceGroupName, - - [parameter(Mandatory=$true)] - [string]$WtpUser, - - [parameter(Mandatory=$true)] - [string]$TenantName, - - # NoEcho stops the output of the signed in user to prevent double echo - [parameter(Mandatory=$false)] - [switch] $NoEcho -) - - -# Stop execution on error -#$ErrorActionPreference = "Stop" - -Import-Module $PSScriptRoot\..\Common\CatalogAndDatabaseManagement -Force -Import-Module $PSScriptRoot\..\Common\SubscriptionManagement -Force - -# Get Azure credentials if not already logged on -Initialize-Subscription -NoEcho:$NoEcho.IsPresent - -$catalog = Get-Catalog ` - -ResourceGroupName $WtpResourceGroupName ` - -WtpUser $WtpUser ` - -$restoredTenantName = (Get-NormalizedTenantName -TenantName $TenantName) + "_old" - -$tenantKey = Get-TenantKey -TenantName $restoredTenantName - -if(Test-TenantKeyInCatalog -Catalog $catalog -TenantKey $tenantKey) -{ - Remove-Tenant ` - -Catalog $catalog ` - -TenantKey $tenantKey - - Write-Output "'$restoredTenantName' is removed." -} -else -{ - Write-Output "'$restoredTenantName' is not in the catalog." - exit -} diff --git a/Learning Modules/Utilities/Remove-UnsoldEventFromTenant.ps1 b/Learning Modules/Utilities/Remove-UnsoldEventFromTenant.ps1 deleted file mode 100644 index d1277bd..0000000 --- a/Learning Modules/Utilities/Remove-UnsoldEventFromTenant.ps1 +++ /dev/null @@ -1,92 +0,0 @@ -<# -.SYNOPSIS - Find the first event with no tickets and delete it from venue registered on Wingtip platform -#> -[cmdletbinding()] -param ( - [parameter(Mandatory=$true)] - [string]$WtpResourceGroupName, - - [parameter(Mandatory=$true)] - [string]$WtpUser, - - [parameter(Mandatory=$true)] - [string]$TenantName, - - # NoEcho stops the output of the signed in user to prevent double echo - [parameter(Mandatory=$false)] - [switch] $NoEcho -) - -Import-Module $PSScriptRoot\..\Common\CatalogAndDatabaseManagement -Force -Import-Module $PSScriptRoot\..\Common\SubscriptionManagement -Force - -# Get Azure credentials if not already logged on -Initialize-Subscription -NoEcho:$NoEcho.IsPresent - -# Get catalog database that contains metadata about all Wingtip tenant databases -$catalog = Get-Catalog ` - -ResourceGroupName $WtpResourceGroupName ` - -WtpUser $WtpUser ` - -$normalizedTenantName = Get-NormalizedTenantName -TenantName $TenantName - -$tenantKey = Get-TenantKey -TenantName $normalizedTenantName - -# Exit script if tenant is not in the catalog -if(!(Test-TenantKeyInCatalog -Catalog $catalog -TenantKey $tenantKey)) -{ - Write-Output "'$TenantName' is not in the catalog." - exit -} - -# Get catalog username and password configuration -$config = Get-Configuration - -$tenantMapping = ($catalog.ShardMap).GetMappingForKey($tenantKey) - -# Get tenant database and server names -$tenantDatabaseName = $tenantMapping.Shard.Location.Database -$fullyQualifiedTenantServerName = $tenantMapping.Shard.Location.Server - -# Get the first unsold event on the tenant database -$queryText = "SELECT TOP(1) EventName FROM EventsWithNoTickets ORDER BY DATE DESC" - -$eventName = Invoke-Sqlcmd ` - -ServerInstance $fullyQualifiedTenantServerName ` - -Username $config.TenantAdminuserName ` - -Password $config.TenantAdminPassword ` - -Database $tenantDatabaseName ` - -Query $queryText ` - -ConnectionTimeout 30 ` - -QueryTimeout 30 ` - -EncryptConnection - -if ($eventName) -{ - # Delete the first unsold event on the tenant database - $queryText = " - DECLARE @TargetEventID int - SET @TargetEventID = (SELECT TOP(1) EventId FROM EventsWithNoTickets ORDER BY DATE DESC) - EXEC sp_DeleteEvent @TargetEventID - " - Invoke-Sqlcmd ` - -ServerInstance $fullyQualifiedTenantServerName ` - -Username $config.TenantAdminuserName ` - -Password $config.TenantAdminPassword ` - -Database $tenantDatabaseName ` - -Query $queryText ` - -ConnectionTimeout 30 ` - -QueryTimeout 30 ` - -EncryptConnection - - Write-Host "Deleted event '$($eventName.EventName)' from $TenantName venue." - return $eventName.EventName -} -else -{ - Write-Error "There are no unsold events that can be deleted for $TenantName. Rerunning the ticket generator will delete tickets for the last event." - exit -} - - diff --git a/Learning Modules/Utilities/Reset-EventDatesForAllTenants.ps1 b/Learning Modules/Utilities/Reset-EventDatesForAllTenants.ps1 deleted file mode 100644 index 0bd503e..0000000 --- a/Learning Modules/Utilities/Reset-EventDatesForAllTenants.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -<# -.SYNOPSIS - Resets the event dates in all tenant databases registered in the catalog - -.DESCRIPTION - Resets the event dates in all tenant databases registered in the catalog. Calls sp_ResetEventDates - in each database. Two events are set in the recent past, the remainder are rescheduled into the future. - -#> -param( - [Parameter(Mandatory=$true)] - [string]$WtpResourceGroupName, - - [Parameter(Mandatory=$true)] - [string]$WtpUser -) - -Import-Module $PSScriptRoot\..\Common\CatalogAndDatabaseManagement -Force - -$config = Get-Configuration - -# Get the catalog -$catalog = Get-Catalog -ResourceGroupName $WtpResourceGroupName -WtpUser $WtpUser - -$databaseLocations = Get-TenantDatabaseLocations -Catalog $catalog - -$commandText = "EXEC sp_ResetEventDates" - -foreach ($dbLocation in $databaseLocations) -{ - Write-Output "Resetting event dates for '$($dblocation.Location.Database)'." - $fullyQualifiedDBServer = $dbLocation.Location.Server - - Invoke-Sqlcmd ` - -ServerInstance $fullyQualifiedDBServer ` - -Username $($config.TenantAdminuserName) ` - -Password $($config.TenantAdminPassword) ` - -Database $($dblocation.Location.Database) ` - -Query $commandText ` - -ConnectionTimeout 30 ` - -QueryTimeout 30 ` - -EncryptConnection - -} \ No newline at end of file diff --git a/Learning Modules/Utilities/SeattleZonedPostalCodes.csv b/Learning Modules/Utilities/SeattleZonedPostalCodes.csv deleted file mode 100644 index 06efd2a..0000000 --- a/Learning Modules/Utilities/SeattleZonedPostalCodes.csv +++ /dev/null @@ -1,120 +0,0 @@ -1,98101 -1,98154 -1,98164 -1,98174 -1,98104 -2,98121 -2,98134 -2,98144 -2,98122 -2,98112 -2,98102 -2,98109 -2,98119 -2,98134 -3,98116 -3,98136 -3,98126 -3,98106 -3,98108 -3,98118 -3,98040 -3,98039 -3,98119 -3,98103 -4,98199 -4,98107 -4,98195 -4,98105 -4,98004 -4,98056 -4,98178 -4,98168 -4,98146 -4,98117 -4,98115 -5,98177 -5,98133 -5,98125 -5,98034 -5,98033 -5,98004 -5,98052 -5,98005 -5,98007 -5,98008 -5,98006 -5,98057 -5,98055 -5,98188 -5,98158 -5,98166 -5,98353 -5,98148 -5,98198 -5,98032 -6,98020 -6,98043 -6,98031 -6,98030 -6,98042 -6,98027 -6,98029 -6,98050 -6,98075 -6,98074 -6,98024 -6,98014 -6,98053 -6,98072 -6,98077 -6,98021 -6,98036 -6,98043 -6,98037 -6,98012 -6,98087 -6,98026 -7,98275 -7,98204 -7,98208 -7,98203 -7,98290 -7,98272 -7,98294 -7,98019 -7,98065 -7,98038 -7,98042 -7,98058 -7,98055 -7,98002 -7,98001 -7,98047 -7,98003 -7,98023 -8,98422 -8,98354 -8,98092 -7,98372 -8,98422 -8,98421 -8,98402 -8,98403 -8,98405 -8,98407 -8,98404 -8,98409 -8,98408 -8,98443 -8,98445 -8,98446 -8,98373 -8,98374 -8,98375 -8,98207 -8,98201 -8,98205 -8,98271 -8,98270 -8,98258 diff --git a/Learning Modules/Utilities/TicketGenerator.ps1 b/Learning Modules/Utilities/TicketGenerator.ps1 deleted file mode 100644 index a2bf4cb..0000000 --- a/Learning Modules/Utilities/TicketGenerator.ps1 +++ /dev/null @@ -1,262 +0,0 @@ -<# -.Synopsis - Simulates customer ticket purchases for events in Wingtip SaaS tenant databases -.DESCRIPTION - Adds customers and creates tickets for events in tenant (venue) databases. Does not - create tickets for the last event in each database to allow this to be deleted to - demonstrate point-in-time restore. -#> - -[CmdletBinding()] -Param -( - # Resource Group Name entered during deployment - [Parameter(Mandatory=$true)] - [String] - $WtpResourceGroupName, - - # The user name used entered during deployment - [Parameter(Mandatory=$true)] - [String] - $WtpUser, - - # The baseline sales % for an event - approx % of tickets to be sold - [Parameter(Mandatory=$false)] - [int]$salesPercent = 80, - - # The variation (+/-) % for ticket sales, gives impression that there is variation in interest in events - [Parameter(Mandatory=$false)] - [int]$salesPercentVariation = 20 - -) -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Import-Module "$PSScriptRoot\..\Common\CatalogAndDatabaseManagement" -Force - -$ErrorActionPreference = "Stop" - -$config = Get-Configuration - -## MAIN SCRIPT ## ---------------------------------------------------------------------------- - -# Ensure logged in to Azure -Initialize-Subscription - -$AdminUserName = $config.TenantAdminUsername -$AdminPassword = $config.TenantAdminPassword - -$ServerName = "customers1-" + $WtpUser.ToLower() - -<# uncomment to generate tickets for the golden databases -$WtpResourceGroupName = "wingtip-gold" -$ServerName = "wingtip-customers-gold" -#> - -$FullyQualifiedServerName = $ServerName + ".database.windows.net" - -# install Microsoft approved list of fictitious customer names -$fictiousNames = Import-Csv -Path ("$PSScriptRoot\FictitiousName_02082017_104844.csv") -Header ("#","FirstName","LastName","Language","Gender") -$fictiousNames += Import-Csv -Path ("$PSScriptRoot\FictitiousName_02082017_104533.csv") -Header ("#","FirstName","LastName","Language","Gender") - -# Get all the databases on the server TODO review changing this to retrieve database list from the catalog) -$venueDatabases = Get-AzureRmSqlDatabase -ResourceGroupName $WtpResourceGroupName -ServerName $ServerName | where {$_.DatabaseName -ne "master"} - -$totalTicketPurchases = 0 -$totalTickets = 0 - -foreach ($db in $venueDatabases) -{ - # Initialize SQL command variables for ticket generation - - $command = "SELECT SectionName FROM [dbo].[Sections]" - $Sections = Invoke-Sqlcmd -Username "$AdminUserName" -Password "$AdminPassword" -ServerInstance $fullyQualifiedServerName -Database $db.DatabaseName -Query $command -ConnectionTimeout 30 -QueryTimeout 30 -EncryptConnection -ErrorAction stop - - $command = "SELECT EventName FROM [dbo].[Events]" - $Events = Invoke-Sqlcmd -Username "$AdminUserName" -Password "$AdminPassword" -ServerInstance $fullyQualifiedServerName -Database $db.DatabaseName -Query $command -ConnectionTimeout 30 -QueryTimeout 30 -EncryptConnection - - # for ticket purchases (TODO VERIFY AND THEN REMOVE COMMENTED) - $tpCommand = ` - "--SET IDENTITY_INSERT [dbo].[TicketPurchases] ON - INSERT INTO [dbo].[TicketPurchases] ([TicketPurchaseId],[CustomerId],[PurchaseDate],[PurchaseTotal]) VALUES`n" - - # TicketPurchaseId - $tpId = 1 - - # for tickets - $tCommand = "INSERT INTO [dbo].[Tickets] ([RowNumber],[SeatNumber],[EventId],[SectionId],[TicketPurchaseId]) VALUES `n" - - # counter for batches of values included in an INSERT statement - $iValues = 1 - - # set the tickets sales % for this db, based on the sales % and sales % variation - $venueSalesPercentVariation = (Get-Random -Maximum $salesPercentVariation -Minimum 0) - if ((Get-Random -Maximum 10 -Minimum 0) -gt 5) - { - $venueSalesPercent = $SalesPercent + $venueSalesPercentVariation - - if ($venueSalesPercent -gt 100) - { - $venueSalesPercent = 100 - } - } - else - { - $venueSalesPercent = $SalesPercent - $venueSalesPercentVariation - } - - # initialize Tickets, TicketPurchases and Customers tables and then insert customers - - $command = "` - DELETE FROM [dbo].[Tickets] - DELETE FROM [dbo].[TicketPurchases] - DELETE FROM [dbo].[Customers] - SET IDENTITY_INSERT [dbo].[Customers] ON - INSERT INTO [dbo].[Customers] - ([CustomerId],[FirstName],[LastName],[Email],[PostalCode],[CountryCode]) - VALUES `n " - - $i = 1 - - # Extend here to provide varied postal codes, countries for customers - $PostalCode = "98052" - $CountryCode = "USA" - - foreach($fName in $fictiousNames) - { - $email = ($fName.FirstName + "." + $fName.LastName + "@outlook.com").ToLower() - - $command += "($i,'$($fName.FirstName)','$($fName.LastName)','$email','$PostalCode','$CountryCode'),`n " - - $i++ - } - - $command = $command.TrimEnd(("`n",","," ")) + ";`nSET IDENTITY_INSERT [dbo].[Customers] OFF" - - $customersExec = Invoke-Sqlcmd -Username "$AdminUserName" -Password "$AdminPassword" -ServerInstance $fullyQualifiedServerName -Database $db.DatabaseName -Query $command -ConnectionTimeout 30 -QueryTimeout 30 -EncryptConnection - - # get the event sections and seating capacity for all events in this venue except the last one - $command = "` - DECLARE @eventCount int - SET @eventCount = (SELECT count(*) FROM events) - 1 - SELECT e.EventId,e.Date as EventDate, es.SectionId,s.SeatRows,s.SeatsPerRow,es.Price - FROM dbo.eventsections AS es - INNER JOIN dbo.sections AS s ON es.SectionId = s.SectionId - INNER JOIN dbo.Events as e ON e.EventId = es.EventId - WHERE e.EventId in - (SELECT top (@EventCount) EventId FROM dbo.Events ORDER BY Date)" - - $eventSections = Invoke-Sqlcmd -Username "$AdminUserName" -Password "$AdminPassword" -ServerInstance $fullyQualifiedServerName -Database $db.DatabaseName -Query $command -ConnectionTimeout 30 -QueryTimeout 30 -EncryptConnection - - if ($eventSections) - { - foreach($eventSection in $eventSections) - { - # for all rows and then for some (randomly selected) seats per row, create ticket purchases for randomly selected customers - - for ($iRow=1; $iRow -le $eventSection.SeatRows; $iRow++) - { - for($iSeat=1; $iSeat -le $eventSection.SeatsPerRow; $iSeat++) - { - $seatRandomizer = Get-Random -Maximum 100 -Minimum 0 - - if($seatRandomizer -le $venueSalesPercent) - { - # buy a ticket for this seat, otherwise, try next seat... - # each ticket is bought 1 per ticket purchase - # pick a purchase date - - # determine if event is in the future - if ($eventSection.EventDate.ToUniversalTime() -gt (Get-Date).ToUniversalTime()) - { - # if so calculate offset in days plus margin - $offset = ($eventSection.EventDate.ToUniversalTime() - (Get-Date).ToUniversalTime()).Days + 1 - } - - If ($offset -le 1) {$offset = 1} - - # set ticket purchase date to a point between today and 90 days prior to the event - $days = Get-Random -Maximum 90 -Minimum $offset - $purchaseDate = $eventSection.EventDate.AddDays(-$days) - - # randomize the purchase time - $mins = Get-Random -Maximum 1440 -Minimum 0 - $purchaseDate = $purchaseDate.AddMinutes(-$mins) - - # pick the customer at random - $randomCustomer = Get-Random -Maximum $fictiousNames.Count -Minimum 1 - - if($iValues -ge 1000) - { - # finalize current INSERT and start new INSERT statements - - $tpCommand = $tpCommand.TrimEnd((" ",",","`n")) + ";`n`n" - - #$tpCommand += "SET IDENTITY_INSERT [dbo].[TicketPurchases] OFF `n`nSET IDENTITY_INSERT [dbo].[TicketPurchases] ON `n" - - $tpCommand += "INSERT INTO [dbo].[TicketPurchases] ([TicketPurchaseId],[CustomerId],[PurchaseDate],[PurchaseTotal]) VALUES`n" - - $tCommand = $tCommand.TrimEnd((" ",",","`n")) + ";`n`n" - - $tCommand += "INSERT INTO [dbo].[Tickets] ([RowNumber],[SeatNumber],[EventId],[SectionId],[TicketPurchaseId]) VALUES `n" - - - $iValues = 0 - } - - # add the ticket purchase to the values being inserted - $tpCommand += " ($tpId,$randomCustomer,'$purchaseDate',$($eventSection.Price)),`n" - - # add the ticket to the values being inserted - $tCommand += " ($iRow,$iSeat,$($eventSection.EventId),$($eventSection.SectionId),$tpId),`n" - - $iValues ++ - - $tpId ++ - - } - } - } - } - - # Finalize the commands and execute - - Write-Output "Adding $tpId TicketPurchases for $($db.DatabaseName)" - - $tpCommand = $tpCommand.TrimEnd((" ",",","`n")) + ";" - - #$tpCommand += "SET IDENTITY_INSERT [dbo].[TicketPurchases] OFF" - - $ticketPurchasesExec = Invoke-Sqlcmd ` - -Username "$AdminUserName" ` - -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedServerName ` - -Database $db.DatabaseName ` - -Query $tpCommand ` - -ConnectionTimeout 30 ` - -QueryTimeout 120 ` - -EncryptConnection - - Write-Output "Adding $tpId Tickets for $($db.DatabaseName)" - - $tCommand = $tCommand.TrimEnd((" ",",","`n")) + ";" - - $ticketsExec = Invoke-Sqlcmd ` - -Username "$AdminUserName" ` - -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedServerName ` - -Database $db.DatabaseName ` - -Query $tCommand ` - -ConnectionTimeout 30 ` - -QueryTimeout 120 ` - -EncryptConnection - - $totalTicketPurchases += $tpId - $totalTickets += $tpId - } - else - { - Write-Output "No events exist in $($db.DatabaseName)" - } -} - -Write-Output "$totalTicketPurchases TicketPurchases total" -Write-Output "$totalTickets Tickets total" \ No newline at end of file diff --git a/Learning Modules/Utilities/TicketGenerator2.ps1 b/Learning Modules/Utilities/TicketGenerator2.ps1 deleted file mode 100644 index f729813..0000000 --- a/Learning Modules/Utilities/TicketGenerator2.ps1 +++ /dev/null @@ -1,475 +0,0 @@ -<# -.Synopsis - Simulates customer ticket purchases for events in WTP tenant databases -.DESCRIPTION - Adds customers and creates tickets for events in tenant (venue) databases. Does not - create tickets for the last event in each database to allow this to be deleted to - demonstrate point-in-time restore. -#> - -[CmdletBinding()] -Param -( - # Resource Group Name entered during deployment - [Parameter(Mandatory=$true)] - [String]$WtpResourceGroupName, - - # The user name used entered during deployment - [Parameter(Mandatory=$true)] - [String]$WtpUser -) -Import-Module "$PSScriptRoot\..\Common\SubscriptionManagement" -Import-Module "$PSScriptRoot\..\Common\CatalogAndDatabaseManagement" -Force -Import-Module "$PSScriptRoot\..\wtpConfig" -Force - -$ErrorActionPreference = "Stop" - -$config = Get-Configuration - -$catalog = Get-Catalog -ResourceGroupName $WtpResourceGroupName -WtpUser $WtpUser - -## Functions - -function Get-PaddedNumber -{ - param ([int] $Number) - - if ($Number -lt 10) {return "000$Number"} - if ($number -lt 100) {return "00$Number"} - if ($Number -lt 1000) {return "0$Number"} - return $Number.ToString() -} - -function Get-CurvedSalesForDay -{ - param - ( - [object] $Curve, - - [ValidateRange(1,60)] - [int] $Day, - - [int] $Seats - - ) - - [decimal] $curvePercent = 0 - - if ($Day -eq 1) { $curvePercent = $Curve.1 } - elseif ($Day -le 5) { $curvePercent = ($Curve.5 / 4) } - elseif ($Day -le 10) { $curvePercent = ($Curve.10 / 5) } - elseif ($Day -le 15) { $curvePercent = ($Curve.15 / 5) } - elseif ($Day -le 20) { $curvePercent = ($Curve.20 / 5) } - elseif ($Day -le 25) { $curvePercent = ($Curve.25 / 5) } - elseif ($Day -le 30) { $curvePercent = ($Curve.30 / 5) } - elseif ($Day -le 35) { $curvePercent = ($Curve.35 / 5) } - elseif ($Day -le 40) { $curvePercent = ($Curve.40 / 5) } - elseif ($Day -le 45) { $curvePercent = ($Curve.45 / 5) } - elseif ($Day -le 50) { $curvePercent = ($Curve.50 / 5) } - elseif ($Day -le 55) { $curvePercent = ($Curve.55 / 5) } - else { $curvePercent = ($Curve.60 / 5) } - - # add some random variation - [decimal] $variance = (-10, -8, -5, -4, 0, 5, 10) | Get-Random - $curvePercent = $curvePercent + ($curvePercent * $variance/100) - - if ($curvePercent -lt 0) {$curvePercent = 0} - elseif ($curvePercent -gt 100) {$curvePercent = 100} - - [decimal]$sales = ($curvePercent * $Seats / 100) - - [int]$roundedSales = [math]::Ceiling($sales) - - return $roundedSales -} - -## MAIN SCRIPT ## ---------------------------------------------------------------------------- - -# Ensure logged in to Azure -Initialize-Subscription - -$startTime = Get-Date - -$AdminUserName = $config.TenantAdminUsername -$AdminPassword = $config.TenantAdminPassword - -# load fictitious customer names, postal codes, -$fictitiousNames = Import-Csv -Path ("$PSScriptRoot\FictitiousNames.csv") -Header ("Id","FirstName","LastName","Language","Gender") -$fictitiousNames = {$fictitiousNames}.Invoke() -$customerCount = $fictitiousNames.Count -$postalCodes = Import-Csv -Path ("$PSScriptRoot\SeattleZonedPostalCodes.csv") -Header ("Zone","PostalCode") - -# load the full set of event sales curves -$importCurves = Import-Csv -Path ("$PSScriptRoot\WtpSalesCurves1.csv") -Header ("Curve","1", "5","10","15","20","25","30","35","40","45","50","55","60") -$curves = @{} -foreach ($importCurve in $importCurves) -{ - $curves += @{$importCurve.Curve = $importCurve} -} - -# create different sets of curves that reflect different venue/event popularities -$popularCurves = $curves.MadRush,$curves.Rush,$curves.SShapedHigh,$curves.FastBurn, $curves.StraightLine, $curves.LastMinuteRush,$curves.MediumBurn -$moderateCurves = $Curves.Rush,$Curves.SShapedMedium, $Curves.MediumBurn, $Curves.LastMinute -$unpopularCurves = $curves.SShapedLow, $curves.QuickFizzle, $curves.SlowBurn,$curves.LastGasp, $curves.Disappointing - -# initialize SQL script for creating fictious customer, same customers are used for all venues, names will be picked at random for events -$customersSql = " - DELETE FROM [dbo].[Tickets] - DELETE FROM [dbo].[TicketPurchases] - DELETE FROM [dbo].[Customers] - SET IDENTITY_INSERT [dbo].[Customers] ON - INSERT INTO [dbo].[Customers] - ([CustomerId],[FirstName],[LastName],[Email],[PostalCode],[CountryCode]) - VALUES `n" - -# all customers are located in the US -$CountryCode = 'USA' - -$CustomerId = 0 -while ($fictitiousNames.Count -gt 0) -{ - # get a name at random then remove from the list - $name = $fictitiousNames | Get-Random - $fictitiousNames.Remove($name) > $null - - $firstName = $name.FirstName.Replace("'","").Trim() - $lastName = $name.LastName.Replace("'","").Trim() - - # form the customers email address - $alias = ($firstName + "." + $lastName).ToLower() - - # shorten to ensure fits in limited length column - if($alias.Length -gt 38) { $alias = $alias.Substring(0,38) } - - # oh look, they all use outlook as their email provider... - $email = $alias + "@outlook.com" - - # randomly assign a postal code - $postalCode = ($postalCodes | Get-Random).PostalCode - - $customerId ++ - - $customersSql += " ($customerId,'$firstName','$lastName','$email','$postalCode','$CountryCode'),`n" - -} - -$customersSql = $customersSql.TrimEnd(("`n",","," ")) + ";`nSET IDENTITY_INSERT [dbo].[Customers] OFF" - -# Get all the venue databases in the catalog -$venues = Get-Shards -ShardMap $catalog.ShardMap - -$totalTicketPurchases = 0 -$totalTickets = 0 - -# load characteristics of known venues from config - -foreach ($venue in $venues) -{ - $venueTickets = 0 - - $venueDatabaseName = $venue.Location.Database - $fullyQualifiedVenueServer = $venue.Location.Server - $venueServer = $fullyQualifiedVenueServer.split('.')[0] - - # set the venue popularity, which determines the sales curves used: 1=popular, 2=moderate, 3=unpopular - - # pre-defined venues use same popularity every time - if ($venueDatabaseName -eq 'contosoconcerthall') { $popularity = "popular"} - elseif ($venueDatabaseName -eq 'fabrikamjazzclub') { $popularity = "moderate"} - elseif ($venueDatabaseName -eq 'dogwooddojo') { $popularity = "unpopular"} - else - { - # set random popularity for all other venues - $popularity = ('popular','moderate','unpopular') | Get-Random - } - - # assign the venue curves based on popularity - switch ($popularity) - { - "popular" {$venueCurves = $popularCurves} - "moderate" {$venueCurves = $moderateCurves} - "unpopular" {$venueCurves = $unpopularCurves} - } - - Write-Output "Purchasing tickets for $venueDatabaseName ($popularity) on server '$venueServer'" - - # add customers to the venue - $results = Invoke-SqlAzureWithRetry ` - -Username "$AdminUserName" -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedVenueServer ` - -Database $venueDatabaseName ` - -Query $customersSql - - # initialize ticket purchase identity for this venue - $ticketPurchaseId = 1 - - # initialize SQL insert batch counters for tickets and ticket purchases - $tBatch = 1 - $tpBatch = 1 - - # initialize SQL batches for tickets and ticket purchases - $ticketSql = " - INSERT INTO [dbo].[Tickets] ([RowNumber],[SeatNumber],[EventId],[SectionId],[TicketPurchaseId]) VALUES `n" - - $ticketPurchaseSql = ` - "SET IDENTITY_INSERT [dbo].[TicketPurchases] ON - INSERT INTO [dbo].[TicketPurchases] ([TicketPurchaseId],[CustomerId],[PurchaseDate],[PurchaseTotal]) VALUES`n" - - # get total number of seats in venue - $command = "SELECT SUM(SeatRows * SeatsPerRow) AS Capacity FROM Sections" - $capacity = Invoke-SqlAzureWithRetry ` - -Username "$AdminUserName" -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedVenueServer ` - -Database $venueDatabaseName ` - -Query $command - - # get events for this venue - $command = " - SELECT EventId, EventName, Date FROM [dbo].[Events] - ORDER BY Date ASC" - - $events = Invoke-SqlAzureWithRetry ` - -Username "$AdminUserName" -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedVenueServer ` - -Database $venueDatabaseName ` - -Query $command - - $eventCount = 1 - foreach ($event in $events) - { - if - ( - $eventCount -eq $events.Count -and - ( - $venueDatabaseName -eq 'contosoconcerthall' -or - $venueDatabaseName -eq 'fabrikamjazzclub' -or - $venueDatabaseName -eq 'dogwooddojo' - ) - ) - { - # don't generate tickets for the last event for the pre-defined venues so they can be deleted - break - } - - # assign a sales curve for this event from the set assigned to this venue - $eventCurve = $venueCurves | Get-Random - - Write-Host -NoNewline " Processing event '$($event.EventName)'..." - - $eventTickets = 0 - - # get seating sections and prices for this event - $command = " - SELECT s.SectionId, s.SectionName, SeatRows, SeatsPerRow, es.Price - FROM [dbo].[EventSections] AS es - INNER JOIN [dbo].[Sections] AS s ON s.SectionId = es.SectionId - WHERE es.EventId = $($event.EventId)" - - $sections = @() - $sections += Invoke-SqlAzureWithRetry ` - -Username "$AdminUserName" -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedVenueServer ` - -Database $venueDatabaseName ` - -Query $command - - # process sections to create collections of seats from which purchased tickets will be drawn - $seating = @{} - $sectionNumber = 1 - foreach ($section in $sections) - { - $sectionSeating = @{} - - for ($row = 1;$row -le $section.SeatRows;$row++) - { - for ($seatNumber = 1;$seatNumber -le $section.SeatsPerRow;$seatNumber++) - { - # create the seat and assign its price - $seat = New-Object psobject -Property @{ - SectionId = $section.SectionId - Row = $row - SeatNumber = $seatNumber - Price = $section.Price - } - - $index = "$(Get-PaddedNumber $row)/$(Get-PaddedNumber $seatNumber)" - $sectionSeating += @{$index = $seat} - } - } - - $seating += @{$sectionNumber = $sectionSeating} - $sectionNumber ++ - } - - # ticket sales start date as (event date - 60) - $ticketStart = $event.Date.AddDays(-60) - - $today = Get-Date - - # loop over 60 day sales period - for($day = 1; $day -le 60 ; $day++) - { - # stop selling tickets when all sold - if ($eventTickets -ge $capacity.Capacity) - { - break - } - - $purchaseDate = $ticketStart.AddDays($day) - - # stop selling tickets after today - if ($purchaseDate -gt $today) - { - break - } - - # find the number of tickets to purchase this day based on this event's curve - [int]$ticketsToPurchase = Get-CurvedSalesForDay -Curve $eventCurve -Day $day -Seats $capacity.Capacity - - # if no tickets to sell this day, skip this day - if ($ticketsToPurchase -eq 0) - { - continue - } - - $ticketsPurchased = 0 - while ($ticketsPurchased -lt $ticketsToPurchase -and $seating.Count -gt 0 ) - { - ## buy tickets on a customer-by-customer basis - - # pick a random customer Id - $customerId = Get-Random -Minimum 1 -Maximum $customerCount - - # pick number of tickets for this customer to purchase (2-10 per person) - $ticketOrder = Get-Random -Minimum 2 -Maximum 10 - - # ensure ticket order does not cause purchases to exceed tickets to buy for this day - $remainingTicketsToBuyThisDay = $ticketsToPurchase - $ticketsPurchased - if ($ticketorder -gt $remainingTicketsToBuyThisDay) - { - $ticketOrder = $remainingTicketsToBuyThisDay - } - - # select seating section (could extend here to bias by section popularity) - $preferredSectionSeatingKey = $seating.Keys | Get-Random - $preferredSectionSeating = $seating.$preferredSectionSeatingKey - - # modify customer order if insufficient seats available in the chosen section (not so realistic but ensures all seats sell quickly) - if ($ticketOrder -gt $preferredSectionSeating.Count) - { - $ticketOrder = $preferredSectionSeating.Count - } - - $PurchaseTotal = 0 - - # assign seats from the chosen section - $seatingAssigned = $false - while ($seatingAssigned -eq $false) - { - # assign seats to this order - for ($s = 1;$s -le $ticketOrder; $s++) - { - $purchasedSeatKey = $preferredSectionSeating.Keys| Sort | Select-Object -First 1 - $purchasedSeat = $preferredSectionSeating.$purchasedSeatKey - - $PurchaseTotal += $purchasedSeat.Price - $ticketsPurchased ++ - - # add ticket to tickets batch - - # max of 1000 inserts per batch - if($tBatch -ge 1000) - { - # finalize current INSERT and start new INSERT statements and reset batch counter - $ticketSql = $ticketSql.TrimEnd((" ",",","`n")) + ";`n`n" - $ticketSql += "INSERT INTO [dbo].[Tickets] ([RowNumber],[SeatNumber],[EventId],[SectionId],[TicketPurchaseId]) VALUES `n" - $tBatch = 0 - } - - $ticketSql += "($($purchasedSeat.Row),$($purchasedSeat.SeatNumber),$($event.EventId),$($purchasedSeat.SectionId),$ticketPurchaseId),`n" - $tBatch ++ - - # remove seat from available seats when sold - $preferredSectionSeating.Remove($purchasedSeatKey) - - # remove section when sold out - if ($preferredSectionSeating.Count -eq 0) - { - $seating.Remove($preferredSectionSeatingKey) - } - } - - # set time of day of purchase - distributed randomly over prior 24 hours - $mins = Get-Random -Maximum 1440 -Minimum 0 - $purchaseTime = $purchaseDate.AddMinutes(-$mins) - - # add ticket purchase to batch - if($tpBatch -ge 1000) - { - # finalize current INSERT and start new INSERT statements and reset batch counter - $ticketPurchaseSql = $ticketPurchaseSql.TrimEnd((" ",",","`n")) + ";`n`n" - $ticketPurchaseSql += "INSERT INTO [dbo].[TicketPurchases] ([TicketPurchaseId],[CustomerId],[PurchaseDate],[PurchaseTotal]) VALUES`n" - $tpBatch = 0 - } - - $ticketPurchaseSql += "($ticketPurchaseId,$CustomerId,'$purchaseTime',$PurchaseTotal),`n" - $tpBatch ++ - - $seatingAssigned = $true - $ticketPurchaseId ++ - - } # tickets one customer - - $totalTicketPurchases ++ - $totalTickets += $ticketOrder - $eventTickets += $ticketOrder - $venueTickets += $ticketOrder - - } # all customer orders (ticket purchases) for one day - - } # purchases for all 60 days - - Write-Output " $eventTickets tickets purchased" - - $eventCount ++ - - } # per event purchases - - Write-Output " $venueTickets tickets purchased for $venueDatabaseName" - - # Finalize batched SQL commands for this venue and execute - - Write-Output " Inserting TicketPurchases" - - $ticketPurchaseSql = $ticketPurchaseSql.TrimEnd((" ",",","`n")) + ";" - $ticketPurchaseSql += "`nSET IDENTITY_INSERT [dbo].[TicketPurchases] OFF" - - $ticketPurchasesExec = Invoke-SqlAzureWithRetry ` - -Username "$AdminUserName" ` - -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedVenueServer ` - -Database $venueDatabaseName ` - -Query $ticketPurchaseSql ` - -QueryTimeout 120 - - Write-Output " Inserting Tickets " - - $ticketSql = $ticketSql.TrimEnd((" ",",","`n")) + ";" - - $ticketsExec = Invoke-SqlAzureWithRetry ` - -Username "$AdminUserName" ` - -Password "$AdminPassword" ` - -ServerInstance $fullyQualifiedVenueServer ` - -Database $venueDatabaseName ` - -Query $ticketSql ` - -QueryTimeout 120 - -# per venue purchases - -} - -Write-Output "$totalTicketPurchases TicketPurchases total" -Write-Output "$totalTickets Tickets total" - -$duration = [math]::Round(((Get-Date) - $startTime).Minutes) - -Write-Output "Duration $duration minutes" \ No newline at end of file diff --git a/Learning Modules/Utilities/WtpSalesCurves1.csv b/Learning Modules/Utilities/WtpSalesCurves1.csv deleted file mode 100644 index c672667..0000000 --- a/Learning Modules/Utilities/WtpSalesCurves1.csv +++ /dev/null @@ -1,15 +0,0 @@ -Curve,1,5,10,15,20,25,30,35,40,45,50,55,60 -MadRush,99,1,0,0,0,0,0,0,0,0,0,0,0 -Rush,45,25,10,5,0,0,0,0,0,0,0,0,0 -QuickFizzle,4,10,15,8,4,2,1,1,0,0,0,0,0 -SShapedHigh,5,16,12,8,4,2,1,1,2,4,8,12,21 -SShapedMedium,2,14,9,6,3,1,1,1,1,3,6,9,16 -SShapedLow,3,9,6,3,2,1,1,1,1,2,3,6,12 -FastBurn,3,15,15,15,15,15,15,0,0,0,8,0,0 -StraightLine,2,6,8,8,8,8,8,8,8,8,8,8,8 -MediumBurn,2,4,6,6,6,6,6,6,6,6,6,6,6 -SlowBurn,1,3,4,4,4,4,4,4,4,4,4,4,4 -LastMinuteRush,3,5,10,4,2,1,1,1,2,5,8,18,30 -LastMinute,3,4,6,3,2,1,1,1,2,5,8,15,20 -Disappointing,0,1,2,3,2,2,1,1,1,1,8,0,0 -LastGasp,1,2,2,2,1,1,1,1,1,2,6,10,15 diff --git a/Learning Modules/WtpConfig.psm1 b/Learning Modules/WtpConfig.psm1 deleted file mode 100644 index eb1e081..0000000 --- a/Learning Modules/WtpConfig.psm1 +++ /dev/null @@ -1,86 +0,0 @@ -# Get and/or set PowerShell session to only run scripts targeting dbpertenant Wingtip deployment -$Global:ErrorActionPreference = "Stop" -$scriptsTarget = 'dbpertenant' -if ($Global:WingtipScriptsTarget -and ($Global:WingtipScriptsTarget -ne $scriptsTarget)) -{ - throw "This PowerShell session is setup to only run scripts targeting Wingtip '$Global:WingtipScriptsTarget' architecture. Open up a new PowerShell session to run scripts targeting Wingtip '$scriptsTarget' architecture." -} -elseif (!$Global:WingtipScriptsTarget) -{ - Write-Verbose "Configuring PowerShell session to only run scripts targeting Wingtip '$scriptsTarget' architecture ..." - Set-Variable WingtipScriptsTarget -option Constant -value $scriptsTarget -scope global -} - - -<# -.SYNOPSIS - Returns default configuration values that will be used by the Wingtip Tickets Platform application -#> -function Get-Configuration -{ - $configuration = @{` - TenantDatabaseCopyTemplate = "tenantdatabasecopytemplate.json" - TenantDatabaseCopyBatchTemplate = "tenantdatabasecopybatchtemplate.json" - FailovergroupTemplate = "failovergrouptemplate.json" - WebApplicationRecoveryTemplate = "webappRecoveryTemplate.json" - LogAnalyticsWorkspaceTemplate = "loganalyticsworkspacetemplate.json" - LogAnalyticsWorkspaceNameStem = "wtploganalytics-" - LogAnalyticsDeploymentLocation = "westcentralus" - EventsAppNameStem = "events-wingtip-dpt-" - GoldenTenantDatabaseName = "baseTenantDB" - CatalogDatabaseName = "tenantcatalog" - CatalogServerNameStem = "catalog-dpt-" - CatalogFailoverGroupNameStem = "catalog-dpt-group-" - TenantServerNameStem = "tenants1-dpt-" - RecoveryRoleSuffix = "-recovery" - TenantPoolNameStem = "Pool" - ActiveCatalogAliasStem = "activecatalog-dpt-" - NewTenantAliasStem = "newtenants-dpt-" - CatalogShardMapName = "tenantcatalog" - CatalogRecoveryTemplate = "tenantcatalogrecoverytemplate.json" - CatalogAdminUserName = "developer" - CatalogAdminPassword = "P@ssword1" - TenantAdminUserName = "developer" - TenantAdminPassword = "P@ssword1" - TenantElasticPoolRestoreBatchTemplate = "tenantelasticpoolrecoverybatchtemplate.json" - TenantServerRestoreBatchTemplate = "tenantserverrecoverybatchtemplate.json" - NewTenantResourcesProvisioningTemplate = "newtenantresourcestemplate.json" - ReconfigureTenantResourcesTemplate = "updatetenantresourcestemplate.json" - JobAgent = "jobagent" - JobAgentDatabaseName = "jobagent" - JobAgentDatabaseServiceObjective = "S2" - JobAgentCredentialName = "mydemocred" - TenantAnalyticsDatabaseName = "tenantanalytics" - TenantAnalyticsCSDatabaseName = "tenantanalytics-cs" - TenantAnalyticsDWDatabaseName = "tenantanalytics-dw" - AdfStorageAccountNameStem = "wingtipstaging" - AdfConfigStorageLocation = "eastus" - AdfConfigContainerName = "configfile" - DataFactoryDeploymentTemplate = "adf_arm_template.json" - DataFactoryNameStem = "dbtodwload-" - DataFactoryLocation = "westeurope" - AdhocReportingDatabaseName = "adhocreporting" - AdhocReportingDatabaseServiceObjective = "S0" - DefaultVenueType = "multipurpose" - TenantNameBatch = @( - ("Poplar Dance Academy","dance","98402"), - ("Blue Oak Jazz Club","blues","98201"), - ("Juniper Jammers Jazz","jazz","98032"), - ("Sycamore Symphony","classicalmusic","98004"), - ("Hornbeam HipHop","dance","98036"), - ("Mahogany Soccer","soccer","98032"), - ("Lime Tree Track","motorracing","98115"), - ("Balsam Blues Club","blues","98104"), - ("Tamarind Studio","dance","98072"), - ("Star Anise Judo", "judo","98103"), - ("Cottonwood Concert Hall","classicalmusic","98402"), - ("Mangrove Soccer Club","soccer","98036"), - ("Foxtail Rock","rockmusic","98107"), - ("Osage Opera","opera","98101"), - ("Papaya Players","soccer","98116"), - ("Magnolia Motor Racing","motorracing","98040"), - ("Sorrel Soccer","soccer","98188") - ) - } - return $configuration -}