Непрерывные интеграция и поставка в Фабрике данных AzureContinuous integration and delivery in Azure Data Factory

Область применения: даФабрика данных Azure нетAzure Synapse Analytics (предварительная версия) APPLIES TO: yesAzure Data Factory noAzure Synapse Analytics (Preview)

ОбзорOverview

Непрерывная интеграция — это способ автоматического тестирования каждого изменения, внесенного в базу кода.Continuous integration is the practice of testing each change made to your codebase automatically and as early as possible. Непрерывная поставка следует за проверкой, которая выполняется во время непрерывной интеграции. Она передает изменения в промежуточную или рабочую систему. Continuous delivery follows the testing that happens during continuous integration and pushes changes to a staging or production system.

В Фабрике данных Azure под непрерывными интеграцией и поставкой (CI/CD) подразумевается перемещение конвейеров Фабрики данных из одной среды (разработки, тестирования, рабочей) в другую.In Azure Data Factory, continuous integration and delivery (CI/CD) means moving Data Factory pipelines from one environment (development, test, production) to another. Фабрика данных Azure использует шаблоны Azure Resource Manager для хранения конфигурации различных сущностей Фабрики данных Azure (конвейеры, наборы данных, потоки данных и т. д.).Azure Data Factory utilizes Azure Resource Manager templates to store the configuration of your various ADF entities (pipelines, datasets, data flows, and so on). Существуют два рекомендуемых метода перемещения фабрики данных в другую среду.There are two suggested methods to promote a data factory to another environment:

  • Автоматизированное развертывание с помощью интеграции Фабрики данных с Azure Pipelines.Automated deployment using Data Factory's integration with Azure Pipelines
  • Отправка шаблона Resource Manager вручную с помощью интеграции пользовательского интерфейса Фабрики данных с Azure Resource Manager.Manually upload a Resource Manager template using Data Factory UX integration with Azure Resource Manager.

Уделите 9 минут вашего времени, чтобы просмотреть это видео с кратким обзором и демонстрацией этой функции:For a nine-minute introduction to this feature and a demonstration, watch this video:

Примечание

Эта статья была изменена и теперь содержит сведения о новом модуле Az для Azure PowerShell.This article has been updated to use the new Azure PowerShell Az module. Вы по-прежнему можете использовать модуль AzureRM, исправления ошибок для которого будут продолжать выпускаться как минимум до декабря 2020 г.You can still use the AzureRM module, which will continue to receive bug fixes until at least December 2020. Дополнительные сведения о совместимости модуля Az с AzureRM см. в статье Introducing the new Azure PowerShell Az module (Знакомство с новым модулем Az для Azure PowerShell).To learn more about the new Az module and AzureRM compatibility, see Introducing the new Azure PowerShell Az module. Инструкции по установке модуля Az см. в статье об установке Azure PowerShell.For Az module installation instructions, see Install Azure PowerShell.

Жизненный цикл CI/CDCI/CD lifecycle

Ниже приведен обзор примера жизненного цикла CI/CD в фабрике данных Azure, настроенной с помощью Azure Repos Git.Below is a sample overview of the CI/CD lifecycle in an Azure data factory that's configured with Azure Repos Git. Дополнительные сведения о настройке репозитория Git см. в статье Система управления версиями в Фабрике данных Azure.For more information on how to configure a Git repository, see Source control in Azure Data Factory.

  1. Фабрика данных для разработки создается и настраивается с помощью Azure Repos Git.A development data factory is created and configured with Azure Repos Git. Все разработчики должны иметь разрешение на создание ресурсов Фабрики данных, таких как конвейеры и наборы данных.All developers should have permission to author Data Factory resources like pipelines and datasets.

  2. Разработчик создает ветвь компонента для внесения изменений.A developer creates a feature branch to make a change. Он отлаживает запуски конвейера с самыми последними изменениями.They debug their pipeline runs with their most recent changes. Дополнительные сведения об отладке запуска конвейера см. в статье Итеративные разработка и отладка в Фабрике данных Azure.For more information on how to debug a pipeline run, see Iterative development and debugging with Azure Data Factory.

  3. После того как разработчик внесет изменения, он создает запрос на вытягивание из своей ветви компонента в главную или ветвь совместной работы, чтобы коллеги могли просмотреть изменения.After a developer is satisfied with their changes, they create a pull request from their feature branch to the master or collaboration branch to get their changes reviewed by peers.

  4. После утверждения запроса на вытягивание и слияния изменений в главной ветви они публикуются в фабрике для разработки.After a pull request is approved and changes are merged in the master branch, the changes get published to the development factory.

  5. Когда команда готова к развертыванию изменений в тестовой или UAT (пользовательское приемочное тестирование), Группа переходит к выпуску Azure Pipelines и развертывает нужную версию фабрики разработки в UAT.When the team is ready to deploy the changes to a test or UAT (User Acceptance Testing) factory, the team goes to their Azure Pipelines release and deploys the desired version of the development factory to UAT. Это развертывание выполняется как часть задачи Azure Pipelines и использует параметры шаблона Resource Manager, чтобы применить соответствующую конфигурацию.This deployment takes place as part of an Azure Pipelines task and uses Resource Manager template parameters to apply the appropriate configuration.

  6. После проверки изменений в фабрике тестовой среды выполните развертывание в фабрике для разработки с помощью следующей задачи выпуска конвейеров.After the changes have been verified in the test factory, deploy to the production factory by using the next task of the pipelines release.

Примечание

Только фабрика для разработки будет связана с репозиторием Git.Only the development factory is associated with a git repository. В тестовой и рабочей фабрике не должно быть связанного с ними репозитория Git. Их следует обновлять только через конвейер Azure DevOps или шаблон управления ресурсами.The test and production factories shouldn't have a git repository associated with them and should only be updated via an Azure DevOps pipeline or via a Resource Management template.

На приведенном ниже рисунке показаны различные этапы этого жизненного цикла.The below image highlights the different steps of this lifecycle.

Схема непрерывной интеграции с помощью Azure Pipelines

Автоматизация непрерывной интеграции с помощью выпусков Azure PipelinesAutomate continuous integration by using Azure Pipelines releases

Ниже приведены шаги настройки выпуска Azure Pipelines для автоматизации развертывания фабрики данных в различные среды.The following is a guide for setting up an Azure Pipelines release that automates the deployment of a data factory to multiple environments.

ТребованияRequirements

