EpinovaAzureToolBucket.psm1
<#
.DESCRIPTION Help functions for Epinova DXP vs Azure Portal. #> Set-StrictMode -Version Latest $azureConnection = $null # PRIVATE METHODS function Test-AzureSqlConnection { param( [Parameter(Mandatory)] [string]$ServerName, [Parameter(Mandatory)] [string]$DatabaseName, [Parameter(Mandatory)] [pscredential]$Credential ) $result = $false $userName = $Credential.UserName $password = $Credential.GetNetworkCredential().Password $connectionString = "Server=tcp:$ServerName,1433;Initial Catalog=$DatabaseName;Persist Security Info=False;User ID=$userName;Password=$password;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" $sqlConnection = New-Object System.Data.SqlClient.SqlConnection $ConnectionString try { $sqlConnection.Open() ## This will run if the Open() method does not throw an exception $result = $true #Write-Host "Connection to database works :)" } catch { Write-Error $Error[0] $result = $false } finally { ## Close the connection when we're done $sqlConnection.Dispose() } return $result } function Invoke-DatabaseExecuteNonQueryCommand{ param( [Parameter(Mandatory)] [string]$ServerName, [Parameter(Mandatory)] [string]$DatabaseName, [Parameter(Mandatory)] [pscredential]$Credential, [Parameter(Mandatory)] [string]$Command ) $result = $false $userName = $Credential.UserName $password = $Credential.GetNetworkCredential().Password $connectionString = "Server=tcp:$ServerName,1433;Initial Catalog=$DatabaseName;Persist Security Info=False;User ID=$userName;Password=$password;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" $sqlConnection = New-Object System.Data.SqlClient.SqlConnection $ConnectionString $cmd = New-Object System.Data.SqlClient.SqlCommand $cmd.Connection = $sqlConnection $cmd.CommandTimeout = 0 $cmd.CommandText = $Command try { $sqlConnection.Open() $cmd.ExecuteNonQuery() # | Out-Null $result = $true } catch { #$Error #$_.Exception.Message Write-Warning $_.Exception.Message #Write-Warning $Error[0] Write-Warning $Command $result = $false } finally { ## Close the connection when we're done $sqlConnection.Dispose() $cmd.Dispose() } return $result } function Get-DefaultSqlServer{ <# .SYNOPSIS List all resources for a resource group and grab the first SqlServer it can find. .DESCRIPTION List all resources for a resource group and grab the first SqlServer it can find. Will only work if connection to Azure aleasy exist. .PARAMETER ResourceGroupName The resource group where we will look for the SqlServer. .EXAMPLE Get-DefaultSqlServer -ResourceGroupName $ResourceGroupName #> param( [Parameter(Mandatory)] [string]$ResourceGroupName ) $sqlServer = Get-AzResource -ResourceGroupName $ResourceGroupName | Where-Object { $_.ResourceType -eq "Microsoft.Sql/servers"} if ($null -eq $sqlServer) { Write-Error "Could not find default SqlServer in ResourceGroup: $ResourceGroupName." exit } return $sqlServer } function Get-DefaultStorageAccount{ <# .SYNOPSIS List all resources for a resource group and grab the first StorageAccount it can find. .DESCRIPTION List all resources for a resource group and grab the first StorageAccount it can find. Will only work if connection to Azure aleasy exist. .PARAMETER ResourceGroupName The resource group where we will look for the StorageAccount. .PARAMETER StorageAccountName The name of the StorageAccount. .EXAMPLE Get-DefaultStorageAccount -ResourceGroupName $ResourceGroupName #> param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ResourceGroupName, [Parameter(Mandatory = $false)] [string] $StorageAccountName ) if ($null -eq $StorageAccountName -or "" -eq $StorageAccountName) { $storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName #$storageAccount #For debuging if ($storageAccount -is [array]){ if ($storageAccount.Count -ne 1) { if ($storageAccount.Count -gt 1) { Write-Warning "Found more then 1 StorageAccount in ResourceGroup: $ResourceGroupName." } if ($storageAccount.Count -eq 0) { Write-Warning "Could not find a StorageAccount in ResourceGroup: $ResourceGroupName." } exit } } } else { $storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName if ($null -eq $storageAccount) { Write-Error "Did not find StorageAccount in ResourceGroup: $ResourceGroupName." exit } } return $storageAccount } function Get-StorageAccountContainer{ <# .SYNOPSIS Get the container for the specified StorageAccount. .DESCRIPTION Get the container for the specified StorageAccount. Will only work if connection to Azure aleasy exist. .PARAMETER StorageAccount The StorageAccount where the container should exist. .PARAMETER ContainerName The container name. .EXAMPLE Get-StorageAccountContainer -StorageAccount $StorageAccount -ContainerName $ContainerName .EXAMPLE $storageAccount = Get-DefaultStorageAccount ResourceGroupName $ResourceGroupName Get-StorageAccountContainer -StorageAccount $storageAccount -ContainerName $ContainerName #> param( [Parameter(Mandatory)] [object]$StorageAccount, [Parameter(Mandatory)] [string]$ContainerName ) $storageContainer = Get-AzRmStorageContainer -StorageAccount $StorageAccount -ContainerName $ContainerName #$storageContainer if ($null -eq $storageContainer) { Write-Warning "Could not find a StorageAccount container '$($storageContainer.Name)' in ResourceGroup: $($StorageAccount.ResourceGroupName))." exit } else { Write-Host "Connected to destination StorageAccount container $($storageContainer.Name)" } return $storageContainer } function Connect-AzureSubscriptionAccount{ if($null -eq $azureConnection -or $null -eq $azureConnection.Account){ try{ $azureConnection = Connect-AzAccount -SubscriptionId $SubscriptionId Write-Host "Connected to subscription $SubscriptionId" } catch { $message = $_.Exception.message Write-Error $message exit } } } # END PRIVATE METHODS function New-OptimizelyCmsResourceGroup{ <# .SYNOPSIS Create a Optimizely CMS resource group in Azure. .DESCRIPTION Create a Optimizely CMS resource group in Azure. .PARAMETER SubscriptionId Your Azure SubscriptionId that you want to create the new resource group in. .PARAMETER ResourceGroupName The client secret used to access the project. .PARAMETER DatabasePassword The password to your database that will be generated. You need to follow the password policy. More information: https://docs.microsoft.com/en-us/previous-versions/azure/jj943764(v=azure.100)?redirectedfrom=MSDN .PARAMETER Tags The tags that will be set on the resource group when it is created. Ex: $resourceGroupTags = @{ "Environment"="dev"; "Owner"="ove.lartelius@epinova.se"; "App"="Optimizely"; "Client"="Customer AB"; "Project"="External Website 2021"; "ManagedBy"="ove.lartelius@epinova.se"; "Cost"="internal"; "Department"="IT"; "Expires"="2030-01-01"; } .PARAMETER ResourceGroupLocation The location where the resource group should be hosted. Default = "westeurope". You can get a complete list of location by using "Get-AzureRmLocation |Format-Table". .PARAMETER ArmTemplateUri The location where we can find your custom ARM template to use in this script. Default = https://raw.githubusercontent.com/Epinova/EpinovaAzureToolBucket-psmodule/main/ArmTemplates/epinova-azure-basic-optimizely-cms.json .EXAMPLE New-OptimizelyCmsResourceGroup -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -DatabasePassword $DatabasePassword -Tags $Tags .EXAMPLE New-OptimizelyCmsResourceGroup -SubscriptionId '95a9fd36-7851-4918-b8c9-f146a219982c' -ResourceGroupName 'mycoolwebsite' -DatabasePassword 'KXIN_rhxh3holt_s8it' -Tags @{ "Environment"="dev";"Owner"="ove.lartelius@epinova.se";"App"="Optimizely";"Client"="Client name";"Project"="Project name";"ManagedBy"="Ove Lartelius";"Cost"="Internal";"Department"="IT";"Expires"=""; } -ResourceGroupLocation = "westeurope" -ArmTemplateUri = "https://raw.githubusercontent.com/yourrepository/arm-templates/main/azure-optimizely-cms.json" #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DatabasePassword, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [hashtable] $Tags, [Parameter(Mandatory = $false)] [string] $ResourceGroupLocation = "westeurope", [Parameter(Mandatory = $false)] [string] $ArmTemplateUri = "https://raw.githubusercontent.com/Epinova/EpinovaAzureToolBucket-psmodule/main/ArmTemplates/epinova-azure-basic-optimizely-cms.json" ) $tagsString = $Tags | Out-String Write-Host "New-OptimizelyCmsResourceGroup - Inputs:----------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "ResourceGroupName: $ResourceGroupName" Write-Host "DatabasePassword: **** (it is a secret...)" Write-Host "ResourceGroupLocation: $ResourceGroupLocation" Write-Host "ARMTemplateUri: $ArmTemplateUri" Write-Host "Tags: $tagsString" Write-Host "------------------------------------------------" $databasePasswordSecureString = ConvertTo-SecureString $DatabasePassword -AsPlainText -Force ############################################################## # Login to Azure Connect-AzAccount -SubscriptionId $SubscriptionId # Try to get the resource group with the specified name Get-AzResourceGroup -Name $ResourceGroupName -ErrorVariable notPresent -ErrorAction SilentlyContinue # Check if the resource group already exist if ($notPresent){ Write-Host "Resource group $ResourceGroupName does not exist. We should be able to create it." } else { # The Resource group $resourceGroupName already exists. Throw error and exit. Write-Error "Resource group $ResourceGroupName already exists." exit } # Create resource group if ($Tags.Count -eq 0) { New-AzResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation } else { New-AzResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation -Tag $Tags # Set tags on resource group $resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName New-AzTag -ResourceId $resourceGroup.ResourceId -Tag $Tags } Get-AzResourceGroup -Name $ResourceGroupName -ErrorVariable notPresent -ErrorAction SilentlyContinue if ($notPresent){ Write-Error "Resource group $ResourceGroupName could not be created. Will cancel resource group deployment." exit } else { # Create resources from deployment template New-AzResourceGroupDeployment -ResourceGroupName $ResourceGroupName -TemplateUri $ArmTemplateUri -sqlserverAdminLoginPassword $databasePasswordSecureString } } function Get-OptimizelyCmsConnectionStrings{ <# .SYNOPSIS Get and print the connection strings for specified resource group. .DESCRIPTION Get and print the connection strings for specified resource group. .PARAMETER SubscriptionId Your Azure SubscriptionId that holds the resource group. .PARAMETER ResourceGroupName The name of the resource group. .PARAMETER DatabasePassword The password to your database. The password that you specified when you created the database. .PARAMETER DatabaseName If you have used your own ARM template. You may have used another database name then Epinovas example template. Then specify the name of the database. .EXAMPLE Get-OptimizelyCmsConnectionStrings -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -DatabasePassword $DatabasePassword .EXAMPLE Get-OptimizelyCmsConnectionStrings -SubscriptionId "95a9fd36-7851-4918-b8c9-f146a219982c" -ResourceGroupName $ResourceGroupName -DatabasePassword $DatabasePassword #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DatabasePassword, [Parameter(Mandatory = $false)] [string] $DatabaseName = "" ) Write-Host "Get-EpiserverCmsConnectionStrings - Inputs:----------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "ResourceGroupName: $ResourceGroupName" Write-Host "DatabasePassword: **** (it is a secret...)" Write-Host "-----------------------------------------------------" # Login to Azure Connect-AzAccount -SubscriptionId $subscriptionId $servicebusName = "$resourceGroupName-servicebus" $servicebusKeys = Get-AzServiceBusKey -ResourceGroup $resourceGroupName -NamespaceName $servicebusName -AuthorizationRuleName "RootManageSharedAccessKey" #$servicebusKeys if ($null -ne $servicebusKeys){ Write-Host "<add name=""EPiServerAzureEvents"" connectionString=""$($servicebusKeys.PrimaryConnectionString)"" />" } else { Write-Warning "Could not find connection string for servicebus with the name $servicebusName in resource group $resourceGroupName." } $storageAccount = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -AccountName $resourceGroupName if ($null -ne $storageAccount){ Write-Host "<add name=""EPiServerAzureBlobs"" connectionString=""DefaultEndpointsProtocol=https;AccountName=$resourceGroupName;AccountKey=$($storageAccount[0].Value);EndpointSuffix=core.windows.net"" />" } else { Write-Warning "Could not find connection string for storage account with the name $resourceGroupName in resource group $resourceGroupName." } $sqlServerName = "$resourceGroupName-sqlserver" if ($DatabaseName -eq ""){ $sqlDatabaseName = "$($resourceGroupName)opticms" } else { $sqlDatabaseName = $DatabaseName } Write-Host "<add name=""EPiServerDB"" connectionString=""Server=tcp:$sqlServerName.database.windows.net,1433;Initial Catalog=$sqlDatabaseName;Persist Security Info=False;User ID=$resourceGroupName-sa;Password=$databasePassword;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"" providerName=""System.Data.SqlClient"" />" } function New-EpiserverCmsResourceGroup{ <# .SYNOPSIS Create a Episerver CMS resource group in Azure. .DESCRIPTION Create a Episerver CMS resource group in Azure. .PARAMETER SubscriptionId Your Azure SubscriptionId that you want to create the new resource group in. .PARAMETER ResourceGroupName The client secret used to access the project. .PARAMETER DatabasePassword The password to your database that will be generated. You need to follow the password policy. More information: https://docs.microsoft.com/en-us/previous-versions/azure/jj943764(v=azure.100)?redirectedfrom=MSDN .PARAMETER Tags The tags that will be set on the resource group when it is created. Ex: $resourceGroupTags = @{ "Environment"="dev"; "Owner"="ove.lartelius@epinova.se"; "App"="Episerver"; "Client"="Customer AB"; "Project"="External Website 2021"; "ManagedBy"="ove.lartelius@epinova.se"; "Cost"="internal"; "Department"="IT"; "Expires"="2030-01-01"; } .PARAMETER ResourceGroupLocation The location where the resource group should be hosted. Default = "westeurope". You can get a complete list of location by using "Get-AzureRmLocation |Format-Table". .PARAMETER ArmTemplateUri The location where we can find your custom ARM template to use in this script. Default = https://raw.githubusercontent.com/Epinova/EpinovaAzureToolBucket-psmodule/main/ArmTemplates/epinova-azure-basic-episerver-cms.json .EXAMPLE New-EpiserverCmsResourceGroup -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -DatabasePassword $DatabasePassword -Tags $Tags .EXAMPLE New-EpiserverCmsResourceGroup -SubscriptionId '95a9fd36-7851-4918-b8c9-f146a219982c' -ResourceGroupName 'mycoolwebsite' -DatabasePassword 'KXIN_rhxh3holt_s8it' -Tags @{ "Environment"="dev";"Owner"="ove.lartelius@epinova.se";"App"="Episerver";"Client"="Client name";"Project"="Project name";"ManagedBy"="Ove Lartelius";"Cost"="Internal";"Department"="IT";"Expires"=""; } -ResourceGroupLocation = "westeurope" -ArmTemplateUri = "https://raw.githubusercontent.com/yourrepository/arm-templates/main/azure-episerver-cms.json" #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DatabasePassword, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [hashtable] $Tags, [Parameter(Mandatory = $false)] [string] $ResourceGroupLocation = "westeurope", [Parameter(Mandatory = $false)] [string] $ArmTemplateUri = "https://raw.githubusercontent.com/Epinova/EpinovaAzureToolBucket-psmodule/main/ArmTemplates/epinova-azure-basic-episerver-cms.json" ) Write-Host "New-EpiserverCmsResourceGroup - Inputs:----------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "ResourceGroupName: $ResourceGroupName" Write-Host "DatabasePassword: **** (it is a secret...)" Write-Host "Tags: $Tags" Write-Host "ResourceGroupLocation: $ResourceGroupLocation" Write-Host "ARMTemplateUri: $ArmTemplateUri" Write-Host "------------------------------------------------" Write-Warning "You should start using New-OptimizelyCmsResourceGroup" $databasePasswordSecureString = ConvertTo-SecureString $DatabasePassword -AsPlainText -Force ############################################################## # Login to Azure Connect-AzAccount -SubscriptionId $SubscriptionId # Try to get the resource group with the specified name Get-AzResourceGroup -Name $ResourceGroupName -ErrorVariable notPresent -ErrorAction SilentlyContinue # Check if the resource group already exist if ($notPresent){ Write-Host "Resource group $ResourceGroupName does not exist." } else { # The Resource group $resourceGroupName already exists. Throw error and exit. Write-Error "Resource group $ResourceGroupName already exists." exit } # Create resource group if ($Tags.Count -eq 0) { New-AzResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation } else { New-AzResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation -Tag $Tags # Set tags on resource group $resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName New-AzTag -ResourceId $resourceGroup.ResourceId -Tag $Tags } # Create resources from deployment template New-AzResourceGroupDeployment -ResourceGroupName $ResourceGroupName -TemplateUri https://raw.githubusercontent.com/ovelartelius/epinova-arm-templates/main/epinova-azure-episerver-cms.json -sqlserverAdminLoginPassword $databasePasswordSecureString } function Get-EpiserverCmsConnectionStrings{ <# .SYNOPSIS Get and print the connection strings for specified resource group. .DESCRIPTION Get and print the connection strings for specified resource group. .PARAMETER SubscriptionId Your Azure SubscriptionId that holds the resource group. .PARAMETER ResourceGroupName The name of the resource group. .PARAMETER DatabasePassword The password to your database. The password that you specified when you created the database. .PARAMETER DatabaseName If you have used your own ARM template. You may have used another database name then Epinovas example template. Then specify the name of the database. .EXAMPLE Get-EpiserverCmsConnectionStrings -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -DatabasePassword $DatabasePassword .EXAMPLE Get-EpiserverCmsConnectionStrings -SubscriptionId "95a9fd36-7851-4918-b8c9-f146a219982c" -ResourceGroupName $ResourceGroupName -DatabasePassword $DatabasePassword #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DatabasePassword, [Parameter(Mandatory = $false)] [string] $DatabaseName = "" ) Write-Host "Get-EpiserverCmsConnectionStrings - Inputs:----------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "ResourceGroupName: $ResourceGroupName" Write-Host "DatabasePassword: **** (it is a secret...)" Write-Host "-----------------------------------------------------" Write-Warning "You should start using Get-OptimizelyCmsConnectionStrings" # Login to Azure Connect-AzAccount -SubscriptionId $subscriptionId $servicebusName = "$resourceGroupName-servicebus" $servicebusKeys = Get-AzServiceBusKey -ResourceGroup $resourceGroupName -NamespaceName $servicebusName -AuthorizationRuleName "RootManageSharedAccessKey" #$servicebusKeys if ($null -ne $servicebusKeys){ Write-Host "<add name=""EPiServerAzureEvents"" connectionString=""$($servicebusKeys.PrimaryConnectionString)"" />" } else { Write-Warning "Could not find connection string for servicebus with the name $servicebusName in resource group $resourceGroupName." } $storageAccount = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -AccountName $resourceGroupName if ($null -ne $storageAccount){ Write-Host "<add name=""EPiServerAzureBlobs"" connectionString=""DefaultEndpointsProtocol=https;AccountName=$resourceGroupName;AccountKey=$($storageAccount[0].Value);EndpointSuffix=core.windows.net"" />" } else { Write-Warning "Could not find connection string for storage account with the name $resourceGroupName in resource group $resourceGroupName." } $sqlServerName = "$resourceGroupName-sqlserver" if ($DatabaseName -eq ""){ $sqlDatabaseName = "$($resourceGroupName)epicms" } else { $sqlDatabaseName = $DatabaseName } Write-Host "<add name=""EPiServerDB"" connectionString=""Server=tcp:$sqlServerName.database.windows.net,1433;Initial Catalog=$sqlDatabaseName;Persist Security Info=False;User ID=$resourceGroupName-sa;Password=$databasePassword;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"" providerName=""System.Data.SqlClient"" />" } function Add-AzureDatabaseUser{ <# .SYNOPSIS Create a database user for a specific database on a Azure SQL Server instance. .DESCRIPTION Create a database user for a specific database on a Azure SQL Server instance. .PARAMETER SqlServer The Azure SQL Server that you want to connect to. Example: mycompany-dev.database.windows.net .PARAMETER SqlServerUsername The username for your SQL Server administrator account. .PARAMETER SqlServerPassword The Password for your SQL Server administrator account. .PARAMETER TargetDatabase The database where you want to create a login/user.. .PARAMETER NewUsername The username for the new user that you want to create in the target database. .PARAMETER NewPassword The password for the new user that you want to create in the target database. .PARAMETER NewUserPermission The role that the new user/login will have on the database. Example: db_owner .EXAMPLE Add-AzureDatabaseUser -SqlServer $SqlServer -SqlServerUsername $SqlServerUsername -SqlServerPassword $SqlServerPassword -TargetDatabase $TargetDatabase -NewUsername $NewUsername -NewPassword $NewPassword -NewUserPermission $NewUserPermission .EXAMPLE Add-AzureDatabaseUser -SqlServer "project1-dev.database.windows.net" -SqlServerUsername "project1-sa" -SqlServerPassword "@wes0mep@ssw0rd" -TargetDatabase "project1-cms-dev" -NewUsername "project1dbuser" -NewPassword "mynew@wes0mep@ssw0rd" -NewUserPermission "db_owner" Example connections string: Server=tcp:project1-dev.database.windows.net,1433;Initial Catalog=project1-cms-dev;Persist Security Info=False;User ID=project1-sa;Password=@wes0mep@ssw0rd;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SqlServer, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SqlServerUsername, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SqlServerPassword, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $TargetDatabase, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $NewUsername, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $NewPassword, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $NewUserPermission ) Write-Host "Add-AzureDatabaseUser - Inputs:----------" Write-Host "SqlServer: $SqlServer" Write-Host "SqlServerUsername: $SqlServerUsername" Write-Host "SqlServerPassword: $SqlServerPassword" Write-Host "TargetDatabase: $TargetDatabase" Write-Host "NewUsername: $NewUsername" Write-Host "NewPassword: $NewPassword" Write-Host "NewUserPermission: $NewUserPermission" Write-Host "-----------------------------------------" if ($NewUsername.Contains("-")){ Write-Error "NewUsername contains '-' chars. Please remove them and try again." exit } # Test the connection to the master database with the administrator account login $password = ConvertTo-SecureString $SqlServerPassword -AsPlainText -Force $credentials = New-Object PSCredential $SqlServerUsername, $password $testConnection = Test-AzureSqlConnection -ServerName $SqlServer -DatabaseName "master" -Credential $credentials if ($true -eq $testConnection) { Write-Host "Connection to SQL Server is working." } else { Write-Error "Connection to SQL Server is not working." exit } Write-Host " " $command = "CREATE LOGIN $NewUsername WITH password='$NewPassword'" $createUserResult = Invoke-DatabaseExecuteNonQueryCommand -ServerName $SqlServer -DatabaseName "master" -Credential $credentials -Command $command if ($false -eq $createUserResult) { Write-Error "Could not create the login." exit } $command = "CREATE USER $NewUsername FROM LOGIN $NewUsername" $createUserResult = Invoke-DatabaseExecuteNonQueryCommand -ServerName $SqlServer -DatabaseName $TargetDatabase -Credential $credentials -Command $command if ($false -eq $createUserResult) { Write-Error "Could not create the user." exit } $command = "EXEC sp_addrolemember '$NewUserPermission', '$NewUsername'" $createUserResult = Invoke-DatabaseExecuteNonQueryCommand -ServerName $SqlServer -DatabaseName $TargetDatabase -Credential $credentials -Command $command if ($true -eq $createUserResult) { Write-Host "Your new user has been created." } else { Write-Error "Could not set permission for the new user/login." exit } Write-Host " " $password = ConvertTo-SecureString $newPassword -AsPlainText -Force $newCredentials = New-Object PSCredential $newUsername, $password $testNewUserResult = Test-AzureSqlConnection -ServerName $sqlServer -DatabaseName $targetDatabase -Credential $newCredentials if ($true -eq $testNewUserResult) { Write-Host "Connection to target database with new user succedded." Write-Host " " Write-Host "Result:----------------------------" Write-Host "SqlServer: $sqlServer" Write-Host "Database: $targetDatabase" Write-Host "Username: $newUsername" Write-Host "Password: $newPassword" Write-Host "ConnectionString: <add name=`"EPiServerDB`" connectionString=`"Server=tcp:$sqlServer,1433;Initial Catalog=$targetDatabase;Persist Security Info=False;User ID=$newUsername;Password=$newPassword;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;`" providerName=`"System.Data.SqlClient`" />" Write-Host "--- JOBS DONE! ---" } else { Write-Warning "The user has been created but we can not login with the new user information. Please look into the problem and figure out why." exit } Write-Host "--- THE END ---" } function Backup-Database{ <# .SYNOPSIS Backup a database and store in storage account container. .DESCRIPTION Backup a database and store in storage account container. .PARAMETER SubscriptionId Your Azure SubscriptionId where the database exist that you want to backup. .PARAMETER ResourceGroupName The resource group name where the database exist that you want to backup. .PARAMETER SqlServerName The Sql Server that contain the database that you want to backup. If empty we will try to find the first SqlServer resource in the specified resource group. .PARAMETER SqlDatabaseName Name of the database that should be backed up. .PARAMETER SqlDatabaseLogin Administrator username for the SqlServer. .PARAMETER SqlDatabasePassword Administrator password for the SqlServer. .PARAMETER StorageAccountName The StorageAccount that should hold the BACPAC file after backup. If empty we will try to find the first StorageAccount resource in the specified resource group. .PARAMETER StorageAccountContainer The StorageAccount container name that should hold the BACPAC file after backup. If empty we will try to find the container with name "db-backups". .EXAMPLE Invoke-AzureDatabaseBackup -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -SqlServerName $SqlServerName -SqlDatabaseName $SqlDatabaseName -SqlDatabaseLogin $SqlDatabaseLogin -SqlDatabasePassword $SqlDatabasePassword -StorageAccountName $StorageAccountName -StorageAccountContainer $StorageAccountContainer #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory = $false)] [string] $SqlServerName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SqlDatabaseName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SqlDatabaseLogin, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SqlDatabasePassword, [Parameter(Mandatory = $false)] [string] $StorageAccountName, [Parameter(Mandatory = $false)] [string] $StorageAccountContainer ) $securePassword = ConvertTo-SecureString -String $SqlDatabasePassword -AsPlainText -Force $sqlCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $SqlDatabaseLogin, $securePassword Connect-AzureSubscriptionAccount if ($null -eq $StorageAccountName -or "" -eq $StorageAccountName){ $storageAccount = Get-DefaultStorageAccount -ResourceGroupName $ResourceGroupName $storageAccountName = $storageAccount.StorageAccountName } else { $storageAccount = Get-DefaultStorageAccount -ResourceGroupName $ResourceGroupName -StorageAccountName $StorageAccountName $storageAccountName = $storageAccount.StorageAccountName } Write-Host "Found StorageAccount '$storageAccountName'" if ($null -eq $StorageAccountContainer -or "" -eq $StorageAccountContainer){ $storageContainer = Get-StorageAccountContainer -StorageAccount $storageAccount -ContainerName $StorageAccountContainer $storageContainerName = $storageContainer.Name } else { $storageContainerName = $StorageAccountContainer } Write-Host "Found StorageAccount container '$storageContainerName'" if ($null -eq $SqlServerName -or "" -eq $SqlServerName) { $SqlServerName = Get-DefaultSqlServer -ResourceGroupName $ResourceGroupName } Write-Host "Found SqlServer '$SqlServerName'" # Fix some information about the destination storage account $bacpacFilename = $SqlDatabaseName + "_" + (Get-Date).ToString("yyyy-MM-dd-HH-mm") + ".bacpac" $storageKeyType = "StorageAccessKey" $storageKey = (Get-AzStorageAccountKey -ResourceGroupName $ResourceGroupName -AccountName $storageAccountName)| Where-Object {$_.KeyName -eq "key2"} $baseStorageUri = "https://" + $storageAccountName + ".blob.core.windows.net" $bacpacUri = $baseStorageUri + "/" + $storageContainerName + "/" + $bacpacFilename Write-Host "Invoke-AzureDatabaseBackup - Inputs:----------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "ResourceGroupName: $ResourceGroupName" Write-Host "SqlServerName: $SqlServerName" Write-Host "SqlDatabaseName: $SqlDatabaseName" Write-Host "ResourceGroupName: $ResourceGroupName" Write-Host "SqlDatabaseLogin: $SqlDatabaseLogin" Write-Host "SqlDatabasePassword: **** (it is a secret...)" Write-Host "StorageAccountName: $storageAccountName" Write-Host "StorageAccountContainer: $storageContainerName" Write-Host "StorageKey: $($storageKey.Value))" Write-Host "Bacpac file: $bacpacFilename" Write-Host "Bacpac URI: $bacpacUri" Write-Host "------------------------------------------------" # Do a database backup of the destination database. $exportRequest = New-AzSqlDatabaseExport -ResourceGroupName $ResourceGroupName -ServerName $SqlServerName -DatabaseName $SqlDatabaseName -StorageKeyType $storageKeyType -StorageKey $storageKey.Value -StorageUri $bacpacUri -AdministratorLogin $sqlCredentials.UserName -AdministratorLoginPassword $sqlCredentials.Password if ($null -ne $exportRequest) { $operationStatusLink = $exportRequest.OperationStatusLink $operationStatusLink $exportStatus = Get-AzSqlDatabaseImportExportStatus -OperationStatusLink $operationStatusLink [Console]::Write("Exporting.") $lastStatusMessage = "" while ($exportStatus.Status -eq "InProgress") { Start-Sleep -s 10 $exportStatus = Get-AzSqlDatabaseImportExportStatus -OperationStatusLink $operationStatusLink if ($lastStatusMessage -ne $exportStatus.StatusMessage) { $lastStatusMessage = $exportStatus.StatusMessage $progress = $lastStatusMessage.Replace("Running, Progress = ", "") [Console]::Write($progress) } [Console]::Write(".") } [Console]::WriteLine("") $exportStatus Write-Host "Database '$SqlDatabaseName' is backed up. '$bacpacUri'" } else { Write-Error "Could not start backup of $SqlDatabaseName" exit } } function Copy-Database{ <# .SYNOPSIS Copy a database from one place to another. .DESCRIPTION Copy a database from one place to another. If the destination database exist it will be 'overwritten'. You can decide if you want to make a backup of the destination database before it is dropped. .PARAMETER SubscriptionId Your Azure SubscriptionId where the databases exist that you want to copy. .PARAMETER SourceResourceGroupName The resource group name where the source database exist that we want to copy. .PARAMETER SourceSqlServerName The Sql Server that contain the database that you want to copy. If empty we will try to find the first SqlServer resource in the specified source resource group. .PARAMETER SourceSqlDatabaseName Name of the database that should be copied. .PARAMETER DestinationResourceGroupName The resource group name where the destination database should be copied to. .PARAMETER DestinationSqlServerName The destination Sql server name. If empty we will try to find the first SqlServer resource in the specified destination resource group. .PARAMETER DestinationSqlDatabaseName The destination database name. .PARAMETER DestinationRunDatabaseBackup If the destination database exist and this param is true a backup of the database will be made first. .PARAMETER DestinationSqlDatabaseLogin Destination Sql server administrator username. Only needed if you want to make a backup of destination database. .PARAMETER DestinationSqlDatabasePassword Destination Sql server administrator password. Only needed if you want to make a backup of destination database. .PARAMETER DestinationStorageAccountName The StorageAccount that should hold the BACPAC file for backup. If empty we will try to find the first StorageAccount resource in the specified destination resource group. .PARAMETER DestinationStorageAccountContainer The StorageAccount container name that should hold the BACPAC file backup. If empty we will try to find the container with name "db-backups". .EXAMPLE Invoke-AzureDatabaseCopy -SubscriptionId $SubscriptionId -SourceResourceGroupName $SourceResourceGroupName -SourceSqlServerName $SourceSqlServerName -SourceSqlDatabaseName $SourceSqlDatabaseName -DestinationResourceGroupName $DestinationResourceGroupName -DestinationSqlServerName $DestinationSqlServerName -DestinationSqlDatabaseName $DestinationSqlDatabaseName -DestinationRunDatabaseBackup $DestinationRunDatabaseBackup .EXAMPLE Invoke-AzureDatabaseCopy -SubscriptionId $SubscriptionId -SourceResourceGroupName $SourceResourceGroupName -SourceSqlServerName $SourceSqlServerName -SourceSqlDatabaseName $SourceSqlDatabaseName -DestinationResourceGroupName $DestinationResourceGroupName -DestinationSqlServerName $DestinationSqlServerName -DestinationSqlDatabaseName $DestinationSqlDatabaseName -DestinationRunDatabaseBackup $DestinationRunDatabaseBackup -DestinationSqlDatabaseLogin $DestinationSqlDatabaseLogin -DestinationSqlDatabasePassword $DestinationSqlDatabasePassword -DestinationStorageAccount $DestinationStorageAccount -DestinationStorageAccountContainer $DestinationStorageAccountContainer #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SourceResourceGroupName, [Parameter(Mandatory = $false)] [string] $SourceSqlServerName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SourceSqlDatabaseName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DestinationResourceGroupName, [Parameter(Mandatory = $false)] [string] $DestinationSqlServerName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DestinationSqlDatabaseName, [Parameter(Mandatory = $true)] [bool] $DestinationRunDatabaseBackup, [Parameter(Mandatory = $false)] [string] $DestinationSqlDatabaseLogin, [Parameter(Mandatory = $false)] [string] $DestinationSqlDatabasePassword, [Parameter(Mandatory = $false)] [string] $DestinationStorageAccount, [Parameter(Mandatory = $false)] [string] $DestinationStorageAccountContainer ) Connect-AzureSubscriptionAccount if ($null -eq $SourceSqlServerName -or "" -eq $SourceSqlServerName) { $SourceSqlServerName = Get-DefaultSqlServer -ResourceGroupName $SourceResourceGroupName } Write-Host "Found source SqlServer '$SourceSqlServerName'" if ($null -eq $DestinationSqlServerName -or "" -eq $DestinationSqlServerName) { $DestinationSqlServerName = Get-DefaultSqlServer -ResourceGroupName $DestinationSqlServerName } Write-Host "Found destination SqlServer '$DestinationSqlServerName'" $destinationDatabaseExist = $false $destinationDatabaseResult = Get-AzSqlDatabase -ResourceGroupName $DestinationResourceGroupName -ServerName $DestinationSqlServerName -DatabaseName $DestinationSqlDatabaseName if ($null -ne $destinationDatabaseResult) { $destinationDatabaseExist = $true Write-Host "Destination database exist." } Write-Host "Invoke-AzureDatabaseCopy - Inputs:----------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "SourceResourceGroupName: $SourceResourceGroupName" Write-Host "SourceSqlServerName: $SourceSqlServerName" Write-Host "SourceSqlDatabaseName: $SourceSqlDatabaseName" Write-Host "DestinationResourceGroupName: $DestinationResourceGroupName" Write-Host "DestinationSqlServerName: $DestinationSqlServerName" Write-Host "DestinationSqlDatabaseName: $DestinationSqlDatabaseName" Write-Host "DestinationRunDatabaseBackup: $DestinationRunDatabaseBackup" Write-Host "DestinationSqlDatabaseLogin: $DestinationSqlDatabaseLogin" Write-Host "DestinationSqlDatabasePassword: **** (it is a secret...)" Write-Host "DestinationDatabaseExist: $destinationDatabaseExist" Write-Host "DestinationStorageAccount: $DestinationStorageAccount" Write-Host "DestinationStorageAccountContainer: $DestinationStorageAccountContainer" Write-Host "------------------------------------------------" if ($true -eq $destinationDatabaseExist -and $true -eq $DestinationRunDatabaseBackup) { $missingParam = $false if($null -eq $DestinationSqlDatabaseLogin -or "" -eq $DestinationSqlDatabaseLogin) { Write-Warning "You want to make a destination database backup and missing the -DestinationSqlDatabaseLogin param." $missingParam = $true } if($null -eq $DestinationSqlDatabasePassword -or "" -eq $DestinationSqlDatabasePassword) { Write-Warning "You want to make a destination database backup and missing the DestinationSqlDatabasePassword param." $missingParam = $true } if ($true -eq $missingParam) { Write-Error "Parameters is missing." exit } Backup-Database -SubscriptionId $SubscriptionId -ResourceGroupName $DestinationResourceGroupName -SqlServerName $DestinationSqlServerName -SqlDatabaseName $DestinationSqlDatabaseName -SqlDatabaseLogin $DestinationSqlDatabaseLogin -SqlDatabasePassword $DestinationSqlDatabasePassword -StorageAccountName $DestinationStorageAccount -StorageAccountContainer $DestinationStorageAccountContainer } # Drop destination database if exist if ($true -eq $destinationDatabaseExist) { Write-Host "Start droping destination database '$DestinationSqlDatabaseName'." Remove-AzSqlDatabase -ResourceGroupName $DestinationResourceGroupName -ServerName $DestinationSqlServerName -DatabaseName $DestinationSqlDatabaseName Write-Host "Droped destination database '$DestinationSqlDatabaseName'." } # Copy the source database to destination database Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "true" Write-Host "Start copying database '$SourceSqlDatabaseName' to '$DestinationSqlDatabaseName'." $databaseCopy = New-AzSqlDatabaseCopy -ResourceGroupName $SourceResourceGroupName -ServerName $SourceSqlServerName -DatabaseName $SourceSqlDatabaseName -CopyResourceGroupName $DestinationResourceGroupName -CopyServerName $DestinationSqlServerName -CopyDatabaseName $DestinationSqlDatabaseName #$databaseCopy Write-Host "Database '$SourceSqlDatabaseName' is copied to '$DestinationSqlDatabaseName'." # # Check the SKU on destination database after copy. # $destinationDatabaseResult = Get-AzSqlDatabase -ResourceGroupName $DestinationResourceGroupName -ServerName $DestinationSqlServerName -DatabaseName $DestinationSqlDatabaseName # $destinationDatabaseResult } function Remove-Blobs{ <# .SYNOPSIS Remove all blobs found in the specified StorageAccount container. .DESCRIPTION Remove all blobs found in the specified StorageAccount container. .PARAMETER SubscriptionId Your Azure SubscriptionId where we can find your StorageAccount. .PARAMETER ResourceGroupName The resource group name where the blobs are that you want to remove. .PARAMETER StorageAccountName The StorageAccount name where the blobs are that you want to remove. .PARAMETER ContainerName The container name where the blobs are that you want to remove. .EXAMPLE Remove-Blobs -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -StorageAccountName $StorageAccountName -ContainerName $ContainerName .EXAMPLE Remove-Blobs -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -ContainerName $ContainerName #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory = $true)] [string] $StorageAccountName, [Parameter(Mandatory = $true)] [string] $ContainerName ) Connect-AzureSubscriptionAccount Write-Host "Remove-Blobs - Inputs:----------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "ResourceGroupName: $ResourceGroupName" Write-Host "StorageAccountName: $StorageAccountName" Write-Host "ContainerName: $ContainerName" Write-Host "------------------------------------------------" $storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName $context = $storageAccount.Context (Get-AzStorageBlob -Container $ContainerName -Context $context | Sort-Object -Property LastModified -Descending) | Remove-AzStorageBlob Write-Host "Remove-Blobs finished" } function Copy-Blobs{ <# .SYNOPSIS Copy all blobs from a StorageAccount container to another. .DESCRIPTION Copy all blobs from a StorageAccount container to another. .PARAMETER SubscriptionId Your Azure SubscriptionId where we can find your blobs. .PARAMETER SourceResourceGroupName The resource group name where the blobs are that we want to copy. .PARAMETER SourceStorageAccountName The StorageAccount name where the blobs are that we want to copy. .PARAMETER SourceContainerName The container name where the blobs are that we want to copy. .PARAMETER DestinationResourceGroupName The destination group name where the blobs should be moved. .PARAMETER DestinationStorageAccountName The destination StorageAccount where the blobs should be moved. .PARAMETER DestinationContainerName The destination container name where the blobs should be moved. .EXAMPLE Copy-Blobs -SubscriptionId $SubscriptionId -SourceResourceGroupName $SourceResourceGroupName -SourceStorageAccountName $SourceStorageAccountName -SourceContainerName $SourceContainerName -DestinationResourceGroupName $DestinationResourceGroupName -DestinationStorageAccountName $DestinationStorageAccountName -DestinationContainerName $DestinationContainerName #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SourceResourceGroupName, [Parameter(Mandatory = $true)] [string] $SourceStorageAccountName, [Parameter(Mandatory = $true)] [string] $SourceContainerName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DestinationResourceGroupName, [Parameter(Mandatory = $true)] [string] $DestinationStorageAccountName, [Parameter(Mandatory = $true)] [string] $DestinationContainerName ) Connect-AzureSubscriptionAccount Write-Host "Copy-Blobs - Inputs:----------------------------" Write-Host "SubscriptionId: $SubscriptionId" Write-Host "SourceResourceGroupName: $SourceResourceGroupName" Write-Host "SourceStorageAccountName: $SourceStorageAccountName" Write-Host "SourceContainerName: $SourceContainerName" Write-Host "DestinationResourceGroupName: $DestinationResourceGroupName" Write-Host "DestinationStorageAccountName: $DestinationStorageAccountName" Write-Host "DestinationContainerName: $DestinationContainerName" Write-Host "------------------------------------------------" $sourceStorageAccount = Get-AzStorageAccount -ResourceGroupName $SourceResourceGroupName -Name $SourceStorageAccountName $sourceContext = $sourceStorageAccount.Context $destinationStorageAccount = Get-AzStorageAccount -ResourceGroupName $DestinationResourceGroupName -Name $DestinationStorageAccountName $destinationContext = $destinationStorageAccount.Context Get-AzStorageBlob -Container $SourceContainerName -Context $sourceContext | Start-AzStorageBlobCopy -DestContainer $DestinationContainerName -Context $destinationContext Write-Host "Copy-Blobs finished" } Export-ModuleMember -Function @( 'New-OptimizelyCmsResourceGroup', 'Get-OptimizelyCmsConnectionStrings', 'New-EpiserverCmsResourceGroup', 'Get-EpiserverCmsConnectionStrings', 'Add-AzureDatabaseUser', 'Backup-Database', 'Copy-Database', 'Remove-Blobs', 'Copy-Blobs' ) |