O Azure Data Factory ou simplesmente (ADF) é o serviço ETL (do inglês Extract Transform Load) de nuvem do Azure para integração e transformação de dados sem servidor. Ele oferece uma interface de usuário intuitiva e sem código para criação, monitoramento e gerenciamento tudo em um painel único. Você também pode carregar e transferir pacotes SQL Server Integration Services (SSIS) existentes para o Azure e executá-los com total compatibilidade no ADF. O SSIS Integration Runtime oferece um serviço totalmente gerenciado, para que você não precise se preocupar com o gerenciamento da infraestrutura.
No Azure Data Factory, integração contínua e entrega contínua (CI/CD) significa mover pipelines, datasets e outras entidades do Data Factory de um ambiente, como desenvolvimento, teste e produção, para outro. O Azure Data Factory usa modelos do Azure Resource Manager (modelos ARM) para armazenar a configuração de suas várias entidades do Data Factory, como pipelines, conjuntos de dados e fluxos de dados.
Abaixo segue um exemplo de como o Azure Data factory usa modelos ARM para armazenar a configuração de suas várias entidades:
{
"name": "myPipeline",
"type": "Microsoft.DataFactory/factories/pipelines",
"apiVersion": "2018-06-01",
"properties": {
"activities": [
{
"name": "CopyFromBlobToBlob",
"type": "Copy",
"dependsOn": [],
"policy": {
"timeout": "7.00:00
Atualmente existem dois fluxos de trabalho para CI/CD para o Azure Data Factory:
Onde:
Onde:
É no segundo fluxo que vamos focar nesse artigo.
Na criação do pipeline de CI/CD será necessário utilizar o pacote @microsoft/azure-data-factory-utilities que é um pacote de utilitários para o Azure Data Factory. Esse pacote contém um conjunto de ferramentas para ajudar a exportar e validar pipelines do Data Factory. Um arquivo package.json
deve ser criado na raiz do repositório e adicionado a seguinte dependência:
{
"scripts":{
"build":"node node_modules/@microsoft/azure-data-factory-utilities/lib/index"
},
"dependencies":{
"@microsoft/azure-data-factory-utilities":"^0.1.6"
}
}
Uma das tarefas do pipeline será copiar os arquivos ARM para um Storage Account. Há alguns limites para a utilização dos arquivos ARM. O tamanho máximo de um arquivo é de 4MB e quantidade de recursos em um arquivo é 800, para conhecer mais sobre os limites dos arquivos ARM acesse esse link. Para contornar esse problema, os arquivos serão divididos em pacotes de 4MB e armazenados no Storage Account.
Para isso, será necessário criar um Service Principal com permissão de acesso ao Storage Account. Para criar o Service Principal, execute o seguinte comando no Azure CLI:
Caso não tenha o Azure CLI instalado, você pode instalar seguindo as instruções aqui.
az ad sp create-for-rbac --name "api://ADF-SP" --role "Storage Blob Data Contributor" --scopes "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>"
O comando acima irá retornar um JSON com as credenciais do Service Principal criado. Anote o appId
, tenantId
, e o password
.
Crie uma service connection no Azure DevOps para o Service Principal criado. Para criar a service connection, acesse o Azure DevOps e acesse o projeto que o ADF foi conectado e clique em Project settings
e em seguida em Service connections
. Clique em New service connection
e selecione Azure Resource Manager
e clique em Next
. Selecione Service principal (manual)
e clique em Next
. Preencha os campos com as informações do Service Principal criado e clique em Verify and save
.
Caso não tenha o Azure Storage Account, acesse esse link e siga as instruções.
Um SAS Token também será necessário para que o processo de CI/CD possa acessar o Storage Account. Para criar o SAS Token, acesse esse link e siga as instruções.
Com o SAS Token em mãos e o Storage Account criado, vamos criar o pipeline de CI/CD.
Para criar o pipeline de CI/CD, acesse o Azure DevOps e acesse o projeto que o ADF foi conectado e clique em Pipelines
e em seguida em New pipeline
. Certifique-se de que o repositório correto está selecionado e clique em Starter pipeline
.
Substitua o conteúdo do arquivo azure-pipelines.yml
pelo seguinte:
trigger:
- main
variables:
- group: adf
- name: subscriptionId
value: <Subscription Id>
- name: serviceConnection
value: <Service Connection Id>
- name: dataFactoryNameDev
value: <Data Factory Name Dev>
- name: dataFactoryNameHml
value: <Data Factory Name Hml>
- name: datafactoryRgName
value: <Resource Group Name>
- name: dataFactoryID
value: /subscriptions/$(subscriptionId)/resourceGroups/$(datafactoryRgName)/providers/Microsoft.DataFactory/factories/$(dataFactoryNameDev)
- name: blobContainerName
value: armartifacts
- name: storageAccountName
value: <Storage Account Name>
- name: StorageURL
value: https://$(storageAccountName).blob.core.windows.net/$(blobContainerName)/ARMTemplate/linkedTemplates
- name: storageAccountUri
value: https://$(storageAccountName).blob.core.windows.net
- name: location
value: <location>
- name: NewAzResourceLock
value: 'False'
- name: LockLevel
value: '--'
- name: LockNotes
value: '--'
- name: LockName
value: '--'
stages:
- stage: Build
displayName: Build ADF
jobs:
- template: Template/build-adf.yml
parameters:
devDataFactoryID: $(dataFactoryID)
- stage: DeployToDev
displayName: Deploy em Dev
dependsOn: Build
condition: succeeded()
jobs:
- template: Template/deploy-adf.yml
parameters:
datafactoryRgName: $(datafactoryRgName)
resourceManagerConnection: $(serviceConnection)
location: $(location)
subscriptionId: $(subscriptionId)
subscription: $(serviceConnection)
dataFactoryName: $(dataFactoryNameDev)
dataFactoryNameDev: $(dataFactoryNameDev)
storageAccountName: $(storageAccountName)
blobContainerName: $(blobContainerName)
StorageURL: $(StorageURL)
StorageSASToken: $(StorageSASToken)
environment: dev
automatePublish: true
- stage: DeployToHml
displayName: Deploy em Hml
dependsOn: DeployToDev
condition: succeeded()
jobs:
- template: Template/deploy-adf.yml
parameters:
datafactoryRgName: $(datafactoryRgName)
resourceManagerConnection: $(serviceConnection)
location: $(location)
subscriptionId: $(subscriptionId)
subscription: $(serviceConnection)
dataFactoryName: $(dataFactoryNameHml)
dataFactoryNameDev: $(dataFactoryNameDev)
storageAccountName: $(storageAccountName)
blobContainerName: $(blobContainerName)
StorageURL: $(StorageURL)
StorageSASToken: $(StorageSASToken)
environment: hml
Substitua os valores das variáveis <Subscription Id>
, <Service Connection Id>
, <Data Factory Name Dev>
(ambiente de desenvolvimento), <Data Factory Name Hml>
(ambiente de homologação), <location>
(ex. brazilsouth, eastus, …), <Resource Group Name>
e <Storage Account Name>
pelos valores correspondentes.
Salve o arquivo, mas não execute o pipeline ainda.
O pipeline de CI/CD criado acima possui 3 stages:
build-adf.yml
, onde o processo de exportação e validação do ADF é executado. Após a execução os arquivos ARM são armazenados no Azure Pipelines e o job é finalizado.deploy-adf.yml
, onde o processo de deploy do ADF atualiza o ambiente de Dev com as alterações realizadas. Após a execução o job é finalizado.deploy-adf.yml
, onde o processo de deploy do ADF atualiza o ambiente de Hml com as alterações realizadas. Após a execução o job é finalizado.No repositório crie duas pastas chamadas Template
e Scripts
.
Navegue a até a pasta Template
e crie um novo arquivo chamado build-adf.yml
. Substitua o conteúdo do arquivo pelo seguinte:
parameters:
- name: devDataFactoryID
jobs:
- job: BuildADFDev
displayName: Build Adf Dev
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- task: Npm@1
inputs:
command: 'install'
workingDir: '$(Build.Repository.LocalPath)'
verbose: true
displayName: 'Install npm package'
- task: Npm@1
inputs:
command: 'custom'
workingDir: '$(Build.Repository.LocalPath)'
customCommand: 'run build validate $(Build.Repository.LocalPath) ${{ parameters.devDataFactoryID }}'
displayName: 'Validate'
- task: Npm@1
inputs:
command: 'custom'
workingDir: '$(Build.Repository.LocalPath)'
customCommand: 'run build export $(Build.Repository.LocalPath) ${{ parameters.devDataFactoryID }} "ArmTemplate"'
displayName: 'Generate ARM template'
- task: CopyFiles@2
displayName: Copiando os arquivos do IaC
inputs:
SourceFolder: '$(Build.Repository.LocalPath)'
Contents: |
ArmTemplate/**/*
Scripts/**/*
TargetFolder: '$(Build.ArtifactStagingDirectory)/dropIAC'
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/dropIAC'
artifact: 'dropIAC'
publishLocation: 'pipeline'
Na sequência, crie um novo arquivo chamado deploy-adf.yml
e substitua o conteúdo do arquivo pelo seguinte:
parameters:
- name: datafactoryRgName
- name: resourceManagerConnection
- name: location
- name: subscriptionId
- name: subscription
- name: dataFactoryNameDev
- name: dataFactoryName
- name: storageAccountName
- name: blobContainerName
- name: StorageURL
- name: StorageSASToken
- name: environment
- name: automatePublish
type: boolean
default: false
jobs:
- deployment: Deploy
displayName: Deploy do Data Factory
pool:
vmImage: 'windows-latest'
environment: ${{ parameters.environment }}
strategy:
runOnce:
deploy:
steps:
- task: DownloadPipelineArtifact@2
displayName: Download do artefato ArmTemplate
inputs:
buildType: 'current'
artifactName: 'dropIAC'
targetPath: '$(Build.ArtifactStagingDirectory)'
- task: AzureFileCopy@4
displayName: 'Copy ARMTemplates to Storage Account'
inputs:
SourcePath: '$(Build.ArtifactStagingDirectory)/ARMTemplate'
azureSubscription: ${{ parameters.subscription }}
Destination: AzureBlob
storage: ${{ parameters.storageAccountName }}
ContainerName: '${{ parameters.blobContainerName }}'
- task: AzurePowerShell@5
displayName: 'Script Pré-Deployment'
inputs:
azureSubscription: ${{ parameters.subscription }}
ScriptType: FilePath
ScriptPath: $(Build.ArtifactStagingDirectory)/Scripts/Pre-Pos-Deployment.ps1
ScriptArguments: -armTemplate $(Build.ArtifactStagingDirectory)/ARMTemplate/ARMTemplateForFactory.json -ResourceGroupName ${{ parameters.datafactoryRgName }} -DataFactoryName ${{ parameters.dataFactoryName }} -predeployment $true -deleteDeployment $false
azurePowerShellVersion: LatestVersion
workingDirectory: $(Build.ArtifactStagingDirectory)
pwsh: true
- task: AzurePowerShell@5
displayName: 'Remove Resource Group Lock'
inputs:
azureSubscription: ${{ parameters.subscription }}
ScriptType: 'FilePath'
ScriptPath: '$(Build.ArtifactStagingDirectory)/Scripts/verifyResourceLock.ps1'
ScriptArguments: '-removeLock "True" -ResourceGroupName ${{ parameters.datafactoryRgName }}'
azurePowerShellVersion: 'LatestVersion'
pwsh: true
- task: AzureResourceManagerTemplateDeployment@3
displayName: ARM Template - Deploy do ADF ${{ parameters.dataFactoryName }}
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: ${{ parameters.resourceManagerConnection }}
subscriptionId: ${{ parameters.subscriptionId }}
action: 'Create Or Update Resource Group'
resourceGroupName: ${{ parameters.datafactoryRgName }}
location: ${{ parameters.location }}
templateLocation: 'Linked artifact'
csmFile: '$(Build.ArtifactStagingDirectory)/ARMTemplate/linkedTemplates/ArmTemplate_master.json'
csmParametersFile: '$(Build.ArtifactStagingDirectory)/ARMTemplate/linkedTemplates/ArmTemplateParameters_master.json'
overrideParameters: >-
-factoryName ${{ parameters.dataFactoryName }} -containerUri ${{ parameters.StorageURL }} -containerSasToken ${{ parameters.StorageSASToken }}
deploymentMode: 'Incremental'
- task: AzurePowerShell@5
displayName: 'Script Pos-Deployment'
inputs:
azureSubscription: ${{ parameters.subscription }}
ScriptType: 'FilePath'
ScriptPath: '$(Build.ArtifactStagingDirectory)/Scripts/Pre-Pos-Deployment.ps1'
ScriptArguments: '-armTemplate $(Build.ArtifactStagingDirectory)/ARMTemplate/ARMTemplateForFactory.json -ResourceGroupName ${{ parameters.datafactoryRgName }} -DataFactoryName ${{ parameters.dataFactoryName }} -predeployment $false -deleteDeployment $true'
azurePowerShellVersion: 'LatestVersion'
pwsh: true
- ${{ if eq(parameters.automatePublish, 'true') }}:
- task: AzurePowerShell@5
displayName: 'Automate Publish'
inputs:
azureSubscription: ${{ parameters.subscription }}
ScriptType: 'FilePath'
ScriptPath: '$(Build.ArtifactStagingDirectory)/Scripts/UpdateLastCommitId.ps1'
ScriptArguments: '-ResourceGroupName ${{ parameters.datafactoryRgName }} -DataFactoryName ${{ parameters.dataFactoryName }} -LastCommitId $(Build.SourceVersion)'
azurePowerShellVersion: 'LatestVersion'
pwsh: true
- task: AzurePowerShell@5
displayName: 'Create Resource Group Lock'
inputs:
azureSubscription: ${{ parameters.subscription }}
ScriptType: 'FilePath'
ScriptPath: '$(Build.ArtifactStagingDirectory)/Scripts/verifyResourceLock.ps1'
ScriptArguments: '-newLock $(NewAzResourceLock) -ResourceGroupName ${{ parameters.datafactoryRgName }} -LockLevel $(LockLevel) -LockNotes $(LockNotes) -LockName $(LockName)'
azurePowerShellVersion: 'LatestVersion'
pwsh: true
O template deploy-adf.yml
utiliza alguns scripts que estão na pasta Scripts
do repositório, eles são necessários para que o processo de deploy funcione corretamente. Cada script tem uma função específica, abaixo estão as descrições e conteúdo de cada script.
Script para parar a execução das triggers do Data Factory O script Pre-Pos-Deployment.ps1
é responsável por parar a execução das triggers do Data Factory. Esse script é utilizado para que a publicação do Data Factory não apresente erro por estar em execução no momento do deploy, esse script também é responsável por reativar as trigger do Data Factory após o deploy e por deletar o deployment do Data Factory após o deploy. para ter acesso ao script acesse esse repositório no Github Azure/Azure-DataFactory.
Script para atualizar o commit id do Data Factory O script UpdateLastCommitId.ps1
é responsável por atualizar o commit id do Data Factory. Esse script é utilizado para que o Data Factory seja publicado automaticamente após o deploy.
param(
[parameter(Mandatory = $true)] [String]$ResourceGroupName,
[parameter(Mandatory = $true)] [String]$DataFactoryName,
[parameter(Mandatory = $true)] [String]$LastCommitId
)
$var = Get-AzDataFactoryV2 -ResourceGroupName $ResourceGroupName -Name $DataFactoryName
Get-AzDataFactoryV2 -ResourceGroupName $ResourceGroupName -Name $DataFactoryName | Set-AzDataFactoryV2 -AccountName $var.RepoConfiguration.AccountName -RepositoryName $var.RepoConfiguration.RepositoryName -CollaborationBranch $var.RepoConfiguration.CollaborationBranch -RootFolder / -ProjectName $var.RepoConfiguration.ProjectName -LastCommitId $LastCommitId -Force
Script para remover o lock do Resource Group O script verifyResourceLock.ps1
é responsável por remover o lock do Resource Group e criar um novo lock com as informações passadas como parâmetro.
param (
[parameter(Mandatory = $false)] [System.String] $removeLock = "False",
[parameter(Mandatory = $false)] [System.String] $newLock = "False",
[parameter(Mandatory = $false)] [System.String] $ResourceGroupName = "rg-name",
[parameter(Mandatory = $false)] [System.String] $LockLevel = "CanNotDelete",#CanNotDelete / ReadOnly
[parameter(Mandatory = $false)] [System.String] $LockNotes = "Azure-DevOps-Pipeline",
[parameter(Mandatory = $false)] [System.String] $LockName = "ProductionLocked"
)
function RemoveAzResourceLock {
param(
[System.String] $ResourceGroupName
)
$AzResourceLock = Get-AzResourceLock -ResourceGroupName $ResourceGroupName -AtScope
# verify if exists an resource lock
if (!$AzResourceLock) {
Write-Host "There is no Resource Lock for Resource Group: " $ResourceGroupName
$return = $false
$return = $return | Select-Object @{Name = 'Removed';Expression = {$_}}
$NewAzResourceLock = "False"
Write-Host "##vso[task.setvariable variable=NewAzResourceLock;]$NewAzResourceLock"
return $return
} else {
$RemoveAzResourceLock = Remove-AzResourceLock -ResourceGroupName $AzResourceLock.ResourceGroupName -LockName $AzResourceLock.Name -Force
Write-Host "Resource Lock Removed? " $RemoveAzResourceLock
$return = $AzResourceLock | Select-Object Name, @{Name = 'Level';Expression = {"$($_.Properties.level)"}}, @{Name = 'Notes';Expression = {"$($_.Properties.notes)"}}, @{Name = 'Removed';Expression = {"True"}}
$ResourceGroupName = $AzResourceLock.ResourceGroupName
$NewAzResourceLock = "True"
$LockLevel = $AzResourceLock.Properties.level
$LockNotes = $AzResourceLock.Properties.notes ? $AzResourceLock.Properties.notes : "Azure-DevOps-Pipeline"
$LockName = $AzResourceLock.Name
Write-Host "##vso[task.setvariable variable=NewAzResourceLock;]$NewAzResourceLock"
Write-Host "##vso[task.setvariable variable=ResourceGroupName;]$ResourceGroupName"
Write-Host "##vso[task.setvariable variable=LockLevel;]$LockLevel"
Write-Host "##vso[task.setvariable variable=LockNotes;]$LockNotes"
Write-Host "##vso[task.setvariable variable=LockName;]$LockName"
return $return
}
}
function NewAzResourceLock {
param(
[System.String] $NewAzResourceLock,
[System.String] $ResourceGroupName,
[System.String] $LockLevel,
[System.String] $LockNotes,
[System.String] $LockName
)
if ($NewAzResourceLock -eq 'True'){
$ResourceLock = New-AzResourceLock -ResourceGroupName $ResourceGroupName -LockLevel $LockLevel -LockNotes $LockNotes -LockName $LockName -Force
Write-Host "Resource Lock Created: " $ResourceLock.ResourceId
} else {
Write-Host "There is no Resource Lock for Resource Group: " $ResourceGroupName
}
}
if ($removeLock -eq 'True') {
Write-Host "Remove lock"
RemoveAzResourceLock -ResourceGroupName $ResourceGroupName
}
if ($newLock -eq 'True') {
Write-Host "Create lock"
NewAzResourceLock -NewAzResourceLock $newLock -ResourceGroupName $ResourceGroupName -LockLevel $LockLevel -LockNotes $LockNotes -LockName $LockName
}
Agora que já todos os scripts estão criados é necessário criar um variable group no Azure DevOps para armazenar o SAS Token criado anteriormente.
Para criar o variable group no Azure DevOps basta clicar em Library
> Variable Groups
> New Variable Group
, preencha o nome do variable group com adf
e clique em Add Variable
. Adicione a variável abaixo:
StorageSASToken | ? + Token gerado anteriormente*
nota: Não esqueça de clicar no cadeado para ocultar o Token.
Após todos os passos anteriores, o pipeline de CI/CD está pronto para ser executado. Clique em Pipelines
> selecione o pipeline criado > Run pipeline
.
Um exemplo de execução com sucesso do pipeline pode ser visto abaixo:
A utilização do Azure DevOps para o CI/CD de Data Factory é uma ótima opção para quem deseja automatizar o processo de deploy de Data Factory. O pipeline criado neste artigo é um exemplo , mas existem outras formas de automação, como por exemplo, utilizando o Azure Data Factory V2 REST API.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.