Настройка выпуска Azure PipelinesSet up an Azure Pipelines release

  1. В Azure DevOps откройте проект, настроенный с фабрикой данных.In Azure DevOps, open the project that's configured with your data factory.

  2. В левой части страницы выберите Конвейеры, а затем Выпуски.On the left side of the page, select Pipelines, and then select Releases.

    Выбор параметров "Конвейеры", "Выпуски"

  3. Выберите Новый конвейер или, если у вас есть конвейеры, выберите Новый, а затем Создать конвейер выпуска.Select New pipeline, or, if you have existing pipelines, select New and then New release pipeline.

  4. Выберите шаблон Пустое задание.Select the Empty job template.

    Выбор шаблона "Пустое задание"

  5. В поле Имя этапа введите имя своей среды.In the Stage name box, enter the name of your environment.

  6. Щелкните Добавить артефакт, а затем выберите репозиторий Git, настроенный с фабрикой данных для разработки.Select Add artifact, and then select the git repository configured with your development data factory. Выберите опубликовать ветвь репозитория для поля Ветвь по умолчанию.Select the publish branch of the repository for the Default branch. По умолчанию эта ветвь публикации имеет значение adf_publish.By default, this publish branch is adf_publish. Для поля Версия по умолчанию выберите Последний из ветви по умолчанию.For the Default version, select Latest from default branch.

    Добавление артефакта

  7. Добавьте задачу развертывания Azure Resource Manager.Add an Azure Resource Manager Deployment task:

    а.a. В представлении этапа выберите Просмотр задач этапа.In the stage view, select View stage tasks.

    Представлении этапа

    b.b. Создайте задачу.Create a new task. Найдите Развертывание шаблона ARMи нажмите кнопку Добавить.Search for ARM Template Deployment, and then select Add.

    c.c. В задаче "Развертывание" выберите подписку, группу ресурсов и расположение для целевой фабрики данных.In the Deployment task, select the subscription, resource group, and location for the target data factory. При необходимости предоставьте учетные данные.Provide credentials if necessary.

    d.d. В списке Действие выберите Create or update resource group (Создание или изменение группы ресурсов).In the Action list, select Create or update resource group.

    д)e. Нажмите кнопку с многоточием ( ) рядом с полем Шаблон.Select the ellipsis button () next to the Template box. Найдите шаблон Azure Resource Manager, созданный в ветви публикации настроенного репозитория Git.Browse for the Azure Resource Manager template that is generated in your publish branch of the configured git repository. Найдите файл ARMTemplateForFactory.json в папке ветви adf_publish.Look for the file ARMTemplateForFactory.json in the folder of the adf_publish branch.

    е)f. Выберите Select рядом с полем Параметры шаблона для выбора файла параметров.next to the Template parameters box to choose the parameters file. Найдите файл ARMTemplateParametersForFactory.json в папке ветви adf_publish.Look for the file ARMTemplateParametersForFactory.json in the folder of the adf_publish branch.

    ж.g. Выберите Select рядом с полем Переопределить параметры шаблона и введите значения необходимых параметров для целевой фабрики данных.next to the Override template parameters box, and enter the desired parameter values for the target data factory. Для учетных данных, полученных из Azure Key Vault, заключите имя секрета в двойные кавычки.For credentials that come from Azure Key Vault, enter the secret's name between double quotation marks. Например, если имя секрета — cred1, введите "$(cred1)" для этого значения.For example, if the secret's name is cred1, enter "$(cred1)" for this value.

    h.h. В поле Режим развертывания выберите Добавочные.Select Incremental for the Deployment mode.

    Предупреждение

    При выборе полного режима развертывания существующие ресурсы могут быть удалены, в том числе ресурсы в целевой группе ресурсов, которые не определены в шаблоне Resource Manager.If you select Complete for the Deployment mode, existing resources might be deleted, including all resources in the target resource group that aren't defined in the Resource Manager template.

    Рабочее развертывание Фабрики данных

  8. Сохраните конвейер выпуска.Save the release pipeline.

  9. Для активации выпуска нажмите кнопку Создать выпуск.To trigger a release, select Create release. Сведения об автоматизации создания выпусков см. в статье Триггеры выпуска.To automate the creation of releases, see Azure DevOps release triggers

    Выбор параметра "Создать выпуск"

Важно!

В сценариях CI/CD тип среды выполнения интеграции (IR) в разных средах должен совпадать.In CI/CD scenarios, the integration runtime (IR) type in different environments must be the same. Например, если у вас есть локальная среда IR в среде разработки, ей необходим также тип "Локальная" в других средах, таких как рабочая и тестовая.For example, if you have a self-hosted IR in the development environment, the same IR must also be of type self-hosted in other environments, such as test and production. Аналогично, если вы совместно используете среды выполнения интеграции в нескольких средах, необходимо настроить их в качестве связанных локальных сред во всех средах (разработки, тестирования и рабочей).Similarly, if you're sharing integration runtimes across multiple stages, you have to configure the integration runtimes as linked self-hosted in all environments, such as development, test, and production.

Получение секретов из Azure Key VaultGet secrets from Azure Key Vault

Если вам нужно передать секреты в шаблоне Azure Resource Manager, рекомендуем использовать Azure Key Vault с выпуском Azure Pipelines.If you have secrets to pass in an Azure Resource Manager template, we recommend that you use Azure Key Vault with the Azure Pipelines release.

Обработать секреты можно двумя способами.There are two ways to handle secrets:

  1. Добавьте секреты в файл параметров.Add the secrets to parameters file. Дополнительные сведения см. в статье Использование Azure Key Vault для передачи защищенного значения параметра во время развертывания.For more info, see Use Azure Key Vault to pass secure parameter value during deployment.

    Создайте копию файла параметров, который отправляется в ветвь публикации.Create a copy of the parameters file that's uploaded to the publish branch. Задайте значения параметров, которые требуется получить из Key Vault, в следующем формате:Set the values of the parameters that you want to get from Key Vault by using this format:

    {
        "parameters": {
            "azureSqlReportingDbPassword": {
                "reference": {
                    "keyVault": {
                        "id": "/subscriptions/<subId>/resourceGroups/<resourcegroupId> /providers/Microsoft.KeyVault/vaults/<vault-name> "
                    },
                    "secretName": " < secret - name > "
                }
            }
        }
    }
    

    При использовании этого метода секрет автоматически извлекается из хранилища ключей.When you use this method, the secret is pulled from the key vault automatically.

    Файл параметров также должен находиться в ветви публикации.The parameters file needs to be in the publish branch as well.

  2. Перед задачей развертывания Azure Resource Manager, описанной в предыдущем разделе, добавьте задачу Azure Key Vault:Add an Azure Key Vault task before the Azure Resource Manager Deployment task described in the previous section:

    1. На вкладке Задачи создайте задачу.On the Tasks tab, create a new task. Найдите Azure Key Vault и добавьте его.Search for Azure Key Vault and add it.

    2. В задаче Key Vault выберите подписку, в которой вы создали хранилище ключей.In the Key Vault task, select the subscription in which you created the key vault. При необходимости предоставьте учетные данные, а затем выберите хранилище ключей.Provide credentials if necessary, and then select the key vault.

    Добавление задачи Key Vault

Предоставление разрешений для агента Azure PipelinesGrant permissions to the Azure Pipelines agent

Задача Azure Key Vault может завершиться ошибкой отказа в доступе, если не заданы правильные разрешения.The Azure Key Vault task might fail with an Access Denied error if the correct permissions aren't set. Загрузите журналы для выпуска и найдите PS1-файл с командой для предоставления разрешений агенту Azure Pipelines.Download the logs for the release, and locate the .ps1 file that contains the command to give permissions to the Azure Pipelines agent. Вы можете выполнить команду напрямую.You can run the command directly. Или скопировать идентификатор участника из файла и вручную добавить политику доступа на портале Azure.Or you can copy the principal ID from the file and add the access policy manually in the Azure portal. Get и List — минимальные требуемые разрешения.Get and List are the minimum permissions required.

Обновление активных триггеровUpdating active triggers

При попытке обновления активных триггеров развертывание может завершиться сбоем.Deployment can fail if you try to update active triggers. Чтобы обновить активные триггеры, необходимо вручную остановить, а затем перезапустить их после развертывания.To update active triggers, you need to manually stop them and then restart them after the deployment. Это можно сделать с помощью задачи Azure PowerShell:You can do this by using an Azure PowerShell task:

  1. На вкладке Задачи выпуска добавьте задачу Azure PowerShell.On the Tasks tab of the release, add an Azure PowerShell task. Выберите версию задачи 4.*.Choose task version 4.*.

  2. Выберите подписку, в которой находится фабрика.Select the subscription your factory is in.

  3. В качестве типа сценария выберите Путь к файлу сценария.Select Script File Path as the script type. Для этого необходимо сохранить сценарий PowerShell в репозитории.This requires you to save your PowerShell script in your repository. Следующий сценарий PowerShell можно использовать для остановки триггеров:The following PowerShell script can be used to stop triggers:

    $triggersADF = Get-AzDataFactoryV2Trigger -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    
    $triggersADF | ForEach-Object { Stop-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.name -Force }
    

