Skip to content

Commit

Permalink
Merge pull request #35 from SQLPlayer/config-json
Browse files Browse the repository at this point in the history
Config in JSON & Support of Global Parameters
  • Loading branch information
NowinskiK authored Sep 8, 2020
2 parents 2af3319 + d19cc8c commit d0e3f69
Show file tree
Hide file tree
Showing 25 changed files with 429 additions and 41 deletions.
43 changes: 37 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The main advantage of the module is the ability to publish all the Azure Data Fa
* Creation of Azure Data Factory, if not exist
* Deployment of all type of objects: pipelines, datasets, linked services, data flows, triggers, integration runtimes
* Finding the **right order** for deploying objects (no more worrying about object names)
* Build-in mechanism to replace the properties with the indicated values (CSV file)
* Build-in mechanism to replace, remove or add the properties with the indicated values (CSV and JSON file formats supported)
* Stop/start triggers
* Dropping objects when not exist in the source (code)
* Filtering (include or exclude) objects to be deployed by name and/or type
Expand All @@ -18,12 +18,13 @@ The main advantage of the module is the ability to publish all the Azure Data Fa
* Whether delete or not objects not in the source
* Whether create or not a new instance of ADF if it not exist
* Tokenisation in config file allows replace any value by Environment Variable or Variable from DevOps Pipeline
* Global Parameters

The following features coming soon:
The following features coming in the future:
* Build function to support validation of files, dependencies and config
* Unit Tests of selected Pipelines and Linked Services

> The module publish code which is created and maintanance by ADF in code repository, when configured.
> The module publishes code, which is created and maintanance by ADF in code repository, when configured.
# Overview

Expand Down Expand Up @@ -125,7 +126,8 @@ $opt = New-AdfPublishOption
* [HashTable] **Excludes** - defines a list of objects to be NOT published (default: *empty*)
* [Boolean] **DeleteNotInSource** - indicates whether the objects not in the source should be deleted or not (default: *false*)
* [Boolean] **StopStartTriggers** - indicates whether the triggers would be stopped and restarted during the deployment (default: *true*)
* [Boolean] **CreateNewInstance** - specifies whether the target ADF should be created when it does not exist. When target ADF doesn't exist and this option is set to *false* then `Publish-AdfV2FromJson` function fails. (default: *true*)
* [Boolean] **CreateNewInstance** - specifies whether the target ADF should be created when it does not exist. When target ADF doesn't exist and this option is set to *false* then `Publish-AdfV2FromJson` function fails. (default: *true*)
* [Boolean] **DeployGlobalParams** - indicates whether deploy Global Parameters of ADF. Nothing happens when parameters are not defined. (default: *true*)

Subsequently, you can define the needed options:

Expand Down Expand Up @@ -308,6 +310,7 @@ Column `type` accepts one of the following values only:
- dataflow
- linkedService
- trigger
- factory *(for Global Parameters)*

### Column PATH

