PSDB.functions.ps1

function _setDefaultResource {
    param (
        [string] $ResourceName,
        [object[]] $Resources
    )
    if ($Resources.Count -gt 1) {
        $Resources = $Resources -join ","
    }
    [System.Environment]::SetEnvironmentVariable("PSDB_$($ResourceName.ToUpper())", $Resources, "Process")
}
function _getDefaultSubscription {
    return $env:PSDB_SUBSCRIPTION
}
function _getDefaultSubscriptions {
    return ($env:PSDB_SUBSCRIPTIONS -split ",")
}
# Using this function only for tab completers.
function _getResources {
    param (
        [switch] $ResourceGroups,
        [switch] $SqlServers,
        [switch] $StorageAccounts,
        [switch] $KeyVaults,
        [switch] $SqlDatabases
    )
    if ($ResourceGroups) {
        $rsgs = $env:PSDB_RESOURCEGROUPS -split ","
        if (-not $rsgs) {
            $resources = Get-AzResource
            $resourceGroupNames = $resources.ResourceGroupName | Select-Object -Unique
            _setDefaultResource -ResourceName "ResourceGroups" -Resources $resourceGroupNames
            [PSDBResources]::ResourceGroups = $env:PSDB_RESOURCEGROUPS  -split ","
            return [PSDBResources]::ResourceGroups
        } else {
            return $rsgs
        }
    }
    if ($SqlServers) {
        $sql = $env:PSDB_SQLSERVERS -split ","
        if (-not $sql) {
            $resources = Get-AzResource
            $servers = $resources | Where-Object {$_.ResourceId -like "*Microsoft.Sql*" -and $_.Name -notlike "*/*"} | Select-Object Name
            _setDefaultResource -ResourceName "SqlServers" -Resources $servers.Name
            [PSDBResources]::SqlServers = $env:PSDB_SQLSERVERS -split ","
            return [PSDBResources]::SqlServers
        } else {
            return $sql
        }
    }
    if ($StorageAccounts) {
        $storage = $env:PSDB_STORAGEACCOUNTS -split ","
        if (-not $storage) {
            $resources = @()
            $currentContext = (Get-AzContext).Subscription.Name
            $subscriptions = if ([string]::IsNullOrEmpty($env:PSDB_SUBSCRIPTIONS)) { (Get-AzContext -ListAvailable).Subscription.Name } else { _getDefaultSubscriptions }
            $subscriptions | ForEach-Object { Set-AzContext -Subscription $_ > $null; $resources += Get-AzResource }
            Set-AzContext -Subscription $currentContext
            # $resources = Get-AzResource
            $accounts = $resources | Where-Object {$_.ResourceId -like "*Microsoft.Storage*"} | Select-Object Name
            _setDefaultResource -ResourceName "StorageAccounts" -Resources $accounts.Name
            [PSDBResources]::StorageAccounts = $env:PSDB_STORAGEACCOUNTS -split ","
            return [PSDBResources]::StorageAccounts
        } else {
            return $storage
        }
    }
    if ($KeyVaults) {
        $kvs = $env:PSDB_KEYVAULTS -split ","
        if (-not $kvs) {
            $resources = Get-AzResource
            $kVaults = $resources | Where-Object {$_.ResourceId -like "*Microsoft.KeyVault*"} | Select-Object Name
            _setDefaultResource -ResourceName "KeyVaults" -Resources $kVaults.Name
            [PSDBResources]::KeyVaults = $env:PSDB_KEYVAULTS -split ","
            return [PSDBResources]::KeyVaults
        } else {
            return $kvs
        }
    }
    if ($SqlDatabases) {
        $dbs = $env:PSDB_DATABASES -split ","
        if (-not $dbs) {
            $resources = Get-AzResource
            $databases = $resources | Where-Object {$_.ResourceType -eq "Microsoft.Sql/servers/databases"} | Select-Object Name
            $databases = $databases | Where-Object { $_.Name -notlike "*master*" }
            _setDefaultResource -ResourceName "DATABASES" -Resources $databases.Name.Split("/")[1]
            [PSDBResources]::SqlDatabases = $env:PSDB_DATABASES -split ","
            return [PSDBResources]::SqlDatabases
        } else {
            return $dbs
        }
    }
    return Get-AzResource
}
function _getStorageAccountKey {
    param (
        [string] $StorageAccountName
    )
    $storageAccounts = Get-AzStorageAccount
    $storage = $storageAccounts | Where-Object {$_.StorageAccountName -eq $StorageAccountName} | Select-Object ResourceGroupName
    $keys = Get-AzStorageAccountKey -ResourceGroupName $storage.ResourceGroupName -Name $StorageAccountName
    return $keys.Value[1]
}
function _getStorageUri {
    param (
        [string] $StorageAccountName,
        [string] $StorageContainerName
    )
    $key = _getStorageAccountKey -StorageAccountName $StorageAccountName
    $context = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $key
    $container = Get-AzStorageContainer -Name $StorageContainerName -Context $context
    return $container.CloudBlobContainer.Uri.AbsoluteUri
}
function _getBacpacName {
    param(
        [string] $DatabaseName
    )
    if ([string]::IsNullOrEmpty($DatabaseName)) {
        return "-$(Get-Date -UFormat %Y-%m-%d-%H-%M).bacpac"
    } else {
        return "$DatabaseName-$(Get-Date -UFormat %Y-%m-%d-%H-%M).bacpac"
    }
}
function _clearDefaults {
    $env:PSDB_RESOURCEGROUPNAME = $null
    $env:PSDB_RESOURCEGROUPS = $null
    $env:PSDB_SQLSERVERS = $null
    # $env:PSDB_STORAGEACCOUNTS = $null
    $env:PSDB_DATABASES = $null
    $env:PSDB_SUBSCRIPTION = $null
    # $env:PSDB_SUBSCRIPTIONS = $null
}
function _getLatestBacPacFile {
    param (
        [string] $StorageAccountName,
        [string] $StorageContainerName
    )
    $key = _getStorageAccountKey -StorageAccountName $StorageAccountName
    $context = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $key
    $blob = Get-AzStorageBlob -Blob "*.bacpac" -Container $StorageContainerName -Context $context
    return ($blob | Sort-Object -Descending | Select-Object -First 1).Name
}
function _convertToPlainText {
    param (
        [securestring] $Password
    )
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
    return [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
}
function _containerValidation {
    param (
        [string] $StorageAccountName,
        [string] $StorageContainerName
    )
    $validated = $false
    #Container validation
    try {
        $Context = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey (_getStorageAccountKey $StorageAccountName)
        $container = Get-AzStorageContainer -Name $StorageContainerName -Context $Context -ErrorAction SilentlyContinue
        if (-not ([string]::IsNullOrEmpty($container))) { $validated = $true } else { $validated = $false }
    }
    catch {
        $validated = $false
    }
    return $validated
}
function Export-PSDBSqlDatabase {
    [CmdletBinding()]
    [Alias("Export")]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ResourceGroupValidateAttribute()]
        [ArgumentCompleter([ResourceGroupCompleter])]
        [ValidateNotNullOrEmpty()]
        [string]$ResourceGroupName,
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [SqlDatabaseValidateAttribute()]
        [ArgumentCompleter([DatabaseCompleter])]
        [ValidateNotNullOrEmpty()]
        [string]$DatabaseName,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [SqlServerValidateAttribute()]
        [ArgumentCompleter([SqlServerCompleter])]
        [ValidateNotNullOrEmpty()]
        [string]$ServerName,
        [string]$StorageKeyType = "StorageAccessKey",
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [StorageAcountValidateAttribute()]
        [ArgumentCompleter([StorageAccountCompleter])]
        [ValidateNotNullOrEmpty()]
        [string]$StorageAccountName,
        [Parameter(Mandatory = $true, HelpMessage = "Provide Container Name to save the exported database .bacpac file.", ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$StorageContainerName,
        [Parameter(HelpMessage = "Provide the name of blob that you want to save as.")]
        [ValidateNotNullOrEmpty()]
        [string] $BlobName,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $AdministratorLogin,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring] $AdministratorLoginPassword,
        [Parameter(Mandatory = $false, HelpMessage = "Provide the subscription name if exported .bacpac file have to be saved in different subscription.")]
        [SubscriptionValidateAttribute()]
        [ArgumentCompleter([SubscriptionCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $Subscription
    )
    process {
        try {
            #region start DB export
            if (-not $BlobName) {
                $BlobName = _getBacpacName -DatabaseName $DatabaseName
            }
            if ($PSBoundParameters["Subscription"]) {
                $context = (Get-AzContext).Subscription.Name
                Set-PSDBDefault -Subscription $Subscription
                if (_containerValidation -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName) {
                    $storageKey = _getStorageAccountKey -StorageAccountName $StorageAccountName
                    $storageUri = _getStorageUri -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName
                    Set-PSDBDefault -Subscription $context
                    $splat = @{
                        DatabaseName = $DatabaseName
                        ServerName = $ServerName
                        StorageKeyType = $StorageKeyType
                        StorageKey = $storageKey
                        StorageUri = "$storageUri/$BlobName"
                        ResourceGroupName = $ResourceGroupName
                        AdministratorLogin = $AdministratorLogin
                        AdministratorLoginPassword = $AdministratorLoginPassword
                        ErrorAction = "Stop"
                    }
                    try {
                        $sqlExport = New-AzSqlDatabaseExport @splat
                        return $sqlExport.OperationStatusLink
                    }
                    catch {
                        if ($_.Exception.Message -match "Login failed") {
                            $Message = "Cannot validate argument on parameter 'AdministratorLogin' and 'AdministratorLoginPassword'. Pass the valid username and password and try again."
                            $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                            Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                        }
                        else {
                            throw "An error occurred: $($_.Exception.Message)"
                        }
                    }
                }
                else {
                    $Message = "Cannot validate argument on parameter 'StorageContainerName'. '$($StorageContainerName)' is not a valid storage container name. Pass the valid storage container name and try again."
                    $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                    Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                }
            }
            else {
                if (_containerValidation -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName) {
                    $storageKey = _getStorageAccountKey -StorageAccountName $StorageAccountName
                    $storageUri = _getStorageUri -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName
                    $splat = @{
                        DatabaseName = $DatabaseName
                        ServerName = $ServerName
                        StorageKeyType = $StorageKeyType
                        StorageKey = $storageKey
                        StorageUri = "$storageUri/$BlobName"
                        ResourceGroupName = $ResourceGroupName
                        AdministratorLogin = $AdministratorLogin
                        AdministratorLoginPassword = $AdministratorLoginPassword
                        ErrorAction = "Stop"
                    }
                    try {
                        $sqlExport = New-AzSqlDatabaseExport @splat
                        return $sqlExport.OperationStatusLink
                    }
                    catch {
                        if ($_.Exception.Message -match "Login failed") {
                            $Message = "Cannot validate argument on parameter 'AdministratorLogin' and 'AdministratorLoginPassword'. Pass the valid username and password and try again."
                            $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                            Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                        }
                        else {
                            throw "An error occurred: $($_.Exception.Message)"
                        }
                    }
                }
                else {
                    $Message = "Cannot validate argument on parameter 'StorageContainerName'. '$($StorageContainerName)' is not a valid storage container name. Pass the valid storage container name and try again."
                    $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                    Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                }
            }
            #end region start DB export
        }
        catch {
            throw "An error occurred: $($_.Exception.Message)"
        }
    }
}
#EOF
function Get-PSDBConnectionString {
    process {
        $AzSqlConnectionStrings = @{
            "Standard" = "Server=tcp:myserver.database.windows.net,1433;Database=myDataBase;User ID=mylogin@myserver;Password=myPassword;Trusted_Connection=False;Encrypt=True;"
            "MARS Enabled" = "Server=tcp:myserver.database.windows.net,1433;Database=myDataBase;User ID=mylogin@myserver;Password=myPassword;Trusted_Connection=False;Encrypt=True;MultipleActiveResultSets=True;"
            "Integrated Windows Authentication with AAD" = "Server=tcp:myserver.database.windows.net,1433;Authentication=Active Directory Integrated;Database=mydatabase;"
            "AAD With Username and Password" = "Server=tcp:myserver.database.windows.net,1433;Authentication=Active Directory Password;Database=myDataBase;UID=myUser@myDomain;PWD=myPassword;"
            "Always Encrypted" = "Data Source=myServer;Initial Catalog=myDB;Integrated Security=true;Column Encryption Setting=enabled;"
        }
        return $AzSqlConnectionStrings
    }
}
function Get-PSDBDatabaseData {
    [CmdletBinding()]
    [OutputType([PSCustomobject])]
    param (
        [Parameter(
            Mandatory = $true,
            Position = 0,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Provide the database connection string.")]
        [ValidateNotNullOrEmpty()]
        [string] $ConnectionString,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $Query
    )
    process {
        try {
            #region open DB connection
            $connection = New-Object -TypeName System.Data.SqlClient.SqlConnection
            $connection.ConnectionString = $ConnectionString
            $command = $connection.CreateCommand()
            $command.CommandText = $Query
            $connection.Open()
            #endregion open DB connection
            # execute the passed query and retrieve data
            $adapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter $command
            $dataset = New-Object -TypeName System.Data.Dataset
            $adapter.Fill($dataset) | Out-Null
            $result = $dataset.Tables
            return $result
        }
        catch {
            throw "An error occurred: $($_.Exception.Message)"
        }
        finally {
            # cleaning up
            $connection.Close()
        }
    }
}
function Get-PSDBImportExportStatus {
    [Alias("Status")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $StatusLink,
        [int] $Interval = 5,
        [int] $TimeOut = 300,
        [switch] $Wait
    )
    process {
        try {
            $Status = Get-AzSqlDatabaseImportExportStatus -OperationStatusLink $StatusLink -ErrorAction Stop
            if ($Wait.IsPresent) {
                $timeSpan = New-TimeSpan -Seconds $TimeOut
                $stopWatch = [System.Diagnostics.Stopwatch]::StartNew()
                while (($Status.Status -eq "InProgress") -and ($stopWatch.Elapsed.Seconds -lt $timeSpan.TotalSeconds)) {
                    Start-Sleep -Seconds $Interval
                    $Status = Get-AzSqlDatabaseImportExportStatus -OperationStatusLink $StatusLink
                    if (($Status.Status -ne "InProgress") -or ($stopWatch.Elapsed.Seconds -lt $timeSpan.TotalSeconds)) {
                        if ($Status.Status -ne "InProgress") {
                            Write-Output "Status has changed to: $($Status.Status)"
                        }
                    }
                    else { Start-Sleep -Seconds $Interval; continue; }
                }
                $stopWatch.Stop()
            }
            else {
                return $Status.Status
            }
        }
        catch {
            if ($_.Exception.Message -match "Invalid URI") {
                $Message = "Cannot validate argument '$($StatusLink)' on parameter StatusLink. Invalid URI. Pass the correct URI and try again."
                $ErrorId = "InvalidArgument,PSDBImportExportStatus\Export-PSDBImportExportStatus"
                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
            }
            elseif ($_.Exception.Message -match "An error occurred while sending the request") {
                $Message = "Cannot validate argument '$($StatusLink)' on parameter StatusLink. Invalid URI. Pass the correct URI and try again."
                $ErrorId = "InvalidArgument,PSDBImportExportStatus\Export-PSDBImportExportStatus"
                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
            }
            else {
                $Message = "$($_.Exception.Message)."
                $ErrorId = "InvalidArgument,PSDBImportExportStatus\Export-PSDBImportExportStatus"
                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
            }
        }
        finally {
            if ($stopWatch.IsRunning) {
                $stopWatch.Stop()
            }
        }
    }
}
# This function helps to retrieve the secrets from key vault and returns as secure string.
# This then can be used with sql import and export operation.
function Get-PSDBKVSecret {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [KeyVaultValidateAttribute()]
        [ArgumentCompleter([KeyVaultCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $VaultName,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $SecretName,
        [switch] $AsPlainText,
        [ValidateNotNullOrEmpty()]
        [string] $Version
    )
    process {
        try {
            $kvSecret = Get-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName -IncludeVersions -ErrorAction stop
            if ($null -eq $kvSecret) {
                $Message = "Cannot validate argument on parameter 'SecretName'. '$($SecretName)' is not a valid SecretName. Pass the valid secret name and try again."
                $ErrorId = "InvalidArgument,PSDBKVSecret\Get-PSDBKVSecret"
                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
            }
            else {
                if ($PSBoundParameters["Version"]) {
                    $secret = $kvSecret | Where-Object { $_.Version -eq $Version }
                    if ($null -eq $secret) {
                        $Message = "Cannot validate argument on parameter 'Version'. '$($Version)' is not a valid Version number. Pass the valid version number and try again."
                        $ErrorId = "InvalidArgument,PSDBKVSecret\Get-PSDBKVSecret"
                        Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                    }
                    else {
                        if ($AsPlainText.IsPresent) {
                            if ($secret.Enabled) {
                                $kv = Get-AzKeyVaultSecret -VaultName $secret.VaultName -Name $secret.Name -Version $secret.Version
                                $PSCmdlet.WriteObject((_convertToPlainText ($kv.SecretValue -as [securestring])))
                            } else {
                                $Message = "Cannot validate argument on parameter 'SecretName'. '$($SecretName)' is not enabled. Pass the valid secret name and try again."
                                $ErrorId = "InvalidArgument,PSDBKVSecret\Get-PSDBKVSecret"
                                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                            }
                        } else {
                            $kv = Get-AzKeyVaultSecret -VaultName $secret.VaultName -Name $secret.Name -Version $secret.Version
                            $PSCmdlet.WriteObject($kv.SecretValue)
                        }
                    }
                }
                else {
                    if ($AsPlainText.IsPresent) {
                        $kvSecret | ForEach-Object {
                            if ($_.Enabled) {
                                $kv = Get-AzKeyVaultSecret -VaultName $_.VaultName -Name $_.Name -Version $_.Version
                                $PSCmdlet.WriteObject((_convertToPlainText ($kv.SecretValue -as [securestring])))
                            }
                        }
                    } else {
                        $kvSecret | ForEach-Object {
                            if ($_.Enabled) {
                                $kv = Get-AzKeyVaultSecret -VaultName $_.VaultName -Name $_.Name -Version $_.Version
                                $PSCmdlet.WriteObject($kv.SecretValue)
                            }
                        }
                    }
                }
            }
        }
        catch {
            $content = $_.Exception.Response.Content
            $content = if ($content) { $content | ConvertFrom-Json }
            if ($content.error.innererror.code -eq "SecretDisabled") {
                $Message = "Cannot validate argument on parameter 'SecretName'. There is no active current version of '$($SecretName)'. Pass the valid secret name and try again."
                $ErrorId = "InvalidArgument,PSDBKVSecret\Get-PSDBKVSecret"
                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
            }
        }
    }
}
function Import-PSDBSqlDatabase {
    [CmdletBinding()]
    [Alias("Import")]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ResourceGroupValidateAttribute()]
        [ArgumentCompleter([ResourceGroupCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $ResourceGroupName,
        [Parameter(HelpMessage = "Provide the name of database to import as. If not provided by default it will take the name of .bacpac file.")]
        [ValidateNotNullOrEmpty()]
        [string] $ImportDatabaseAs,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [SqlServerValidateAttribute()]
        [ArgumentCompleter([SqlServerCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $ServerName,
        [string] $StorageKeyType = "StorageAccessKey",
        [string] $Edition,
        [string] $ServiceObjectiveName,
        [string] $DatabaseMaxSizeBytes,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [StorageAcountValidateAttribute()]
        [ArgumentCompleter([StorageAccountCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $StorageAccountName,
        [Parameter(Mandatory = $true, HelpMessage = "Provide Container Name to import database .bacpac file from.", ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $StorageContainerName,
        [Parameter(HelpMessage = "Provide the name of .bacpac file. If not provided it tries to retrieve latest '.bacpac' file from provided container.")]
        [ValidateNotNullOrEmpty()]
        [string] $BacpacName,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $AdministratorLogin,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring] $AdministratorLoginPassword,
        [Parameter(Mandatory = $false, HelpMessage = "Provide the subscription name to import .bacpac file from.")]
        [SubscriptionValidateAttribute()]
        [ArgumentCompleter([SubscriptionCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $Subscription
    )
    process {
        try {
            if (-not $Edition) {
                $Edition = "Standard"
            }
            if (-not $DatabaseMaxSizeBytes) {
                $DatabaseMaxSizeBytes = "5000000"
            }
            if (-not $ServiceObjectiveName) {
                $ServiceObjectiveName = "S0"
            }
            #region start DB import
            if ($PSBoundParameters["Subscription"]) {
                $context = (Get-AzContext).Subscription.Name
                Set-PSDBDefault -Subscription $Subscription
                if (_containerValidation -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName) {
                    $storageKey = _getStorageAccountKey -StorageAccountName $StorageAccountName
                    $storageUri = _getStorageUri -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName
                    # Placing this check here because when I'm retrieving the information for different subscription it has to
                    # fetch the correct latest bacpac file. If this is out of this check then the context will be different and
                    # I'm receiving error.
                    if (-not $BacpacName) {
                        $BacpacName = _getLatestBacPacFile -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName
                    }
                    if (-not $ImportDatabaseAs) {
                        $ImportDatabaseAs = $BacpacName.Replace(".bacpac", "")
                    }
                    Set-PSDBDefault -Subscription $context
                    $splat = @{
                        DatabaseName = $ImportDatabaseAs
                        ResourceGroupName = $ResourceGroupName
                        ServerName = $ServerName
                        StorageKeyType = "StorageAccessKey"
                        StorageKey = $storageKey
                        StorageUri = "$storageUri/$BacpacName"
                        Edition = $Edition
                        ServiceObjectiveName = $ServiceObjectiveName
                        DatabaseMaxSizeBytes = $DatabaseMaxSizeBytes
                        AdministratorLogin = $AdministratorLogin
                        AdministratorLoginPassword = $AdministratorLoginPassword
                        ErrorAction = "Stop"
                    }
                    try {
                        $sqlImport = New-AzSqlDatabaseImport @splat
                        return $sqlImport.OperationStatusLink
                    }
                    catch {
                        if ($_.Exception.Message -match "Login failed") {
                            $Message = "Cannot validate argument on parameter 'AdministratorLogin' and 'AdministratorLoginPassword'. Pass the valid username and password and try again."
                            $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                            Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                        }
                        else {
                            throw "An error occurred: $($_.Exception.Message)"
                        }
                    }
                }
                else {
                    $Message = "Cannot validate argument on parameter 'StorageContainerName'. '$($StorageContainerName)' is not a valid storage container name. Pass the valid storage container name and try again."
                    $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                    Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                }
            }
            else {
                if (_containerValidation -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName) {
                    $storageKey = _getStorageAccountKey -StorageAccountName $StorageAccountName
                    $storageUri = _getStorageUri -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName
                    if (-not $BacpacName) {
                        $BacpacName = _getLatestBacPacFile -StorageAccountName $StorageAccountName -StorageContainerName $StorageContainerName
                    }
                    if (-not $ImportDatabaseAs) {
                        $ImportDatabaseAs = $BacpacName.Replace(".bacpac", "")
                    }
                    $splat = @{
                        DatabaseName = $ImportDatabaseAs
                        ResourceGroupName = $ResourceGroupName
                        ServerName = $ServerName
                        StorageKeyType = "StorageAccessKey"
                        StorageKey = $storageKey
                        StorageUri = "$storageUri/$BacpacName"
                        Edition = $Edition
                        ServiceObjectiveName = $ServiceObjectiveName
                        DatabaseMaxSizeBytes = $DatabaseMaxSizeBytes
                        AdministratorLogin = $AdministratorLogin
                        AdministratorLoginPassword = $AdministratorLoginPassword
                        ErrorAction = "Stop"
                    }
                    try {
                        $sqlImport = New-AzSqlDatabaseImport @splat
                        return $sqlImport.OperationStatusLink
                    }
                    catch {
                        if ($_.Exception.Message -match "Login failed") {
                            $Message = "Cannot validate argument on parameter 'AdministratorLogin' and 'AdministratorLoginPassword'. Pass the valid username and password and try again."
                            $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                            Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                        }
                        else {
                            throw "An error occurred: $($_.Exception.Message)"
                        }
                    }
                }
                else {
                    $Message = "Cannot validate argument on parameter 'StorageContainerName'. '$($StorageContainerName)' is not a valid storage container name. Pass the valid storage container name and try again."
                    $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                    Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
                }
            }
            #endregion start DB import
        }
        catch {
            if ($_.Exception.Message -match "The variable cannot be validated") {
                $Message = "Cannot validate argument on parameter 'StorageContainerName'. '$($StorageContainerName)' is not found in storage account '$($StorageAccountName)'. Pass the valid storage container name and try again."
                $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
            }
            elseif ($_.Exception.Message -match "Target database is not empty") {
                $Message = "Database with name '$($ImportDatabaseAs)' already exists in '$($ServerName)'. Pass different name and try again."
                $ErrorId = "InvalidArgument,PSDBSqlDatabase\Export-PSDBSqlDatabase"
                Write-Error -Exception ArgumentException -Message $Message -Category InvalidArgument -ErrorId $ErrorId
            }
            else {
                throw "An error occurred: $($_.Exception.Message)"
            }
        }
    }
}
function Invoke-PSDBDatabaseQuery {
    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory = $true,
            Position = 0,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "Provide the database connection string.")]
        [ValidateNotNullOrEmpty()]
        [string] $ConnectionString,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $Query
    )
    process {
        try {
            #region open DB connection
            $connection = New-Object -TypeName System.Data.SqlClient.SqlConnection
            $connection.ConnectionString = $ConnectionString
            $command = $connection.CreateCommand()
            $command.CommandText = $Query
            $connection.Open()
            #endregion open DB connection
            # execute query
            $command.ExecuteNonQuery() > $null
        }
        catch {
            throw "An error occurred: $($_.Exception.Message)"
        }
        finally {
            # cleaning up
            $connection.Close()
        }
    }
}
function New-PSDBConnectionString {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = "AADIntegrated")]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = "Standard")]
        [Parameter(Mandatory = $true, ParameterSetName = "MARSEnabled")]
        [Parameter(Mandatory = $true, ParameterSetName = "AADIntegrated")]
        [Parameter(Mandatory = $true, ParameterSetName = "AAD")]
        [ArgumentCompleter([SqlServerCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $SqlServerName,
        [Parameter(Mandatory = $true, ParameterSetName = "Standard")]
        [Parameter(Mandatory = $true, ParameterSetName = "MARSEnabled")]
        [Parameter(Mandatory = $true, ParameterSetName = "AADIntegrated")]
        [Parameter(Mandatory = $true, ParameterSetName = "AAD")]
        [ArgumentCompleter([DatabaseCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $DatabaseName,
        [Parameter(Mandatory = $true, ParameterSetName = "Standard")]
        [Parameter(Mandatory = $true, ParameterSetName = "MARSEnabled")]
        [Parameter(Mandatory = $true, ParameterSetName = "AAD", HelpMessage = "Provide the username of database")]
        [ValidateNotNullOrEmpty()]
        [string] $UserName,
        [Parameter(Mandatory = $true, ParameterSetName = "AAD", HelpMessage = "Provide the domain name")]
        [ValidateNotNullOrEmpty()]
        [string] $Domain,
        [Parameter(Mandatory = $true, ParameterSetName = "Standard")]
        [Parameter(Mandatory = $true, ParameterSetName = "MARSEnabled")]
        [Parameter(Mandatory = $true, ParameterSetName = "AAD", HelpMessage = "Provide the database secure password")]
        [ValidateNotNullOrEmpty()]
        [securestring] $Password,
        [Parameter(Mandatory = $true, ParameterSetName = "MARSEnabled")]
        [switch] $MultipleActiveResultSets,
        [Parameter(Mandatory = $true, ParameterSetName = "AADIntegrated")]
        [Parameter(Mandatory = $true, ParameterSetName = "AAD")]
        [ValidateSet("Active Directory Integrated", "Active Directory Password")]
        [ValidateNotNullOrEmpty()]
        [string] $Authentication,
        [Parameter(Mandatory = $true, ParameterSetName = "Encrypted")]
        [ArgumentCompleter([SqlServerCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $DataSource,
        [Parameter(Mandatory = $true, ParameterSetName = "Encrypted")]
        [ValidateNotNullOrEmpty()]
        [string] $InitialCatalog,
        [Parameter(ParameterSetName = "Encrypted")]
        [ValidateNotNullOrEmpty()]
        [switch] $IntegratedSecurity,
        [Parameter(Mandatory = $false, ParameterSetName = "Encrypted")]
        [ValidateNotNullOrEmpty()]
        [string] $ColumnEncryptionSetting = "enabled"
    )
    process {
        try {
            [PSDBConnectionString] $ConnectionString = [PSDBConnectionString]::new()
            $CS = $null
            if ($PSCmdlet.ParameterSetName -eq "Standard") {
                $pswd = _convertToPlainText -Password $Password
                $CS = $ConnectionString.BuildConnectionString($SqlServerName, $DatabaseName, $UserName, $pswd)
            }
            elseif ($PSCmdlet.ParameterSetName -eq "MARSEnabled") {
                $pswd = _convertToPlainText -Password $Password
                $CS = $ConnectionString.BuildConnectionString($SqlServerName, $DatabaseName, $UserName, $pswd, $MultipleActiveResultSets.IsPresent)
            }
            elseif ($PSCmdlet.ParameterSetName -eq "AADIntegrated") {
                $CS = $ConnectionString.BuildConnectionString($SqlServerName, $DatabaseName, $Authentication)
            }
            elseif ($PSCmdlet.ParameterSetName -eq "AAD") {
                $pswd = _convertToPlainText -Password $Password
                $CS = $ConnectionString.BuildConnectionString($SqlServerName, $DatabaseName, $Authentication, $UserName, $pswd, $Domain)
            }
            elseif ($PSCmdlet.ParameterSetName -eq "Encrypted") {
                $CS = $ConnectionString.BuildConnectionString($DataSource, $InitialCatalog, $IntegratedSecurity, $ColumnEncryptionSetting)
            }
            return $CS
        }
        catch {
            throw "An error occurred: $($_.Exception.Message)"
        }
    }
}
function Set-PSDBDefault {
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [ArgumentCompleter([SubscriptionCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $Subscription,
        [Parameter(Position = 1, ValueFromPipeline = $true)]
        [ArgumentCompleter([ResourceGroupCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $ResourceGroupName,
        [ArgumentCompleter([SqlServerCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $ServerName,
        [ArgumentCompleter([DatabaseCompleter])]
        [ValidateNotNullOrEmpty()]
        [string] $DatabaseName,
        [ValidateNotNullOrEmpty()]
        [ValidateSet("Processs", "User", "Machine")]
        [string] $Level = "Process"
    )
    process {
        try {
            # setting the default subscription in current context.
            # It is expected that user should have logged into Azure already.
            # clearing the defaults. It returns old values if session is not restarted.
            _clearDefaults
            Write-Verbose "Setting default subscription"
            [System.Environment]::SetEnvironmentVariable("PSDB_SUBSCRIPTION", $Subscription, $Level)
            [PSDBResources]::Subscription = $env:PSDB_SUBSCRIPTION
            Set-AzContext -Subscription $Subscription > $null
            # setting default parameters helps to pick the mandatory parameters from current running process.
            if ($ResourceGroupName) {
                [System.Environment]::SetEnvironmentVariable("PSDB_RESOURCEGROUPNAME", $ResourceGroupName, $Level)
                $Global:PSDefaultParameterValues["*-PSDB*:ResourceGroupName"] = $ResourceGroupName
                [PSDBResources]::ResourceGroupName = $env:PSDB_RESOURCEGROUPNAME
            }
            if ($ServerName) {
                [System.Environment]::SetEnvironmentVariable("PSDB_SERVERNAME", $ServerName, $Level)
                $Global:PSDefaultParameterValues["*-PSDB*:ServerName"] = $ServerName
                [PSDBResources]::ServerName = $env:PSDB_SERVERNAME
            }
            if ($DatabaseName) {
                [System.Environment]::SetEnvironmentVariable("PSDB_DATABASENAME", $DatabaseName, $Level)
                $Global:PSDefaultParameterValues["*-PSDB*:DatabaseName"] = $DatabaseName
                [PSDBResources]::DatabaseName = $env:PSDB_DATABASENAME
            }
        }
        catch {
            throw "An error occurred: $($_.Exception.Message)"
        }
    }
}