Вы можете выполнить те же шаги (с функцией Start-AzDataFactoryV2Trigger), чтобы перезапустить триггеры после развертывания.You can complete similar steps (with the Start-AzDataFactoryV2Trigger function) to restart the triggers after deployment.

В конце статьи команда фабрики данных предоставила пример сценария, выполняемого до и после развертывания.The data factory team has provided a sample pre- and post-deployment script located at the bottom of this article.

Перемещение шаблона Resource Manager для каждой среды вручнуюManually promote a Resource Manager template for each environment

  1. В списке Шаблон ARM выберите Export ARM Template (Экспорт шаблона ARM), чтобы экспортировать шаблон Resource Manager в фабрику данных в среде разработки.In the ARM Template list, select Export ARM Template to export the Resource Manager template for your data factory in the development environment.

    Экспорт шаблона Resource Manager

  2. В тестовой и рабочей средах выберите Import ARM Template (Импорт шаблона ARM).In your test and production data factories, select Import ARM Template. После этого вы перейдете на портал Azure, где сможете импортировать экспортированный шаблон.This action takes you to the Azure portal, where you can import the exported template. Выберите Создать собственный шаблон в редакторе, чтобы открыть редактор шаблона Resource Manager.Select Build your own template in the editor to open the Resource Manager template editor.

    Создание собственного шаблона

  3. Выберите Загрузить файл, а затем — созданный шаблон Resource Manager.Select Load file, and then select the generated Resource Manager template. Это файл arm_template.json, расположенный в ZIP-файле (экспортирован на шаге 1).This is the arm_template.json file located in the .zip file exported in step 1.

    Изменить шаблон

  4. В разделе параметров введите значения конфигурации, например учетные данные связанной службы.In the settings section, enter the configuration values, like linked service credentials. После этого нажмите кнопку Оплатить, чтобы развернуть шаблон Resource Manager.When you're done, select Purchase to deploy the Resource Manager template.

    Раздел параметров

Использование пользовательских параметров с шаблоном Resource ManagerUse custom parameters with the Resource Manager template

Если у вашей фабрики для разработки есть связанный репозиторий Git, можно переопределить параметры шаблона Resource Manager по умолчанию, созданного путем публикации или экспорта шаблона.If your development factory has an associated git repository, you can override the default Resource Manager template parameters of the Resource Manager template generated by publishing or exporting the template. Вам может потребоваться переопределить шаблон параметризации по умолчанию в этих сценариях:You might want to override the default parameterization template in these scenarios:

  • Вы используете автоматизацию CI/CD и хотите изменить некоторые свойства во время развертывания Resource Manager, но свойства не будут параметризованы по умолчанию.You use automated CI/CD and you want to change some properties during Resource Manager deployment, but the properties aren't parameterized by default.
  • Ваша фабрика настолько велика, что шаблон Resource Manager по умолчанию является недопустимым, так как он превышает максимально допустимое количество параметров (256).Your factory is so large that the default Resource Manager template is invalid because it has more than the maximum allowed parameters (256).

Чтобы переопределить шаблон параметризации по умолчанию, создайте файл с именем arm-template-parameters-definition.json в корневой папке своей ветви Git.To override the default parameterization template, create a file named arm-template-parameters-definition.json in the root folder of your git branch. Необходимо использовать точное имя файла.You must use that exact file name.

Файл пользовательских параметров

При публикации из ветви совместной работы Фабрика данных считывает этот файл и использует его конфигурацию, чтобы определить свойства, которые будут параметризованы.When publishing from the collaboration branch, Data Factory will read this file and use its configuration to generate which properties get parameterized. Если файл не найден, используется шаблон по умолчанию.If no file is found, the default template is used.

При экспорте шаблона Resource Manager Фабрика данных считывает этот файл из ветви, в которой вы в данный момент работаете, а не только из ветви совместной работы.When exporting a Resource Manager template, Data Factory reads this file from whichever branch you're currently working on, not just from the collaboration branch. Вы можете создать или изменить файл из частной ветви, где можно протестировать изменения, выбрав Export ARM Template (Экспорт шаблона ARM) в пользовательском интерфейсе.You can create or edit the file from a private branch, where you can test your changes by selecting Export ARM Template in the UI. Затем можно выполнить слияние файла в ветвь совместной работы.You can then merge the file into the collaboration branch.

Примечание

Пользовательский шаблон параметризации не изменяет ограничение на количество параметров шаблона Resource Manager равное 256.A custom parameterization template doesn't change the ARM template parameter limit of 256. Он позволяет выбирать и уменьшать количество параметризованных свойств.It lets you choose and decrease the number of parameterized properties.

Синтаксис пользовательских параметровCustom parameter syntax

Ниже приведены некоторые рекомендации, которые следует выполнить при создании файла пользовательских параметров arm-template-parameters-definition.json.The following are some guidelines to follow when you create the custom parameters file, arm-template-parameters-definition.json. Файл состоит из раздела для каждого типа сущности: триггера, конвейера, связанной службы, набора данных, среды выполнения интеграции и потока данных.The file consists of a section for each entity type: trigger, pipeline, linked service, dataset, integration runtime, and data flow.

  • Введите путь к свойству в соответствующем типе сущности.Enter the property path under the relevant entity type.
  • Присвоение имени свойства значения  * указывает, что необходимо параметризовать все свойства под ним (только до первого уровня, но не рекурсивно).Setting a property name to * indicates that you want to parameterize all properties under it (only down to the first level, not recursively). Вы также можете указать исключения в этой конфигурации.You can also provide exceptions to this configuration.
  • Установка значения свойства в виде строки указывает, что вы хотите параметризовать свойство.Setting the value of a property as a string indicates that you want to parameterize the property. Используйте формат  <action>:<name>:<stype>.Use the format <action>:<name>:<stype>.
    • <action>  может быть одним из следующих знаков:<action> can be one of these characters:
      • =  означает сохранение текущего значения как значения по умолчанию для параметра.= means keep the current value as the default value for the parameter.
      • -  означает, что не следует хранить значение по умолчанию для параметра.- means don't keep the default value for the parameter.
      • |  используется для секретов из Azure Key Vault для строк подключения или ключей.| is a special case for secrets from Azure Key Vault for connection strings or keys.
    • <name>  — это имя параметра.<name> is the name of the parameter. Если оно пустое, то принимает имя свойства.If it's blank, it takes the name of the property. Если значение начинается со знака -, то оно сокращено.If the value starts with a - character, the name is shortened. Например, AzureStorage1_properties_typeProperties_connectionString будет сокращено до AzureStorage1_connectionString.For example, AzureStorage1_properties_typeProperties_connectionString would be shortened to AzureStorage1_connectionString.
    • <stype>  — это тип параметра.<stype> is the type of parameter. Если  <stype>  является пустым, то типом по умолчанию будет string.If <stype> is blank, the default type is string. Поддерживаются такие значения: string, bool, number, object и securestring.Supported values: string, bool, number, object, and securestring.
  • При указании массива в файле определения вы можете указать, что соответствующее свойство в шаблоне является массивом.Specifying an array in the definition file indicates that the matching property in the template is an array. Фабрика данных выполняет итерацию всех объектов в массиве с помощью определения, указанного в объекте среды выполнения интеграции массива.Data Factory iterates through all the objects in the array by using the definition that's specified in the integration runtime object of the array. Второй объект, строка, становится именем свойства, которое используется в качестве имени параметра для каждой итерации.The second object, a string, becomes the name of the property, which is used as the name for the parameter for each iteration.
  • Определение не может быть указано для экземпляра ресурса.A definition can't be specific to a resource instance. Любое определение применяется ко всем ресурсам этого типа.Any definition applies to all resources of that type.
  • По умолчанию все защищенные строки, такие как секреты Key Vault и защищенные строки, такие как строки подключения, ключи и токены, являются параметризованными.By default, all secure strings, like Key Vault secrets, and secure strings, like connection strings, keys, and tokens, are parameterized.

