Octopus.Script exported 2020-06-15 by bobjwalker belongs to ‘SQL Server’ category.
This step template will deploy SSIS ispac projects to SQL Server Integration Services Catalog. The template uses a referenced package and is Worker compatible.
This template will install the Nuget package provider if it is not present on the machine it is running on.
Parameters
When steps based on the template are included in a project’s deployment process, the parameters below can be set.
Database server name (\instance)
ServerName =
Name of the SQL Server you are deploying to.
SQL Authentication Username
sqlAccountUsername =
(Optional) Username of the SQL Authentication account. Use this approach when deploying to Azure Databases with SSIS configured. If SQL Authentication Username and Password are blank, Integrated Authentication is used.
SQL Authentication Password
sqlAccountPassword =
(Optional) Password of the SQL Authentication account.
Enable SQL CLR
EnableCLR = False
This will reconfigure SQL Server to enable the SQL CLR. It is highly recommended that this be previously authorized by your Database Administrator.
Catalog name
CatalogName = SSISDB
Name of the catalog to create in Integration Services Catalogs on SQL Server. When using the GUI, this value gets hardcoded to SSISDB and cannot be changed. It is recommended that you do not change the default value.
Catalog password
CatalogPwd =
Password to the Integration Services Catalog.
Folder name
FolderName =
Name of the folder to use within the Integration Services Catalog
Project name
ProjectName =
Name of the project within the folder of the Integration Services catalog
Use environment
UseEnvironment = False
This will make a project reference to the defined environment.
Environment name
EnvironmentName =
Name of the environment to reference the project to. If the environment doesn’t exist, it will create it.
Reference project parameters to environment variables
ReferenceProjectParametersToEnvironmentVairables = False
Checking this box will make Project Parameters reference Environment Variables. If the Environment Variable doesn’t exist, it will create it. This expects that an Octopus variable of the same name exists.
Reference package parameters to environment variables
ReferencePackageParametersToEnvironmentVairables = False
Checking this box will make Package Parameters reference Environment Variables. If the Environment Variable doesn’t exist, it will create it. This expects than an Octopus variable of the same name exists.
Use Fully Qualified Variable Names
UseFullyQualifiedVariableNames = False
When true the package variables names must be represented in dtsx_name_without_extension.variable_name
Use Custom Filter for connection manager properties
UseCustomFilter = False
Custom filter should contain the regular expression for ignoring properties when setting will occur during the auto-mapping
Custom Filter for connection manager properties
CustomFilter =
Regular expression for filtering out the connection manager properties during the auto-mapping process. This string is used when UseCustomFilter
is set to true
Clean obsolete variables from environment
SyncEnvironment = False
When true
synchronizes the environment:
- Removes obsolete variables
- Removes renamed variables
- Replaces values of valid variables (also when
false
)
Package Id
ssisPackageId =
Id of the package to deploy, used to support deployment with Workers.
Package Feed Id
TemplatePackageFeedId = feeds-builtin
ID of the package feed the referenced package resides in.
Script body
Steps based on this template will execute the following PowerShell script.
#region Functions
# Define functions
function Get-SqlModuleInstalled
{
# Define parameters
param(
$PowerShellModuleName
)
# Check to see if the module is installed
if ($null -ne (Get-Module -ListAvailable -Name $PowerShellModuleName))
{
# It is installed
return $true
}
else
{
# Module not installed
return $false
}
}
function Get-NugetPackageProviderNotInstalled
{
# See if the nuget package provider has been installed
return ($null -eq (Get-PackageProvider -ListAvailable -Name Nuget -ErrorAction SilentlyContinue))
}
function Install-SqlServerPowerShellModule
{
# Define parameters
param(
$PowerShellModuleName,
$LocalModulesPath
)
# Check to see if the package provider has been installed
if ((Get-NugetPackageProviderNotInstalled) -ne $false)
{
# Display that we need the nuget package provider
Write-Host "Nuget package provider not found, installing ..."
# Install Nuget package provider
Install-PackageProvider -Name Nuget -Force
}
# Save the module in the temporary location
Save-Module -Name $PowerShellModuleName -Path $LocalModulesPath -Force -RequiredVersion "21.1.18256"
# Display
Write-Output "Importing module $PowerShellModuleName ..."
# Import the module
Import-Module -Name $PowerShellModuleName
}
Function Load-SqlServerAssmblies
{
# Declare parameters
# Get the folder where the SqlServer module ended up in
$sqlServerModulePath = [System.IO.Path]::GetDirectoryName((Get-Module SqlServer).Path)
# Loop through the assemblies
foreach($assemblyFile in (Get-ChildItem -Path $sqlServerModulePath -Exclude msv*.dll | Where-Object {$_.Extension -eq ".dll"}))
{
# Load the assembly
[Reflection.Assembly]::LoadFile($assemblyFile.FullName) | Out-Null
}
}
#region Get-Catalog
Function Get-Catalog
{
# define parameters
Param ($CatalogName)
# NOTE: using $integrationServices variable defined in main
# define working varaibles
$Catalog = $null
# check to see if there are any catalogs
if($integrationServices.Catalogs.Count -gt 0 -and $integrationServices.Catalogs[$CatalogName])
{
# get reference to catalog
$Catalog = $integrationServices.Catalogs[$CatalogName]
}
else
{
if((Get-CLREnabled) -eq 0)
{
if(-not $EnableCLR)
{
# throw error
throw "SQL CLR is not enabled."
}
else
{
# display sql clr isn't enabled
Write-Warning "SQL CLR is not enabled on $($sqlConnection.DataSource). This feature must be enabled for SSIS catalogs."
# enablign SQLCLR
Write-Host "Enabling SQL CLR ..."
Enable-SQLCLR
Write-Host "SQL CLR enabled"
}
}
# Provision a new SSIS Catalog
Write-Host "Creating SSIS Catalog ..."
$Catalog = New-Object "$ISNamespace.Catalog" ($integrationServices, $CatalogName, $CatalogPwd)
$Catalog.Create()
}
# return the catalog
return $Catalog
}
#endregion
#region Get-CLREnabled
Function Get-CLREnabled
{
# define parameters
# Not using any parameters, but am using $sqlConnection defined in main
# define working variables
$Query = "SELECT * FROM sys.configurations WHERE name = 'clr enabled'"
# execute script
$CLREnabled = Invoke-Sqlcmd -ServerInstance $sqlConnection.DataSource -Database "master" -Query $Query | Select value
# return value
return $CLREnabled.Value
}
#endregion
#region Enable-SQLCLR
Function Enable-SQLCLR
{
$QueryArray = "sp_configure 'show advanced options', 1", "RECONFIGURE", "sp_configure 'clr enabled', 1", "RECONFIGURE "
# execute script
foreach($Query in $QueryArray)
{
Invoke-Sqlcmd -ServerInstance $sqlConnection.DataSource -Database "master" -Query $Query
}
# check that it's enabled
if((Get-CLREnabled) -ne 1)
{
# throw error
throw "Failed to enable SQL CLR"
}
}
#endregion
#region Get-Folder
Function Get-Folder
{
# parameters
Param($FolderName, $Catalog)
$Folder = $null
# try to get reference to folder
if(!($Catalog.Folders -eq $null))
{
$Folder = $Catalog.Folders[$FolderName]
}
# check to see if $Folder has a value
if($Folder -eq $null)
{
# display
Write-Host "Folder $FolderName doesn't exist, creating folder..."
# create the folder
$Folder = New-Object "$ISNamespace.CatalogFolder" ($Catalog, $FolderName, $FolderName)
$Folder.Create()
}
# return the folde reference
return $Folder
}
#endregion
#region Get-Environment
Function Get-Environment
{
# define parameters
Param($Folder, $EnvironmentName)
$Environment = $null
# get reference to Environment
if(!($Folder.Environments -eq $null) -and $Folder.Environments.Count -gt 0)
{
$Environment = $Folder.Environments[$EnvironmentName]
}
# check to see if it's a null reference
if($Environment -eq $null)
{
# display
Write-Host "Environment $EnvironmentName doesn't exist, creating environment..."
# create environment
$Environment = New-Object "$ISNamespace.EnvironmentInfo" ($Folder, $EnvironmentName, $EnvironmentName)
$Environment.Create()
}
# return the environment
return $Environment
}
#endregion
#region Set-EnvironmentReference
Function Set-EnvironmentReference
{
# define parameters
Param($Project, $Environment, $Folder)
# get reference
$Reference = $null
if(!($Project.References -eq $null))
{
$Reference = $Project.References[$Environment.Name, $Folder.Name]
}
# check to see if it's a null reference
if($Reference -eq $null)
{
# display
Write-Host "Project does not reference environment $($Environment.Name), creating reference..."
# create reference
$Project.References.Add($Environment.Name, $Folder.Name)
$Project.Alter()
}
}
#endregion
#region Set-ProjectParametersToEnvironmentVariablesReference
Function Set-ProjectParametersToEnvironmentVariablesReference
{
# define parameters
Param($Project, $Environment)
$UpsertedVariables = @()
if($Project.Parameters -eq $null)
{
Write-Host "No project parameters exist"
return
}
# loop through project parameters
foreach($Parameter in $Project.Parameters)
{
# skip if the parameter is included in custom filters
if ($UseCustomFilter)
{
if ($Parameter.Name -match $CustomFilter)
{
Write-Host "- $($Parameter.Name) skipped due to CustomFilters."
continue
}
}
# Add variable to list of variable
$UpsertedVariables += $Parameter.Name
$Variable = $null
if(!($Environment.Variables -eq $null))
{
# get reference to variable
$Variable = $Environment.Variables[$Parameter.Name]
}
# check to see if variable exists
if($Variable -eq $null)
{
# add the environment variable
Add-EnvironmentVariable -Environment $Environment -Parameter $Parameter -ParameterName $Parameter.Name
# get reference to the newly created variable
$Variable = $Environment.Variables[$Parameter.Name]
}
# set the environment variable value
Set-EnvironmentVariableValue -Variable $Variable -Parameter $Parameter -ParameterName $Parameter.Name
}
# alter the environment
$Environment.Alter()
$Project.Alter()
return $UpsertedVariables
}
#endregion
Function Set-PackageVariablesToEnvironmentVariablesReference
{
# define parameters
Param($Project, $Environment)
$Variables = @()
$UpsertedVariables = @()
# loop through packages in project in order to store a temp collection of variables
foreach($Package in $Project.Packages)
{
# loop through parameters of package
foreach($Parameter in $Package.Parameters)
{
# add to the temporary variable collection
$Variables += $Parameter.Name
}
}
# loop through packages in project
foreach($Package in $Project.Packages)
{
# loop through parameters of package
foreach($Parameter in $Package.Parameters)
{
if ($UseFullyQualifiedVariableNames)
{
# Set fully qualified variable name
$ParameterName = $Parameter.ObjectName.Replace(".dtsx", "")+"."+$Parameter.Name
}
else
{
# check if exists a variable with the same name
$VariableNameOccurrences = $($Variables | Where-Object { $_ -eq $Parameter.Name }).count
$ParameterName = $Parameter.Name
if ($VariableNameOccurrences -gt 1)
{
$ParameterName = $Parameter.ObjectName.Replace(".dtsx", "")+"."+$Parameter.Name
}
}
if ($UseCustomFilter)
{
if ($ParameterName -match $CustomFilter)
{
Write-Host "- $($Parameter.Name) skipped due to CustomFilters."
continue
}
}
# get reference to variable
$Variable = $Environment.Variables[$ParameterName]
# Add variable to list of variable
$UpsertedVariables += $ParameterName
# check to see if the parameter exists
if(!$Variable)
{
# add the environment variable
Add-EnvironmentVariable -Environment $Environment -Parameter $Parameter -ParameterName $ParameterName
# get reference to the newly created variable
$Variable = $Environment.Variables[$ParameterName]
}
# set the environment variable value
Set-EnvironmentVariableValue -Variable $Variable -Parameter $Parameter -ParameterName $ParameterName
}
# alter the package
$Package.Alter()
}
# alter the environment
$Environment.Alter()
return $UpsertedVariables
}
Function Sync-EnvironmentVariables
{
# define parameters
Param($Environment, $VariablesToPreserveInEnvironment)
foreach($VariableToEvaluate in $Environment.Variables)
{
if ($VariablesToPreserveInEnvironment -notcontains $VariableToEvaluate.Name)
{
Write-Host "- Removing environment variable: $($VariableToEvaluate.Name)"
$VariableToRemove = $Environment.Variables[$VariableToEvaluate.Name]
$Environment.Variables.Remove($VariableToRemove) | Out-Null
}
}
# alter the environment
$Environment.Alter()
}
#region Add-EnvironmentVariable
Function Add-EnvironmentVariable
{
# define parameters
Param($Environment, $Parameter, $ParameterName)
# display
Write-Host "- Adding environment variable $($ParameterName)"
# check to see if design default value is emtpy or null
if([string]::IsNullOrEmpty($Parameter.DesignDefaultValue))
{
# give it something
$DefaultValue = "" # sensitive variables will not return anything so when trying to use the property of $Parameter.DesignDefaultValue, the Alter method will fail.
}
else
{
# take the design
$DefaultValue = $Parameter.DesignDefaultValue
}
# add variable with an initial value
$Environment.Variables.Add($ParameterName, $Parameter.DataType, $DefaultValue, $Parameter.Sensitive, $Parameter.Description)
}
#endregion
#region Set-EnvironmentVariableValue
Function Set-EnvironmentVariableValue
{
# define parameters
Param($Variable, $Parameter, $ParameterName)
# check to make sure variable value is available
if($OctopusParameters -and $OctopusParameters.ContainsKey($ParameterName))
{
# display
Write-Host "- Updating environment variable $($ParameterName)"
# set the variable value
$Variable.Value = $OctopusParameters["$($ParameterName)"]
}
else
{
# warning
Write-Host "**- OctopusParameters collection is empty or $($ParameterName) not in the collection -**"
}
# Set reference
$Parameter.Set([Microsoft.SqlServer.Management.IntegrationServices.ParameterInfo+ParameterValueType]::Referenced, "$($ParameterName)")
}
#endregion
# Define PowerShell Modules path
$LocalModules = (New-Item "$PSScriptRoot\Modules" -ItemType Directory -Force).FullName
$env:PSModulePath = "$LocalModules;$env:PSModulePath"
# Check to see if SqlServer module is installed
if ((Get-SqlModuleInstalled -PowerShellModuleName "SqlServer") -ne $true)
{
# Display message
Write-Output "PowerShell module SqlServer not present, downloading temporary copy ..."
#Enable TLS 1.2 as default protocol
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12
# Download and install temporary copy
Install-SqlServerPowerShellModule -PowerShellModuleName "SqlServer" -LocalModulesPath $LocalModules
#region Dependent assemblies
Load-SqlServerAssmblies
}
else
{
# Load the IntegrationServices Assembly
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.IntegrationServices") | Out-Null # Out-Null supresses a message that would normally be displayed saying it loaded out of GAC
}
#endregion
# Store the IntegrationServices Assembly namespace to avoid typing it every time
$ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
#endregion
#region Main
try
{
# ensure all boolean variables are true booleans
$EnableCLR = [System.Convert]::ToBoolean("$EnableCLR")
$UseEnvironment = [System.Convert]::ToBoolean("$UseEnvironment")
$ReferenceProjectParametersToEnvironmentVairables = [System.Convert]::ToBoolean("$ReferenceProjectParametersToEnvironmentVairables")
$ReferencePackageParametersToEnvironmentVairables = [System.Convert]::ToBoolean("$ReferencePackageParametersToEnvironmentVairables")
$UseFullyQualifiedVariableNames = [System.Convert]::ToBoolean("$UseFullyQualifiedVariableNames")
$SyncEnvironment = [System.Convert]::ToBoolean("$SyncEnvironment")
# custom names for filtering out the excluded variables by design
$UseCustomFilter = [System.Convert]::ToBoolean("$UseCustomFilter")
$CustomFilter = [System.Convert]::ToString("$CustomFilter")
# list of variables names to keep in target environment
$VariablesToPreserveInEnvironment = @()
# Get the extracted path
$DeployedPath = $OctopusParameters["Octopus.Action.Package[$ssisPackageId].ExtractedPath"]
# Get all .ispac files from the deployed path
$IsPacFiles = Get-ChildItem -Recurse -Path $DeployedPath | Where {$_.Extension.ToLower() -eq ".ispac"}
# display number of files
Write-Host "$($IsPacFiles.Count) .ispac file(s) found."
Write-Host "Connecting to server ..."
# Create a connection to the server
$sqlConnectionString = "Data Source=$ServerName;Initial Catalog=SSISDB;"
if (![string]::IsNullOrEmpty($sqlAccountUsername) -and ![string]::IsNullOrEmpty($sqlAccountPassword))
{
# Add username and password to connection string
$sqlConnectionString += "User ID=$sqlAccountUsername; Password=$sqlAccountPassword;"
}
else
{
# Use integrated
$sqlConnectionString += "Integrated Security=SSPI;"
}
# Create new connection object with connection string
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString
# create integration services object
$integrationServices = New-Object "$ISNamespace.IntegrationServices" $sqlConnection
# get reference to the catalog
Write-Host "Getting reference to catalog $CataLogName"
$Catalog = Get-Catalog -CatalogName $CataLogName
# get folder reference
$Folder = Get-Folder -FolderName $FolderName -Catalog $Catalog
# loop through ispac files
foreach($IsPacFile in $IsPacFiles)
{
# read project file
$ProjectFile = [System.IO.File]::ReadAllBytes($IsPacFile.FullName)
# deploy project
Write-Host "Deploying project $($IsPacFile.Name)..."
$Folder.DeployProject($ProjectName, $ProjectFile) | Out-Null
# get reference to deployed project
$Project = $Folder.Projects[$ProjectName]
# check to see if they want to use environments
if($UseEnvironment)
{
# get environment reference
$Environment = Get-Environment -Folder $Folder -EnvironmentName $EnvironmentName
# set environment reference
Set-EnvironmentReference -Project $Project -Environment $Environment -Folder $Folder
# check to see if the user wants to convert project parameters to environment variables
if($ReferenceProjectParametersToEnvironmentVairables)
{
# set environment variables
Write-Host "Referencing Project Parameters to Environment Variables..."
$VariablesToPreserveInEnvironment += Set-ProjectParametersToEnvironmentVariablesReference -Project $Project -Environment $Environment
}
# check to see if the user wants to convert the package parameters to environment variables
if($ReferencePackageParametersToEnvironmentVairables)
{
# set package variables
Write-Host "Referencing Package Parameters to Environment Variables..."
$VariablesToPreserveInEnvironment += Set-PackageVariablesToEnvironmentVariablesReference -Project $Project -Environment $Environment
}
# Removes all unused variables from the environment
if ($SyncEnvironment)
{
Write-Host "Sync package environment variables..."
Sync-EnvironmentVariables -Environment $Environment -VariablesToPreserveInEnvironment $VariablesToPreserveInEnvironment
}
}
}
}
finally
{
# check to make sure sqlconnection isn't null
if($sqlConnection)
{
# check state of sqlconnection
if($sqlConnection.State -eq [System.Data.ConnectionState]::Open)
{
# close the connection
$sqlConnection.Close()
}
# cleanup
$sqlConnection.Dispose()
}
}
#endregion
Provided under the Apache License version 2.0.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "0c8167e9-49fe-4f2a-a007-df5ef2e63fac",
"Name": "Deploy ispac SSIS project from Referenced Package",
"Description": "This step template will deploy SSIS ispac projects to SQL Server Integration Services Catalog. The template uses a referenced package and is Worker compatible.\n\nThis template will install the Nuget package provider if it is not present on the machine it is running on.",
"Version": 7,
"ExportedAt": "2020-06-15T18:38:53.170Z",
"ActionType": "Octopus.Script",
"Author": "bobjwalker",
"Packages": [
{
"Id": "7a1f5eb6-0b6a-4319-a0e8-7b4d13ea609e",
"Name": "SSIS Package",
"PackageId": "#{ssisPackageId}",
"FeedId": "#{TemplatePackageFeedId}",
"AcquisitionLocation": "Server",
"Properties": {
"Extract": "True"
}
}
],
"Parameters": [
{
"Id": "97e5d9db-fd73-4620-84f7-91d6e31e0c4b",
"Name": "ServerName",
"Label": "Database server name (\\instance)",
"HelpText": "Name of the SQL Server you are deploying to.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "b5adc9a7-2bcb-45c8-9a64-02e0dc286981",
"Name": "sqlAccountUsername",
"Label": "SQL Authentication Username",
"HelpText": "(Optional) Username of the SQL Authentication account. Use this approach when deploying to Azure Databases with SSIS configured. If SQL Authentication Username and Password are blank, Integrated Authentication is used.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "0646a66e-5ba5-4761-a388-8a8bc73492ec",
"Name": "sqlAccountPassword",
"Label": "SQL Authentication Password",
"HelpText": "(Optional) Password of the SQL Authentication account.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
}
},
{
"Id": "46e7103c-2833-4bf0-9f98-b5d2a0bbcabf",
"Name": "EnableCLR",
"Label": "Enable SQL CLR",
"HelpText": "This will reconfigure SQL Server to enable the SQL CLR. It is highly recommended that this be previously authorized by your Database Administrator.",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "970702e3-e0bf-4ea7-b1ba-067128a3f8c3",
"Name": "CatalogName",
"Label": "Catalog name",
"HelpText": "Name of the catalog to create in Integration Services Catalogs on SQL Server. When using the GUI, this value gets hardcoded to SSISDB and cannot be changed. It is recommended that you do not change the default value.",
"DefaultValue": "SSISDB",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "ccc0a5bf-467b-4695-bcef-912043e27d49",
"Name": "CatalogPwd",
"Label": "Catalog password",
"HelpText": "Password to the Integration Services Catalog.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
}
},
{
"Id": "5c02a114-b06e-4547-bb0f-b2aba1869d8e",
"Name": "FolderName",
"Label": "Folder name",
"HelpText": "Name of the folder to use within the Integration Services Catalog",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "88ffb943-5525-4484-9963-0a34c89a7085",
"Name": "ProjectName",
"Label": "Project name",
"HelpText": "Name of the project within the folder of the Integration Services catalog",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "b8c29eac-a60f-4b25-96b8-f3b2e3fd8806",
"Name": "UseEnvironment",
"Label": "Use environment",
"HelpText": "This will make a project reference to the defined environment.",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "156a4b55-26e4-4c70-bf91-8729ca131c62",
"Name": "EnvironmentName",
"Label": "Environment name",
"HelpText": "Name of the environment to reference the project to. If the environment doesn't exist, it will create it.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "0339168d-9a53-4d8a-97d9-7a76605bdc55",
"Name": "ReferenceProjectParametersToEnvironmentVairables",
"Label": "Reference project parameters to environment variables",
"HelpText": "Checking this box will make Project Parameters reference Environment Variables. If the Environment Variable doesn't exist, it will create it. This expects that an Octopus variable of the same name exists.",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "5f6b7788-4957-451a-bec7-092e471291f2",
"Name": "ReferencePackageParametersToEnvironmentVairables",
"Label": "Reference package parameters to environment variables",
"HelpText": "Checking this box will make Package Parameters reference Environment Variables. If the Environment Variable doesn't exist, it will create it. This expects than an Octopus variable of the same name exists.",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "cc4819a4-a6e4-4220-a581-0f2aed51f4a6",
"Name": "UseFullyQualifiedVariableNames",
"Label": "Use Fully Qualified Variable Names",
"HelpText": "When true the package variables names must be represented in `dtsx_name_without_extension.variable_name`",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "bb22984a-43ed-45c7-978d-72fddec96fe1",
"Name": "UseCustomFilter",
"Label": "Use Custom Filter for connection manager properties",
"HelpText": "Custom filter should contain the regular expression for ignoring properties when setting will occur during the auto-mapping",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "d9bba05b-d8a7-40b0-9893-4c878a264de6",
"Name": "CustomFilter",
"Label": "Custom Filter for connection manager properties",
"HelpText": "Regular expression for filtering out the connection manager properties during the auto-mapping process. This string is used when `UseCustomFilter` is set to true",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "77237f87-bc01-4541-b477-005f7416dce2",
"Name": "SyncEnvironment",
"Label": "Clean obsolete variables from environment",
"HelpText": "When `true` synchronizes the environment:\n- Removes obsolete variables\n- Removes renamed variables\n- Replaces values of valid variables (also when `false`)",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "34dcc677-6091-4e92-8b40-9ab09ed18fa3",
"Name": "ssisPackageId",
"Label": "Package Id",
"HelpText": "Id of the package to deploy, used to support deployment with Workers.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "8338dc00-27ab-4c60-809a-2badd22250e9",
"Name": "TemplatePackageFeedId",
"Label": "Package Feed Id",
"HelpText": "ID of the package feed the referenced package resides in.",
"DefaultValue": "feeds-builtin",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
}
],
"Properties": {
"Octopus.Action.Script.Syntax": "PowerShell",
"Octopus.Action.Script.ScriptBody": "#region Functions\n\n# Define functions\nfunction Get-SqlModuleInstalled\n{\n # Define parameters\n param(\n $PowerShellModuleName\n )\n\n # Check to see if the module is installed\n if ($null -ne (Get-Module -ListAvailable -Name $PowerShellModuleName))\n {\n # It is installed\n return $true\n }\n else\n {\n # Module not installed\n return $false\n }\n}\n\nfunction Get-NugetPackageProviderNotInstalled\n{\n\t# See if the nuget package provider has been installed\n return ($null -eq (Get-PackageProvider -ListAvailable -Name Nuget -ErrorAction SilentlyContinue))\n}\n\nfunction Install-SqlServerPowerShellModule\n{\n # Define parameters\n param(\n $PowerShellModuleName,\n $LocalModulesPath\n )\n\n\t# Check to see if the package provider has been installed\n if ((Get-NugetPackageProviderNotInstalled) -ne $false)\n {\n \t# Display that we need the nuget package provider\n Write-Host \"Nuget package provider not found, installing ...\"\n \n # Install Nuget package provider\n Install-PackageProvider -Name Nuget -Force\n }\n\n\t# Save the module in the temporary location\n Save-Module -Name $PowerShellModuleName -Path $LocalModulesPath -Force -RequiredVersion \"21.1.18256\" \n\n\t# Display\n Write-Output \"Importing module $PowerShellModuleName ...\"\n\n # Import the module\n Import-Module -Name $PowerShellModuleName\n}\n\nFunction Load-SqlServerAssmblies\n{\n\t# Declare parameters\n \n\t# Get the folder where the SqlServer module ended up in\n\t$sqlServerModulePath = [System.IO.Path]::GetDirectoryName((Get-Module SqlServer).Path)\n \n # Loop through the assemblies\n foreach($assemblyFile in (Get-ChildItem -Path $sqlServerModulePath -Exclude msv*.dll | Where-Object {$_.Extension -eq \".dll\"}))\n {\n # Load the assembly\n [Reflection.Assembly]::LoadFile($assemblyFile.FullName) | Out-Null\n } \n}\n\n#region Get-Catalog\nFunction Get-Catalog\n{\n # define parameters\n Param ($CatalogName)\n # NOTE: using $integrationServices variable defined in main\n \n # define working varaibles\n $Catalog = $null\n # check to see if there are any catalogs\n if($integrationServices.Catalogs.Count -gt 0 -and $integrationServices.Catalogs[$CatalogName])\n {\n \t# get reference to catalog\n \t$Catalog = $integrationServices.Catalogs[$CatalogName]\n }\n else\n {\n \tif((Get-CLREnabled) -eq 0)\n \t{\n \t\tif(-not $EnableCLR)\n \t\t{\n \t\t\t# throw error\n \t\t\tthrow \"SQL CLR is not enabled.\"\n \t\t}\n \t\telse\n \t\t{\n \t\t\t# display sql clr isn't enabled\n \t\t\tWrite-Warning \"SQL CLR is not enabled on $($sqlConnection.DataSource). This feature must be enabled for SSIS catalogs.\"\n \n \t\t\t# enablign SQLCLR\n \t\t\tWrite-Host \"Enabling SQL CLR ...\"\n \t\t\tEnable-SQLCLR\n \t\t\tWrite-Host \"SQL CLR enabled\"\n \t\t}\n \t}\n \n \t# Provision a new SSIS Catalog\n \tWrite-Host \"Creating SSIS Catalog ...\"\n \n \t$Catalog = New-Object \"$ISNamespace.Catalog\" ($integrationServices, $CatalogName, $CatalogPwd)\n \t$Catalog.Create()\n \n \n }\n \n # return the catalog\n return $Catalog\n}\n#endregion\n\n#region Get-CLREnabled\nFunction Get-CLREnabled\n{\n # define parameters\n # Not using any parameters, but am using $sqlConnection defined in main\n \n # define working variables\n $Query = \"SELECT * FROM sys.configurations WHERE name = 'clr enabled'\"\n \n # execute script\n $CLREnabled = Invoke-Sqlcmd -ServerInstance $sqlConnection.DataSource -Database \"master\" -Query $Query | Select value\n \n # return value\n return $CLREnabled.Value\n}\n#endregion\n\n#region Enable-SQLCLR\nFunction Enable-SQLCLR\n{\n $QueryArray = \"sp_configure 'show advanced options', 1\", \"RECONFIGURE\", \"sp_configure 'clr enabled', 1\", \"RECONFIGURE \"\n # execute script\n \n foreach($Query in $QueryArray)\n {\n \tInvoke-Sqlcmd -ServerInstance $sqlConnection.DataSource -Database \"master\" -Query $Query\n }\n \n # check that it's enabled\n if((Get-CLREnabled) -ne 1)\n {\n \t# throw error\n \tthrow \"Failed to enable SQL CLR\"\n }\n}\n#endregion\n\n#region Get-Folder\nFunction Get-Folder\n{\n # parameters\n Param($FolderName, $Catalog)\n \n $Folder = $null\n # try to get reference to folder\n \n if(!($Catalog.Folders -eq $null))\n {\n \t$Folder = $Catalog.Folders[$FolderName]\n }\n \n # check to see if $Folder has a value\n if($Folder -eq $null)\n {\n \t# display\n \tWrite-Host \"Folder $FolderName doesn't exist, creating folder...\"\n \n \t# create the folder\n \t$Folder = New-Object \"$ISNamespace.CatalogFolder\" ($Catalog, $FolderName, $FolderName) \n \t$Folder.Create() \n }\n \n # return the folde reference\n return $Folder\n}\n#endregion\n\n#region Get-Environment\nFunction Get-Environment\n{\n # define parameters\n Param($Folder, $EnvironmentName)\n \n $Environment = $null\n # get reference to Environment\n if(!($Folder.Environments -eq $null) -and $Folder.Environments.Count -gt 0)\n {\n \t$Environment = $Folder.Environments[$EnvironmentName]\n }\n \n # check to see if it's a null reference\n if($Environment -eq $null)\n {\n \t# display\n \tWrite-Host \"Environment $EnvironmentName doesn't exist, creating environment...\"\n \n \t# create environment\n \t$Environment = New-Object \"$ISNamespace.EnvironmentInfo\" ($Folder, $EnvironmentName, $EnvironmentName)\n \t$Environment.Create() \n }\n \n # return the environment\n return $Environment\n}\n#endregion\n\n#region Set-EnvironmentReference\nFunction Set-EnvironmentReference\n{\n # define parameters\n Param($Project, $Environment, $Folder)\n \n # get reference\n $Reference = $null\n \n if(!($Project.References -eq $null))\n {\n \t$Reference = $Project.References[$Environment.Name, $Folder.Name]\n \n }\n \n # check to see if it's a null reference\n if($Reference -eq $null)\n {\n \t# display\n \tWrite-Host \"Project does not reference environment $($Environment.Name), creating reference...\"\n \n \t# create reference\n \t$Project.References.Add($Environment.Name, $Folder.Name)\n \t$Project.Alter() \n }\n}\n#endregion\n\n#region Set-ProjectParametersToEnvironmentVariablesReference\nFunction Set-ProjectParametersToEnvironmentVariablesReference\n{\n # define parameters\n Param($Project, $Environment)\n \n $UpsertedVariables = @()\n\n if($Project.Parameters -eq $null)\n {\n Write-Host \"No project parameters exist\"\n return\n }\n\n # loop through project parameters\n foreach($Parameter in $Project.Parameters)\n {\n # skip if the parameter is included in custom filters\n if ($UseCustomFilter) \n {\n if ($Parameter.Name -match $CustomFilter)\n {\n Write-Host \"- $($Parameter.Name) skipped due to CustomFilters.\" \n continue\n }\n }\n\n # Add variable to list of variable\n $UpsertedVariables += $Parameter.Name\n\n $Variable = $null\n if(!($Environment.Variables -eq $null))\n {\n \t # get reference to variable\n \t $Variable = $Environment.Variables[$Parameter.Name]\n }\n \n \t# check to see if variable exists\n \tif($Variable -eq $null)\n \t{\n \t\t# add the environment variable\n \t\tAdd-EnvironmentVariable -Environment $Environment -Parameter $Parameter -ParameterName $Parameter.Name\n \n \t\t# get reference to the newly created variable\n \t\t$Variable = $Environment.Variables[$Parameter.Name]\n \t}\n \n \t# set the environment variable value\n \tSet-EnvironmentVariableValue -Variable $Variable -Parameter $Parameter -ParameterName $Parameter.Name\n }\n \n # alter the environment\n $Environment.Alter()\n $Project.Alter()\n\n return $UpsertedVariables\n}\n#endregion\n\nFunction Set-PackageVariablesToEnvironmentVariablesReference\n{\n # define parameters\n Param($Project, $Environment)\n\n $Variables = @()\n $UpsertedVariables = @()\n\n # loop through packages in project in order to store a temp collection of variables\n foreach($Package in $Project.Packages)\n {\n \t# loop through parameters of package\n \tforeach($Parameter in $Package.Parameters)\n \t{\n \t\t# add to the temporary variable collection\n \t\t$Variables += $Parameter.Name\n \t}\n }\n\n # loop through packages in project\n foreach($Package in $Project.Packages)\n {\n \t# loop through parameters of package\n \tforeach($Parameter in $Package.Parameters)\n \t{\n if ($UseFullyQualifiedVariableNames)\n {\n # Set fully qualified variable name\n $ParameterName = $Parameter.ObjectName.Replace(\".dtsx\", \"\")+\".\"+$Parameter.Name\n }\n else\n {\n # check if exists a variable with the same name\n $VariableNameOccurrences = $($Variables | Where-Object { $_ -eq $Parameter.Name }).count\n $ParameterName = $Parameter.Name\n \n if ($VariableNameOccurrences -gt 1)\n {\n $ParameterName = $Parameter.ObjectName.Replace(\".dtsx\", \"\")+\".\"+$Parameter.Name\n }\n }\n \n if ($UseCustomFilter)\n {\n if ($ParameterName -match $CustomFilter)\n {\n Write-Host \"- $($Parameter.Name) skipped due to CustomFilters.\" \n continue\n }\n }\n\n # get reference to variable\n \t\t$Variable = $Environment.Variables[$ParameterName]\n\n # Add variable to list of variable\n $UpsertedVariables += $ParameterName\n\n # check to see if the parameter exists\n \t\tif(!$Variable)\n \t\t{\n \t\t\t# add the environment variable\n \t\t\tAdd-EnvironmentVariable -Environment $Environment -Parameter $Parameter -ParameterName $ParameterName\n \n \t\t\t# get reference to the newly created variable\n \t\t\t$Variable = $Environment.Variables[$ParameterName]\n \t\t}\n \n \t\t# set the environment variable value\n \t\tSet-EnvironmentVariableValue -Variable $Variable -Parameter $Parameter -ParameterName $ParameterName\n \t}\n \n \t# alter the package\n \t$Package.Alter()\n }\n \n # alter the environment\n $Environment.Alter()\n\n return $UpsertedVariables\n}\n\nFunction Sync-EnvironmentVariables\n{\n # define parameters\n Param($Environment, $VariablesToPreserveInEnvironment)\n\n foreach($VariableToEvaluate in $Environment.Variables)\n {\n if ($VariablesToPreserveInEnvironment -notcontains $VariableToEvaluate.Name)\n {\n Write-Host \"- Removing environment variable: $($VariableToEvaluate.Name)\"\n $VariableToRemove = $Environment.Variables[$VariableToEvaluate.Name]\n $Environment.Variables.Remove($VariableToRemove) | Out-Null\n }\n }\n\n # alter the environment\n $Environment.Alter()\n}\n\n#region Add-EnvironmentVariable\nFunction Add-EnvironmentVariable\n{\n # define parameters\n Param($Environment, $Parameter, $ParameterName)\n \n # display \n Write-Host \"- Adding environment variable $($ParameterName)\"\n \n # check to see if design default value is emtpy or null\n if([string]::IsNullOrEmpty($Parameter.DesignDefaultValue))\n {\n \t# give it something\n \t$DefaultValue = \"\" # sensitive variables will not return anything so when trying to use the property of $Parameter.DesignDefaultValue, the Alter method will fail.\n }\n else\n {\n \t# take the design\n \t$DefaultValue = $Parameter.DesignDefaultValue\n }\n \n # add variable with an initial value\n $Environment.Variables.Add($ParameterName, $Parameter.DataType, $DefaultValue, $Parameter.Sensitive, $Parameter.Description)\n}\n#endregion\n\n#region Set-EnvironmentVariableValue\nFunction Set-EnvironmentVariableValue\n{\n # define parameters\n Param($Variable, $Parameter, $ParameterName)\n\n # check to make sure variable value is available\n if($OctopusParameters -and $OctopusParameters.ContainsKey($ParameterName))\n {\n # display \n Write-Host \"- Updating environment variable $($ParameterName)\"\n\n \t# set the variable value\n \t$Variable.Value = $OctopusParameters[\"$($ParameterName)\"]\n }\n else\n {\n \t# warning\n \tWrite-Host \"**- OctopusParameters collection is empty or $($ParameterName) not in the collection -**\"\n }\n \n # Set reference\n $Parameter.Set([Microsoft.SqlServer.Management.IntegrationServices.ParameterInfo+ParameterValueType]::Referenced, \"$($ParameterName)\")\n}\n#endregion\n\n# Define PowerShell Modules path\n$LocalModules = (New-Item \"$PSScriptRoot\\Modules\" -ItemType Directory -Force).FullName\n$env:PSModulePath = \"$LocalModules;$env:PSModulePath\"\n\n# Check to see if SqlServer module is installed\nif ((Get-SqlModuleInstalled -PowerShellModuleName \"SqlServer\") -ne $true)\n{\n\t# Display message\n Write-Output \"PowerShell module SqlServer not present, downloading temporary copy ...\"\n\n\t#Enable TLS 1.2 as default protocol\n\t[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12\n\n # Download and install temporary copy\n Install-SqlServerPowerShellModule -PowerShellModuleName \"SqlServer\" -LocalModulesPath $LocalModules\n \n\t#region Dependent assemblies\n\tLoad-SqlServerAssmblies \n}\nelse\n{\n\t# Load the IntegrationServices Assembly\n\t[Reflection.Assembly]::LoadWithPartialName(\"Microsoft.SqlServer.Management.IntegrationServices\") | Out-Null # Out-Null supresses a message that would normally be displayed saying it loaded out of GAC\n}\n\n#endregion\n\n# Store the IntegrationServices Assembly namespace to avoid typing it every time\n$ISNamespace = \"Microsoft.SqlServer.Management.IntegrationServices\"\n\n#endregion\n\n#region Main\ntry\n{ \n # ensure all boolean variables are true booleans\n $EnableCLR = [System.Convert]::ToBoolean(\"$EnableCLR\")\n $UseEnvironment = [System.Convert]::ToBoolean(\"$UseEnvironment\")\n $ReferenceProjectParametersToEnvironmentVairables = [System.Convert]::ToBoolean(\"$ReferenceProjectParametersToEnvironmentVairables\")\n \n $ReferencePackageParametersToEnvironmentVairables = [System.Convert]::ToBoolean(\"$ReferencePackageParametersToEnvironmentVairables\")\n $UseFullyQualifiedVariableNames = [System.Convert]::ToBoolean(\"$UseFullyQualifiedVariableNames\")\n $SyncEnvironment = [System.Convert]::ToBoolean(\"$SyncEnvironment\")\n # custom names for filtering out the excluded variables by design\n $UseCustomFilter = [System.Convert]::ToBoolean(\"$UseCustomFilter\")\n $CustomFilter = [System.Convert]::ToString(\"$CustomFilter\")\n # list of variables names to keep in target environment\n $VariablesToPreserveInEnvironment = @()\n \n\t# Get the extracted path\n\t$DeployedPath = $OctopusParameters[\"Octopus.Action.Package[$ssisPackageId].ExtractedPath\"]\n \n\t# Get all .ispac files from the deployed path\n\t$IsPacFiles = Get-ChildItem -Recurse -Path $DeployedPath | Where {$_.Extension.ToLower() -eq \".ispac\"}\n\n\t# display number of files\n\tWrite-Host \"$($IsPacFiles.Count) .ispac file(s) found.\"\n\n\tWrite-Host \"Connecting to server ...\"\n\n\t# Create a connection to the server\n $sqlConnectionString = \"Data Source=$ServerName;Initial Catalog=SSISDB;\"\n \n if (![string]::IsNullOrEmpty($sqlAccountUsername) -and ![string]::IsNullOrEmpty($sqlAccountPassword))\n {\n \t# Add username and password to connection string\n $sqlConnectionString += \"User ID=$sqlAccountUsername; Password=$sqlAccountPassword;\"\n }\n else\n {\n \t# Use integrated\n $sqlConnectionString += \"Integrated Security=SSPI;\"\n }\n \n \n # Create new connection object with connection string\n $sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString\n\n\t# create integration services object\n\t$integrationServices = New-Object \"$ISNamespace.IntegrationServices\" $sqlConnection\n\n\t# get reference to the catalog\n\tWrite-Host \"Getting reference to catalog $CataLogName\"\n\t$Catalog = Get-Catalog -CatalogName $CataLogName\n\n\t# get folder reference\n\t$Folder = Get-Folder -FolderName $FolderName -Catalog $Catalog\n\n\t# loop through ispac files\n\tforeach($IsPacFile in $IsPacFiles)\n\t{\n\t\t# read project file\n\t\t$ProjectFile = [System.IO.File]::ReadAllBytes($IsPacFile.FullName)\n\n\t\t# deploy project\n\t\tWrite-Host \"Deploying project $($IsPacFile.Name)...\"\n\t\t$Folder.DeployProject($ProjectName, $ProjectFile) | Out-Null\n\n\t\t# get reference to deployed project\n\t\t$Project = $Folder.Projects[$ProjectName]\n\n\t\t# check to see if they want to use environments\n\t\tif($UseEnvironment)\n\t\t{\n\t\t\t# get environment reference\n\t\t\t$Environment = Get-Environment -Folder $Folder -EnvironmentName $EnvironmentName\n\n\t\t\t# set environment reference\n\t\t\tSet-EnvironmentReference -Project $Project -Environment $Environment -Folder $Folder\n\n\t\t\t# check to see if the user wants to convert project parameters to environment variables\n\t\t\tif($ReferenceProjectParametersToEnvironmentVairables)\n\t\t\t{\n\t\t\t\t# set environment variables\n\t\t\t\tWrite-Host \"Referencing Project Parameters to Environment Variables...\"\n\t\t\t\t$VariablesToPreserveInEnvironment += Set-ProjectParametersToEnvironmentVariablesReference -Project $Project -Environment $Environment\n\t\t\t}\n\n\t\t\t# check to see if the user wants to convert the package parameters to environment variables\n\t\t\tif($ReferencePackageParametersToEnvironmentVairables)\n\t\t\t{\n\t\t\t\t# set package variables\n\t\t\t\tWrite-Host \"Referencing Package Parameters to Environment Variables...\"\n\t\t\t\t$VariablesToPreserveInEnvironment += Set-PackageVariablesToEnvironmentVariablesReference -Project $Project -Environment $Environment\n\t\t\t}\n \n # Removes all unused variables from the environment\n if ($SyncEnvironment)\n {\n Write-Host \"Sync package environment variables...\"\n Sync-EnvironmentVariables -Environment $Environment -VariablesToPreserveInEnvironment $VariablesToPreserveInEnvironment\n }\n\t\t}\n\t}\n}\n\nfinally\n{\n\t# check to make sure sqlconnection isn't null\n\tif($sqlConnection)\n\t{\n\t\t# check state of sqlconnection\n\t\tif($sqlConnection.State -eq [System.Data.ConnectionState]::Open)\n\t\t{\n\t\t\t# close the connection\n\t\t\t$sqlConnection.Close()\n\t\t}\n\n\t\t# cleanup\n\t\t$sqlConnection.Dispose()\n\t}\n}\n#endregion\n",
"Octopus.Action.Script.ScriptSource": "Inline"
},
"Category": "SQL Server",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/ssis-deploy-ispac-from-referenced-package.json",
"Website": "/step-templates/0c8167e9-49fe-4f2a-a007-df5ef2e63fac",
"Logo": "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",
"$Meta": {
"Type": "ActionTemplate"
}
}
Page updated on Monday, June 15, 2020