Expand All @@ -326,6 +329,7 @@ linkedService,BlobSampleData,typeProperties.connectionString,"DefaultEndpointsPr
linkedService,BlobSampleData,-typeProperties.encryptedCredential,
# PLUS means the desired action is to ADD new property with associated value:
linkedService,BlobSampleData,+typeProperties.accountKey,"$($Env:VARIABLE)"
factory,BigFactorySample2,"$.properties.globalParameters.'Env-Code'.value","PROD"
```


Expand Down Expand Up @@ -371,6 +375,7 @@ SQLPlayerDemo
deployment (new folder)
config-uat.csv (file for UAT environment)
config-prod.csv (file for PROD environment)
factory
integrationRuntime
linkedService
pipeline
Expand All @@ -383,6 +388,32 @@ SQLPlayerDemo
The second way is to provide full path to configuration file.
For example, if you provide `c:\MyCode\adf\uat-parameters.csv`, an exact file will be use to read configuration as the value ends with ".csv". Although, in that case, the file may be located anywhere, it's recommended to keep them along with other ADF files.

### JSON format of Config file
If you prefer using JSON rather than CSV for setting up configuration - JSON files are also supported now. Take a look at the following example:
```JSON
{
"LS_AzureDatabricks": [
{
"name": "$.properties.typeProperties.existingClusterId",
"value": "$($Env:DatabricksClusterId)",
"action": "add"
},
{
"name": "$.properties.typeProperties.encryptedCredential",
"value": "",
"action": "remove"
}
],
"LS_AzureKeyVault": [
{
"name": "$.properties.typeProperties.baseUrl",
"value": "https://kv-$($Env:Environment).vault.azure.net/",
"action": "update"
}
]
}
```


## Step: Stoping triggers
This block stops all triggers which must be stopped due to deployment.
Expand Down Expand Up @@ -426,7 +457,7 @@ Having PowerShell module it is very ease to configure Release Pipeline in Azure
Both steps you can find here:
```powershell
# Step 1
Install-Module Az.DataFactory -MinimumVersion "1.7.0" -Force
Install-Module Az.DataFactory -MinimumVersion "1.10.0" -Force
Install-Module -Name "azure.datafactory.tools" -Force
Import-Module -Name "azure.datafactory.tools" -Force
Expand All @@ -441,7 +472,7 @@ variables:
DataFactoryName: 'SQLPlayerDemo'
steps:
- powershell: |
Install-Module Az.DataFactory -MinimumVersion "1.7.0" -Force
Install-Module Az.DataFactory -MinimumVersion "1.10.0" -Force
Install-Module -Name "azure.datafactory.tools" -Force
Import-Module -Name "azure.datafactory.tools" -Force
displayName: 'PowerShell Script'
Expand Down
Binary file modified azure.datafactory.tools.psd1
Binary file not shown.
2 changes: 1 addition & 1 deletion azure.datafactory.tools.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if (Test-Path -Path "$PSScriptRoot\public" -ErrorAction Ignore)
}