Пример шаблона параметризацииSample parameterization template

Ниже приведен пример того, как может выглядеть шаблон параметризации:Here's an example of what a parameterization template might look like:

{
    "Microsoft.DataFactory/factories/pipelines": {
        "properties": {
            "activities": [{
                "typeProperties": {
                    "waitTimeInSeconds": "-::number",
                    "headers": "=::object"
                }
            }]
        }
    },
    "Microsoft.DataFactory/factories/integrationRuntimes": {
        "properties": {
            "typeProperties": {
                "*": "="
            }
        }
    },
    "Microsoft.DataFactory/factories/triggers": {
        "properties": {
            "typeProperties": {
                "recurrence": {
                    "*": "=",
                    "interval": "=:triggerSuffix:number",
                    "frequency": "=:-freq"
                },
                "maxConcurrency": "="
            }
        }
    },
    "Microsoft.DataFactory/factories/linkedServices": {
        "*": {
            "properties": {
                "typeProperties": {
                    "accountName": "=",
                    "username": "=",
                    "connectionString": "|:-connectionString:secureString",
                    "secretAccessKey": "|"
                }
            }
        },
        "AzureDataLakeStore": {
            "properties": {
                "typeProperties": {
                    "dataLakeStoreUri": "="
                }
            }
        }
    },
    "Microsoft.DataFactory/factories/datasets": {
        "properties": {
            "typeProperties": {
                "*": "="
            }
        }
    }
}

Ниже приведено объяснение того, как создавался предыдущий шаблон, упорядоченный по типу ресурсов.Here's an explanation of how the preceding template is constructed, broken down by resource type.

КонвейерыPipelines

  • Любое свойство в пути activities/typeProperties/waitTimeInSeconds параметризовано.Any property in the path activities/typeProperties/waitTimeInSeconds is parameterized. Любое действие в конвейере, которое имеет свойство уровня кода с именем waitTimeInSeconds (например, действие Wait) параметризовано как число с именем по умолчанию.Any activity in a pipeline that has a code-level property named waitTimeInSeconds (for example, the Wait activity) is parameterized as a number, with a default name. Но в шаблоне Resource Manager не будет значения по умолчанию.But it won't have a default value in the Resource Manager template. Оно будет обязательным во время развертывания Resource Manager.It will be a mandatory input during the Resource Manager deployment.
  • Аналогично свойство headers (например, в действии Web) параметризовано с типом object (JObject).Similarly, a property called headers (for example, in a Web activity) is parameterized with type object (JObject). Оно имеет значение по умолчанию, то есть то же значение, что и у исходной фабрики.It has a default value, which is the same value as that of the source factory.

IntegrationRuntimesIntegrationRuntimes

  • Все свойства в пути typeProperties параметризованы с соответствующими значениями по умолчанию.All properties under the path typeProperties are parameterized with their respective default values. Например, в свойствах типа IntegrationRuntimes есть два свойства: computeProperties и ssisProperties.For example, there are two properties under IntegrationRuntimes type properties: computeProperties and ssisProperties. Оба типа свойств создаются с соответствующими значениями по умолчанию и типами (объект).Both property types are created with their respective default values and types (Object).

ТриггерыTriggers

  • В разделе typeProperties два свойства являются параметризованными.Under typeProperties, two properties are parameterized. Первое из них maxConcurrency, для которого задано значение по умолчанию и имеет тип string.The first one is maxConcurrency, which is specified to have a default value and is of typestring. Имя параметра по умолчанию — <entityName>_properties_typeProperties_maxConcurrency.It has the default parameter name <entityName>_properties_typeProperties_maxConcurrency.
  • Свойство recurrence также параметризовано.The recurrence property also is parameterized. В его разделе все свойства на этом уровне указываются для параметризации в виде строк со значениями по умолчанию и имен параметров.Under it, all properties at that level are specified to be parameterized as strings, with default values and parameter names. Исключением является свойство interval, параметризованное как тип number.An exception is the interval property, which is parameterized as type number. Имя параметра имеет суффикс <entityName>_properties_typeProperties_recurrence_triggerSuffix.The parameter name is suffixed with <entityName>_properties_typeProperties_recurrence_triggerSuffix. Аналогичным образом свойство freq — это строка, и параметризовано как строка.Similarly, the freq property is a string and is parameterized as a string. Однако свойство freq параметризовано без значения по умолчанию.However, the freq property is parameterized without a default value. Имя сокращено и имеет суффикс.The name is shortened and suffixed. Например, <entityName>_freq.For example, <entityName>_freq.

LinkedServicesLinkedServices

  • Связанные службы уникальны.Linked services are unique. Так как связанные службы и наборы данных имеют широкий спектр типов, можно указать настройку для конкретного типа.Because linked services and datasets have a wide range of types, you can provide type-specific customization. В этом примере для всех связанных служб типа AzureDataLakeStore будет применен конкретный шаблон.In this example, for all linked services of type AzureDataLakeStore, a specific template will be applied. Для всех остальных (через *) будет применен другой шаблон.For all others (via *), a different template will be applied.
  • Свойство connectionString будет параметризовано как значение securestring.The connectionString property will be parameterized as a securestring value. Оно не имеет значения по умолчаниюIt won't have a default value. У него будет имя сокращенного параметра с суффиксом connectionString.It will have a shortened parameter name that's suffixed with connectionString.
  • Свойство secretAccessKey является AzureKeyVaultSecret (например, в связанной службе Amazon S3).The property secretAccessKey happens to be an AzureKeyVaultSecret (for example, in an Amazon S3 linked service). Оно автоматически параметризовано как секрет Azure Key Vault и получено из настроенного хранилища ключей.It's automatically parameterized as an Azure Key Vault secret and fetched from the configured key vault. Вы также можете параметризовать само хранилище ключей.You can also parameterize the key vault itself.

Наборы данныхDatasets

  • Хотя настройка для наборов данных доступна для конкретного типа, можно предоставить конфигурацию без явной настройки уровня *.Although type-specific customization is available for datasets, you can provide configuration without explicitly having a *-level configuration. В предыдущем примере все свойства набора данных в разделе typeProperties были параметризованными.In the preceding example, all dataset properties under typeProperties are parameterized.

Шаблон параметризации по умолчаниюDefault parameterization template

Ниже приведен текущий шаблон параметризации по умолчанию.Below is the current default parameterization template. Если необходимо добавить только несколько параметров, то рекомендуется изменить этот шаблон напрямую, так как вы не потеряете существующую структуру параметризации.If you need to add only a few parameters, editing this template directly might be a good idea because you won't lose the existing parameterization structure.

