The following link will explain the reasons for using infra as code:
My recent setup involved: Azure data factory, SSIS, Azure SQL server, Azure dev-ops, key vault and ARM templates.
Git
I’ve made two different repositories. One for ADF and one for SSIS and SQL Server. Why?
Reason 1 quicker building and deploying
Changes to ADF are build and deployed independently of SSIS and Sql server. This is quicker.
Reason 2 less pulling Changes in ADF don’t require a pull in visual studio.
Reason 3 cleaner repo ADF has it’s own feature, developer, main and release branches.
Dev-ops variable libary
We have chosen to store secrets in the Azure devops libary of variables. This way we have 1 central location to manage secrets. From here we deploy the secrets in a seperate key vault for every DTAP environment.
Build pipeline for ADF
Variable groups
# https://aka.ms/yaml
trigger:
- Release/*
- develop-adf
#- main
pool:
vmImage: 'ubuntu-latest'
steps:
# first we will recreate the required resources (adf and keyvault) so that everything is created from azure dev ops.
#- script: echo pipeline.workspace!
# displayName: 'Run a one-line script'
#- script: 'dir $(pipeline.workspace)'
#- script: 'dir $(Agent.BuildDirectory)'
#- script: 'dir $(Agent.BuildDirectory)\s'
#- script: 'dir $(Agent.BuildDirectory)\s'
#- script: 'dir $(Agent.BuildDirectory)\s\*.*'
# Installs Node and the npm packages saved in your package.json file in the build
- task: NodeTool@0
inputs:
versionSpec: '10.x'
displayName: 'Install Node.js'
- task: Npm@1
inputs:
command: 'install'
workingDir: '$(Build.SourcesDirectory)'
verbose: true
displayName: 'Install npm package'
# Validates all of the Data Factory resources in the repository. You'll get the same validation errors as when "Validate All" is selected.
# Enter the appropriate subscription and name for the source factory.
#- task: Npm@1
# inputs:
# command: 'custom'
# workingDir: '$(Build.SourcesDirectory)' #replace with the package.json folder
# customCommand: 'run build validate $(Build.SourcesDirectory) /subscriptions/<subscription-id>/resourceGroups/testResourceGroup/providers/Microsoft.DataFactory/factories/<adf-name>'
# displayName: 'Validate'
# Validate and then generate the ARM template into the destination folder, which is the same as selecting "Publish" from the UX.
# The ARM template generated isn't published to the live version of the factory. Deployment should be done by using a CI/CD pipeline.
- task: Npm@1
inputs:
command: 'custom'
workingDir: '$(Build.SourcesDirectory)' #replace with the package.json folder
customCommand: 'run build export $(Build.SourcesDirectory) /subscriptions/<subscription-id>/resourceGroups/testResourceGroup/providers/Microsoft.DataFactory/factories/<adf-name>"ArmTemplate"'
displayName: 'Validate and Generate ARM template'
- task: CopyFiles@2
inputs:
sourceFolder: '$(Build.SourcesDirectory)/dev-ops'
contents: '**'
targetFolder: '$(Build.SourcesDirectory)/ArmTemplate/dev-ops'
#cleanTargetFolder: false # Optional
#overWrite: false # Optional
#flattenFolders: false # Optional
#preserveTimestamp: false # Optional
# Publish the artifact to be used as a source for a release pipeline.
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(Build.SourcesDirectory)/ArmTemplate'
artifact: 'ArmTemplates'
publishLocation: 'pipeline'
- Make sure that you specify a secret only once. Hence I made a group for DTA and a group for DTAP. For example because DTA contains the reference to the service connection which is different for production.
- you can use variables inside other variable expressions. I have a variable called env which is used for building the generic names for resources. For example sql server: ms-sqls-dwh-$(env).database.windows.net ( <cloud environment><resource type><application name><environment name>)
Deploy ADF pipeline
Deploy resources ADF and Keyvault. This uses an ARM template that is build and tested in Visual studio using the ARM Extension. ( You can validate the template and deploy it from Visual studio). In Dev-ops the parameters are replaced by using the Override template parameters option. This ARM template is placed in the same branch as the ADF pipelines (folder dev-ops) and there is copy step in the build pipeline to copy the ARM template into the build artifact.
-adfName "$(adfName)" -keyVaultName "$(keyVaultName)" -connectionStringSqldbConf "$(connectionStringSqldbConf)" -connectionStringSqldbDwh "$(connectionStringSqldbDwh)" -connectionStringSqldbDM "$(connectionStringSqldbDM)" -connectionStringSqldbSTA "$(connectionStringSqldbSTA)"
This is what the ARM template file looks like ( azuredeploy.json )
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adfName": {
"type": "string"
},
"env": {
"type": "string"
},
"keyVaultName": {
"type": "string"
},
"keyVaultAdminObjectId": {
"type": "string"
},
"connectionStringSqldbBetl": {
"type": "string"
},
"connectionStringSqldbAW": {
"type": "string"
},
"connectionStringSqldbRDW": {
"type": "string"
},
"gitAccountName": {
"type": "String"
},
"gitRepositoryName": {
"type": "String"
},
"gitCollaborationBranch": {
"type": "String"
},
"gitRootFolder": {
// "defaultValue": "/",
"type": "String"
},
"gitProjectName": {
"type": "String"
}
},
"variables": {
"location": "westeurope",
"fullAdfName": "[concat('Microsoft.DataFactory/factories/', parameters('adfName'))]",
"fullKeyVaultName": "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]",
"tenantId": "[subscription().tenantId]",
"repoConfigurationGit": {
"type": "FactoryVSTSConfiguration",
"accountName": "[parameters('gitAccountName')]",
"repositoryName": "[parameters('gitRepositoryName')]",
"collaborationBranch": "[parameters('gitCollaborationBranch')]",
"rootFolder": "[parameters('gitRootFolder')]",
"projectName": "[parameters('gitProjectName')]"
},
// only setup git for DEV!
"repoConf": "[if(equals(toUpper(parameters('env')),'DEV'), variables('repoConfigurationGit'), '')]"
},
"resources": [
{ //Data Factory
"name": "[parameters('adfName')]",
"apiVersion": "2018-06-01",
"type": "Microsoft.DataFactory/factories",
"properties": {
"repoConfiguration": "[variables('repoConf')]",
"globalParameters": {
"Environment": {
"type": "string",
"value": "[parameters('env')]"
}
}
},
"location": "[variables('location')]",
"identity": {
"type": "SystemAssigned"
},
"tags": {}
},
{ // keyVault
"apiVersion": "2016-10-01",
"type": "Microsoft.KeyVault/vaults",
"name": "[parameters('keyVaultName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.DataFactory/factories', parameters('adfName'))]"
],
"properties": {
"sku": {
"name": "standard",
"family": "A"
},
"tenantId": "[variables('tenantId')]",
"accessPolicies": [
{
"tenantId": "[variables('tenantId')]",
"objectId": "[reference(concat(variables('fullAdfName'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]",
"permissions": {
"keys": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"Backup",
"Restore"
],
"secrets": [
"Get",
"List",
"Set",
"Delete",
"Recover",
"Backup",
"Restore"
],
"certificates": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"Backup",
"Restore",
"ManageContacts",
"ManageIssuers",
"GetIssuers",
"ListIssuers",
"SetIssuers",
"DeleteIssuers"
]
}
},
{
"tenantId": "[variables('tenantId')]",
"objectId": "[parameters('keyVaultAdminObjectId')]",
"permissions": {
"keys": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"Backup",
"Restore"
],
"secrets": [
"Get",
"List",
"Set",
"Delete",
"Recover",
"Backup",
"Restore"
],
"certificates": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"Backup",
"Restore",
"ManageContacts",
"ManageIssuers",
"GetIssuers",
"ListIssuers",
"SetIssuers",
"DeleteIssuers"
]
}
}
]
},
"resources": [
{
"type": "secrets",
"name": "connectionStringSqldbBetl",
"apiVersion": "2016-10-01",
"properties": {
"value": "[parameters('connectionStringSqldbBetl')]"
},
"dependsOn": [
"[variables('fullKeyVaultName')]"
]
},
{
"type": "secrets",
"name": "connectionStringSqldbAw",
"apiVersion": "2016-10-01",
"properties": {
"value": "[parameters('connectionStringSqldbAw')]"
},
"dependsOn": [
"[variables('fullKeyVaultName')]"
]
},
{
"type": "secrets",
"name": "connectionStringSqldbRdw",
"apiVersion": "2016-10-01",
"properties": {
"value": "[parameters('connectionStringSqldbRdw')]"
},
"dependsOn": [
"[variables('fullKeyVaultName')]"
]
}
]
} // key vault
],
"outputs": {}
}
the pre and post deployment steps are standard microsoft scipts:
https://docs.microsoft.com/en-us/azure/data-factory/continuous-integration-deployment
I place this script in my dev-ops folder and call it using the following path:
$(System.DefaultWorkingDirectory)/Build ADF/ArmTemplates/dev-ops/adf_util.ps1
and these arguments:
-armTemplate "$(System.DefaultWorkingDirectory)/Build ADF/ArmTemplates/ARMTemplateForFactory.json" -ResourceGroupName "$(resourceGroupName)" -DataFactoryName "$(adfName)" -predeployment $true -deleteDeployment $false
188 thoughts on “Infra as code in Azure”
Comments are closed.