SqlBIManager.psm1
$Script:asServer = $null $Script:LogVerbose = $false $Script:LogInformation = $true $Script:LogWarning = $true $Script:SBMLogFilePath = $null #region Logging function Use-SbmLog{ param( [string]$FilePath, [switch]$LogVerbose, [switch]$IgnoreInformation, [switch]$IgnoreWarning ) $Script:LogVerbose = $LogVerbose $Script:LogInformation = !$IgnoreInformation $Script:LogWarning = !$IgnoreInformation $Script:SBMLogFilePath = $FilePath } function Write-SbmLogVerbose{ param([string] $Message) Write-Verbose $Message if ($Script:LogVerbose) { if ($Script:SBMLogFilePath){ $Message | Out-File FilePath $Script:SBMLogFilePath -Append -Force } } } function Write-SbmLogInformation{ param([string] $Message) Write-Information $Message if ($Script:LogInformation) { if ($Script:SBMLogFilePath){ $Message | Out-File FilePath $Script:SBMLogFilePath -Append -Force } } } function Write-SbmLogWarning{ param([string] $Message) Write-Warning $Message if ($Script:LogWarning) { if ($Script:SBMLogFilePath){ $Message | Out-File FilePath $Script:SBMLogFilePath -Append -Force } } } function Write-SbmLogError{ param([string] $Message) Write-Error $Message if ($Script:SBMLogFilePath){ $Message | Out-File FilePath $Script:SBMLogFilePath -Append -Force } } #endregion #region SSAS function Connect-ASServer{ <# .SYNOPSIS Connect to Analysis Services instance .PARAMETER ASInstance Server name or server name with instance name .PARAMETER ConnectionOptions Optional string added to the connection string .EXAMPLE Connect-ASServer MyAsServer .EXAMPLE Connect-ASServer mySqlSerer.Internal.MyDomain.com\TABULAR #> [CmdletBinding(DefaultParameterSetName = 'default')] param( [parameter(Mandatory=$true)][string]$ASInstance, [string] $ConnectionOptions ) if ($Script:asServer.Connected) { $Script:asServer.Disconnect() } # Create a connection to the server instance $connString = "Provider=MSOLAP;DataSource=" + $ASInstance + ";" + $ConnectionOptions $Script:asServer = New-Object -TypeName Microsoft.AnalysisServices.Server $Script:asServer.Connect($connString) Write-SbmLogInformation "Connected to instance $ASInstance ($($asServer.ServerMode))" } function ConvertTo-AsmlDatabase { <# .SYNOPSIS Gets the meta data for a database. .DESCRIPTION Creates a Analysis server Meta Language database object for a database on the currently connected AS server. .PARAMETER Database ASDatabase object (from Get-ASDatabase). Accepts input from pipeline. .EXAMPLE #Exports all databases on the server Connect-ASServer "myServer\mySsasTabInstancer" Get-ASDatabase | ConvertTo-AsmlDatabase | Write-AsmlDatabase -ScriptFolder "C:\Sources\ASDatabases" #> param( [parameter(ValueFromPipeline, Mandatory=$true)][object] $Database ) Process { $jscript = [Microsoft.AnalysisServices.Tabular.JsonScripter]::ScriptCreateOrReplace($Database) $createorReplace = ConvertFrom-Json $jscript Write-Output $createorReplace.createOrReplace.database } } function Get-ASDatabase { <# .SYNOPSIS Get List of all databases on the currently connected Analysis Services server. #> $script:asServer.Databases } function Clear-ASDatabase { <# .SYNOPSIS Clears the data in a Analysis Server database. .DESCRIPTION Executes a process ClearValues on the currently connected server. .PARAMETER Database ASDatabase object (from Get-ASDatabase). Accepts input from pipeline. .EXAMPLE #Clears data in all databases on the server Connect-ASServer "myServer\mySsasTabInstancer" Get-ASDatabase | Clear-ASDatabase .EXAMPLE #Clears data in a specified database on the server Connect-ASServer "myServer\mySsasTabInstancer" Clear-ASDatabase -Name TestDatabase #> param( [parameter(ValueFromPipeline, Mandatory=$true, ParameterSetName="ByObject")][PSObject]$Database, [parameter(Mandatory=$true, ParameterSetName="ByName")][string] $Name ) Process { if ($PSBoundParameters.ContainsKey("Database")) { $Name = $Database.Name } $processDatabase = [PSCustomObject]@{ "refresh"= [PSCustomObject]@{ "type" = "clearValues" "objects"= @([PSCustomObject]@{ "database" = $Name}) }} $ScriptContent = ConvertTo-Json $processDatabase -Depth 25 Write-SbmLogInformation "Refresh clearValues $($Database.name)" $result = $script:asServer.Execute($scriptcontent) if ($result.Messages.Count -gt 0) { Write-SbmLogError("Error Clear-ASDatabase $($Name). $($result.Messages[0].Description)") } } } function Publish-AsmlDatabase { <# .SYNOPSIS Deploy Analysis Server Meta Language database object to server. .DESCRIPTION Generates CreateOrReplace script from asdatabase definition and executes on currently connected server. .PARAMETER Database ASML database object (deserialized from asdatabase file). Accepts input from pipeline. .PARAMETER ClearValues Execute process clear values before altering the database. Prevents failure from inconsistent existing data. Ignore if the database doesn't already exist. .EXAMPLE Get-ChildItem C:\MySources\Release1 -Filter "*.asdata" | Read-AsmlDatabase | Publish-ASDatabase .EXAMPLE Publish-ASDatabase -Database $TestDatabase -ClearValues #> param( [parameter(ValueFromPipeline, Mandatory=$true)][PSObject]$Database, [switch] $ClearValues ) Begin { #get list of databases alreday on target server $dbs = Get-ASDatabase } Process { if ($ClearValues) { #check if database already exists if ($dbs.Name -contains $Database.name) { Clear-ASDatabase -Name $Database.name } } $createorReplace = [PSCustomObject]@{ "createOrReplace"= [PSCustomObject]@{ "object"= [PSCustomObject]@{ "database" = $Database.name}; "database" = $Database }} $ScriptContent = ConvertTo-Json $createorReplace -Depth 25 Write-SbmLogInformation "Create or replace $($Database.name)" $result = $script:asServer.Execute($scriptcontent) if ($result.Messages.Count -gt 0) { Write-SbmLogError("Error Clear-ASDatabase $($Name). $($result.Messages[0].Description)") } } } function Read-AsmlDatabase { <# .SYNOPSIS Create object from file. .DESCRIPTION Reads json file (default extension: asdatabase) en serializes it to a (Analysis Server Meta Language) database object. .PARAMETER File Full path to .database file. .EXAMPLE $myDb = Read-AsmlDatabase -File "C:\Sources\ASTabularDatabase\MyDb.asdatabase" .EXAMPLE #Deploy all datases in folder to server Get-ChildItem C:\MySources\Release1 -Filter "*.asdata" | Read-AsmlDatabase | Publish-ASDatabase #> param( [parameter(ValueFromPipelineByPropertyName,Mandatory=$true)][Alias('PSPath')][string]$File ) Process { $ScriptPath = Convert-Path $File $scriptcontent = [System.IO.File]::ReadAllText($ScriptPath) $jsonObject = ConvertFrom-Json -InputObject $ScriptContent Write-Output $jsonObject } } function Write-AsmlDatabase { <# .SYNOPSIS Serialize database object to file. .DESCRIPTION Serializes a (Analysis Server Meta Language) database object to a JSON string and saves it to a file. The databas name will be used as file name. .PARAMETER Database Analysis Server Meta Language database object .PARAMETER ScriptFolder The folder the file is written to .PARAMETER FileExtension The file extension. If not supplied .asdatabase is used. .EXAMPLE #Exports all databases on the server Connect-ASServer "myServer\mySsasTabInstancer" Get-ASDatabase | ConvertTo-AsmlDatabase | Write-AsmlDatabase -ScriptFolder "C:\Sources\ASDatabases" #> param( [parameter(ValueFromPipeline,Mandatory=$true)][PSObject]$Database, [Parameter(Mandatory=$true)][string] $ScriptFolder, [string] $FileExtension = "asdatabase" ) Process { $ScriptContent = ConvertTo-Json $Database -Depth 25 [System.IO.File]::WriteAllText([System.IO.Path]::Combine( $ScriptFolder,$Database.name + "." + $FileExtension),$scriptcontent) } } function Write-ASDatabaseCreateReplaceScript { <# .SYNOPSIS creates a CreateOrReplace script from a database object. .DESCRIPTION Serializes a (Analysis Server Meta Language) database object to a JSON string and wraps it in a createOrReplace script syntax. The script name will be used as file name. .PARAMETER Database Analysis Server Meta Language database object .PARAMETER ScriptFolder The folder the file is written to .PARAMETER FileExtension The file extension. If not supplied .jsona is used. #> param( [parameter(ValueFromPipeline,Mandatory=$true)][PSObject]$Database, [Parameter(Mandatory=$true)][string] $ScriptFolder, [string] $FileExtension = "jsona" ) Process { $createorReplace = [PSCustomObject]@{ "createOrReplace"= [PSCustomObject]@{ "object"= [PSCustomObject]@{ "database" = $Database.name}; "database" = $Database }} $ScriptContent = ConvertTo-Json $createorReplace -Depth 25 [System.IO.File]::WriteAllText([System.IO.Path]::Combine( $ScriptFolder,$Database.name + "." + $FileExtension),$scriptcontent) } } <# .SYNOPSIS Edit data source details on an anlysis services database. .PARAMETER Database Analysis Server Meta Language database object. .PARAMETER Name Name of the data source in the database. .PARAMETER ConnectionString New connection string for the data source. .PARAMETER ImpersonationMode New ImpersonationMode for the data source. #> function Set-AsmlDatasource { [CmdletBinding(SupportsShouldProcess = $true ,ConfirmImpact='Medium',DefaultParameterSetName = 'default')] param( [parameter(ValueFromPipeline,Mandatory=$true)][PSObject]$Database, [string] $Name, [string] $ConnectionString, [string] $ImpersonationMode ) Process { $datasource = $Database.model.dataSources | Where-Object name -eq $Name if (!$datasource) { Write-SbmLogVerbose "Datasource $($Name) not found." } else { if ($PSCmdlet.ShouldProcess("Alter Datasource $($Name).")){ Write-SbmLogInformation "Altering Datasource $($Name)." if ($PSBoundParameters.ContainsKey('ConnectionString')) { $datasource.connectionString = $ConnectionString } if ($PSBoundParameters.ContainsKey('ImpersonationMode')) { $datasource.impersonationMode=$ImpersonationMode } } } Write-Output $Database } } <# .SYNOPSIS Add or Replace a member in an Analysis Services database role. .DESCRIPTION Add a new User or Group to a role, or replace an existing one. .PARAMETER Database Analysis Server Meta Language database object. .PARAMETER RoleName Analysis Services database role name .PARAMETER Member AsmlRoleMember object containing AD user or Group SID and name. .PARAMETER ReplaceMember Optional AsmlRoleMember object containing AD user or Group SID and name for the member to replace. .PARAMETER Filter Optional Filter expression evaluated for every database/role. If filter evaluates to false, member is not added. .PARAMETER FilterArgumentList Optional arguments evaluated in the filter. #> function Add-AsmlRoleMember { param( [parameter(ValueFromPipeline,Mandatory=$true)][PSObject]$Database, [Parameter(Mandatory=$true)][string] $RoleName, [Parameter(Mandatory=$true)][PSObject] $Member, [PSObject] $ReplaceMember, [ScriptBlock] $Filter, [Object[]] $FilterArgumentList ) Process { $roles = $Database.model.roles if ($PSBoundParameters.ContainsKey("RoleName")) { $roles = $roles | Where-Object name -eq $RoleName } Foreach ($role in $roles) { $filterResult = $true if ($PSBoundParameters.ContainsKey("Filter")) { $filterInput = [PSCustomObject] @{ "Database"=$Database "Role"=$role } $filterResult = Invoke-Command -ScriptBlock $Filter -ArgumentList $FilterArgumentList -InputObject $filterInput } if ($filterResult -eq $true) { if ($PSBoundParameters.ContainsKey("ReplaceMember")) { if($role.members.memberId -contains $ReplaceMember.memberId) { Write-SbmLogInformation "Replacing member with $($Member.memberName) in role $($role.name)." $crm = $role.members | Where-Object memberId -eq $ReplaceMember.memberId $crm.memberId = $Member.memberId $crm.memberName = $Member.memberName } } else { Write-SbmLogInformation "Adding member $($Member.memberName) to role $($role.name)." #Does the memberscollection exist? if ($role.PSObject.Properties.match("Members").Count -eq 0) { $_membersColection = @($Member) $role | Add-Member "members" $_membersColection } else { $role.Members+=$Member } } } } Write-Output $Database } } #endregion #region SSAS ActiveDirectory function ConvertTo-AsmlRoleMember <# .SYNOPSIS Create AsmlRoleMember object from ActiveDirectory group or user .PARAMETER ADObject ActiveDirectory User or Group object .PARAMETER Domain Domain as in Domanin\AccountName #> { param( [parameter(Mandatory=$true,ValueFromPipeline)][object] $ADObject, [Parameter(Mandatory=$true)][string] $Domain ) Process { $rm = [PSCustomObject]@{ "memberId"=$ADObject.SID.value "memberName"= "$($Domain)\$($ASObject.SamAccountName)" } if ($PSCmdlet.MyInvocation.ExpectingInput) { Write-Output $rm } } End { if (!$PSCmdlet.MyInvocation.ExpectingInput) { $rm } } } function Select-AsmlRoleMember { <# .SYNOPSIS Get ActiveDirectory group or user and create AsmlRoleMember object .PARAMETER Domain Domain as in Domanin\AccountName .PARAMETER ADGroup ActiveDirectory Group Identity .PARAMETER ADUser ActiveDirectory User Identity #> param( [Parameter(Mandatory=$true, ParameterSetName="ADGroup")] [Parameter(Mandatory=$true, ParameterSetName="ADUser")][string] $Domain, [Parameter(Mandatory=$true, ParameterSetName="ADGroup")][string] $ADGroup, [Parameter(Mandatory=$true, ParameterSetName="ADUser")][string] $ADUser ) $adinfo = $null if ($PSBoundParameters.ContainsKey("ADGroup")) { $adinfo = Get-ADGroup -Identity $ADGroup } else { $adinfo = Get-ADUser -Identity $ADUser } ConvertTo-ASMLRoleMember -ADObject $adinfo -Domain $Domain } #endregion #region SSIS function Connect-SsisCatalog{ <# .SYNOPSIS Connect to the SSIS catalog .PARAMETER Server The SQL Server instance that contains the catalog database. .PARAMETER Catalog The SSIS catalog database name. if omitted, SSISDB is assumed .EXAMPLE Connect-SsisCatalog MyServerName\MySqlInstance #> param( [Parameter(Mandatory=$true)][string]$Server, [string]$Catalog="SSISDB" ) # Create a connection to the server $SqlConnectionstring = "Data Source=" + $Server + ";Initial Catalog=master;Integrated Security=SSPI;" $script:isSqlConnection = New-Object System.Data.SqlClient.SqlConnection $SqlConnectionstring try{ $script:isSqlConnection.Open() } catch{ Write-SbmLogError "Error Connection to $($Server). Details: $($_.Exception.Message)" return } #check if the catalog exists $script:isCatalog = $Catalog try { $sqlStatement = "SELECT COUNT(1) FolderCount FROM [$($script:isCatalog)].[catalog].[folders]" $sqlCmd = New-Object System.Data.SqlClient.SqlCommand $sqlStatement , $script:isSqlConnection $foldercount = $sqlCmd.ExecuteScalar() Write-SbmLogInformation "Connected to Catalog $($Catalog) on $($Server). Found $($foldercount) Folders." } catch{ Write-SbmLogError "Error Connection to Catalog $($Catalog) on $($Server). Details: $($_.Exception.Message)" return } } function Get-SsisFolder{ <# .SYNOPSIS Get SSIS catalog folders #> $sqlStatement = "SELECT [folder_id] FolderId, [name] [FolderName] FROM [$($script:isCatalog)].[catalog].[folders]" $sqlCmd = New-Object System.Data.SqlClient.SqlCommand $sqlStatement , $script:isSqlConnection $sqlReader = $sqlCmd.ExecuteReader() $dataTable = New-Object System.Data.DataTable $dataTable.Load($sqlReader) $dataTable } function Get-SsisProject{ <# .SYNOPSIS Get SSIS catalog projects. .PARAMETER FolderName Optional filter on folder name. .PARAMETER ProjectName Optional filter on project name. #> param( [Parameter(Mandatory=$false)][string]$FolderName, [string]$ProjectName ) $sqlStatement = "SELECT p.[project_id] ProjectId,p.[name] ProjectName,p.[description] ProjectDescription,p.[project_format_version] ProjectFormatversion ,p.[deployed_by_sid] DeployedBySid,p.[deployed_by_name] DeployedByName,p.[last_deployed_time] LastDeployed,p.[created_time] Created ,p.[object_version_lsn] VersionLsn,p.[validation_status] ValidationStatus,p.[last_validation_time] LastValidation ,fl.[folder_id] FolderId, fl.[name] FolderName FROM [$($script:isCatalog)].[catalog].[projects] p JOIN [$($script:isCatalog)].[catalog].[folders] fl ON fl.[folder_id]=p.[folder_id] WHERE 1=1" if ($PSBoundParameters.ContainsKey("FolderName")) { $sqlStatement = $sqlStatement + "AND fl.[name]='$($FolderName)'" } if ($PSBoundParameters.ContainsKey("ProjectName")) { $sqlStatement = $sqlStatement + "AND p.[name]='$($ProjectName)'" } $sqlCmd = New-Object System.Data.SqlClient.SqlCommand $sqlStatement , $script:isSqlConnection $sqlReader = $sqlCmd.ExecuteReader() $dataTable = New-Object System.Data.DataTable $dataTable.Load($sqlReader) $dataTable } function Publish-SsisProject{ <# .SYNOPSIS Deploy SSIS Project to SSIS Catalog. .DESCRIPTION Deploy ispac file to SSIS Catalog folder. .PARAMETER FilePath Full path of ispac file .PARAMETER FolderName Existing Folder name on currently connected server. #> param( [parameter(ValueFromPipelineByPropertyName, Mandatory=$true)][Alias('PSPath')][string]$FilePath, [Parameter(Mandatory=$true)][string]$FolderName ) Begin { $importprojectCmd = New-Object System.Data.SqlClient.SqlCommand $importprojectCmd.CommandType = [System.Data.CommandType]::StoredProcedure $importprojectCmd.CommandText = "[$($script:isCatalog)].catalog.deploy_project" $importprojectCmd.Connection = $script:isSqlConnection $importprojectCmd.Parameters.Add("@folder_name", [System.Data.SqlDbType]::NVarChar, 128).Value = $FolderName $importprojectCmd.Parameters.Add("@project_name", [System.Data.SqlDbType]::NVarChar, 128) | Out-Null $importprojectCmd.Parameters.Add("@project_stream", [System.Data.SqlDbType]::VarBinary, -1) | Out-Null $importprojectCmd.Parameters.Add("@operation_id", [System.Data.SqlDbType]::BigInt).Direction = [System.Data.ParameterDirection]::InputOutput } Process { $fp = Convert-Path $FilePath $ProjectName = [System.IO.Path]::GetFileNameWithoutExtension($fp) #see if the project already exists $prevProject = Get-SsisProject -FolderName $FolderName -ProjectName $ProjectName # Reading ispac file as binary [byte[]] $IspacFile = [System.IO.File]::ReadAllBytes($fp) $importprojectCmd.Parameters["@project_name"].Value = $ProjectName $importprojectCmd.Parameters["@project_stream"].Value = $IspacFile $importprojectCmd.ExecuteNonQuery() if ($prevProject){ Write-SbmLogInformation "Deployed $($ProjectName) to $($FolderName) (REPLACE)" } else { Write-SbmLogInformation "Deployed $($ProjectName) to $($FolderName) (ADD)" } } } function Export-SsisProject { <# .SYNOPSIS Export a SSIS project from the catalog to an ispac file. .PARAMETER ProjectName SSIS catalog project name. .PARAMETER FolderName SSIS catalog folder containing the project. .PARAMETER FolderPath Full path of folder the ispac file is saved to. #> param( [parameter(ValueFromPipelineByPropertyName, Mandatory=$true)][string]$ProjectName, [Parameter(Mandatory=$true)][string]$FolderName, [Parameter(Mandatory=$true)][string]$FolderPath ) Begin { $ExportProjectCmd = New-Object System.Data.SqlClient.SqlCommand $ExportProjectCmd.CommandType = [System.Data.CommandType]::StoredProcedure $ExportProjectCmd.CommandText = "[$($script:isCatalog)].catalog.get_project" $ExportProjectCmd.Connection = $script:isSqlConnection $ExportProjectCmd.Parameters.Add("@folder_name", [System.Data.SqlDbType]::NVarChar, 128).Value = $FolderName $ExportProjectCmd.Parameters.Add("@project_name", [System.Data.SqlDbType]::NVarChar, 128) | Out-Null } Process { $ExportProjectCmd.Parameters["@project_name"].Value = $ProjectName [Byte[]]$fd= $ExportProjectCmd.ExecuteScalar() [System.IO.File]::WriteAllBytes([System.IO.Path]::Combine($FolderPath,$ProjectName + ".ispac") , $fd) } } function Get-SsisParameter{ <# .SYNOPSIS Get SSIS catalog parameter. .PARAMETER FolderName Optional filter on folder name. .PARAMETER ProjectName Optional filter on project name. .PARAMETER ObjectName Optional filter on object name (parameter scope). .PARAMETER WhereValueSet Optional filter only parameters that override the default #> param( [Parameter(Mandatory=$false)][string]$FolderName, [string]$ProjectName, [string]$ObjectName, [switch]$WhereValueSet ) $sqlStatement = "SELECT par.[parameter_id] ParameterId ,par.[project_id] ProjectId ,prj.[name] ProjectName ,par.[object_type] ObjectType ,par.[object_name] ObjectName ,par.[parameter_name] ParameterName ,par.[data_type] DataType ,par.[required] [Required] ,par.[sensitive][Sensitive] ,par.[description] [Description] ,par.[design_default_value] [DesignDefaultValue] ,par.[default_value] [DefaultValue] ,par.[value_type] [ValueType] ,par.[value_set] [ValueSet] ,par.[referenced_variable_name] [ReferencedVariableName] ,par.[validation_status] [ValidationStatus] ,par.[last_validation_time] [LastValidation] ,fl.[name] [FolderName] FROM [$($script:isCatalog)].[catalog].[object_parameters] par JOIN [$($script:isCatalog)].[catalog].[projects] prj ON prj.[project_id]=par.[project_id] JOIN [$($script:isCatalog)].[catalog].[folders] fl ON fl.[folder_id]=prj.[folder_id] WHERE 1=1" if ($PSBoundParameters.ContainsKey("FolderName")){ $sqlStatement = $sqlStatement + "AND fl.[name]='$($FolderName)'" } if ($PSBoundParameters.ContainsKey("ProjectName")){ $sqlStatement = $sqlStatement + "AND prj.[name]='$($ProjectName)'" } if ($PSBoundParameters.ContainsKey("ObjectName")){ $sqlStatement = $sqlStatement + "AND par.[object_name]='$($ObjectName)'" } if ($WhereValueSet) { $sqlStatement = $sqlStatement + "AND par.[value_set]=1" } $sqlCmd = New-Object System.Data.SqlClient.SqlCommand $sqlStatement , $script:isSqlConnection $sqlReader = $sqlCmd.ExecuteReader() $dataTable = New-Object System.Data.DataTable $dataTable.Load($sqlReader) $dataTable } #endregion |