{
    "Microsoft.DataFactory/factories/pipelines": {
    },
    "Microsoft.DataFactory/factories/dataflows": {
    },
    "Microsoft.DataFactory/factories/integrationRuntimes":{
        "properties": {
            "typeProperties": {
                "ssisProperties": {
                    "catalogInfo": {
                        "catalogServerEndpoint": "=",
                        "catalogAdminUserName": "=",
                        "catalogAdminPassword": {
                            "value": "-::secureString"
                        }
                    },
                    "customSetupScriptProperties": {
                        "sasToken": {
                            "value": "-::secureString"
                        }
                    }
                },
                "linkedInfo": {
                    "key": {
                        "value": "-::secureString"
                    },
                    "resourceId": "="
                },
                "computeProperties": {
                    "dataFlowProperties": {
                        "externalComputeInfo": [{
                                "accessToken": "-::secureString"
                            }
                        ]
                    }
                }
            }
        }
    },
    "Microsoft.DataFactory/factories/triggers": {
        "properties": {
            "pipelines": [{
                    "parameters": {
                        "*": "="
                    }
                },  
                "pipelineReference.referenceName"
            ],
            "pipeline": {
                "parameters": {
                    "*": "="
                }
            },
            "typeProperties": {
                "scope": "="
            }

        }
    },
    "Microsoft.DataFactory/factories/linkedServices": {
        "*": {
            "properties": {
                "typeProperties": {
                    "accountName": "=",
                    "username": "=",
                    "userName": "=",
                    "accessKeyId": "=",
                    "servicePrincipalId": "=",
                    "userId": "=",
                    "host": "=",
                    "clientId": "=",
                    "clusterUserName": "=",
                    "clusterSshUserName": "=",
                    "hostSubscriptionId": "=",
                    "clusterResourceGroup": "=",
                    "subscriptionId": "=",
                    "resourceGroupName": "=",
                    "tenant": "=",
                    "dataLakeStoreUri": "=",
                    "baseUrl": "=",
                    "database": "=",
                    "serviceEndpoint": "=",
                    "batchUri": "=",
                    "poolName": "=",
                    "databaseName": "=",
                    "systemNumber": "=",
                    "server": "=",
                    "url":"=",
                    "functionAppUrl":"=",
                    "environmentUrl": "=",
                    "aadResourceId": "=",
                    "sasUri": "|:-sasUri:secureString",
                    "sasToken": "|",
                    "connectionString": "|:-connectionString:secureString"
                }
            }
        },
        "Odbc": {
            "properties": {
                "typeProperties": {
                    "userName": "=",
                    "connectionString": {
                        "secretName": "="
                    }
                }
            }
        }
    },
    "Microsoft.DataFactory/factories/datasets": {
        "*": {
            "properties": {
                "typeProperties": {
                    "folderPath": "=",
                    "fileName": "="
                }
            }
        }}
}

Пример. Параметризация существующего идентификатора интерактивного кластера Azure DatabricksExample: parameterizing an existing Azure Databricks interactive cluster ID

В следующем примере показано, как добавить одно значение в шаблон параметризации по умолчанию.The following example shows how to add a single value to the default parameterization template. Нужно только добавить существующий идентификатор интерактивного кластера Azure Databricks для связанной службы Databricks в файле параметров.We only want to add an existing Azure Databricks interactive cluster ID for a Databricks linked service to the parameters file. Обратите внимание, что этот файл совпадает с предыдущим файлом, за исключением добавления existingClusterId в поле свойства Microsoft.DataFactory/factories/linkedServices.Note that this file is the same as the previous file except for the addition of existingClusterId under the properties field of Microsoft.DataFactory/factories/linkedServices.

{
    "Microsoft.DataFactory/factories/pipelines": {
    },
    "Microsoft.DataFactory/factories/dataflows": {
    },
    "Microsoft.DataFactory/factories/integrationRuntimes":{
        "properties": {
            "typeProperties": {
                "ssisProperties": {
                    "catalogInfo": {
                        "catalogServerEndpoint": "=",
                        "catalogAdminUserName": "=",
                        "catalogAdminPassword": {
                            "value": "-::secureString"
                        }
                    },
                    "customSetupScriptProperties": {
                        "sasToken": {
                            "value": "-::secureString"
                        }
                    }
                },
                "linkedInfo": {
                    "key": {
                        "value": "-::secureString"
                    },
                    "resourceId": "="
                }
            }
        }
    },
    "Microsoft.DataFactory/factories/triggers": {
        "properties": {
            "pipelines": [{
                    "parameters": {
                        "*": "="
                    }
                },  
                "pipelineReference.referenceName"
            ],
            "pipeline": {
                "parameters": {
                    "*": "="
                }
            },
            "typeProperties": {
                "scope": "="
            }
 
        }
    },
    "Microsoft.DataFactory/factories/linkedServices": {
        "*": {
            "properties": {
                "typeProperties": {
                    "accountName": "=",
                    "username": "=",
                    "userName": "=",
                    "accessKeyId": "=",
                    "servicePrincipalId": "=",
                    "userId": "=",
                    "clientId": "=",
                    "clusterUserName": "=",
                    "clusterSshUserName": "=",
                    "hostSubscriptionId": "=",
                    "clusterResourceGroup": "=",
                    "subscriptionId": "=",
                    "resourceGroupName": "=",
                    "tenant": "=",
                    "dataLakeStoreUri": "=",
                    "baseUrl": "=",
                    "database": "=",
                    "serviceEndpoint": "=",
                    "batchUri": "=",
            "poolName": "=",
                    "databaseName": "=",
                    "systemNumber": "=",
                    "server": "=",
                    "url":"=",
                    "aadResourceId": "=",
                    "connectionString": "|:-connectionString:secureString",
                    "existingClusterId": "-"
                }
            }
        },
        "Odbc": {
            "properties": {
                "typeProperties": {
                    "userName": "=",
                    "connectionString": {
                        "secretName": "="
                    }
                }
            }
        }
    },
    "Microsoft.DataFactory/factories/datasets": {
        "*": {
            "properties": {
                "typeProperties": {
                    "folderPath": "=",
                    "fileName": "="
                }
            }
        }}
}

Связанные шаблоны Resource ManagerLinked Resource Manager templates

Если вы настроили CI/CD для фабрик данных, вы можете превысить ограничения шаблонов Azure Resource Manager по мере роста фабрики.If you've set up CI/CD for your data factories, you might exceed the Azure Resource Manager template limits as your factory grows bigger. Например, одним из ограничений является максимальное количество ресурсов в шаблоне Resource Manager.For example, one limit is the maximum number of resources in a Resource Manager template. Для размещения больших фабрик при создании полного шаблона Resource Manager для фабрики Фабрика данных теперь создает связанные шаблоны Resource Manager.To accommodate large factories while generating the full Resource Manager template for a factory, Data Factory now generates linked Resource Manager templates. С помощью этой функции полезные данные всей фабрики можно разбить на несколько файлов, чтобы не превысить ограничения.With this feature, the entire factory payload is broken down into several files so that you aren't constrained by the limits.

Если у вас настроен Git, связанные шаблоны создаются и сохраняются вместе с полными шаблонами Resource Manager в ветви adf_publish в новой папке с именем linkedTemplates:If you've configured Git, the linked templates are generated and saved alongside the full Resource Manager templates in the adf_publish branch in a new folder called linkedTemplates:

Папка связанных шаблонов Resource Manager

Связанные шаблоны Resource Manager обычно имеют главный шаблон и набор дочерних шаблонов, связанных с главным.The linked Resource Manager templates usually consist of a master template and a set of child templates that are linked to the master. Родительский шаблон называется ArmTemplate_master.json, а дочерние шаблоны именуются по образцу: ArmTemplate_0.json, ArmTemplate_1.json и т. д.The parent template is called ArmTemplate_master.json, and child templates are named with the pattern ArmTemplate_0.json, ArmTemplate_1.json, and so on.

Для использования связанных шаблонов вместо полного шаблона Resource Manager обновите вашу задачу CI/CD, чтобы она указывала ArmTemplate_master.json вместо ArmTemplateForFactory.json (полный шаблон Resource Manager).To use linked templates instead of the full Resource Manager template, update your CI/CD task to point to ArmTemplate_master.json instead of ArmTemplateForFactory.json (the full Resource Manager template). Для Resource Manager также необходимо отправлять связанные шаблоны в учетную запись хранения, чтобы они были доступны в Azure во время развертывания.Resource Manager also requires that you upload the linked templates into a storage account so Azure can access them during deployment. Дополнительные сведения см. в статье Развертывание связанных шаблонов Resource Manager с помощью VSTS.For more info, see Deploying linked Resource Manager templates with VSTS.

