diff --git a/migrateconnections.ps1 b/migrateconnections.ps1 new file mode 100644 index 0000000..f353a40 --- /dev/null +++ b/migrateconnections.ps1 @@ -0,0 +1,268 @@ +<# + .SYNOPSIS + Exports/imports data connections + + .DESCRIPTION + This command exports data connections to a file, or imports from a file into Qlik Sense. + + .PARAMETER Connection + Command to connect to Qlik Sense prior to executing this script + + .PARAMETER FilePath + Path of file to read/store data connections + + .PARAMETER Export + Export data connections to a file + + .PARAMETER Filter + Filter to limit the data connections to be exported + https://help.qlik.com/en-US/sense-developer/April2020/Subsystems/RepositoryServiceAPI/Content/Sense_RepositoryServiceAPI/RepositoryServiceAPI-Filtering.htm + + .PARAMETER Import + Import data connections from a file into Qlik Sense + + .PARAMETER Force + Update data connections that already exist + + .EXAMPLE + PS C:\> .\MigrateConnections.ps1 -Export -FilePath connections.json + + Export all data connections to a file + .EXAMPLE + PS C:\> .\MigrateConnections.ps1 -Export -FilePath connections.json -Filter "tags.name eq 'MyTag'" + + Export data connections tagged with 'MyTag' to a file + .EXAMPLE + PS C:\> .\MigrateConnections.ps1 -Import -FilePath connections.json + + Import all data connections from connections.json that don't exist + .EXAMPLE + PS C:\> .\MigrateConnections.ps1 -Import -FilePath connections.json -Force + + Import all data connections from connections.json and update existing connections +#> + +[CmdletBinding()] +param ( + [ScriptBlock]$Connection, + + [Parameter(Mandatory = $true)] + [System.IO.FileInfo]$FilePath, + + [Parameter(ParameterSetName = "Export")] + [switch]$Export, + [Parameter(ParameterSetName = "Export")] + [string]$Filter, + + [Parameter(ParameterSetName = "Import")] + [switch]$Import, + [Parameter(ParameterSetName = "Import")] + [switch]$Force +) + +function ImportConnection { + param ( + [Parameter(ValueFromPipelineByPropertyName = $true)] + [string]$name, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [string]$connectionstring, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [string]$type, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [object[]]$customProperties, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [object[]]$tags, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [string]$architecture, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [string]$logOn, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [string]$username, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [string]$password, + [Parameter(ValueFromPipelineByPropertyName = $true)] + [object]$owner + ) + + process { + $existing = Get-QlikDataConnection -filter "name eq '$name'" -Verbose:$VerbosePreference + if ($existing -and !$Force) { + Write-Warning "Data connection already exists, $name $($existing.id)" + return + } + + $conn = @{ + name = $name + type = $type + connectionString = $connectionString + architecture = $architecture + logon = $logon + } + + if ($username) { + if ($password) { + $securepass = ConvertTo-SecureString -AsPlainText -String $password -Force + } + else { + $securepass = New-Object System.Security.SecureString + } + $conn.credential = New-Object System.Management.Automation.PSCredential($username, $securepass) + } + + if (! $existing) { + Write-Verbose "Creating new data connection, $name" + $existing = New-QlikDataConnection @conn -Verbose:$VerbosePreference + } + $conn.id = $existing.id + + if ($owner) { + $conn.owner = ($owner | GetUser).id + Write-Verbose "Assigned $(CanonicalUsername $owner) as new owner" + } + + if ($tags) { + $conn.tags = @(($tags | GetTag).name) + Write-Verbose "Added $($conn.tags.Count) tags" + } + + if ($customProperties) { + $conn.customProperties = @($customProperties | GetProperty) + Write-Verbose "Added $($conn.customProperties.Count) property values" + } + + Write-Verbose "Updating existing data connection, $name" + $conn.Remove('type') + $conn.Remove('name') + $conn.Remove('architecture') + $conn.Remove('logon') + Update-QlikDataConnection @conn -Verbose:$VerbosePreference + } +} + +function CanonicalUsername() { $args[0].userDirectory + '\' + $args[0].userId } + +function GetUser { + param ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + $InputObject + ) + + begin { + if (! $Script:users) { $Script:users = @{} } + } + + process { + $username = CanonicalUsername $InputObject + if ($users.ContainsKey($username)) { + Write-Verbose "Found user in cache, $username" + return $users.$username + } + + $filter = "userDirectory eq '{0}' and userId eq '{1}'" -f $InputObject.userDirectory, $InputObject.userId + $user = Get-QlikUser -filter $filter -Verbose:$VerbosePreference + + if ($user.Count -eq 0) { + Write-Verbose "User not found in repository" + $userProps = @{ + userDirectory = $InputObject.userDirectory + userId = $InputObject.userId + name = $InputObject.name + } + Write-Verbose "Creating user, $(CanonicalUsername $userProps)" + $user = New-QlikUser @userProps -Verbose:$VerbosePreference + } + + $users.add($username, $user) + return $user + } +} + +function GetTag { + param ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + $InputObject + ) + + begin { + if (! $Script:tagsCache) { $Script:tagsCache = @{} } + } + + process { + $name = $InputObject.name + + if ($tagsCache.ContainsKey($name)) { + Write-Verbose "Found tag in cache, $($name)" + return $tagsCache.$name + } + + $tag = Get-QlikTag -filter "name eq '$name'" -Verbose:$VerbosePreference + if ($tag.Count -eq 0) { + Write-Verbose "Tag not found in repository, $name" + $tag = New-QlikTag -name $name -Verbose:$VerbosePreference + } + + $tagsCache.add($name, $tag) + return $tag + } +} + +function GetProperty { + param ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + $InputObject + ) + + begin { + if (! $Script:propCache) { $Script:propCache = @{} } + } + + process { + $name = $InputObject.definition.name + $canonical = $name + '=' + $InputObject.value + + if ($propCache.ContainsKey($name)) { + Write-Verbose "Found property in cache, $($name)" + $prop = $propCache.$name + } + else { + $prop = Get-QlikCustomProperty -filter "name eq '$name'" -raw -full -Verbose:$VerbosePreference + } + + if (! $prop) { + Write-Verbose "Property not found in repository, $name" + $prop = New-QlikCustomProperty -name $name -choiceValues $InputObject.value -objectTypes 'DataConnection' -Verbose:$VerbosePreference + } + elseif ($prop.choiceValues -notcontains $InputObject.value) { + Write-Verbose "Adding $($InputObject.value) to property $name" + $prop = Update-QlikCustomProperty -id $prop.id -choiceValues ($prop.choiceValues + $InputObject.value) + } + + if ($prop.objectTypes -notcontains 'DataConnection') { + Write-Verbose "Enabling $name property to be assigned to DataConnection" + $prop = Update-QlikCustomProperty -id $prop.id -objectTypes (@($prop.objectTypes) + 'DataConnection') + } + + $propCache.$name = $prop + return $canonical + } +} + +$ErrorActionPreference = 'Stop' + +if ($Connection) { + $Connection.Invoke() | Out-Null +} + +# Check that we are connected +Get-QlikAbout | Out-Null + +if ($Export) { + $connections = @(Get-QlikDataConnection -filter $Filter -full -raw -Verbose:$VerbosePreference) + + ConvertTo-Json -InputObject $connections -Depth 10 -Compress | Out-File -FilePath $FilePath +} +elseif ($Import) { + $connections = Get-Content -Path $FilePath -Raw | ConvertFrom-Json + + $connections | ImportConnection +}