$module = Get-Module Az.DataFactory
$minVer = "1.8.2"
$minVer = "1.10.0"
if ($null -ne $module -and $module.Version.ToString().CompareTo($minVer) -lt 0)
{
Write-Error "This module requires Az.DataFactory version $minVer. An earlier version of Az.DataFactory is imported in the current PowerShell session. Please open a new session before importing this module. This error could indicate that multiple incompatible versions of the Azure PowerShell cmdlets are installed on your system. Please see https://aka.ms/azps-version-error for troubleshooting information." -ErrorAction Stop
Expand Down
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [0.15.0] - 2020-09-08
### Added
* Support of JSON format for config files
* Support of Global Parameters (#29)
### Changed
* Function GetObjectsByFolderName (Adf class) uses LIKE operator (#31)

## [0.14.0] - 2020-07-26
### Changed
* Removed workaround for deployment of Azure Integration Runtimes.
Expand Down
13 changes: 11 additions & 2 deletions private/Adf.class.ps1
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
class AdfGlobalProp {
[string] $FilePath = ""
[string] $body = ""
[PSCustomObject] $GlobalParameters
}

class Adf {
[string] $Name = ""
[string] $ResourceGroupName = ""
[string] $Region = ""
[System.Collections.ArrayList] $Pipelines = @{}
[System.Collections.ArrayList] $LinkedServices = @{}
[System.Collections.ArrayList] $DataSets = @{}
[System.Collections.ArrayList] $DataFlows = @{}
[System.Collections.ArrayList] $Triggers = @{}
[System.Collections.ArrayList] $IntegrationRuntimes = @{}
[System.Collections.ArrayList] $Factories = @{}
[string] $Location = ""
[AdfGlobalProp] $GlobalFactory = [AdfGlobalProp]::new()

[System.Collections.ArrayList] AllObjects()
{
return $this.LinkedServices + $this.Pipelines + $this.DataSets + $this.DataFlows + $this.Triggers + $this.IntegrationRuntimes
return $this.LinkedServices + $this.Pipelines + $this.DataSets + $this.DataFlows + $this.Triggers + $this.IntegrationRuntimes + $this.Factories
}

[hashtable] GetObjectsByFullName([string]$pattern)
Expand All @@ -31,7 +40,7 @@ class Adf {
[hashtable] $r = @{}
$this.AllObjects() | ForEach-Object {
$ofn = $_.GetFolderName()
if ($ofn -eq $folder)
if ($ofn -like $folder)
{
$oname = $_.FullName($false);
$null = $r.Add($oname, $_)
Expand Down
2 changes: 1 addition & 1 deletion private/AdfObject.class.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ class AdfObject {
}

if (!(Get-Variable ADF_FOLDERS -ErrorAction:SilentlyContinue)) {
Set-Variable ADF_FOLDERS -option ReadOnly -value ('integrationRuntime', 'pipeline', 'dataset', 'dataflow', 'linkedService', 'trigger')
Set-Variable ADF_FOLDERS -option ReadOnly -value ('integrationRuntime', 'pipeline', 'dataset', 'dataflow', 'linkedService', 'trigger', 'factory')
}
1 change: 1 addition & 0 deletions private/AdfPublishOption.class.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ class AdfPublishOption {
[Boolean] $DeleteNotInSource = $false
[Boolean] $StopStartTriggers = $true
[Boolean] $CreateNewInstance = $true
[Boolean] $DeployGlobalParams = $true
}
7 changes: 7 additions & 0 deletions private/ConfigLine.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ConfigLine {
[string] $type = ""
[string] $name = ""
[string] $path = ""
[string] $value = ""
}

9 changes: 9 additions & 0 deletions private/Deploy-AdfObjectOnly.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ function Deploy-AdfObjectOnly {

$type = $obj.Type
if ($script:PublishMethod -eq "AzResource") { $type = "AzResource" }
# Global parameters is being deployed with different method:
if ($obj.Type -eq "factory") { $type = "GlobalParameters" }

switch -Exact ($type)
{
'integrationRuntime'
Expand Down Expand Up @@ -129,6 +132,12 @@ function Deploy-AdfObjectOnly {
-Properties $json `
-IsFullObject -Force | Out-Null
}
'GlobalParameters'
{
$adf.GlobalFactory.GlobalParameters = $json
$adf.GlobalFactory.body = $body
Update-GlobalParameters -adf $adf -targetAdf $targetAdf
}
default
{
Write-Error "Type $($obj.Type) is not supported."
Expand Down
4 changes: 4 additions & 0 deletions private/Get-AdfObjectByName.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ function Get-AdfObjectByName {
{
$r = $adf.Triggers | Where-Object { $_.Name -eq $name } | Select-Object -First 1
}
'Factory'
{
$r = $adf.Factories | Where-Object { $_.Name -eq $name } | Select-Object -First 1
}
default
{
Write-Error "Type [$type] is not supported."
Expand Down
1 change: 1 addition & 0 deletions private/Get-AzureResourceType.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function Get-AzureResourceType {
'dataflow' { $resType = 'Microsoft.DataFactory/factories/dataflows' }
'linkedService' { $resType = 'Microsoft.DataFactory/factories/linkedservices' }
'trigger' { $resType = 'Microsoft.DataFactory/factories/triggers' }
'factory' { $resType = 'Microsoft.DataFactory/factories' }
default { Write-Error "Type '$Type' is not supported." }
}

Expand Down
51 changes: 51 additions & 0 deletions private/Read-JsonConfigFile.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
function Read-JsonConfigFile {
[CmdletBinding()]
param (
[Parameter(Mandatory)] [string] $Path,
[Parameter(Mandatory)] [Adf] $adf
)

Write-Debug "BEGIN: Read-JsonConfigFile(path=$path)"
$configFileName = $Path

Write-Debug "Testing config file..."
Test-Path -Path $configFileName -PathType Leaf | Out-Null

#$configFileName = "X:\!WORK\GitHub\!SQLPlayer\azure.datafactory.tools\test\BigFactorySample2\deployment\config-c100.json"
$configtxt = Get-Content $configFileName -Raw | Out-String
$json = ConvertFrom-Json $configtxt

# Creating CSV-like an object
[System.Collections.ArrayList] $config = @{}

Set-StrictMode -Version 1.0 # Due to field 'action' which may not exist
$json.psobject.properties.name | ForEach-Object {
$name = $_
$o = $json.($name)
$o | ForEach-Object {
$dst = $adf.AllObjects() | Where-Object { $_.Name -eq $name } | Select-Object -First 1
if ($null -ne $dst) {
$cl = New-Object -TypeName ConfigLine
$cl.name = $name
$cl.type = $dst.Type
$cl.value = $_.value
$cl.path = $_.name
if ($_.action -eq "remove") { $cl.path = "-$($cl.path)" }
if ($_.action -eq "add") { $cl.path = "+$($cl.path)" }
$null = $config.Add($cl)
} else {
Write-Error "Object [$name] could not be found."
}
}
}

# Expanding string (replace Environment Variables with values)
$config | ForEach-Object {
$_.value = $ExecutionContext.InvokeCommand.ExpandString($_.value);
}

return $config.ToArray()

Write-Debug "END: Read-JsonConfigFile"

}
42 changes: 42 additions & 0 deletions private/Update-GlobalParameters.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
function Update-GlobalParameters {
[CmdletBinding()]
param
(
[Parameter(Mandatory)] [Adf] $adf,
[Parameter(Mandatory)] $targetAdf
)

Write-Debug "BEGIN: Update-GlobalParameters"

if ($adf.GlobalFactory.body.Length -gt 0)
{
$newGlobalParameters = New-Object 'system.collections.generic.dictionary[string,Microsoft.Azure.Management.DataFactory.Models.GlobalParameterSpecification]'
Write-Verbose "Parsing JSON..."
$globalFactoryObject = [Newtonsoft.Json.Linq.JObject]::Parse($adf.GlobalFactory.body)
#$globalParametersObject = $globalFactoryObject.properties.globalParameters

Write-Host "Adding global parameter..."
foreach ($p in $adf.GlobalFactory.GlobalParameters.properties.globalParameters.PSObject.Properties)
{
# $p.Name
# $p.Value.type
# $p.Value.value
$gpspec = New-Object 'Microsoft.Azure.Management.DataFactory.Models.GlobalParameterSpecification'
$gpspec.Type = $p.Value.type
$gpspec.Value = $p.Value.value
$globalParameterValue = $gpspec
$newGlobalParameters.Add($p.Name, $globalParameterValue)
}
$targetAdf.GlobalParameters = $newGlobalParameters

# Write-Host "--- newGlobalParameters ---"
#$newGlobalParameters.Values | Out-Host

Write-Verbose "Updating $($newGlobalParameters.Count) global parameters..."
Set-AzDataFactoryV2 -InputObject $targetAdf -Force | Out-Null
Write-Host "Update of $($newGlobalParameters.Count) global parameters complete."
}

Write-Debug "END: Update-GlobalParameters"

}
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
function Update-PropertiesFromCsvFile {
function Update-PropertiesFromFile {
[CmdletBinding()]
param (
[Parameter(Mandatory)] [Adf] $adf,
[Parameter(Mandatory)] [string] $stage
)

Write-Debug "BEGIN: Update-PropertiesFromCsvFile(adf=$adf, stage=$stage)"
Write-Debug "BEGIN: Update-PropertiesFromFile(adf=$adf, stage=$stage)"

$srcFolder = $adf.Location
if ([string]::IsNullOrEmpty($srcFolder)) {
Write-Error "adf.Location property has not been provided."
}

$ext = "CSV"
if ($stage.EndsWith(".csv")) {
$configFileName = $stage
} else {
} elseif ($stage.EndsWith(".json")) {
$configFileName = $stage
$ext = "JSON"
}
else {
$configFileName = Join-Path $srcFolder "deployment\config-$stage.csv"
}

Write-Verbose "Replacing values for ADF properties from CSV config file"
Write-Verbose "Replacing values for ADF properties from $ext config file"
Write-Host "Config file: $configFileName"

$configcsv = Read-CsvConfigFile -Path $configFileName
if ($ext -eq "CSV") {
$config = Read-CsvConfigFile -Path $configFileName
} else {
$config = Read-JsonConfigFile -Path $configFileName -adf $adf
}
# $config | Out-Host

$report = @{ Updated = 0; Added = 0; Removed = 0}
$configcsv | ForEach-Object {
$config | ForEach-Object {
Write-Debug "Item: $_"
$path = $_.path
$value = $_.value
Expand Down Expand Up @@ -55,6 +65,9 @@ function Update-PropertiesFromCsvFile {
Write-Error "Could not find object: $type.$name"
}
$json = $o.Body
if ($null -eq $json) {
Write-Error "Body of the object is empty!"
}

Write-Verbose "- Performing: $action for path: properties.$path"
try {
Expand Down Expand Up @@ -94,7 +107,7 @@ function Update-PropertiesFromCsvFile {
Write-Host "*** Properties modification report ***"
$report | Out-Host

Write-Debug "END: Update-PropertiesFromCsvFile"
Write-Debug "END: Update-PropertiesFromFile"

}

Loading

0 comments on commit d0e3f69

Please sign in to comment.