Не забудьте добавить сценарии фабрики данных в конвейер CI/CD до и после завершения развертывания.Remember to add the Data Factory scripts in your CI/CD pipeline before and after the deployment task.

Если у вас не настроен Git, доступ к связанным шаблонам можно получить, выбрав Export ARM Template (Экспорт шаблона ARM) в списке шаблонов ARM.If you don't have Git configured, you can access the linked templates via Export ARM Template in the ARM Template list.

Рабочая среда исправленияHotfix production environment

Если вы развертываете фабрику в рабочей среде и понимаете, что существует ошибка, которую необходимо исправить немедленно, но вы не можете развернуть текущую ветвь совместной работы, может потребоваться развернуть пакет исправлений.If you deploy a factory to production and realize there's a bug that needs to be fixed right away, but you can't deploy the current collaboration branch, you might need to deploy a hotfix. Этот подход известен как наложение исправлений или исправление QFE.This approach is as known as quick-fix engineering or QFE.

  1. В Azure DevOps перейдите к выпуску, развернутому в рабочей среде.In Azure DevOps, go to the release that was deployed to production. Найдите последнюю развернутую фиксацию.Find the last commit that was deployed.

  2. В сообщении о фиксации получите идентификатор фиксации ветви совместной работы.From the commit message, get the commit ID of the collaboration branch.

  3. Создайте ветвь исправления из этой фиксации.Create a new hotfix branch from that commit.

  4. Перейдите в пользовательский интерфейс Фабрики данных Azure и переключитесь на ветвь исправлений.Go to the Azure Data Factory UX and switch to the hotfix branch.

  5. Исправьте ошибку с помощью пользовательского интерфейса Фабрики данных Azure.By using the Azure Data Factory UX, fix the bug. Проверьте изменения.Test your changes.

  6. После проверки исправления выберите Export ARM Template (Экспорт шаблона ARM), чтобы получить исправления шаблона Resource Manager.After the fix is verified, select Export ARM Template to get the hotfix Resource Manager template.

  7. Проверьте эту сборку в ветви adf_publish вручную.Manually check this build into the adf_publish branch.

  8. Если конвейер выпуска настроен на автоматическую активацию на основе синхронизаций adf_publish, новый выпуск будет запущен автоматически.If you've configured your release pipeline to automatically trigger based on adf_publish check-ins, a new release will start automatically. В противном случае поставьте выпуск в очередь вручную.Otherwise, manually queue a release.

  9. Разверните выпуск исправления на тестовой и рабочей фабриках.Deploy the hotfix release to the test and production factories. Этот выпуск содержит предыдущие полезные данные, а также исправление, выполненное на шаге 5.This release contains the previous production payload plus the fix that you made in step 5.

  10. Добавьте изменения из исправления в ветвь разработки, чтобы в последующих выпусках не включалась та же ошибка.Add the changes from the hotfix to the development branch so that later releases won't include the same bug.

Рекомендации для CI/CDBest practices for CI/CD

Если вы используете интеграцию Git с фабрикой данных и у вас есть конвейер CI/CD, перемещающий изменения из среды разработки в тестовую, а затем в рабочую, рекомендуем следующее:If you're using Git integration with your data factory and have a CI/CD pipeline that moves your changes from development into test and then to production, we recommend these best practices:

  • Интеграция Git.Git integration. Настройте интеграцию Git только для своей фабрики данных для разработки.Configure only your development data factory with Git integration. Изменения в тестовую и рабочую среду развертываются в рамках процесса CI/CD, и им не требуется интеграция Git.Changes to test and production are deployed via CI/CD and don't need Git integration.

  • Сценарий, выполняемый до и после развертывания.Pre- and post-deployment script. Перед шагом развертывания с помощью Resource Manager в CI/CD необходимо выполнить определенные задачи, такие как остановка и перезапуск триггеров и выполнение очистки.Before the Resource Manager deployment step in CI/CD, you need to complete certain tasks, like stopping and restarting triggers and performing cleanup. Рекомендуется использовать сценарии PowerShell до и после задачи развертывания.We recommend that you use PowerShell scripts before and after the deployment task. Дополнительные сведения см. в разделе Обновление активных триггеров.For more information, see Update active triggers. В нижней части этой страницы команда фабрики данных предоставила сценарий, который необходимо использовать.The data factory team has provided a script to use located at the bottom of this page.

  • Среды выполнения интеграции и общий доступ.Integration runtimes and sharing. Среды выполнения интеграции не меняются часто и похожи на всех этапах в CI/CD.Integration runtimes don't change often and are similar across all stages in your CI/CD. Поэтому Фабрика данных ожидает, что имена и типы сред выполнения интеграции одинаковы на всех этапах CI/CD.So Data Factory expects you to have the same name and type of integration runtime across all stages of CI/CD. Если вы хотите совместно использовать среды выполнения интеграции на всех этапах, рассмотрите возможность использования тернарной фабрики только для хранения общих сред выполнения интеграции.If you want to share integration runtimes across all stages, consider using a ternary factory just to contain the shared integration runtimes. Эту общую фабрику можно использовать во всех средах в качестве связанного типа среды выполнения интеграции.You can use this shared factory in all of your environments as a linked integration runtime type.

  • Key Vault.Key Vault. При использовании связанных служб, сведения о соединении которых хранятся в Azure Key Vault, рекомендуется сохранять отдельные хранилищах ключей для разных сред.When you use linked services whose connection information is stored in Azure Key Vault, it is recommended to keep separate key vaults for different environments. Вы также можете настроить отдельные уровни разрешений для каждого из хранилища ключей.You can also configure separate permission levels for each key vault. Например, вы не хотите, чтобы у участников вашей команды были разрешения на доступ к секретам рабочей среды.For example, you might not want your team members to have permissions to production secrets. При таком подходе рекомендуем использовать одинаковые имена секретов на всех этапах.If you follow this approach, we recommend that you to keep the same secret names across all stages. Если вы сохраняете одни и те же имена секретов, вам не нужно параметризовать каждую строку подключения в средах CI/CD, так как единственное, что изменяется — это имя хранилища ключей, которое является отдельным параметром.If you keep the same secret names, you don't need to parameterize each connection string across CI/CD environments because the only thing that changes is the key vault name, which is a separate parameter.

Неподдерживаемые функцииUnsupported features

  • Ожидаемо, что Фабрика данных не допускает выборочного отбора фиксаций или публикации ресурсов.By design, Data Factory doesn't allow cherry-picking of commits or selective publishing of resources. Публикации будут включать все изменения, внесенные в фабрику данных.Publishes will include all changes made in the data factory.

    • Сущности фабрики данных зависят друг от друга.Data factory entities depend on each other. Например, триггеры зависят от конвейеров, а конвейеры зависят от наборов данных и других конвейеров.For example, triggers depend on pipelines, and pipelines depend on datasets and other pipelines. Выборочная публикация подмножества ресурсов может привести к непредвиденному поведению и ошибкам.Selective publishing of a subset of resources could lead to unexpected behaviors and errors.
    • В редких случаях, когда требуется выборочная публикация, рассмотрите возможность использования исправления.On rare occasions when you need selective publishing, consider using a hotfix. Дополнительные сведения см. в статье исправление рабочей среды.For more information, see Hotfix production environment.
  • Вы не можете опубликовать из частных ветвей.You can't publish from private branches.

  • В настоящее время вы не можете размещать проекты на Bitbucket.You can't currently host projects on Bitbucket.

Пример сценария, выполняемого до и после развертыванияSample pre- and post-deployment script

Ниже приведен пример сценария для остановки триггеров перед развертыванием и их последующего перезапуска.The following sample script can be used to stop triggers before deployment and restart them afterward. Скрипт также содержит код для удаления ресурсов.The script also includes code to delete resources that have been removed. Сохраните сценарий в репозитории Azure DevOps Git и ссылайтесь на него с помощью задачи Azure PowerShell, используя версию 4.*.Save the script in an Azure DevOps git repository and reference it via an Azure PowerShell task using version 4.*.

При запуске сценария, выполняемого перед развертыванием, необходимо указать вариант следующих параметров в поле Аргументы скрипта.When running a pre-deployment script, you will need to specify a variation of the following parameters in the Script Arguments field.

-armTemplate "$(System.DefaultWorkingDirectory)/<your-arm-template-location>" -ResourceGroupName <your-resource-group-name> -DataFactoryName <your-data-factory-name> -predeployment $true -deleteDeployment $false

При запуске сценария, выполняемого после развертывания, необходимо указать вариант следующих параметров в поле Аргументы скрипта.When running a post-deployment script, you will need to specify a variation of the following parameters in the Script Arguments field.

-armTemplate "$(System.DefaultWorkingDirectory)/<your-arm-template-location>" -ResourceGroupName <your-resource-group-name> -DataFactoryName <your-data-factory-name> -predeployment $false -deleteDeployment $true

Задача Azure PowerShell

Ниже приведен сценарий, который можно использовать до и после развертывания.Here is the script that can be used for pre- and post-deployment. Он учитывает удаленные ресурсы и ссылки на ресурсы.It accounts for deleted resources and resource references.

param
(
    [parameter(Mandatory = $false)] [String] $armTemplate,
    [parameter(Mandatory = $false)] [String] $ResourceGroupName,
    [parameter(Mandatory = $false)] [String] $DataFactoryName,
    [parameter(Mandatory = $false)] [Bool] $predeployment=$true,
    [parameter(Mandatory = $false)] [Bool] $deleteDeployment=$false
)

function getPipelineDependencies {
    param([System.Object] $activity)
    if ($activity.Pipeline) {
        return @($activity.Pipeline.ReferenceName)
    } elseif ($activity.Activities) {
        $result = @()
        $activity.Activities | ForEach-Object{ $result += getPipelineDependencies -activity $_ }
        return $result
    } elseif ($activity.ifFalseActivities -or $activity.ifTrueActivities) {
        $result = @()
        $activity.ifFalseActivities | Where-Object {$_ -ne $null} | ForEach-Object{ $result += getPipelineDependencies -activity $_ }
        $activity.ifTrueActivities | Where-Object {$_ -ne $null} | ForEach-Object{ $result += getPipelineDependencies -activity $_ }
        return $result
    } elseif ($activity.defaultActivities) {
        $result = @()
        $activity.defaultActivities | ForEach-Object{ $result += getPipelineDependencies -activity $_ }
        if ($activity.cases) {
            $activity.cases | ForEach-Object{ $_.activities } | ForEach-Object{$result += getPipelineDependencies -activity $_ }
        }
        return $result
    } else {
        return @()
    }
}

function pipelineSortUtil {
    param([Microsoft.Azure.Commands.DataFactoryV2.Models.PSPipeline]$pipeline,
    [Hashtable] $pipelineNameResourceDict,
    [Hashtable] $visited,
    [System.Collections.Stack] $sortedList)
    if ($visited[$pipeline.Name] -eq $true) {
        return;
    }
    $visited[$pipeline.Name] = $true;
    $pipeline.Activities | ForEach-Object{ getPipelineDependencies -activity $_ -pipelineNameResourceDict $pipelineNameResourceDict}  | ForEach-Object{
        pipelineSortUtil -pipeline $pipelineNameResourceDict[$_] -pipelineNameResourceDict $pipelineNameResourceDict -visited $visited -sortedList $sortedList
    }
    $sortedList.Push($pipeline)

}

function Get-SortedPipelines {
    param(
        [string] $DataFactoryName,
        [string] $ResourceGroupName
    )
    $pipelines = Get-AzDataFactoryV2Pipeline -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    $ppDict = @{}
    $visited = @{}
    $stack = new-object System.Collections.Stack
    $pipelines | ForEach-Object{ $ppDict[$_.Name] = $_ }
    $pipelines | ForEach-Object{ pipelineSortUtil -pipeline $_ -pipelineNameResourceDict $ppDict -visited $visited -sortedList $stack }
    $sortedList = new-object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSPipeline]
    
    while ($stack.Count -gt 0) {
        $sortedList.Add($stack.Pop())
    }
    $sortedList
}

function triggerSortUtil {
    param([Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]$trigger,
    [Hashtable] $triggerNameResourceDict,
    [Hashtable] $visited,
    [System.Collections.Stack] $sortedList)
    if ($visited[$trigger.Name] -eq $true) {
        return;
    }
    $visited[$trigger.Name] = $true;
    if ($trigger.Properties.DependsOn) {
        $trigger.Properties.DependsOn | Where-Object {$_ -and $_.ReferenceTrigger} | ForEach-Object{
            triggerSortUtil -trigger $triggerNameResourceDict[$_.ReferenceTrigger.ReferenceName] -triggerNameResourceDict $triggerNameResourceDict -visited $visited -sortedList $sortedList
        }
    }
    $sortedList.Push($trigger)
}

function Get-SortedTriggers {
    param(
        [string] $DataFactoryName,
        [string] $ResourceGroupName
    )
    $triggers = Get-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName
    $triggerDict = @{}
    $visited = @{}
    $stack = new-object System.Collections.Stack
    $triggers | ForEach-Object{ $triggerDict[$_.Name] = $_ }
    $triggers | ForEach-Object{ triggerSortUtil -trigger $_ -triggerNameResourceDict $triggerDict -visited $visited -sortedList $stack }
    $sortedList = new-object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSTrigger]
    
    while ($stack.Count -gt 0) {
        $sortedList.Add($stack.Pop())
    }
    $sortedList
}

function Get-SortedLinkedServices {
    param(
        [string] $DataFactoryName,
        [string] $ResourceGroupName
    )
    $linkedServices = Get-AzDataFactoryV2LinkedService -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName
    $LinkedServiceHasDependencies = @('HDInsightLinkedService', 'HDInsightOnDemandLinkedService', 'AzureBatchLinkedService')
    $Akv = 'AzureKeyVaultLinkedService'
    $HighOrderList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]
    $RegularList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]
    $AkvList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]

    $linkedServices | ForEach-Object {
        if ($_.Properties.GetType().Name -in $LinkedServiceHasDependencies) {
            $HighOrderList.Add($_)
        }
        elseif ($_.Properties.GetType().Name -eq $Akv) {
            $AkvList.Add($_)
        }
        else {
            $RegularList.Add($_)
        }
    }

    $SortedList = New-Object Collections.Generic.List[Microsoft.Azure.Commands.DataFactoryV2.Models.PSLinkedService]($HighOrderList.Count + $RegularList.Count + $AkvList.Count)
    $SortedList.AddRange($HighOrderList)
    $SortedList.AddRange($RegularList)
    $SortedList.AddRange($AkvList)
    $SortedList
}

$templateJson = Get-Content $armTemplate | ConvertFrom-Json
$resources = $templateJson.resources

#Triggers 
Write-Host "Getting triggers"
$triggersInTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/triggers" }
$triggerNamesInTemplate = $triggersInTemplate | ForEach-Object {$_.name.Substring(37, $_.name.Length-40)}

$triggersDeployed = Get-SortedTriggers -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName

$triggersToStop = $triggersDeployed | Where-Object { $triggerNamesInTemplate -contains $_.Name } | ForEach-Object { 
    New-Object PSObject -Property @{
        Name = $_.Name
        TriggerType = $_.Properties.GetType().Name 
    }
}
$triggersToDelete = $triggersDeployed | Where-Object { $triggerNamesInTemplate -notcontains $_.Name } | ForEach-Object { 
    New-Object PSObject -Property @{
        Name = $_.Name
        TriggerType = $_.Properties.GetType().Name 
    }
}
$triggersToStart = $triggersInTemplate | Where-Object { $_.properties.runtimeState -eq "Started" -and ($_.properties.pipelines.Count -gt 0 -or $_.properties.pipeline.pipelineReference -ne $null)} | ForEach-Object { 
    New-Object PSObject -Property @{
        Name = $_.name.Substring(37, $_.name.Length-40)
        TriggerType = $_.Properties.type
    }
}

if ($predeployment -eq $true) {
    #Stop all triggers
    Write-Host "Stopping deployed triggers`n"
    $triggersToStop | ForEach-Object {
        if ($_.TriggerType -eq "BlobEventsTrigger") {
            Write-Host "Unsubscribing" $_.Name "from events"
            $status = Remove-AzDataFactoryV2TriggerSubscription -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
            while ($status.Status -ne "Disabled"){
                Start-Sleep -s 15
                $status = Get-AzDataFactoryV2TriggerSubscriptionStatus -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
            }
        }
        Write-Host "Stopping trigger" $_.Name
        Stop-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name -Force
    }
}
else {
    #Deleted resources
    #pipelines
    Write-Host "Getting pipelines"
    $pipelinesADF = Get-SortedPipelines -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    $pipelinesTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/pipelines" }
    $pipelinesNames = $pipelinesTemplate | ForEach-Object {$_.name.Substring(37, $_.name.Length-40)}
    $deletedpipelines = $pipelinesADF | Where-Object { $pipelinesNames -notcontains $_.Name }
    #dataflows
    $dataflowsADF = Get-AzDataFactoryV2DataFlow -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    $dataflowsTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/dataflows" }
    $dataflowsNames = $dataflowsTemplate | ForEach-Object {$_.name.Substring(37, $_.name.Length-40) }
    $deleteddataflow = $dataflowsADF | Where-Object { $dataflowsNames -notcontains $_.Name }
    #datasets
    Write-Host "Getting datasets"
    $datasetsADF = Get-AzDataFactoryV2Dataset -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    $datasetsTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/datasets" }
    $datasetsNames = $datasetsTemplate | ForEach-Object {$_.name.Substring(37, $_.name.Length-40) }
    $deleteddataset = $datasetsADF | Where-Object { $datasetsNames -notcontains $_.Name }
    #linkedservices
    Write-Host "Getting linked services"
    $linkedservicesADF = Get-SortedLinkedServices -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    $linkedservicesTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/linkedservices" }
    $linkedservicesNames = $linkedservicesTemplate | ForEach-Object {$_.name.Substring(37, $_.name.Length-40)}
    $deletedlinkedservices = $linkedservicesADF | Where-Object { $linkedservicesNames -notcontains $_.Name }
    #Integrationruntimes
    Write-Host "Getting integration runtimes"
    $integrationruntimesADF = Get-AzDataFactoryV2IntegrationRuntime -DataFactoryName $DataFactoryName -ResourceGroupName $ResourceGroupName
    $integrationruntimesTemplate = $resources | Where-Object { $_.type -eq "Microsoft.DataFactory/factories/integrationruntimes" }
    $integrationruntimesNames = $integrationruntimesTemplate | ForEach-Object {$_.name.Substring(37, $_.name.Length-40)}
    $deletedintegrationruntimes = $integrationruntimesADF | Where-Object { $integrationruntimesNames -notcontains $_.Name }

    #Delete resources
    Write-Host "Deleting triggers"
    $triggersToDelete | ForEach-Object { 
        Write-Host "Deleting trigger "  $_.Name
        $trig = Get-AzDataFactoryV2Trigger -name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName
        if ($trig.RuntimeState -eq "Started") {
            if ($_.TriggerType -eq "BlobEventsTrigger") {
                Write-Host "Unsubscribing trigger" $_.Name "from events"
                $status = Remove-AzDataFactoryV2TriggerSubscription -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                while ($status.Status -ne "Disabled"){
                    Start-Sleep -s 15
                    $status = Get-AzDataFactoryV2TriggerSubscriptionStatus -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
                }
            }
            Stop-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name -Force 
        }
        Remove-AzDataFactoryV2Trigger -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force 
    }
    Write-Host "Deleting pipelines"
    $deletedpipelines | ForEach-Object { 
        Write-Host "Deleting pipeline " $_.Name
        Remove-AzDataFactoryV2Pipeline -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force 
    }
    Write-Host "Deleting dataflows"
    $deleteddataflow | ForEach-Object { 
        Write-Host "Deleting dataflow " $_.Name
        Remove-AzDataFactoryV2DataFlow -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force 
    }
    Write-Host "Deleting datasets"
    $deleteddataset | ForEach-Object { 
        Write-Host "Deleting dataset " $_.Name
        Remove-AzDataFactoryV2Dataset -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force 
    }
    Write-Host "Deleting linked services"
    $deletedlinkedservices | ForEach-Object { 
        Write-Host "Deleting Linked Service " $_.Name
        Remove-AzDataFactoryV2LinkedService -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force 
    }
    Write-Host "Deleting integration runtimes"
    $deletedintegrationruntimes | ForEach-Object { 
        Write-Host "Deleting integration runtime " $_.Name
        Remove-AzDataFactoryV2IntegrationRuntime -Name $_.Name -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Force 
    }

    if ($deleteDeployment -eq $true) {
        Write-Host "Deleting ARM deployment ... under resource group: " $ResourceGroupName
        $deployments = Get-AzResourceGroupDeployment -ResourceGroupName $ResourceGroupName
        $deploymentsToConsider = $deployments | Where { $_.DeploymentName -like "ArmTemplate_master*" -or $_.DeploymentName -like "ArmTemplateForFactory*" } | Sort-Object -Property Timestamp -Descending
        $deploymentName = $deploymentsToConsider[0].DeploymentName

       Write-Host "Deployment to be deleted: " $deploymentName
        $deploymentOperations = Get-AzResourceGroupDeploymentOperation -DeploymentName $deploymentName -ResourceGroupName $ResourceGroupName
        $deploymentsToDelete = $deploymentOperations | Where { $_.properties.targetResource.id -like "*Microsoft.Resources/deployments*" }

        $deploymentsToDelete | ForEach-Object { 
            Write-host "Deleting inner deployment: " $_.properties.targetResource.id
            Remove-AzResourceGroupDeployment -Id $_.properties.targetResource.id
        }
        Write-Host "Deleting deployment: " $deploymentName
        Remove-AzResourceGroupDeployment -ResourceGroupName $ResourceGroupName -Name $deploymentName
    }

    #Start active triggers - after cleanup efforts
    Write-Host "Starting active triggers"
    $triggersToStart | ForEach-Object { 
        if ($_.TriggerType -eq "BlobEventsTrigger") {
            Write-Host "Subscribing" $_.Name "to events"
            $status = Add-AzDataFactoryV2TriggerSubscription -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
            while ($status.Status -ne "Enabled"){
                Start-Sleep -s 15
                $status = Get-AzDataFactoryV2TriggerSubscriptionStatus -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name
            }
        }
        Write-Host "Starting trigger" $_.Name
        Start-AzDataFactoryV2Trigger -ResourceGroupName $ResourceGroupName -DataFactoryName $DataFactoryName -Name $_.Name -Force
    }
}