├── templates ├── ant.properties.json ├── gcc.properties.json ├── go.properties.json ├── html.properties.json ├── php.properties.json ├── ruby.properties.json ├── android.properties.json ├── empty.properties.json ├── gradle.properties.json ├── maven.properties.json ├── node.js.properties.json ├── xcode.properties.json ├── .net-desktop.properties.json ├── asp.net.properties.json ├── node.js-with-vue.properties.json ├── python-django.properties.json ├── xamarin.ios.properties.json ├── asp.net-core.properties.json ├── jekyll-container.properties.json ├── node.js-with-grunt.properties.json ├── node.js-with-gulp.properties.json ├── node.js-with-react.properties.json ├── python-package.properties.json ├── xamarin.android.properties.json ├── node.js-with-angular.properties.json ├── node.js-with-webpack.properties.json ├── asp.net-core-.net-framework.properties.json ├── universal-windows-platform.properties.json ├── docker-build.properties.json ├── docker-container-to-acr.properties.json ├── docker-container-to-aks.properties.json ├── docker-container-webapp.properties.json ├── resources │ ├── k8s │ │ ├── service.yml │ │ └── deployment.yml │ └── arm │ │ ├── acr.json │ │ ├── aks.json │ │ └── webapp-on-containers.json ├── docker-container-functionapp.properties.json ├── gcc.yml ├── icons │ └── svg │ │ ├── universalwindowsplatform.svg │ │ ├── vue.svg │ │ ├── empty.svg │ │ ├── angular.svg │ │ ├── webpack.svg │ │ ├── django.svg │ │ ├── xamarin.svg │ │ ├── functionapp.svg │ │ ├── aks.svg │ │ ├── cxx.svg │ │ ├── html.svg │ │ ├── azurecontainerregistry.svg │ │ ├── gradle.svg │ │ ├── dotnet.svg │ │ ├── golang.svg │ │ ├── xcode.svg │ │ ├── android.svg │ │ ├── ant.svg │ │ ├── react.svg │ │ ├── python.svg │ │ ├── php.svg │ │ ├── aspdotnet.svg │ │ └── dotnetcore.svg ├── html.yml ├── asp.net-core.yml ├── node.js.yml ├── node.js-with-vue.yml ├── empty.yml ├── node.js-with-react.yml ├── ant.yml ├── android.yml ├── node.js-with-grunt.yml ├── node.js-with-gulp.yml ├── ruby.yml ├── node.js-with-angular.yml ├── docker-build.yml ├── node.js-with-webpack.yml ├── maven.yml ├── xcode.yml ├── docker-container.properties.json ├── gradle.yml ├── jekyll-container.yml ├── xamarin.android.yml ├── php-webapp-to-linux-on-azure.properties.json ├── python-to-linux-webapp-on-azure.properties.json ├── maven-webapp-to-linux-on-azure.properties.json ├── .net-desktop.yml ├── node.js-react-webapp-to-linux-on-azure.properties.json ├── node.js-express-webapp-to-linux-on-azure.properties.json ├── php.yml ├── node.js-functionapp-to-linux-on-azure.properties.json ├── universal-windows-platform.yml ├── python-package.yml ├── asp.net.yml ├── python-functionapp-to-linux-on-azure.properties.json ├── asp.net-core-functionapp-to-windows-on-azure.properties.json ├── xamarin.ios.yml ├── powershell-functionapp-to-windows-on-azure.properties.json ├── asp.net-core-.net-framework.yml ├── go.yml ├── docker-container.yml ├── deploy-to-existing-kubernetes-cluster.properties.json ├── python-django.yml ├── maven-webapp-to-linux-on-azure.yml ├── powershell-functionapp-to-windows-on-azure.yml ├── node.js-react-webapp-to-linux-on-azure.yml ├── asp.net-core-functionapp-to-windows-on-azure.yml ├── node.js-express-webapp-to-linux-on-azure.yml ├── node.js-functionapp-to-linux-on-azure.yml ├── python-functionapp-to-linux-on-azure.yml ├── php-webapp-to-linux-on-azure.yml ├── python-to-linux-webapp-on-azure.yml ├── docker-container-to-acr.yml ├── docker-container-webapp.yml ├── docker-container-functionapp.yml ├── deploy-to-existing-kubernetes-cluster.yml └── docker-container-to-aks.yml ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ └── yaml-issues.md └── workflows │ ├── autoAssignABTT.yml │ └── stale.yml ├── design ├── images │ ├── environment.png │ ├── run-panel-with-params.png │ ├── jira-traceability-build-1.png │ ├── jira-traceability-build-2.png │ ├── jira-traceability-deploy-1.png │ ├── jira-traceability-deploy-2.png │ └── pipeline-caching-task-editor.jpg ├── README.md ├── yaml-principles.md ├── variables.md ├── checkout-path.md ├── node10-agent-support.md ├── step-target-restricted-mode.md ├── deprecated │ ├── container-steps.md │ └── container-steps-implementation.md ├── deployment.md ├── each-expression.md ├── readonly-variables.md ├── sidecar-containers.md ├── step-target.md └── deployment-strategies.md ├── .vscode └── extensions.json ├── LICENSE ├── README.md └── SECURITY.md /templates/ant.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "ant" 3 | } 4 | -------------------------------------------------------------------------------- /templates/gcc.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "cxx" 3 | } 4 | -------------------------------------------------------------------------------- /templates/go.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "golang" 3 | } 4 | -------------------------------------------------------------------------------- /templates/html.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "html" 3 | } 4 | -------------------------------------------------------------------------------- /templates/php.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "php" 3 | } 4 | -------------------------------------------------------------------------------- /templates/ruby.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "ruby" 3 | } 4 | -------------------------------------------------------------------------------- /templates/android.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "android" 3 | } 4 | -------------------------------------------------------------------------------- /templates/empty.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "empty" 3 | } 4 | -------------------------------------------------------------------------------- /templates/gradle.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "gradle" 3 | } 4 | -------------------------------------------------------------------------------- /templates/maven.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "maven" 3 | } 4 | -------------------------------------------------------------------------------- /templates/node.js.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "nodejs" 3 | } 4 | -------------------------------------------------------------------------------- /templates/xcode.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "xcode" 3 | } 4 | -------------------------------------------------------------------------------- /templates/.net-desktop.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "dotnet" 3 | } 4 | -------------------------------------------------------------------------------- /templates/asp.net.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "aspdotnet" 3 | } 4 | -------------------------------------------------------------------------------- /templates/node.js-with-vue.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "vue" 3 | } 4 | -------------------------------------------------------------------------------- /templates/python-django.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "django" 3 | } 4 | -------------------------------------------------------------------------------- /templates/xamarin.ios.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "xamarin" 3 | } 4 | -------------------------------------------------------------------------------- /templates/asp.net-core.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "dotnetcore" 3 | } 4 | -------------------------------------------------------------------------------- /templates/jekyll-container.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "jekyll" 3 | } 4 | -------------------------------------------------------------------------------- /templates/node.js-with-grunt.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "grunt" 3 | } 4 | -------------------------------------------------------------------------------- /templates/node.js-with-gulp.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "gulp" 3 | } 4 | -------------------------------------------------------------------------------- /templates/node.js-with-react.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "react" 3 | } 4 | -------------------------------------------------------------------------------- /templates/python-package.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "python" 3 | } 4 | -------------------------------------------------------------------------------- /templates/xamarin.android.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "xamarin" 3 | } 4 | -------------------------------------------------------------------------------- /templates/node.js-with-angular.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "angular" 3 | } 4 | -------------------------------------------------------------------------------- /templates/node.js-with-webpack.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "webpack" 3 | } 4 | -------------------------------------------------------------------------------- /templates/asp.net-core-.net-framework.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "dotnetcore" 3 | } 4 | -------------------------------------------------------------------------------- /templates/universal-windows-platform.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "universalwindowsplatform" 3 | } 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Global rule: 2 | * @microsoft/akvelon-build-task-team @microsoft/azure-pipelines-platform 3 | -------------------------------------------------------------------------------- /design/images/environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-yaml/HEAD/design/images/environment.png -------------------------------------------------------------------------------- /design/images/run-panel-with-params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-yaml/HEAD/design/images/run-panel-with-params.png -------------------------------------------------------------------------------- /design/images/jira-traceability-build-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-yaml/HEAD/design/images/jira-traceability-build-1.png -------------------------------------------------------------------------------- /design/images/jira-traceability-build-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-yaml/HEAD/design/images/jira-traceability-build-2.png -------------------------------------------------------------------------------- /design/images/jira-traceability-deploy-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-yaml/HEAD/design/images/jira-traceability-deploy-1.png -------------------------------------------------------------------------------- /design/images/jira-traceability-deploy-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-yaml/HEAD/design/images/jira-traceability-deploy-2.png -------------------------------------------------------------------------------- /design/images/pipeline-caching-task-editor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-yaml/HEAD/design/images/pipeline-caching-task-editor.jpg -------------------------------------------------------------------------------- /templates/docker-build.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "docker", 3 | "parameters": [ 4 | { 5 | "name": "dockerfilePath", 6 | "type": "string", 7 | "required": "true", 8 | "displayName": "Dockerfile", 9 | "defaultValue": "**/Dockerfile" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /templates/docker-container-to-acr.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "docker", 3 | "parameters": [ 4 | { "name" : "azureServiceConnectionId", "type": "connectedService:azureRM", "required": "true", "displayName": "Azure subscription" }, 5 | { "name" : "repositoryName", "type": "string", "required": "true", "displayName": "Repository name" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /templates/docker-container-to-aks.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "aks", 3 | "parameters": [ 4 | { "name" : "azureServiceConnectionId", "type": "connectedService:azureRM", "required": "true", "displayName": "Azure subscription" }, 5 | { "name" : "repositoryName", "type": "string", "required": "true", "displayName": "Repository name" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /templates/docker-container-webapp.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "docker", 3 | "parameters": [ 4 | { "name" : "azureServiceConnectionId", "type": "connectedService:azureRM", "required": "true", "displayName": "Azure subscription" }, 5 | { "name" : "repositoryName", "type": "string", "required": "true", "displayName": "Repository name" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /templates/resources/k8s/service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}} 5 | spec: 6 | type: LoadBalancer 7 | ports: 8 | - port: {{ servicePort }} 9 | selector: 10 | app: {{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Developer Community 4 | url: https://developercommunity.visualstudio.com/spaces/21/index.html 5 | about: Feature ideas and problem reports go here. 6 | - name: Azure DevOps Support 7 | url: https://azure.microsoft.com/support/devops/ 8 | about: Get product support here. 9 | -------------------------------------------------------------------------------- /templates/docker-container-functionapp.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "functionapp", 3 | "parameters": [ 4 | { "name" : "azureServiceConnectionId", "type": "connectedService:azureRM", "required": "true", "displayName": "Azure subscription" }, 5 | { "name" : "repositoryName", "type": "string", "required": "true", "displayName": "Repository name" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /templates/gcc.yml: -------------------------------------------------------------------------------- 1 | # C/C++ with GCC 2 | # Build your C/C++ project with GCC using make. 3 | # Add steps that publish test results, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - script: | 14 | make 15 | displayName: 'make' 16 | -------------------------------------------------------------------------------- /templates/icons/svg/universalwindowsplatform.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/html.yml: -------------------------------------------------------------------------------- 1 | # HTML 2 | # Archive your static HTML project and save it with the build record. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: ArchiveFiles@2 14 | inputs: 15 | rootFolderOrFile: '$(build.sourcesDirectory)' 16 | includeRootFolder: false 17 | - task: PublishBuildArtifacts@1 18 | -------------------------------------------------------------------------------- /templates/icons/svg/vue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /templates/icons/svg/empty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/asp.net-core.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET Core 2 | # Build and test ASP.NET Core projects targeting .NET Core. 3 | # Add steps that run tests, create a NuGet package, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | variables: 13 | buildConfiguration: 'Release' 14 | 15 | steps: 16 | - script: dotnet build --configuration $(buildConfiguration) 17 | displayName: 'dotnet build $(buildConfiguration)' 18 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "ms-azure-devops.azure-pipelines" 8 | ], 9 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 10 | "unwantedRecommendations": [ 11 | 12 | ] 13 | } -------------------------------------------------------------------------------- /templates/icons/svg/angular.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/node.js.yml: -------------------------------------------------------------------------------- 1 | # Node.js 2 | # Build a general Node.js project with npm. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install 20 | npm run build 21 | displayName: 'npm install and build' 22 | -------------------------------------------------------------------------------- /templates/node.js-with-vue.yml: -------------------------------------------------------------------------------- 1 | # Node.js with Vue 2 | # Build a Node.js project that uses Vue. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install 20 | npm run build 21 | displayName: 'npm install and build' 22 | -------------------------------------------------------------------------------- /templates/empty.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - script: echo Hello, world! 14 | displayName: 'Run a one-line script' 15 | 16 | - script: | 17 | echo Add other tasks to build, test, and deploy your project. 18 | echo See https://aka.ms/yaml 19 | displayName: 'Run a multi-line script' 20 | -------------------------------------------------------------------------------- /templates/node.js-with-react.yml: -------------------------------------------------------------------------------- 1 | # Node.js with React 2 | # Build a Node.js project that uses React. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install 20 | npm run build 21 | displayName: 'npm install and build' 22 | -------------------------------------------------------------------------------- /templates/ant.yml: -------------------------------------------------------------------------------- 1 | # Ant 2 | # Build your Java projects and run tests with Apache Ant. 3 | # Add steps that save build artifacts and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/java 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: Ant@1 14 | inputs: 15 | workingDirectory: '' 16 | buildFile: 'build.xml' 17 | javaHomeOption: 'JDKVersion' 18 | jdkVersionOption: '1.8' 19 | jdkArchitectureOption: 'x64' 20 | publishJUnitResults: true 21 | testResultsFiles: '**/TEST-*.xml' 22 | -------------------------------------------------------------------------------- /templates/android.yml: -------------------------------------------------------------------------------- 1 | # Android 2 | # Build your Android project with Gradle. 3 | # Add steps that test, sign, and distribute the APK, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/android 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'macos-latest' 11 | 12 | steps: 13 | - task: Gradle@2 14 | inputs: 15 | workingDirectory: '' 16 | gradleWrapperFile: 'gradlew' 17 | gradleOptions: '-Xmx3072m' 18 | publishJUnitResults: false 19 | testResultsFiles: '**/TEST-*.xml' 20 | tasks: 'assembleDebug' 21 | -------------------------------------------------------------------------------- /templates/node.js-with-grunt.yml: -------------------------------------------------------------------------------- 1 | # Node.js with Grunt 2 | # Build a Node.js project using the Grunt task runner. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install 20 | grunt --gruntfile Gruntfile.js 21 | displayName: 'npm install and run grunt' 22 | -------------------------------------------------------------------------------- /templates/node.js-with-gulp.yml: -------------------------------------------------------------------------------- 1 | # Node.js with gulp 2 | # Build a Node.js project using the gulp task runner. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install 20 | gulp default --gulpfile gulpfile.js 21 | displayName: 'npm install and run gulp' 22 | -------------------------------------------------------------------------------- /templates/ruby.yml: -------------------------------------------------------------------------------- 1 | # Ruby 2 | # Package your Ruby project. 3 | # Add steps that install rails, analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/ruby 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: UseRubyVersion@0 14 | inputs: 15 | versionSpec: '>= 2.5' 16 | 17 | - script: | 18 | gem install bundler 19 | bundle install --retry=3 --jobs=4 20 | displayName: 'bundle install' 21 | 22 | - script: bundle exec rake 23 | displayName: 'bundle exec rake' 24 | -------------------------------------------------------------------------------- /templates/node.js-with-angular.yml: -------------------------------------------------------------------------------- 1 | # Node.js with Angular 2 | # Build a Node.js project that uses Angular. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install -g @angular/cli 20 | npm install 21 | ng build --prod 22 | displayName: 'npm install and build' 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/yaml-issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Talk about YAML 3 | about: 'We can talk about YAML here. We cannot take problem reports or support requests.' 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | This repo is for working in the open on Azure Pipelines YAML features. It's not a great place to make [new feature requests](https://developercommunity.visualstudio.com/spaces/21/index.html), [report problems](https://developercommunity.visualstudio.com/spaces/21/index.html), or [get support](https://azure.microsoft.com/en-us/support/devops/). Issues you open here may sit unreviewed for a very long time. 11 | -------------------------------------------------------------------------------- /templates/docker-build.yml: -------------------------------------------------------------------------------- 1 | # Docker 2 | # Build a Docker image 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - {{ branch }} 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | tag: '$(Build.BuildId)' 13 | 14 | stages: 15 | - stage: Build 16 | displayName: Build image 17 | jobs: 18 | - job: Build 19 | displayName: Build 20 | pool: 21 | {{ pool }} 22 | steps: 23 | - task: Docker@2 24 | displayName: Build an image 25 | inputs: 26 | command: build 27 | dockerfile: '{{ dockerfilePath }}' 28 | tags: | 29 | $(tag) 30 | -------------------------------------------------------------------------------- /templates/node.js-with-webpack.yml: -------------------------------------------------------------------------------- 1 | # Node.js with webpack 2 | # Build a Node.js project using the webpack CLI. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install -g webpack webpack-cli --save-dev 20 | npm install 21 | npx webpack --config webpack.config.js 22 | displayName: 'npm install, run webpack' 23 | -------------------------------------------------------------------------------- /templates/maven.yml: -------------------------------------------------------------------------------- 1 | # Maven 2 | # Build your Java project and run tests with Apache Maven. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/java 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: Maven@3 14 | inputs: 15 | mavenPomFile: 'pom.xml' 16 | mavenOptions: '-Xmx3072m' 17 | javaHomeOption: 'JDKVersion' 18 | jdkVersionOption: '1.8' 19 | jdkArchitectureOption: 'x64' 20 | publishJUnitResults: true 21 | testResultsFiles: '**/surefire-reports/TEST-*.xml' 22 | goals: 'package' 23 | -------------------------------------------------------------------------------- /templates/xcode.yml: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # Build, test, and archive an Xcode workspace on macOS. 3 | # Add steps that install certificates, test, sign, and distribute an app, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/xcode 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'macos-latest' 11 | 12 | steps: 13 | - task: Xcode@5 14 | inputs: 15 | actions: 'build' 16 | scheme: '' 17 | sdk: 'iphoneos' 18 | configuration: 'Release' 19 | xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' 20 | xcodeVersion: 'default' # Options: 8, 9, 10, 11, 12, default, specifyPath 21 | -------------------------------------------------------------------------------- /templates/docker-container.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "docker", 3 | "parameters": [ 4 | { 5 | "name": "containerRegistryConnection", 6 | "type": "endpoint:containerRegistry", 7 | "required": "true", 8 | "displayName": "Container Registry" 9 | }, 10 | { 11 | "name": "imageRepository", 12 | "type": "string", 13 | "required": "true", 14 | "displayName": "Image Name" 15 | }, 16 | { 17 | "name": "dockerfilePath", 18 | "type": "string", 19 | "required": "false", 20 | "displayName": "Dockerfile", 21 | "defaultValue": "**/Dockerfile" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /templates/gradle.yml: -------------------------------------------------------------------------------- 1 | # Gradle 2 | # Build your Java project and run tests with Gradle using a Gradle wrapper script. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/java 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: Gradle@2 14 | inputs: 15 | workingDirectory: '' 16 | gradleWrapperFile: 'gradlew' 17 | gradleOptions: '-Xmx3072m' 18 | javaHomeOption: 'JDKVersion' 19 | jdkVersionOption: '1.8' 20 | jdkArchitectureOption: 'x64' 21 | publishJUnitResults: true 22 | testResultsFiles: '**/TEST-*.xml' 23 | tasks: 'build' 24 | -------------------------------------------------------------------------------- /templates/jekyll-container.yml: -------------------------------------------------------------------------------- 1 | # Jekyll site 2 | # Package your Jekyll site using the jekyll/builder Docker container image. 3 | # Add steps that build, test, save build artifacts, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | steps: 13 | - task: Docker@0 14 | displayName: 'Run Jekyll' 15 | inputs: 16 | containerRegistryType: 'Container Registry' 17 | action: 'Run an image' 18 | imageName: 'jekyll/builder:latest' 19 | volumes: | 20 | $(build.sourcesDirectory):/srv/jekyll 21 | $(build.binariesDirectory):/srv/jekyll/_site 22 | containerCommand: 'jekyll build --future' 23 | detached: false 24 | -------------------------------------------------------------------------------- /templates/xamarin.android.yml: -------------------------------------------------------------------------------- 1 | # Xamarin.Android 2 | # Build a Xamarin.Android project. 3 | # Add steps that test, sign, and distribute an app, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/xamarin 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'macos-latest' 11 | 12 | variables: 13 | buildConfiguration: 'Release' 14 | outputDirectory: '$(build.binariesDirectory)/$(buildConfiguration)' 15 | 16 | steps: 17 | - task: NuGetToolInstaller@1 18 | 19 | - task: NuGetCommand@2 20 | inputs: 21 | restoreSolution: '**/*.sln' 22 | 23 | - task: XamarinAndroid@1 24 | inputs: 25 | projectFile: '**/*droid*.csproj' 26 | outputDirectory: '$(outputDirectory)' 27 | configuration: '$(buildConfiguration)' 28 | -------------------------------------------------------------------------------- /templates/icons/svg/webpack.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/php-webapp-to-linux-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "php", 3 | "parameters": [ 4 | { 5 | "name": "azureRmServiceConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true" 8 | }, 9 | { 10 | "name": "webAppName", 11 | "type": "dataSourcePicklist", 12 | "required": "true", 13 | "displayName": "Web App name" 14 | } 15 | ], 16 | "dataSourceBindings": [ 17 | { 18 | "dataSourceName": "AzureRMWebAppNamesByAppType", 19 | "endpointParameterName": "azureRmServiceConnection", 20 | "parameters": { 21 | "WebAppKind": "webAppLinux" 22 | }, 23 | "target": "webAppName" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /.github/workflows/autoAssignABTT.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign ABTT to Project Board 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | assign_one_project: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | name: Assign to ABTT Project 14 | steps: 15 | - name: "Add triage and area labels" 16 | uses: actions-ecosystem/action-add-labels@v1 17 | with: 18 | github_token: ${{ secrets.GITHUB_TOKEN }} 19 | labels: | 20 | Area: Yaml 21 | triage 22 | 23 | - name: "Assign issues with 'Area: ABTT' label to project board" 24 | uses: actions/add-to-project@v0.4.1 25 | with: 26 | project-url: https://github.com/orgs/microsoft/projects/755 27 | github-token: ${{ secrets.ABTT_TOKEN }} 28 | -------------------------------------------------------------------------------- /design/README.md: -------------------------------------------------------------------------------- 1 | # Azure Pipelines YAML - Design Docs 2 | 3 | The design docs within this repo are created at different times during the development of Azure Pipelines, to support collaborative contributions to the design process. Designs documents are for, 4 | 5 | * features considered for implementation but never implemented 6 | * already implemented features 7 | * future ideas for features 8 | 9 | The design docs in this repo may not represent the current state of an Azure Pipelines feature. 10 | 11 | For the most upto date information about a feature refer to the [Azure Pipeline Docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/?view=azure-devops) and specifically the [Azure Pipeline Yaml Reference](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema) 12 | -------------------------------------------------------------------------------- /templates/python-to-linux-webapp-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "python", 3 | "parameters": [ 4 | { 5 | "name": "azureServiceConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true" 8 | }, 9 | { 10 | "name": "webAppName", 11 | "type": "dataSourcePicklist", 12 | "required": "true", 13 | "displayName": "Web App name" 14 | } 15 | ], 16 | "dataSourceBindings": [ 17 | { 18 | "dataSourceName": "AzureRMWebAppNamesByAppType", 19 | "endpointParameterName": "azureServiceConnection", 20 | "parameters": { 21 | "WebAppKind": "webAppLinux" 22 | }, 23 | "target": "webAppName" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /templates/resources/arm/acr.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "registryName": { 6 | "type": "string" 7 | }, 8 | "registryLocation": { 9 | "type": "string" 10 | }, 11 | "registrySku": { 12 | "defaultValue": "Standard", 13 | "type": "string" 14 | } 15 | }, 16 | "resources": [ 17 | { 18 | "type": "Microsoft.ContainerRegistry/registries", 19 | "sku": { 20 | "name": "[parameters('registrySku')]" 21 | }, 22 | "name": "[parameters('registryName')]", 23 | "apiVersion": "2017-10-01", 24 | "location": "[parameters('registryLocation')]", 25 | "properties": { 26 | "adminUserEnabled": true 27 | } 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /templates/resources/k8s/deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion : apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}} 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: {{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}} 10 | template: 11 | metadata: 12 | labels: 13 | app: {{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}} 14 | spec: 15 | containers: 16 | - name: {{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}} 17 | image: {{ containerRegistryConnection.Authorization.Parameters.loginServer }}/{{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}} 18 | ports: 19 | - containerPort: {{ servicePort }} -------------------------------------------------------------------------------- /templates/icons/svg/django.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/maven-webapp-to-linux-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "maven", 3 | "parameters": [ 4 | { 5 | "name": "azureRmConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true", 8 | "displayName": "Azure Connection" 9 | }, 10 | { 11 | "name": "webAppName", 12 | "type": "dataSourcePicklist", 13 | "required": "true", 14 | "displayName":"Web App name" 15 | } 16 | ], 17 | "dataSourceBindings": [ 18 | { 19 | "dataSourceName": "AzureRMWebAppNamesByAppType", 20 | "endpointParameterName": "azureRmConnection", 21 | "parameters": { 22 | "WebAppKind": "webAppLinux" 23 | }, 24 | "target": "webAppName" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /templates/.net-desktop.yml: -------------------------------------------------------------------------------- 1 | # .NET Desktop 2 | # Build and run tests for .NET Desktop or Windows classic desktop solutions. 3 | # Add steps that publish symbols, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@1 19 | 20 | - task: NuGetCommand@2 21 | inputs: 22 | restoreSolution: '$(solution)' 23 | 24 | - task: VSBuild@1 25 | inputs: 26 | solution: '$(solution)' 27 | platform: '$(buildPlatform)' 28 | configuration: '$(buildConfiguration)' 29 | 30 | - task: VSTest@2 31 | inputs: 32 | platform: '$(buildPlatform)' 33 | configuration: '$(buildConfiguration)' 34 | -------------------------------------------------------------------------------- /templates/icons/svg/xamarin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | logo_xamarin 10 | 11 | 12 | -------------------------------------------------------------------------------- /templates/node.js-react-webapp-to-linux-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "react", 3 | "parameters": [ 4 | { 5 | "name": "azureRmConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true", 8 | "displayName": "Azure Connection" 9 | }, 10 | { 11 | "name": "webAppName", 12 | "type": "dataSourcePicklist", 13 | "required": "true", 14 | "displayName": "Web App name" 15 | } 16 | ], 17 | "dataSourceBindings": [ 18 | { 19 | "dataSourceName": "AzureRMWebAppNamesByAppType", 20 | "endpointParameterName": "azureRmConnection", 21 | "parameters": { 22 | "WebAppKind": "webAppLinux" 23 | }, 24 | "target": "webAppName" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /templates/node.js-express-webapp-to-linux-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "nodejs", 3 | "parameters": [ 4 | { 5 | "name": "azureRmConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true", 8 | "displayName": "Azure Connection" 9 | }, 10 | { 11 | "name": "webAppName", 12 | "type": "dataSourcePicklist", 13 | "required": "true", 14 | "displayName": "Web App name" 15 | } 16 | ], 17 | "dataSourceBindings": [ 18 | { 19 | "dataSourceName": "AzureRMWebAppNamesByAppType", 20 | "endpointParameterName": "azureRmConnection", 21 | "parameters": { 22 | "WebAppKind": "webAppLinux" 23 | }, 24 | "target": "webAppName" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /templates/php.yml: -------------------------------------------------------------------------------- 1 | # PHP 2 | # Test and package your PHP project. 3 | # Add steps that run tests, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/php 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | variables: 13 | phpVersion: 7.2 14 | 15 | steps: 16 | - script: | 17 | sudo update-alternatives --set php /usr/bin/php$(phpVersion) 18 | sudo update-alternatives --set phar /usr/bin/phar$(phpVersion) 19 | sudo update-alternatives --set phpdbg /usr/bin/phpdbg$(phpVersion) 20 | sudo update-alternatives --set php-cgi /usr/bin/php-cgi$(phpVersion) 21 | sudo update-alternatives --set phar.phar /usr/bin/phar.phar$(phpVersion) 22 | php -version 23 | displayName: 'Use PHP version $(phpVersion)' 24 | 25 | - script: composer install --no-interaction --prefer-dist 26 | displayName: 'composer install' 27 | -------------------------------------------------------------------------------- /templates/node.js-functionapp-to-linux-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "functionapp", 3 | "parameters": [ 4 | { 5 | "name": "azureRmConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true", 8 | "displayName": "Azure Connection" 9 | }, 10 | { 11 | "name": "functionAppName", 12 | "type": "dataSourcePicklist", 13 | "required": "true", 14 | "displayName":"Function App name" 15 | } 16 | ], 17 | "dataSourceBindings": [ 18 | { 19 | "dataSourceName": "AzureFunctionAppNamesByAppType", 20 | "endpointParameterName": "azureRmConnection", 21 | "parameters": { 22 | "WebAppKind": "functionAppLinux" 23 | }, 24 | "target": "functionAppName" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /templates/icons/svg/functionapp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/universal-windows-platform.yml: -------------------------------------------------------------------------------- 1 | # Universal Windows Platform 2 | # Build a Universal Windows Platform project using Visual Studio. 3 | # Add steps that test and distribute an app, save build artifacts, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'x86|x64|ARM' 15 | buildConfiguration: 'Release' 16 | appxPackageDir: '$(build.artifactStagingDirectory)\AppxPackages\\' 17 | 18 | steps: 19 | - task: NuGetToolInstaller@1 20 | 21 | - task: NuGetCommand@2 22 | inputs: 23 | restoreSolution: '$(solution)' 24 | 25 | - task: VSBuild@1 26 | inputs: 27 | platform: 'x86' 28 | solution: '$(solution)' 29 | configuration: '$(buildConfiguration)' 30 | msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload' 31 | -------------------------------------------------------------------------------- /templates/python-package.yml: -------------------------------------------------------------------------------- 1 | # Python package 2 | # Create and test a Python package on multiple Python versions. 3 | # Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/python 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | strategy: 12 | matrix: 13 | Python27: 14 | python.version: '2.7' 15 | Python35: 16 | python.version: '3.5' 17 | Python36: 18 | python.version: '3.6' 19 | Python37: 20 | python.version: '3.7' 21 | 22 | steps: 23 | - task: UsePythonVersion@0 24 | inputs: 25 | versionSpec: '$(python.version)' 26 | displayName: 'Use Python $(python.version)' 27 | 28 | - script: | 29 | python -m pip install --upgrade pip 30 | pip install -r requirements.txt 31 | displayName: 'Install dependencies' 32 | 33 | - script: | 34 | pip install pytest pytest-azurepipelines 35 | pytest 36 | displayName: 'pytest' 37 | -------------------------------------------------------------------------------- /templates/asp.net.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET 2 | # Build and test ASP.NET projects. 3 | # Add steps that publish symbols, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@1 19 | 20 | - task: NuGetCommand@2 21 | inputs: 22 | restoreSolution: '$(solution)' 23 | 24 | - task: VSBuild@1 25 | inputs: 26 | solution: '$(solution)' 27 | msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"' 28 | platform: '$(buildPlatform)' 29 | configuration: '$(buildConfiguration)' 30 | 31 | - task: VSTest@2 32 | inputs: 33 | platform: '$(buildPlatform)' 34 | configuration: '$(buildConfiguration)' 35 | -------------------------------------------------------------------------------- /templates/python-functionapp-to-linux-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "functionapp", 3 | "parameters": [ 4 | { 5 | "name": "azureRmConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true", 8 | "displayName": "Azure Connection" 9 | }, 10 | { 11 | "name": "functionAppName", 12 | "type": "dataSourcePicklist", 13 | "required": "true", 14 | "displayName":"Function App name" 15 | }, 16 | { 17 | "name": "workingDirectory", 18 | "type": "pickList", 19 | "required": "true", 20 | "displayName": "Working Directory" 21 | } 22 | ], 23 | "dataSourceBindings": [ 24 | { 25 | "dataSourceName": "AzureFunctionAppNamesByAppType", 26 | "endpointParameterName": "azureRmConnection", 27 | "parameters": { 28 | "WebAppKind": "functionAppLinux" 29 | }, 30 | "target": "functionAppName" 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /templates/asp.net-core-functionapp-to-windows-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "functionapp", 3 | "parameters": [ 4 | { 5 | "name": "azureRmConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true", 8 | "displayName": "Azure Connection" 9 | }, 10 | { 11 | "name": "functionAppName", 12 | "type": "dataSourcePicklist", 13 | "required": "true", 14 | "displayName":"Function App name" 15 | }, 16 | { 17 | "name": "workingDirectory", 18 | "type": "pickList", 19 | "required": "true", 20 | "displayName": "Working Directory" 21 | } 22 | ], 23 | "dataSourceBindings": [ 24 | { 25 | "dataSourceName": "AzureFunctionAppNamesByAppType", 26 | "endpointParameterName": "azureRmConnection", 27 | "parameters": { 28 | "WebAppKind": "functionApp" 29 | }, 30 | "target": "functionAppName" 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /templates/xamarin.ios.yml: -------------------------------------------------------------------------------- 1 | # Xamarin.iOS 2 | # Build a Xamarin.iOS project. 3 | # Add steps that install certificates, test, sign, and distribute an app, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/xamarin 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'macos-latest' 11 | 12 | steps: 13 | # To manually select a Xamarin SDK version on the Microsoft-hosted macOS agent, 14 | # configure this task with the *Mono* version that is associated with the 15 | # Xamarin SDK version that you need, and set the "enabled" property to true. 16 | # See https://go.microsoft.com/fwlink/?linkid=871629 17 | - script: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh 5_12_0 18 | displayName: 'Select the Xamarin SDK version' 19 | enabled: false 20 | 21 | - task: NuGetToolInstaller@1 22 | 23 | - task: NuGetCommand@2 24 | inputs: 25 | restoreSolution: '**/*.sln' 26 | 27 | - task: XamariniOS@2 28 | inputs: 29 | solutionFile: '**/*.sln' 30 | configuration: 'Release' 31 | buildForSimulator: true 32 | packageApp: false 33 | -------------------------------------------------------------------------------- /templates/powershell-functionapp-to-windows-on-azure.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "functionapp", 3 | "parameters": [ 4 | { 5 | "name": "azureRmConnection", 6 | "type": "endpoint:azureRm", 7 | "required": "true", 8 | "displayName": "Azure Connection" 9 | }, 10 | { 11 | "name": "functionAppName", 12 | "type": "dataSourcePicklist", 13 | "required": "true", 14 | "displayName":"Function App name" 15 | }, 16 | { 17 | "name": "workingDirectory", 18 | "type": "pickList", 19 | "required": "false", 20 | "defaultValue": "$(System.DefaultWorkingDirectory)", 21 | "displayName": "Working Directory" 22 | } 23 | ], 24 | "dataSourceBindings": [ 25 | { 26 | "dataSourceName": "AzureFunctionAppNamesByAppType", 27 | "endpointParameterName": "azureRmConnection", 28 | "parameters": { 29 | "WebAppKind": "functionApp" 30 | }, 31 | "target": "functionAppName" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | daysUntilStale: 1 2 | 3 | daysUntilClose: 1 4 | 5 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 6 | onlyLabels: [] 7 | 8 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 9 | exemptLabels: [] 10 | 11 | # Set to true to ignore issues in a project (defaults to false) 12 | exemptProjects: false 13 | 14 | # Set to true to ignore issues in a milestone (defaults to false) 15 | exemptMilestones: false 16 | 17 | # Set to true to ignore issues with an assignee (defaults to false) 18 | exemptAssignees: false 19 | 20 | # Label to use when marking as stale 21 | staleLabel: dont-fear-the-reaper 22 | 23 | # Comment to post when marking as stale. Set to `false` to disable 24 | markComment: > 25 | In order to consolidate to fewer feedback channels, we've moved suggestions and 26 | issue reporting to [Developer Community](https://developercommunity.visualstudio.com/spaces/21/index.html). 27 | Sorry for any confusion resulting from this move. 28 | 29 | limitPerRun: 30 30 | 31 | # Limit to only `issues` or `pulls` 32 | only: issues 33 | -------------------------------------------------------------------------------- /templates/asp.net-core-.net-framework.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET Core (.NET Framework) 2 | # Build and test ASP.NET Core projects targeting the full .NET Framework. 3 | # Add steps that publish symbols, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@1 19 | 20 | - task: NuGetCommand@2 21 | inputs: 22 | restoreSolution: '$(solution)' 23 | 24 | - task: VSBuild@1 25 | inputs: 26 | solution: '$(solution)' 27 | msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"' 28 | platform: '$(buildPlatform)' 29 | configuration: '$(buildConfiguration)' 30 | 31 | - task: VSTest@2 32 | inputs: 33 | platform: '$(buildPlatform)' 34 | configuration: '$(buildConfiguration)' 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /templates/go.yml: -------------------------------------------------------------------------------- 1 | # Go 2 | # Build your Go project. 3 | # Add steps that test, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/go 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | 12 | variables: 13 | GOBIN: '$(GOPATH)/bin' # Go binaries path 14 | GOROOT: '/usr/local/go1.11' # Go installation path 15 | GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path 16 | modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code 17 | 18 | steps: 19 | - script: | 20 | mkdir -p '$(GOBIN)' 21 | mkdir -p '$(GOPATH)/pkg' 22 | mkdir -p '$(modulePath)' 23 | shopt -s extglob 24 | shopt -s dotglob 25 | mv !(gopath) '$(modulePath)' 26 | echo '##vso[task.prependpath]$(GOBIN)' 27 | echo '##vso[task.prependpath]$(GOROOT)/bin' 28 | displayName: 'Set up the Go workspace' 29 | 30 | - script: | 31 | go version 32 | go get -v -t -d ./... 33 | if [ -f Gopkg.toml ]; then 34 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 35 | dep ensure 36 | fi 37 | go build -v . 38 | workingDirectory: '$(modulePath)' 39 | displayName: 'Get dependencies, then build' 40 | -------------------------------------------------------------------------------- /templates/docker-container.yml: -------------------------------------------------------------------------------- 1 | # Docker 2 | # Build and push an image to Azure Container Registry 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - {{ branch }} 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | # Container registry service connection established during pipeline creation 13 | dockerRegistryServiceConnection: '{{ containerRegistryConnection.Id }}' 14 | imageRepository: '{{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}}' 15 | containerRegistry: '{{ containerRegistryConnection.Authorization.Parameters.loginServer }}' 16 | dockerfilePath: '{{ dockerfilePath }}' 17 | tag: '$(Build.BuildId)' 18 | 19 | # Agent VM image name 20 | vmImageName: 'ubuntu-latest' 21 | 22 | stages: 23 | - stage: Build 24 | displayName: Build and push stage 25 | jobs: 26 | - job: Build 27 | displayName: Build 28 | pool: 29 | vmImage: $(vmImageName) 30 | steps: 31 | - task: Docker@2 32 | displayName: Build and push an image to container registry 33 | inputs: 34 | command: buildAndPush 35 | repository: $(imageRepository) 36 | dockerfile: $(dockerfilePath) 37 | containerRegistry: $(dockerRegistryServiceConnection) 38 | tags: | 39 | $(tag) 40 | -------------------------------------------------------------------------------- /templates/icons/svg/aks.svg: -------------------------------------------------------------------------------- 1 | Kubernetes -------------------------------------------------------------------------------- /templates/icons/svg/cxx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/icons/svg/html.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/deploy-to-existing-kubernetes-cluster.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "iconName": "aks", 3 | "parameters": [ 4 | { 5 | "name": "k8sResource", 6 | "type": "environmentResource:kubernetes", 7 | "required": "true", 8 | "displayName": "Kubernetes Resource" 9 | }, 10 | { 11 | "name": "containerRegistryConnection", 12 | "type": "endpoint:containerRegistry", 13 | "required": "true", 14 | "displayName": "Container Registry" 15 | }, 16 | { 17 | "name": "imageRepository", 18 | "type": "string", 19 | "required": "true", 20 | "displayName": "Image Name" 21 | }, 22 | { 23 | "name": "servicePort", 24 | "type": "pickList", 25 | "required": "true", 26 | "displayName": "Service Port" 27 | }, 28 | { 29 | "name": "reviewApp", 30 | "type": "boolean", 31 | "displayName": "Enable Review App flow for Pull Requests" 32 | } 33 | ], 34 | "assets": [ 35 | { 36 | "type": "file", 37 | "path": "k8s/deployment.yml", 38 | "destinationPath": "manifests/deployment.yml", 39 | "description": "Kubernetes manifest (deployment)" 40 | }, 41 | { 42 | "type": "file", 43 | "path": "k8s/service.yml", 44 | "destinationPath": "manifests/service.yml", 45 | "description": "Kubernetes manifest (service)" 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /design/yaml-principles.md: -------------------------------------------------------------------------------- 1 | # YAML language principles 2 | 3 | Here are the principles we're using to evaluate YAML suggestions, especially when they affect the base syntax. 4 | These are *very* rough in priority order. 5 | 6 | 1. Make simple things simple, complex things possible. 7 | Consider the new user and how they grow-up / level-up. 8 | Don't forget the deep expert who has to read and write these things day in and day out. 9 | 2. Be opinionated. 10 | Try to do the right thing automatically. 11 | Give a way to bail out if we guess wrong. 12 | 3. Wherever possible, have only one way of doing things. 13 | That way, when customers discover it, they're (by definition) doing it the *right* way. 14 | 4. Follow the principle of least surprise. 15 | If you have to make a decision with no clear "right" answer, consider which way will be less surprising once learned. 16 | That'll be easier to *remember*. 17 | 5. Understand what others chose to do and why. 18 | Our system is different in large and small ways from other CI/CD systems. 19 | This principle *doesn't* mean "just do what \ does". 20 | 6. Our language is terse, moreso for common things. 21 | It's "job", not "jobToRun". 22 | A well-placed adjective can clear up a lot of confusion, though: it's "displayName", not "name". 23 | 24 | ## Notes for internal contributors 25 | 26 | When you want to add new YAML schema to the platform, please write up a functional spec with examples and proposed syntax. 27 | Open a PR into the `design/` directory in this repo. 28 | Send mail to `azpipeyamlreview` with the PR number. 29 | We'll take a look and get back to you with high-quality feedback within 5 business days. 30 | This way we get both public and internal eyes on the proposal before it's implemented. 31 | -------------------------------------------------------------------------------- /templates/icons/svg/azurecontainerregistry.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/icons/svg/gradle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/python-django.yml: -------------------------------------------------------------------------------- 1 | # Python Django 2 | # Test a Django project on multiple versions of Python. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/python 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | pool: 10 | {{ pool }} 11 | strategy: 12 | matrix: 13 | Python35: 14 | PYTHON_VERSION: '3.5' 15 | Python36: 16 | PYTHON_VERSION: '3.6' 17 | Python37: 18 | PYTHON_VERSION: '3.7' 19 | maxParallel: 3 20 | 21 | steps: 22 | - task: UsePythonVersion@0 23 | inputs: 24 | versionSpec: '$(PYTHON_VERSION)' 25 | architecture: 'x64' 26 | 27 | - task: PythonScript@0 28 | displayName: 'Export project path' 29 | inputs: 30 | scriptSource: 'inline' 31 | script: | 32 | """Search all subdirectories for `manage.py`.""" 33 | from glob import iglob 34 | from os import path 35 | # Python >= 3.5 36 | manage_py = next(iglob(path.join('**', 'manage.py'), recursive=True), None) 37 | if not manage_py: 38 | raise SystemExit('Could not find a Django project') 39 | project_location = path.dirname(path.abspath(manage_py)) 40 | print('Found Django project in', project_location) 41 | print('##vso[task.setvariable variable=projectRoot]{}'.format(project_location)) 42 | 43 | - script: | 44 | python -m pip install --upgrade pip setuptools wheel 45 | pip install -r requirements.txt 46 | pip install unittest-xml-reporting 47 | displayName: 'Install prerequisites' 48 | 49 | - script: | 50 | pushd '$(projectRoot)' 51 | python manage.py test --testrunner xmlrunner.extra.djangotestrunner.XMLTestRunner --no-input 52 | displayName: 'Run tests' 53 | 54 | - task: PublishTestResults@2 55 | inputs: 56 | testResultsFiles: "**/TEST-*.xml" 57 | testRunTitle: 'Python $(PYTHON_VERSION)' 58 | condition: succeededOrFailed() 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure Pipelines YAML 2 | 3 | YAML templates, samples, and community interaction for designing [Azure Pipelines](https://docs.microsoft.com/azure/devops/pipelines/). 4 | 5 | We've consolidated issue and suggestion tracking in [Developer Community](https://developercommunity.visualstudio.com/spaces/21/index.html). 6 | This repo will remain for working in the open on YAML pipelines, so feedback on PRs will be the primary way to use it. 7 | You might also want the [docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/?view=azure-devops) or to open a [support ticket](https://azure.microsoft.com/support/devops/). 8 | 9 | - [Templates](templates/) 10 | - [Design docs](design/README.md) 11 | 12 | ## Contributing 13 | 14 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 15 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 16 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 17 | 18 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 19 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 20 | provided by the bot. You will only need to do this once across all repos using our CLA. 21 | 22 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 23 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 24 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 25 | 26 | ## Security issues 27 | 28 | Do you think there might be a security issue with Azure Pipelines? 29 | Have you been phished or identified a security vulnerability? 30 | Please don't report it here - let us know by sending an email to secure@microsoft.com. 31 | -------------------------------------------------------------------------------- /templates/icons/svg/dotnet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/resources/arm/aks.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "clusterName": { 6 | "type": "string" 7 | }, 8 | "clusterLocation": { 9 | "type": "string" 10 | }, 11 | "servicePrincipalId": { 12 | "type": "securestring" 13 | }, 14 | "servicePrincipalKey": { 15 | "type": "securestring" 16 | }, 17 | "agentCount": { 18 | "defaultValue": 1, 19 | "type": "int" 20 | }, 21 | "agentVMSize": { 22 | "defaultValue": "Standard_D2_v2", 23 | "type": "string" 24 | }, 25 | "registryName": { 26 | "type": "string" 27 | }, 28 | "registryLocation": { 29 | "type": "string" 30 | }, 31 | "registrySku": { 32 | "defaultValue": "Standard", 33 | "type": "string" 34 | } 35 | }, 36 | "variables": {}, 37 | "resources": [ 38 | { 39 | "type": "Microsoft.ContainerRegistry/registries", 40 | "sku": { 41 | "name": "[parameters('registrySku')]" 42 | }, 43 | "name": "[parameters('registryName')]", 44 | "apiVersion": "2017-10-01", 45 | "location": "[parameters('registryLocation')]", 46 | "properties": { 47 | "adminUserEnabled": true 48 | } 49 | }, 50 | { 51 | "apiVersion": "2018-03-31", 52 | "type": "Microsoft.ContainerService/managedClusters", 53 | "location": "[parameters('clusterLocation')]", 54 | "name": "[parameters('clusterName')]", 55 | "dependsOn": [], 56 | "properties": { 57 | "dnsPrefix": "[parameters('clusterName')]", 58 | "agentPoolProfiles": [ 59 | { 60 | "name": "agentpool", 61 | "count": "[parameters('agentCount')]", 62 | "vmSize": "[parameters('agentVMSize')]" 63 | } 64 | ], 65 | "servicePrincipalProfile": { 66 | "clientId": "[parameters('servicePrincipalId')]", 67 | "secret": "[parameters('servicePrincipalKey')]" 68 | } 69 | } 70 | } 71 | ] 72 | } -------------------------------------------------------------------------------- /design/variables.md: -------------------------------------------------------------------------------- 1 | # Variables context and condition simplification 2 | 3 | ## Processing within a file 4 | 5 | Changes: 6 | - Add variables to the context, as they are read (completed) 7 | - Add `else` and `elif` (not completed) 8 | 9 | Example jobs template: 10 | 11 | ```yaml 12 | parameters: 13 | jobName: '' 14 | jobs: 15 | - job: ${{ parameters.jobName }} 16 | variables: 17 | public: ${{ eq(variables['System.TeamProject'], 'public') }} 18 | publicOrPR: ${{ or(eq(variables.public, 'true'), eq(variables['Build.Reason'], 'PullRequest')) }} 19 | steps: 20 | - ${{ if eq(variables.public, 'true') }}: 21 | - script: ./setup-internal-tools.sh 22 | - ${{ else }}: 23 | - script: ./setup-tools.sh 24 | - script: build 25 | - script: test 26 | - ${{ if eq(variables.publicOrPR, 'true') }}: 27 | - script: publish-telemetry 28 | ``` 29 | 30 | Note, attempts to override system variables will fail. 31 | 32 | Note, although variables are added to the context as the are read, they do not flow downstream across files during template compilation. 33 | 34 | ## Reuse variable definitions 35 | 36 | Allow variables to be imported from a file. 37 | 38 | Variables can be imported wherever they can normally be defined. That is, at the root of the pipeline, on a stage, or on a job. 39 | 40 | For example: 41 | 42 | ```yaml 43 | # my-variables.yml 44 | 45 | parameters: 46 | config: debug 47 | variables: 48 | arch: x64 49 | config: ${{ parameters.config }} 50 | sign: ${{ eq(parameters.config, 'debug') }} 51 | publish: ${{ or(eq(parameters.config, 'release'), startsWith(variables['build.sourceBranch'], 'refs/heads/dogfood/')) }} 52 | ``` 53 | 54 | ```yaml 55 | # my-jobs.yml 56 | 57 | parameters: 58 | jobName: '' 59 | jobs: 60 | - job: ${{ parameters.jobName }} 61 | variables: 62 | - template: my-variables.yml # reference variables 63 | parameters: 64 | config: release 65 | steps: 66 | - script: build --config ${{ variables.config }} --arch ${{ variables.arch }} 67 | - ${{ if eq(variables.sign, 'true') }}: 68 | - script: sign 69 | - ${{ if eq(variables.publish, 'true') }}: 70 | - script: publish 71 | ``` 72 | -------------------------------------------------------------------------------- /templates/maven-webapp-to-linux-on-azure.yml: -------------------------------------------------------------------------------- 1 | # Maven package Java project Web App to Linux on Azure 2 | # Build your Java project and deploy it to Azure as a Linux web app 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/java 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | 11 | # Azure Resource Manager connection created during pipeline creation 12 | azureSubscription: '{{ azureRmConnection.Id }}' 13 | 14 | # Web app name 15 | webAppName: '{{ webAppName }}' 16 | 17 | # Environment name 18 | environmentName: '{{ webAppName }}' 19 | 20 | # Agent VM image name 21 | vmImageName: 'ubuntu-latest' 22 | 23 | stages: 24 | - stage: Build 25 | displayName: Build stage 26 | jobs: 27 | - job: MavenPackageAndPublishArtifacts 28 | displayName: Maven Package and Publish Artifacts 29 | pool: 30 | vmImage: $(vmImageName) 31 | 32 | steps: 33 | - task: Maven@3 34 | displayName: 'Maven Package' 35 | inputs: 36 | mavenPomFile: 'pom.xml' 37 | 38 | - task: CopyFiles@2 39 | displayName: 'Copy Files to artifact staging directory' 40 | inputs: 41 | SourceFolder: '$(System.DefaultWorkingDirectory)' 42 | Contents: '**/target/*.?(war|jar)' 43 | TargetFolder: $(Build.ArtifactStagingDirectory) 44 | 45 | - upload: $(Build.ArtifactStagingDirectory) 46 | artifact: drop 47 | 48 | - stage: Deploy 49 | displayName: Deploy stage 50 | dependsOn: Build 51 | condition: succeeded() 52 | jobs: 53 | - deployment: DeployLinuxWebApp 54 | displayName: Deploy Linux Web App 55 | environment: $(environmentName) 56 | pool: 57 | vmImage: $(vmImageName) 58 | strategy: 59 | runOnce: 60 | deploy: 61 | steps: 62 | - task: AzureWebApp@1 63 | displayName: 'Azure Web App Deploy: {{ webAppName }}' 64 | inputs: 65 | azureSubscription: $(azureSubscription) 66 | appType: webAppLinux 67 | appName: $(webAppName) 68 | package: '$(Pipeline.Workspace)/drop/**/target/*.?(war|jar)' 69 | -------------------------------------------------------------------------------- /templates/powershell-functionapp-to-windows-on-azure.yml: -------------------------------------------------------------------------------- 1 | # PowerShell Function App to Windows on Azure 2 | # Build a PowerShell Function App and deploy it to Azure as a Windows function app. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-powershell 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | # Azure Resource Manager connection created during pipeline creation 11 | azureSubscription: '{{ azureRmConnection.Id }}' 12 | 13 | # Function app name 14 | functionAppName: '{{ functionAppName }}' 15 | 16 | # Agent VM image name 17 | vmImageName: 'windows-2019' 18 | 19 | # Working Directory 20 | workingDirectory: '{{ workingDirectory }}' 21 | 22 | stages: 23 | - stage: Build 24 | displayName: Build stage 25 | 26 | jobs: 27 | - job: Build 28 | displayName: Build 29 | pool: 30 | vmImage: $(vmImageName) 31 | 32 | steps: 33 | - powershell: | 34 | if (Test-Path "extensions.csproj") { 35 | dotnet build extensions.csproj --output ./$(workingDirectory)/bin 36 | } 37 | displayName: 'Build extensions' 38 | 39 | - task: ArchiveFiles@2 40 | displayName: 'Archive files' 41 | inputs: 42 | rootFolderOrFile: $(workingDirectory) 43 | includeRootFolder: false 44 | archiveType: zip 45 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 46 | replaceExistingArchive: true 47 | 48 | - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 49 | artifact: drop 50 | 51 | - stage: Deploy 52 | displayName: Deploy stage 53 | dependsOn: Build 54 | condition: succeeded() 55 | 56 | jobs: 57 | - deployment: Deploy 58 | displayName: Deploy 59 | environment: $(functionAppName) 60 | pool: 61 | vmImage: $(vmImageName) 62 | 63 | strategy: 64 | runOnce: 65 | deploy: 66 | 67 | steps: 68 | - task: AzureFunctionApp@1 69 | displayName: 'Azure functions app deploy' 70 | inputs: 71 | azureSubscription: '$(azureSubscription)' 72 | appType: functionApp 73 | appName: $(functionAppName) 74 | package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' -------------------------------------------------------------------------------- /templates/node.js-react-webapp-to-linux-on-azure.yml: -------------------------------------------------------------------------------- 1 | # Node.js React Web App to Linux on Azure 2 | # Build a Node.js React app and deploy it to Azure as a Linux web app. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | 11 | # Azure Resource Manager connection created during pipeline creation 12 | azureSubscription: '{{ azureRmConnection.Id }}' 13 | 14 | # Web app name 15 | webAppName: '{{ webAppName }}' 16 | 17 | # Environment name 18 | environmentName: '{{ webAppName }}' 19 | 20 | # Agent VM image name 21 | vmImageName: 'ubuntu-latest' 22 | 23 | stages: 24 | - stage: Build 25 | displayName: Build stage 26 | jobs: 27 | - job: Build 28 | displayName: Build 29 | pool: 30 | vmImage: $(vmImageName) 31 | 32 | steps: 33 | - task: ArchiveFiles@2 34 | displayName: 'Archive files' 35 | inputs: 36 | rootFolderOrFile: '$(System.DefaultWorkingDirectory)' 37 | includeRootFolder: false 38 | archiveType: zip 39 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 40 | replaceExistingArchive: true 41 | 42 | - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 43 | artifact: drop 44 | 45 | - stage: Deploy 46 | displayName: Deploy stage 47 | dependsOn: Build 48 | condition: succeeded() 49 | jobs: 50 | - deployment: Deploy 51 | displayName: Deploy 52 | environment: $(environmentName) 53 | pool: 54 | vmImage: $(vmImageName) 55 | strategy: 56 | runOnce: 57 | deploy: 58 | steps: 59 | - task: AzureRmWebAppDeployment@4 60 | displayName: 'Azure App Service Deploy: {{ webAppName }}' 61 | inputs: 62 | azureSubscription: $(azureSubscription) 63 | appType: webAppLinux 64 | WebAppName: $(webAppName) 65 | packageForLinux: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' 66 | RuntimeStack: 'NODE|10.10' 67 | StartupCommand: 'npm run start' 68 | ScriptType: 'Inline Script' 69 | InlineScript: | 70 | npm install 71 | npm run build --if-present -------------------------------------------------------------------------------- /design/checkout-path.md: -------------------------------------------------------------------------------- 1 | # Control the checkout location of code 2 | 3 | Customers often wish to control the location of checked-out code. 4 | While we're still designing and working on [multiple checkout](multi-checkout.md), we can make progress on controlling the checkout of `self`. 5 | 6 | Scenarios: 7 | - Previous versions of Go were quite particular about directory structure. 8 | This would let users more easily meet the default expectations, rather than play games with environment variables. 9 | - Some infrastructure makes assumptions about directory structure. 10 | For example, Facebook's Jest repo used to assume the code was in a directory called `jest`. 11 | - Containers. 12 | libgit2's CI pipeline maps sources into a particular directory in the container. 13 | Tools installed in the container expect to find the sources there. 14 | 15 | Dependency: 16 | - ~~Introduction of a new `$(Pipeline.Workspace)` variable which always resolves to the workspace for a particular pipeline.~~ 17 | _Postponed for now -- Build.SourcesDirectory gives the right loation._ See [the pipeline artifacts spec](pipeline-artifacts.md) for the feature which introduces this variable. 18 | 19 | ## Schema 20 | 21 | ```yaml 22 | steps: 23 | - checkout: self 24 | path: string # where to put the repo; always rooted at $(Pipeline.Workspace) 25 | ``` 26 | 27 | `$(Build.SourcesDirectory)` should also point to the actual `self` checkout location. Over time, we'll deprecate the `$(Build.*)` series of variables. 28 | 29 | ### Example 30 | 31 | ```yaml 32 | steps: 33 | - checkout: self 34 | path: PutMyCodeHere # will checkout at $(Pipeline.Workspace)/PutMyCodeHere 35 | - script: ../PutMyCodeHere/build.sh 36 | # default working directory still point to $(Agent.BuildDirectory)/s, 37 | # so the extra ../PutMyCodeHere/ is needed 38 | ``` 39 | 40 | ### Relative vs absolute paths 41 | 42 | Relative paths are supported cross-platform. 43 | Consider `path: foo/src`. 44 | That resolves to `$(Pipeline.Workspace)/foo/src`, which correctly resolves on both Windows and Linux. 45 | 46 | Absolute paths are challenging to support cross-platform in a clean way. 47 | They also make it harder to trust running multiple agents on a single host. 48 | Finally, they can lead to hard-to-debug issues with permissions and cleanup. 49 | The agent's work is supposed to be self-contained, so for our initial implementation, we will not support absolute paths. 50 | -------------------------------------------------------------------------------- /templates/asp.net-core-functionapp-to-windows-on-azure.yml: -------------------------------------------------------------------------------- 1 | # .NET Core Function App to Windows on Azure 2 | # Build a .NET Core function app and deploy it to Azure as a Windows function App. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/dotnet-core 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | # Azure Resource Manager connection created during pipeline creation 11 | azureSubscription: '{{ azureRmConnection.Id }}' 12 | 13 | # Function app name 14 | functionAppName: '{{ functionAppName }}' 15 | 16 | # Agent VM image name 17 | vmImageName: 'vs2017-win2016' 18 | 19 | # Working Directory 20 | workingDirectory: '{{ workingDirectory }}' 21 | 22 | stages: 23 | - stage: Build 24 | displayName: Build stage 25 | 26 | jobs: 27 | - job: Build 28 | displayName: Build 29 | pool: 30 | vmImage: $(vmImageName) 31 | 32 | steps: 33 | - task: DotNetCoreCLI@2 34 | displayName: Build 35 | inputs: 36 | command: 'build' 37 | projects: | 38 | $(workingDirectory)/*.csproj 39 | arguments: --output $(System.DefaultWorkingDirectory)/publish_output --configuration Release 40 | 41 | - task: ArchiveFiles@2 42 | displayName: 'Archive files' 43 | inputs: 44 | rootFolderOrFile: '$(System.DefaultWorkingDirectory)/publish_output' 45 | includeRootFolder: false 46 | archiveType: zip 47 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 48 | replaceExistingArchive: true 49 | 50 | - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 51 | artifact: drop 52 | 53 | - stage: Deploy 54 | displayName: Deploy stage 55 | dependsOn: Build 56 | condition: succeeded() 57 | 58 | jobs: 59 | - deployment: Deploy 60 | displayName: Deploy 61 | environment: 'development' 62 | pool: 63 | vmImage: $(vmImageName) 64 | 65 | strategy: 66 | runOnce: 67 | deploy: 68 | 69 | steps: 70 | - task: AzureFunctionApp@1 71 | displayName: 'Azure functions app deploy' 72 | inputs: 73 | azureSubscription: '$(azureSubscription)' 74 | appType: functionApp 75 | appName: $(functionAppName) 76 | package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' -------------------------------------------------------------------------------- /templates/node.js-express-webapp-to-linux-on-azure.yml: -------------------------------------------------------------------------------- 1 | # Node.js Express Web App to Linux on Azure 2 | # Build a Node.js Express app and deploy it to Azure as a Linux web app. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | 11 | # Azure Resource Manager connection created during pipeline creation 12 | azureSubscription: '{{ azureRmConnection.Id }}' 13 | 14 | # Web app name 15 | webAppName: '{{ webAppName }}' 16 | 17 | # Environment name 18 | environmentName: '{{ webAppName }}' 19 | 20 | # Agent VM image name 21 | vmImageName: 'ubuntu-latest' 22 | 23 | stages: 24 | - stage: Build 25 | displayName: Build stage 26 | jobs: 27 | - job: Build 28 | displayName: Build 29 | pool: 30 | vmImage: $(vmImageName) 31 | 32 | steps: 33 | - task: NodeTool@0 34 | inputs: 35 | versionSpec: '16.x' 36 | displayName: 'Install Node.js' 37 | 38 | - script: | 39 | npm install 40 | npm run build --if-present 41 | npm run test --if-present 42 | displayName: 'npm install, build and test' 43 | 44 | - task: ArchiveFiles@2 45 | displayName: 'Archive files' 46 | inputs: 47 | rootFolderOrFile: '$(System.DefaultWorkingDirectory)' 48 | includeRootFolder: false 49 | archiveType: zip 50 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 51 | replaceExistingArchive: true 52 | 53 | - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 54 | artifact: drop 55 | 56 | - stage: Deploy 57 | displayName: Deploy stage 58 | dependsOn: Build 59 | condition: succeeded() 60 | jobs: 61 | - deployment: Deploy 62 | displayName: Deploy 63 | environment: $(environmentName) 64 | pool: 65 | vmImage: $(vmImageName) 66 | strategy: 67 | runOnce: 68 | deploy: 69 | steps: 70 | - task: AzureWebApp@1 71 | displayName: 'Azure Web App Deploy: {{ webAppName }}' 72 | inputs: 73 | azureSubscription: $(azureSubscription) 74 | appType: webAppLinux 75 | appName: $(webAppName) 76 | runtimeStack: 'NODE|16-lts' 77 | package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip 78 | startUpCommand: 'npm run start' 79 | -------------------------------------------------------------------------------- /templates/node.js-functionapp-to-linux-on-azure.yml: -------------------------------------------------------------------------------- 1 | # Node.js Function App to Linux on Azure 2 | # Build a Node.js function app and deploy it to Azure as a Linux function app. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | 11 | # Azure Resource Manager connection created during pipeline creation 12 | azureSubscription: '{{ azureRmConnection.Id }}' 13 | 14 | # Function app name 15 | functionAppName: '{{ functionAppName }}' 16 | 17 | # Environment name 18 | environmentName: '{{ functionAppName }}' 19 | 20 | # Agent VM image name 21 | vmImageName: 'ubuntu-latest' 22 | 23 | stages: 24 | - stage: Build 25 | displayName: Build stage 26 | jobs: 27 | - job: Build 28 | displayName: Build 29 | pool: 30 | vmImage: $(vmImageName) 31 | 32 | steps: 33 | - task: NodeTool@0 34 | inputs: 35 | versionSpec: '10.x' 36 | displayName: 'Install Node.js' 37 | 38 | - script: | 39 | if [ -f extensions.csproj ] 40 | then 41 | dotnet build extensions.csproj --runtime ubuntu.16.04-x64 --output ./bin 42 | fi 43 | displayName: 'Build extensions' 44 | 45 | - script: | 46 | npm install 47 | npm run build --if-present 48 | npm run test --if-present 49 | displayName: 'Prepare binaries' 50 | 51 | - task: ArchiveFiles@2 52 | displayName: 'Archive files' 53 | inputs: 54 | rootFolderOrFile: '$(System.DefaultWorkingDirectory)' 55 | includeRootFolder: false 56 | archiveType: zip 57 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 58 | replaceExistingArchive: true 59 | 60 | - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 61 | artifact: drop 62 | 63 | - stage: Deploy 64 | displayName: Deploy stage 65 | dependsOn: Build 66 | condition: succeeded() 67 | jobs: 68 | - deployment: Deploy 69 | displayName: Deploy 70 | environment: $(environmentName) 71 | pool: 72 | vmImage: $(vmImageName) 73 | strategy: 74 | runOnce: 75 | deploy: 76 | steps: 77 | - task: AzureFunctionApp@1 78 | displayName: 'Azure Functions App Deploy: {{ functionAppName }}' 79 | inputs: 80 | azureSubscription: '$(azureSubscription)' 81 | appType: functionAppLinux 82 | appName: $(functionAppName) 83 | package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' -------------------------------------------------------------------------------- /templates/python-functionapp-to-linux-on-azure.yml: -------------------------------------------------------------------------------- 1 | # Python Function App to Linux on Azure 2 | # Build a Python function app and deploy it to Azure as a Linux function app. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/python 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | # Azure Resource Manager connection created during pipeline creation 11 | azureSubscription: '{{ azureRmConnection.Id }}' 12 | 13 | # Function app name 14 | functionAppName: '{{ functionAppName }}' 15 | 16 | # Agent VM image name 17 | vmImageName: 'ubuntu-latest' 18 | 19 | # Working Directory 20 | workingDirectory: '{{ workingDirectory }}' 21 | 22 | stages: 23 | - stage: Build 24 | displayName: Build stage 25 | 26 | jobs: 27 | - job: Build 28 | displayName: Build 29 | pool: 30 | vmImage: $(vmImageName) 31 | 32 | steps: 33 | - bash: | 34 | if [ -f extensions.csproj ] 35 | then 36 | dotnet build extensions.csproj --runtime ubuntu.16.04-x64 --output ./bin 37 | fi 38 | workingDirectory: $(workingDirectory) 39 | displayName: 'Build extensions' 40 | 41 | - task: UsePythonVersion@0 42 | displayName: 'Use Python 3.6' 43 | inputs: 44 | versionSpec: 3.6 # Functions V2 supports Python 3.6 as of today 45 | 46 | - bash: | 47 | pip install --target="./.python_packages/lib/site-packages" -r ./requirements.txt 48 | workingDirectory: $(workingDirectory) 49 | displayName: 'Install application dependencies' 50 | 51 | - task: ArchiveFiles@2 52 | displayName: 'Archive files' 53 | inputs: 54 | rootFolderOrFile: '$(workingDirectory)' 55 | includeRootFolder: false 56 | archiveType: zip 57 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 58 | replaceExistingArchive: true 59 | 60 | - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 61 | artifact: drop 62 | 63 | - stage: Deploy 64 | displayName: Deploy stage 65 | dependsOn: Build 66 | condition: succeeded() 67 | 68 | jobs: 69 | - deployment: Deploy 70 | displayName: Deploy 71 | environment: 'development' 72 | pool: 73 | vmImage: $(vmImageName) 74 | 75 | strategy: 76 | runOnce: 77 | deploy: 78 | 79 | steps: 80 | - task: AzureFunctionApp@1 81 | displayName: 'Azure functions app deploy' 82 | inputs: 83 | azureSubscription: '$(azureSubscription)' 84 | appType: functionAppLinux 85 | appName: $(functionAppName) 86 | package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' -------------------------------------------------------------------------------- /templates/php-webapp-to-linux-on-azure.yml: -------------------------------------------------------------------------------- 1 | # PHP as Linux Web App on Azure 2 | # Build, package and deploy your PHP project to Azure Linux Web App. 3 | # Add steps that run tests and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/php 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | # Azure Resource Manager connection created during pipeline creation 11 | azureSubscription: '{{ azureRmServiceConnection.Id }}' 12 | 13 | # Web app name 14 | webAppName: '{{ webAppName }}' 15 | 16 | # Agent VM image name 17 | vmImageName: 'ubuntu-latest' 18 | 19 | # Environment name 20 | environmentName: '{{ webAppName }}' 21 | 22 | # Root folder under which your composer.json file is available. 23 | rootFolder: $(System.DefaultWorkingDirectory) 24 | 25 | stages: 26 | - stage: Build 27 | displayName: Build stage 28 | variables: 29 | phpVersion: '7.3' 30 | jobs: 31 | - job: BuildJob 32 | pool: 33 | vmImage: $(vmImageName) 34 | steps: 35 | - script: | 36 | sudo update-alternatives --set php /usr/bin/php$(phpVersion) 37 | sudo update-alternatives --set phar /usr/bin/phar$(phpVersion) 38 | sudo update-alternatives --set phpdbg /usr/bin/phpdbg$(phpVersion) 39 | sudo update-alternatives --set php-cgi /usr/bin/php-cgi$(phpVersion) 40 | sudo update-alternatives --set phar.phar /usr/bin/phar.phar$(phpVersion) 41 | php -version 42 | workingDirectory: $(rootFolder) 43 | displayName: 'Use PHP version $(phpVersion)' 44 | 45 | - script: composer install --no-interaction --prefer-dist 46 | workingDirectory: $(rootFolder) 47 | displayName: 'Composer install' 48 | 49 | - task: ArchiveFiles@2 50 | displayName: 'Archive files' 51 | inputs: 52 | rootFolderOrFile: '$(rootFolder)' 53 | includeRootFolder: false 54 | archiveType: zip 55 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 56 | replaceExistingArchive: true 57 | 58 | - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 59 | displayName: 'Upload package' 60 | artifact: drop 61 | 62 | - stage: Deploy 63 | displayName: 'Deploy Web App' 64 | dependsOn: Build 65 | condition: succeeded() 66 | jobs: 67 | - deployment: DeploymentJob 68 | pool: 69 | vmImage: $(vmImageName) 70 | environment: $(environmentName) 71 | strategy: 72 | runOnce: 73 | deploy: 74 | steps: 75 | - task: AzureWebApp@1 76 | displayName: 'Deploy Azure Web App : {{ webAppName }}' 77 | inputs: 78 | azureSubscription: $(azureSubscription) 79 | appName: $(webAppName) 80 | package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip -------------------------------------------------------------------------------- /templates/python-to-linux-webapp-on-azure.yml: -------------------------------------------------------------------------------- 1 | # Python to Linux Web App on Azure 2 | # Build your Python project and deploy it to Azure as a Linux Web App. 3 | # Change python version to one thats appropriate for your application. 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/python 5 | 6 | trigger: 7 | - {{ branch }} 8 | 9 | variables: 10 | # Azure Resource Manager connection created during pipeline creation 11 | azureServiceConnectionId: '{{ azureServiceConnection.Id }}' 12 | 13 | # Web app name 14 | webAppName: '{{ webAppName }}' 15 | 16 | # Agent VM image name 17 | vmImageName: 'ubuntu-latest' 18 | 19 | # Environment name 20 | environmentName: '{{ webAppName }}' 21 | 22 | # Project root folder. Point to the folder containing manage.py file. 23 | projectRoot: $(System.DefaultWorkingDirectory) 24 | 25 | # Python version: 3.7 26 | pythonVersion: '3.7' 27 | 28 | stages: 29 | - stage: Build 30 | displayName: Build stage 31 | jobs: 32 | - job: BuildJob 33 | pool: 34 | vmImage: $(vmImageName) 35 | steps: 36 | - task: UsePythonVersion@0 37 | inputs: 38 | versionSpec: '$(pythonVersion)' 39 | displayName: 'Use Python $(pythonVersion)' 40 | 41 | - script: | 42 | python -m venv antenv 43 | source antenv/bin/activate 44 | python -m pip install --upgrade pip 45 | pip install setup 46 | pip install -r requirements.txt 47 | workingDirectory: $(projectRoot) 48 | displayName: "Install requirements" 49 | 50 | - task: ArchiveFiles@2 51 | displayName: 'Archive files' 52 | inputs: 53 | rootFolderOrFile: '$(projectRoot)' 54 | includeRootFolder: false 55 | archiveType: zip 56 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 57 | replaceExistingArchive: true 58 | 59 | - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 60 | displayName: 'Upload package' 61 | artifact: drop 62 | 63 | - stage: Deploy 64 | displayName: 'Deploy Web App' 65 | dependsOn: Build 66 | condition: succeeded() 67 | jobs: 68 | - deployment: DeploymentJob 69 | pool: 70 | vmImage: $(vmImageName) 71 | environment: $(environmentName) 72 | strategy: 73 | runOnce: 74 | deploy: 75 | steps: 76 | 77 | - task: UsePythonVersion@0 78 | inputs: 79 | versionSpec: '$(pythonVersion)' 80 | displayName: 'Use Python version' 81 | 82 | - task: AzureWebApp@1 83 | displayName: 'Deploy Azure Web App : {{ webAppName }}' 84 | inputs: 85 | azureSubscription: $(azureServiceConnectionId) 86 | appName: $(webAppName) 87 | package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /templates/icons/svg/golang.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/icons/svg/xcode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | logo_xcode 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /design/node10-agent-support.md: -------------------------------------------------------------------------------- 1 | # Azure Pipelines agent Node v10 support 2 | 3 | ## Overview 4 | 5 | Currently, the Azure Pipelines agent supports running JavaScript code via the Node handler. This handler runs version 6 of the node runtime, released in April 2016. As of November 2018, Node v6 is under "maintenance" status. 6 | 7 | Because task developers may desire to use features from a newer Node API, and also consume libraries which may no longer support Node v6, the agent will benefit from supporting more up-to-date Node versions. 8 | 9 | Node v10 is the latest version under LTS status. 10 | 11 | ## Architecture 12 | 13 | The agent will support a new handler: Node10. Node10 will syntactically look the same as the existing Node handler, with the only exception that Node10 will end up invoking the Node runtime, version 10. 14 | 15 | We'll add a minimum agent version demand should the task.json require the Node10 handler. 16 | 17 | **task.json sample** 18 | 19 | ``` json 20 | "execution": { 21 | "Node10": { 22 | "target": "myscript.js", 23 | "argumentFormat": "" 24 | } 25 | }, 26 | ``` 27 | 28 | Initially, the agent is going to package both v6 and v10 Node runtimes, and leave the current v6 workflow unaltered. A feature flag `AGENT_USE_NODE10` will be introduced to the Node handler to switch between the v6 and v10 backends. 29 | 30 | The Node v10 switching mechanism will be implemented by the existing `NodeHandler.cs` class. 31 | 32 | ## Rolling out feature 33 | 34 | Rolling out Node v10 support will be executed in three phases: 35 | 36 | 1. New versions of the agents (with node v10) will be deployed to Ring 0. Then, we'll monitor pipeline runs from the build canary, with the feature flag off, and look out for any regressions. This phase is basically equivalent to what we already do for every new build release. 37 | 38 | ``` 39 | "Node10" -> NodeHandler.cs -> Node v10 runtime 40 | 41 | 42 | 43 | (Feature flag off) 44 | "Node" -> NodeHandler.cs -> Node v6 runtime 45 | ``` 46 | 47 | 2. Once we're sure there are no pipeline run regressions, we turn the feature flag on to switch "Node" to the v10 handler in the build canary, and again, look out for any regressions. Simultaneously, we continue deploying the agent to later scale units. Initially with the feature flag off, then turned on. 48 | 49 | ``` 50 | "Node10" -> NodeHandler.cs -> Node v10 runtime 51 | ^ 52 | / 53 | / 54 | (Feature flag on) / 55 | "Node" -> NodeHandler.cs --- Node v6 runtime 56 | ``` 57 | 58 | 3. Once we've turned the feature flag everywhere on hosted, we'll still keep the v6 and v10 runtimes, and the feature flag OFF for the next on-prem release. This is because we don't have control over the tasks that are written against those on-prem environments. After the on-prem release, we'll remove the Node v6 runtime from the agent, in addition to the feature flag. 59 | 60 | ``` 61 | "Node10" -> NodeHandler.cs -> Node v10 runtime 62 | ^ 63 | / 64 | / 65 | (No feature flag) / 66 | "Node" -> NodeHandler.cs --- 67 | ``` 68 | -------------------------------------------------------------------------------- /templates/docker-container-to-acr.yml: -------------------------------------------------------------------------------- 1 | # Docker image and Azure Container Registry 2 | # Build a Docker image and push it to an Azure Container Registry. 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - {{ branch }} 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | # ======================================================================== 13 | # Mandatory variables 14 | # ======================================================================== 15 | 16 | # Update Azure.ResourceGroupName value with Azure resource group name. 17 | Azure.ResourceGroupName: '{{#toAlphaNumericString repositoryName 50}}{{/toAlphaNumericString}}' 18 | 19 | # Update Azure.ServiceConnectionId value with AzureRm service endpoint. 20 | Azure.ServiceConnectionId: '{{ azureServiceConnectionId }}' 21 | 22 | # Update Azure.Location value with Azure Location. 23 | Azure.Location: 'eastus' 24 | 25 | # Update ACR.Name value with ACR name. Please note ACR names should be all lower-case and alphanumeric only. 26 | ACR.Name: '{{#toAlphaNumericString repositoryName 46}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}' 27 | 28 | # ======================================================================== 29 | # Optional variables 30 | # ======================================================================== 31 | 32 | ACR.ImageName: '$(ACR.Name):$(Build.BuildId)' 33 | ACR.FullName: '$(ACR.Name).azurecr.io' 34 | Azure.CreateResources: 'true' # Update Azure.CreateResources to false if you have already created resources like resource group and azure container registry. 35 | System.Debug: 'false' 36 | 37 | jobs: 38 | 39 | - job: CreateResources 40 | displayName: Create required resources 41 | condition: and(succeeded(), eq(variables['Azure.CreateResources'], 'true')) 42 | 43 | pool: 44 | {{ pool }} 45 | 46 | steps: 47 | - task: AzureResourceGroupDeployment@2 48 | displayName: 'Azure Deployment:Create Azure Container Registry' 49 | inputs: 50 | azureSubscription: '$(Azure.ServiceConnectionId)' 51 | resourceGroupName: '$(Azure.ResourceGroupName)' 52 | location: '$(Azure.Location)' 53 | templateLocation: 'URL of the file' 54 | csmFileLink: 'https://raw.githubusercontent.com/Microsoft/azure-pipelines-yaml/master/templates/resources/arm/acr.json' 55 | overrideParameters: '-registryName "$(ACR.Name)" -registryLocation "$(Azure.Location)"' 56 | 57 | - job: BuildImage 58 | displayName: Build 59 | dependsOn: CreateResources 60 | condition: or(succeeded(), ne(variables['Azure.CreateResources'], 'true')) 61 | 62 | pool: 63 | {{ pool }} 64 | 65 | steps: 66 | - task: Docker@1 67 | displayName: 'Build an image' 68 | inputs: 69 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 70 | azureContainerRegistry: '$(ACR.FullName)' 71 | imageName: '$(ACR.ImageName)' 72 | command: build 73 | dockerFile: '**/Dockerfile' 74 | 75 | - task: Docker@1 76 | displayName: 'Push an image' 77 | inputs: 78 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 79 | azureContainerRegistry: '$(ACR.FullName)' 80 | imageName: '$(ACR.ImageName)' 81 | command: push 82 | -------------------------------------------------------------------------------- /templates/icons/svg/android.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/resources/arm/webapp-on-containers.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "webAppName": { 6 | "type": "string" 7 | }, 8 | "hostingPlanName": { 9 | "type": "string" 10 | }, 11 | "sku": { 12 | "defaultValue": "S1 Standard", 13 | "type": "string" 14 | }, 15 | "registryName": { 16 | "type": "string" 17 | }, 18 | "imageName": { 19 | "type": "string" 20 | }, 21 | "registryLocation": { 22 | "type": "string" 23 | }, 24 | "registrySku": { 25 | "defaultValue": "Standard", 26 | "type": "string" 27 | }, 28 | "startupCommand": { 29 | "defaultValue": "", 30 | "type": "string" 31 | } 32 | }, 33 | "resources": [ 34 | { 35 | "type": "Microsoft.Web/sites", 36 | "name": "[parameters('webAppName')]", 37 | "apiVersion": "2018-02-01", 38 | "location": "[resourceGroup().location]", 39 | "properties": { 40 | "name": "[parameters('webAppName')]", 41 | "siteConfig": { 42 | "appSettings": [ 43 | { 44 | "name": "DOCKER_REGISTRY_SERVER_URL", 45 | "value": "[concat('https://', reference(concat('Microsoft.ContainerRegistry/registries/', parameters('registryName'))).loginServer)]" 46 | }, 47 | { 48 | "name": "DOCKER_REGISTRY_SERVER_USERNAME", 49 | "value": "[listCredentials(concat('Microsoft.ContainerRegistry/registries/', parameters('registryName')), '2017-10-01').username]" 50 | }, 51 | { 52 | "name": "DOCKER_REGISTRY_SERVER_PASSWORD", 53 | "value": "[listCredentials(concat('Microsoft.ContainerRegistry/registries/', parameters('registryName')), '2017-10-01').passwords[0].value]" 54 | } 55 | ], 56 | "appCommandLine": "[parameters('startupCommand')]", 57 | "linuxFxVersion": "[concat('DOCKER|', reference(concat('Microsoft.ContainerRegistry/registries/', parameters('registryName'))).loginServer, '/', parameters('imageName'))]" 58 | }, 59 | "serverFarmId": "[concat('/subscriptions/', subscription().subscriptionId,'/resourcegroups/', resourceGroup().name, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" 60 | }, 61 | "dependsOn": [ 62 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", 63 | "[concat('Microsoft.ContainerRegistry/registries/', parameters('registryName'))]" 64 | ] 65 | }, 66 | { 67 | "type": "Microsoft.ContainerRegistry/registries", 68 | "sku": { 69 | "name": "[parameters('registrySku')]" 70 | }, 71 | "name": "[parameters('registryName')]", 72 | "apiVersion": "2017-10-01", 73 | "location": "[parameters('registryLocation')]", 74 | "properties": { 75 | "adminUserEnabled": true 76 | } 77 | }, 78 | { 79 | "type": "Microsoft.Web/serverfarms", 80 | "sku": { 81 | "tier": "[first(skip(split(parameters('sku'), ' '), 1))]", 82 | "name": "[first(split(parameters('sku'), ' '))]" 83 | }, 84 | "kind": "linux", 85 | "name": "[parameters('hostingPlanName')]", 86 | "apiVersion": "2018-02-01", 87 | "location": "[resourceGroup().location]", 88 | "properties": { 89 | "name": "[parameters('hostingPlanName')]" 90 | } 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /templates/icons/svg/ant.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /design/step-target-restricted-mode.md: -------------------------------------------------------------------------------- 1 | # Step target command "restricted mode" 2 | 3 | With the introduction of [step targets](step-target.md), the code running inside a given step can be mostly isolated from the agent host. 4 | Agent logging commands remain a vector for untrusted user code to make permanent changes to its environment. 5 | For instance, `task.setVariable` can alter system variables for the duration of the job. 6 | `artifact.upload` can exfiltrate bits off the machine or poison otherwise-trusted artifacts. 7 | 8 | This spec proposes an extension to step targets allowing them to be placed in "command restricted mode". 9 | In command restricted mode, only a small, approved subset of logging commands are processed. 10 | These are necessary for communicating the status or outcome of a step. 11 | All other commands are excluded. 12 | 13 | ## What gets isolated? 14 | 15 | By default, all commands are denied. 16 | In order to be allowed, a command should meet all of these criteria: 17 | - Command is necessary to update the agent about the current step's progress or state 18 | - There is no safer way to perform the action 19 | - After reasonable threat modeling, we can't see a way for untrusted code to exploit the command 20 | 21 | ## Syntax 22 | 23 | The `target` property of a step will be expanded to allow either a simple string or a mapping. 24 | If it's a string, the string is the target of the step (either `host` or a container resource name) and safe mode is off (as if `commands: any` were specified). 25 | If it's a mapping, the following keys are supported: 26 | 27 | ```yaml 28 | - script: ... 29 | target: 30 | container: string (container name or the word `host`) 31 | commands: enum (one of "restricted" or "any", with "any" the default) 32 | ``` 33 | 34 | ## Example 35 | 36 | ```yaml 37 | resources: 38 | containers: 39 | - container: somecontainer 40 | image: azcr.io/secureazurecontainer:latest 41 | 42 | - script: echo running this step on host 43 | - script: echo running this step in container 44 | target: 45 | container: somecontainer 46 | - script: echo running this step in container, in restricted mode 47 | target: 48 | container: somecontainer 49 | commands: restricted 50 | ``` 51 | 52 | ## Which commands are allowed? 53 | 54 | As of October 2019, the following commands exist. 55 | Commands allowed in restricted mode are marked with ✅: 56 | 57 | | Area | Command | Allowed? | Notes 58 | |------|---------|----------|------ 59 | | `artifact` | `associate` 60 | | `artifact` | `upload` 61 | | `build` | `uploadlog` 62 | | `build` | `uploadsummary` 63 | | `build` | `updatebuildnumber` 64 | | `build` | `addbuildtag` 65 | | `codecoverage` | `publish` 66 | | `codecoverage` | `enable` 67 | | `plugininternal` | `updaterepositorypath` 68 | | `release` | `updatereleasename` | | not needed since `release` plugin isn't loaded for YAML 69 | | `task` | `addattachment` 70 | | `task` | `complete` | ✅ 71 | | `task` | `debug` | ✅ 72 | | `task` | `logdetail` | ✅ 73 | | `task` | `logissue` | ✅ 74 | | `task` | `prependpath` | ✅ 75 | | `task` | `setprogress` | ✅ 76 | | `task` | `setsecret` | ✅ 77 | | `task` | `setvariable` | ✅ | requires future "readonly variables" feature to be secure 78 | | `task` | `settaskvariable` | ✅ | investigating whether this is truly needed 79 | | `task` | `setendpoint` 80 | | `task` | `uploadfile` 81 | | `task` | `uploadsummary` 82 | | `telemetry` | `publish` | ✅ 83 | | `results` | `publish` 84 | 85 | Many of the "publish" commands are probably allowable, but they apply to the job rather than the particular step. 86 | Therefore, given the principles above, they're disallowed in restricted mode. 87 | A follow-up script running outside of safe mode can do any sanitization required and then call the relevant command. 88 | -------------------------------------------------------------------------------- /design/deprecated/container-steps.md: -------------------------------------------------------------------------------- 1 | # Container steps 2 | 3 | Status: **ON HOLD, NOT IMPLEMENTING RIGHT NOW** 4 | 5 | An emerging pattern is running discrete steps of a job in different containers. 6 | Each one carries along a persistent, read/write volume as its workspace. 7 | 8 | An example straight from a customer: 9 | - microsoft/dotnet => build C# code 10 | - yarn => build and bundle JS 11 | - aws/sdk => push static files to S3 12 | - gcloud/sdk => download database files from private buckets, update some GCP stuff 13 | - docker => build and push image for this app 14 | - kubectl => deploy this new image on Kubernetes 15 | - our custom container => do checks and send email/Slack 16 | 17 | Containers steps can be seen as an alternative to tasks. 18 | Instead of writing to our custom task.json system and packaging dependencies into a VSIX, you write to Docker's system and package dependencies in an image. 19 | 20 | ## Solving the file permissions problem 21 | When containers write files to volumes, they're written with the UID of the container user. 22 | This means you either have to be root or running as that UID on the host to access the files. 23 | This will break things like uploading artifacts unless we run that in a container as root. 24 | We'll need agent work to move those operations out to a plugin, which we can then run in a container as root. 25 | 26 | ## Other considerations 27 | - We don't (and can't easily) support Alpine Linux containers at the job level. 28 | There are too many runtime dependencies that it doesn't support. 29 | But, `docker run`ning into an arbitrary container doesn't require any of that infrastructure. 30 | So we'd have a great story for using teeny containers and not feel bad about having some extra requirements on full-job containers. 31 | - Need to support custom workspace mapping, environment variables, and custom UIDs. 32 | - Need to support mapping in the Docker daemon so that `docker` CLI will work as expected. 33 | - Need to support the job running as a container when a container step is used. 34 | 35 | ## Later 36 | - Arbitrary volume mappings 37 | - Boolean to de-privilege the container so it can't call out to the host Docker daemon 38 | - Build and run from a Dockerfile in the repo. 39 | The .NET CLI team would use this; talk to @livarcocc as needed. 40 | Consider these scenarios: 41 | - I have have a tool that I want to define as part of a container but don't want to push it to a registry. 42 | - My deployment tooling is packaged as a container and whenever I run that tooling I want the version of that tooling from the branch. 43 | 44 | ## Challenges 45 | - This will be tricky to support on-premises. 46 | Containers in general rely on access to a Docker registry, and those tend to be cloud-hosted. 47 | Many Azure DevOps Server instances won't have internet connectivity. 48 | 49 | ## YAML syntax 50 | 51 | This is loosely based on `docker` command line syntax. 52 | 53 | ```yaml 54 | resources: 55 | containers: 56 | - image: microsoft/dotnet:latest 57 | name: dotnet 58 | - image: facebook/yarn:latest 59 | name: yarn 60 | 61 | steps: 62 | - run: dotnet 63 | env: 64 | DOTNET_CLI_TELEMETRY_OPTOUT: true # add an environment variable 65 | cmd: build # override the default CMD/ENTRYPOINT in the container 66 | - run: yarn 67 | workspace: /my/custom/workspace # override the default workspace mapping 68 | uid: 1001 # overrides the default user ID that we create 69 | ``` 70 | 71 | If your container image is from DockerHub, you can skip the forward-declaration and use the image name directly. 72 | 73 | ```yaml 74 | - run: microsoft/dotnet:latest 75 | env: 76 | DOTNET_CLI_TELEMETRY_OPTOUT: true # add an environment variable 77 | - run: facebook/yarn:latest 78 | ``` 79 | 80 | If you need a custom Docker registry, you have to go with forward-declarations in the `resources` section. 81 | -------------------------------------------------------------------------------- /design/deployment.md: -------------------------------------------------------------------------------- 1 | # A deployment job 2 | 3 | A "deployment" job is a collection of steps to be run sequentially against the environment. 4 | We recommend specifying deployment steps in a deployment job. 5 | 6 | ## Scenarios 7 | 8 | - Run a set of steps against an environment. Record deployment history and status of the deployments. 9 | - Perform safe deployments. In other words, define how your application is rolled-out. 10 | 11 | 12 | ### Schema 13 | 14 | ```yaml 15 | jobs: 16 | - deployment: string # name of the deployment job, A-Z, a-z, 0-9, and underscore 17 | displayName: string # friendly name to display in the UI 18 | dependsOn: string | [ string ] 19 | environment: string # target environment name and optionally a resource-name to record the deployment history; format: . 20 | condition: string 21 | continueOnError: boolean # 'true' if future jobs should run even if this job fails; defaults to 'false' 22 | pool: pool # see pool schema 23 | timeoutInMinutes: number # how long to run the job before automatically cancelling 24 | cancelTimeoutInMinutes: number # how much time to give 'run always even if cancelled tasks' before killing them 25 | variables: { string: string } | [ variable | variableReference ] 26 | strategy: 27 | runOnce: # default runOnce strategy, useful for running the steps sequentially once against the environment. 28 | deploy: 29 | displayName: string # friendly name to display in the UI 30 | steps: [ script | bash | pwsh | powershell | checkout | task | templateReference ] 31 | ``` 32 | 33 | Following properties are on hold 34 | ```yaml 35 | container: jobContainer 36 | services: jobServices 37 | workspace: jobWorkSpace. 38 | ``` 39 | 40 | ### Example 41 | 42 | ```YAML 43 | jobs: 44 | # track deployments on the environment 45 | - deployment: DeployWeb 46 | displayName: deploy Web App 47 | pool: 48 | vmImage: 'Ubuntu-16.04' 49 | # creates an environment if it doesn't exist 50 | environment: 'smarthotel-dev' 51 | strategy: 52 | # default deployment strategy, more coming... 53 | runOnce: 54 | deploy: 55 | steps: 56 | - script: echo my first deployment 57 | ``` 58 | 59 | In the above example, with each run of this job, deployment history is recorded against the "smarthotel-dev" environment. 60 | 61 | > Note: 62 | > - Currently only Kubernetes resources are supported within an environment, with support for VMs and other resources on the roadmap. 63 | > - It is also possible to create an environment with empty resources and use that as namespace to record deployment history as shown in the example above. 64 | 65 | The following example snippet demonstrates how a pipeline can refer an environment and a resource within the same to be used as the target for a deployment job, 66 | 67 | ```YAML 68 | jobs: 69 | - deployment: DeployWeb 70 | displayName: deploy Web App 71 | pool: 72 | vmImage: 'Ubuntu-16.04' 73 | # records deployment against bookings resource - Kubernetes namespace 74 | environment: 'smarthotel-dev.bookings' 75 | strategy: 76 | runOnce: 77 | deploy: 78 | steps: 79 | # No need to explicitly pass the connection details 80 | - task: KubernetesManifest@0 81 | displayName: Deploy to Kubernetes cluster 82 | inputs: 83 | action: deploy 84 | namespace: $(k8sNamespace) 85 | manifests: | 86 | $(System.ArtifactsDirectory)/manifests/* 87 | imagePullSecrets: | 88 | $(imagePullSecret) 89 | containers: | 90 | $(containerRegistry)/$(imageRepository):$(tag) 91 | ``` 92 | 93 | The above approach has the following benefits - 94 | - Records deployment history on a specific resource within the environment as opposed to recording the history on all resources within the environment. 95 | - Steps in the deployment job **automatically inherit** the connection details of the resource (in this case, a kubernetes namespace: *smarthotel-dev.bookings*) as the deployment job is linked to the environment. 96 | This is particularly useful in the cases where the same connection detail is to be set for multiple steps of the job. 97 | -------------------------------------------------------------------------------- /templates/icons/svg/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/icons/svg/python.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/docker-container-webapp.yml: -------------------------------------------------------------------------------- 1 | # Docker image, Azure Container Registry, and Azure Web App 2 | # Build a Docker image, push it to an Azure Container Registry, and deploy it to an Azure Web App. 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - {{ branch }} 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | # ======================================================================== 13 | # Mandatory variables 14 | # ======================================================================== 15 | 16 | # Update Azure.ResourceGroupName value with Azure resource group name. 17 | Azure.ResourceGroupName: '{{#toAlphaNumericString repositoryName 50}}{{/toAlphaNumericString}}' 18 | 19 | # Update Azure.ServiceConnectionId value with AzureRm service endpoint. 20 | Azure.ServiceConnectionId: '{{ azureServiceConnectionId }}' 21 | 22 | # Update Azure.Location value with Azure Location. 23 | Azure.Location: 'eastus' 24 | 25 | # Update ACR.Name value with ACR name. Please note ACR names should be all lower-case and alphanumeric only. 26 | ACR.Name: '{{#toAlphaNumericString repositoryName 46}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}' 27 | 28 | # Update Web.Name value with a name that identifies your new Web app. Valid characters are a-z, 0-9, and -. 29 | WebApp.Name: '{{#toAlphaNumericString repositoryName 46}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}' 30 | 31 | # Update ServicePlan.Name value with a name of the app service plan. 32 | ServicePlan.Name: '{{#toAlphaNumericString repositoryName 45}}{{/toAlphaNumericString}}-plan' 33 | 34 | # ======================================================================== 35 | # Optional variables 36 | # ======================================================================== 37 | 38 | ACR.ImageName: '$(ACR.Name):$(Build.BuildId)' 39 | ACR.FullName: '$(ACR.Name).azurecr.io' 40 | Azure.CreateResources: 'true' # Update Azure.CreateResources to false if you have already created resources like resource group and azure container registry. 41 | System.Debug: 'false' 42 | 43 | jobs: 44 | 45 | - job: CreateResources 46 | displayName: Create resources 47 | condition: and(succeeded(), eq(variables['Azure.CreateResources'], 'true')) 48 | 49 | pool: 50 | {{ pool }} 51 | 52 | steps: 53 | - task: AzureResourceGroupDeployment@2 54 | displayName: 'Azure Deployment:Create Azure Container Registry, Azure WebApp Service' 55 | inputs: 56 | azureSubscription: '$(Azure.ServiceConnectionId)' 57 | resourceGroupName: '$(Azure.ResourceGroupName)' 58 | location: '$(Azure.Location)' 59 | templateLocation: 'URL of the file' 60 | csmFileLink: 'https://raw.githubusercontent.com/Microsoft/azure-pipelines-yaml/master/templates/resources/arm/webapp-on-containers.json' 61 | overrideParameters: '-registryName "$(ACR.Name)" -registryLocation "$(Azure.Location)" -imageName "$(ACR.ImageName)" -webAppName "$(WebApp.Name)" -hostingPlanName "$(ServicePlan.Name)"' 62 | 63 | - job: BuildImage 64 | displayName: Build 65 | dependsOn: CreateResources 66 | condition: or(succeeded(), ne(variables['Azure.CreateResources'], 'true')) 67 | 68 | pool: 69 | {{ pool }} 70 | 71 | steps: 72 | - task: Docker@1 73 | displayName: 'Build an image' 74 | inputs: 75 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 76 | azureContainerRegistry: '$(ACR.FullName)' 77 | imageName: '$(ACR.ImageName)' 78 | command: build 79 | dockerFile: '**/Dockerfile' 80 | 81 | - task: Docker@1 82 | displayName: 'Push an image' 83 | inputs: 84 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 85 | azureContainerRegistry: '$(ACR.FullName)' 86 | imageName: '$(ACR.ImageName)' 87 | command: push 88 | 89 | - job: DeployApp 90 | displayName: Deploy 91 | dependsOn: BuildImage 92 | condition: succeeded() 93 | 94 | pool: 95 | {{ pool }} 96 | 97 | steps: 98 | - task: AzureWebAppContainer@1 99 | displayName: 'Azure Web App on Container Deploy: $(WebApp.Name)' 100 | inputs: 101 | azureSubscription: '$(Azure.ServiceConnectionId)' 102 | appName: $(WebApp.Name) 103 | imageName: '$(ACR.FullName)/$(ACR.ImageName)' 104 | -------------------------------------------------------------------------------- /templates/docker-container-functionapp.yml: -------------------------------------------------------------------------------- 1 | # Docker image, Azure Container Registry, and Azure Functions app 2 | # Build a Docker image, push it to an Azure Container Registry, and deploy it to an Azure Functions app. 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - {{ branch }} 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | # ======================================================================== 13 | # Mandatory variables 14 | # ======================================================================== 15 | 16 | # Update Azure.ResourceGroupName value with Azure resource group name. 17 | Azure.ResourceGroupName: '{{#toAlphaNumericString repositoryName 50}}{{/toAlphaNumericString}}' 18 | 19 | # Update Azure.ServiceConnectionId value with AzureRm service endpoint. 20 | Azure.ServiceConnectionId: '{{ azureServiceConnectionId }}' 21 | 22 | # Update Azure.Location value with Azure Location. 23 | Azure.Location: 'eastus' 24 | 25 | # Update ACR.Name value with ACR name. Please note ACR names should be all lower-case and alphanumeric only. 26 | ACR.Name: '{{#toAlphaNumericString repositoryName 46}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}' 27 | 28 | # Update FunctionApp.Name value with a name that identifies your new function app. Valid characters are a-z, 0-9, and -. 29 | FunctionApp.Name: '{{#toAlphaNumericString repositoryName 46}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}' 30 | 31 | # Update StorageAccount.Name value with Storage account name. Storage account names must be between 3 and 24 characters in length and use numbers and lower-case letters only. 32 | StorageAccount.Name: '{{#toAlphaNumericString repositoryName 20}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}' 33 | 34 | # Update ServicePlan.Name value with a name of the app service plan. 35 | ServicePlan.Name: '{{#toAlphaNumericString repositoryName 45}}{{/toAlphaNumericString}}-plan' 36 | 37 | # ======================================================================== 38 | # Optional variables 39 | # ======================================================================== 40 | 41 | ACR.ImageName: '$(ACR.Name):$(Build.BuildId)' 42 | ACR.FullName: '$(ACR.Name).azurecr.io' 43 | ACR.Sku: 'Standard' 44 | Azure.CreateResources: 'true' # Update Azure.CreateResources to false if you have already created resources like resource group and azure container registry. 45 | System.Debug: 'false' 46 | 47 | jobs: 48 | 49 | - job: CreateResources 50 | displayName: Create resources 51 | condition: and(succeeded(), eq(variables['Azure.CreateResources'], 'true')) 52 | 53 | pool: 54 | {{ pool }} 55 | 56 | steps: 57 | - task: AzureResourceGroupDeployment@2 58 | displayName: 'Azure Deployment:Create Azure Container Registry, Azure WebApp Service' 59 | inputs: 60 | azureSubscription: '$(Azure.ServiceConnectionId)' 61 | resourceGroupName: '$(Azure.ResourceGroupName)' 62 | location: '$(Azure.Location)' 63 | templateLocation: 'URL of the file' 64 | csmFileLink: 'https://raw.githubusercontent.com/Microsoft/azure-pipelines-yaml/master/templates/resources/arm/functionapp.json' 65 | overrideParameters: '-registryName "$(ACR.Name)" -registryLocation "$(Azure.Location)" -functionAppName "$(FunctionApp.Name)" -hostingPlanName "$(ServicePlan.Name)" -storageAccountName "$(StorageAccount.Name)"' 66 | 67 | - job: BuildImage 68 | displayName: Build 69 | dependsOn: CreateResources 70 | condition: or(succeeded(), ne(variables['Azure.CreateResources'], 'true')) 71 | 72 | pool: 73 | {{ pool }} 74 | 75 | steps: 76 | - task: Docker@1 77 | displayName: 'Build an image' 78 | inputs: 79 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 80 | azureContainerRegistry: '$(ACR.FullName)' 81 | imageName: '$(ACR.ImageName)' 82 | command: build 83 | dockerFile: '**/Dockerfile' 84 | 85 | - task: Docker@1 86 | displayName: 'Push an image' 87 | inputs: 88 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 89 | azureContainerRegistry: '$(ACR.FullName)' 90 | imageName: '$(ACR.ImageName)' 91 | command: push 92 | 93 | - job: DeployApp 94 | displayName: Deploy 95 | dependsOn: BuildImage 96 | condition: succeeded() 97 | 98 | pool: 99 | {{ pool }} 100 | 101 | steps: 102 | - task: AzureFunctionAppContainer@1 103 | displayName: 'Azure Function App on Container Deploy: $(FunctionApp.Name)' 104 | inputs: 105 | azureSubscription: '$(Azure.ServiceConnectionId)' 106 | appName: $(FunctionApp.Name) 107 | imageName: '$(ACR.FullName)/$(ACR.ImageName)' 108 | -------------------------------------------------------------------------------- /design/deprecated/container-steps-implementation.md: -------------------------------------------------------------------------------- 1 | # Container Steps Implementation 2 | 3 | **Important note** 4 | This design doc exists for historical interest and possible future implementation. 5 | Azure Pipelines currently has no plans to build this feature. 6 | For a workload like this, we currently recommend customers use `- script: docker run ` steps. 7 | 8 | ## Overview and Requirements 9 | 10 | - Container steps should be an abstraction of what the actual technology is used 11 | - We're going to start with Docker containers running on the host 12 | - When we enable a Kubernetes pool provider, this would have to work 13 | - One command, and one container per step 14 | - Don't re-use containers between steps 15 | - Container creation is fast, so use that fact in our favor 16 | - Simpler mental model; user avoids needing to worry about cleaning up resources between steps 17 | - Begin support on Linux. As we continue our internal testing, we'll investigate and test support for Windows Containers 18 | - Sane defaults, but let the user override those defaults to enable more advanced scenarios 19 | 20 | ## Inputs 21 | 22 | |Name|Type|Required|Description| 23 | |---|---|---|---| 24 | |image|string|true|The name of the image to run the command (e.g. ubuntu or microsoft/dotnet-samples:latest)| 25 | |cmd|string[]|false|The command that is passed to the entry point (e.g. build or echo Hello World)| 26 | |entrypoint|string|false|The entry point| 27 | |env|string[]|false|A list of key-value pairs in this format: key=value. Each env var goes in its own line.| 28 | |uid|int|false|The UID used to run the command as (unset: default UID)| 29 | |workspace|string|false|The agent workspace path to map (unset: map to a known, consistent location)| 30 | 31 | ## Agent plugin, or TS task? 32 | 33 | - We can implement the container steps feature in two ways: 34 | - As an agent plugin, in C# 35 | - As a task, in TypeScript 36 | 37 | Both have their advantages and disadvantages. The current prototype is currently being written as an agent plugin, but can later be written as a task if we think it would be better over the long term. 38 | 39 | ## Agent plugin architecture 40 | 41 | - Module: Agent.Plugins 42 | - Namespace: Agent.Plugins.RunInContainer 43 | 44 | |Class Name|Description| 45 | |---|---| 46 | |`RunInContainerPlugin`|Agent plugin which calls the appropriate CLI manager to run a command in a container (for now, Docker)| 47 | |`DockerCliManager`|Helper class, which encapsulates the logic to build the command line to run `docker run` with the right image, environment variables, etc.| 48 | 49 | ## Environment variables 50 | 51 | `Docker run` supports passing in environment variables in two ways: 52 | - Using a set of -e flags (e.g. `-e foo=bar -e foo2=bar2`) 53 | - Saving a list of env vars in a file, and reference it with the --env-file flag (e.g. `--env-file env.list`) 54 | 55 | Using the --env-file would be the preferred option as it avoids two problems: 56 | - Having the risk of exposing secrets in log files 57 | - Running into a command line length limit 58 | 59 | ## Workspace mappings and file permissions 60 | 61 | The user that runs inside the agent may not be the same as the user that runs inside the container. 62 | Given this, the agent infrastructure should take special care with file permissions, to ensure that files created by the agent are accessible from the container, and vice versa. 63 | 64 | One option is to ensure that the root workspace folder is accessible by both the agent user and container user. This requires knowing the UID of both the user running in the agent, and the container in advance. 65 | 66 | The other option is to force the UIDs for both the agent and container to be the same. This would imply running a step as a custom UID is not supported. 67 | 68 | ## \#\#[vso… support 69 | 70 | \#\#[vso… variables are a mechanism for steps to output information back to the agent. Because these variables may contain paths, we want to consider the case where the path representing the resource inside the container is different than the path outside the container, in the agent at the host level. 71 | 72 | - **Don't support \#\#[vso… commands inside the container steps at all** 73 | - Easiest to implement, but most restrictive to user scenarios 74 | - **Support \#\#[vso… commands, but do not attempt path translation** 75 | - Medium complexity, user would need to keep in mind that paths are not being translated if workspace was overridden 76 | - **Support \#\#[vso… commands, and perform path translation** 77 | - Most complicated to implement, and also we can fall into the risk of mis-translating vars that look like paths but they're not in reality 78 | 79 | Tentative suggestion: We can start out by not supporting \#\#[vso… variables, and later on see if adding support is beneficial for our customer workflows. 80 | 81 | ## Resources 82 | 83 | `Docker run`: https://docs.docker.com/engine/reference/commandline/run 84 | -------------------------------------------------------------------------------- /templates/icons/svg/php.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/icons/svg/aspdotnet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /templates/icons/svg/dotnetcore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /design/each-expression.md: -------------------------------------------------------------------------------- 1 | # "Each" Template Expression 2 | 3 | ## Scenarios 4 | 5 | ### Scenario: Wrap steps 6 | 7 | For example, consider the following template: 8 | 9 | ```yaml 10 | parameters: 11 | jobs: [] 12 | 13 | jobs: 14 | - ${{ each job in parameters.jobs }}: # Each job 15 | - job: ${{ job.job }} # Copy the job properties 16 | displayName: ${{ job.displayName }} 17 | ${{ if job.dependsOn }}: 18 | dependsOn: ${{ job.dependsOn }} 19 | ${{ if job.condition }}: 20 | condition: ${{ job.condition }} 21 | ${{ if job.strategy }}: 22 | strategy: ${{ job.strategy }} 23 | ${{ if job.continueOnError }}: 24 | continueOnError: ${{ job.continueOnError }} 25 | ${{ if job.timeoutInMinutes }}: 26 | timeoutInMinutes: ${{ job.timeoutInMinutes }} 27 | ${{ if job.cancelTimeoutInMinutes }}: 28 | cancelTimeoutInMinutes: ${{ job.cancelTimeoutInMinutes }} 29 | ${{ if job.container }}: 30 | container: ${{ job.container }} 31 | ${{ if job.variables }}: 32 | variables: ${{ job.variables }} 33 | ${{ if job.pool }}: 34 | pool: ${{ job.pool }} 35 | ${{ if job.workspace }}: 36 | workspace: ${{ job.workspace }} 37 | steps: # Inject steps 38 | - task: SetupMyBuildTools@1 # Pre steps 39 | - ${{ job.steps }} # Users steps 40 | - task: PublishMyTelemetry@1 # Post steps 41 | condition: always() 42 | ``` 43 | 44 | We can shorten the example above, by leveraging an `each` expression to iterate over the job mapping itself: 45 | 46 | ```yaml 47 | parameters: 48 | jobs: [] 49 | 50 | jobs: 51 | - ${{ each job in parameters.jobs }}: # Each job 52 | - ${{ each pair in job }}: # Insert all properties other than "steps" 53 | ${{ if ne(pair.key, 'steps') }}: 54 | ${{ pair.key }}: ${{ pair.value }} 55 | steps: # Wrap the steps 56 | - task: SetupMyBuildTools@1 # Pre steps 57 | - ${{ job.steps }} # Users steps 58 | - task: PublishMyTelemetry@1 # Post steps 59 | condition: always() 60 | ``` 61 | 62 | ### Scenario: Wrap jobs 63 | 64 | For example, consider the following template: 65 | 66 | ```yaml 67 | parameters: 68 | jobs: [] 69 | 70 | jobs: 71 | - job: CredScan # Cred scan first 72 | pool: MyCredScanPool 73 | steps: 74 | - task: MyCredScanTask@1 75 | - ${{ each job in parameters.jobs }}: # Then each job 76 | - job: ${{ job.job }} 77 | dependsOn: # Inject dependency 78 | - CredScan 79 | - ${{ if job.dependsOn }}: 80 | - ${{ job.dependsOn }} 81 | ${{ if job.displayName }}: # Copy all other job properties 82 | displayName: ${{ job.displayName }} 83 | ${{ if job.condition }}: 84 | condition: ${{ job.condition }} 85 | ${{ if job.strategy }}: 86 | strategy: ${{ job.strategy }} 87 | ${{ if job.continueOnError }}: 88 | continueOnError: ${{ job.continueOnError }} 89 | ${{ if job.timeoutInMinutes }}: 90 | timeoutInMinutes: ${{ job.timeoutInMinutes }} 91 | ${{ if job.cancelTimeoutInMinutes }}: 92 | cancelTimeoutInMinutes: ${{ job.cancelTimeoutInMinutes }} 93 | ${{ if job.container }}: 94 | container: ${{ job.container }} 95 | ${{ if job.variables }}: 96 | variables: ${{ job.variables }} 97 | ${{ if job.pool }}: 98 | pool: ${{ job.pool }} 99 | ${{ if job.workspace }}: 100 | workspace: ${{ job.workspace }} 101 | ${{ if job.steps }}: 102 | steps: ${{ job.steps }} 103 | ``` 104 | 105 | We can shorten the example above, by leveraging an `each` expression to iterate over the job mapping itself: 106 | 107 | ```yaml 108 | parameters: 109 | jobs: [] 110 | 111 | jobs: 112 | - job: CredScan # Cred scan first 113 | pool: MyCredScanPool 114 | steps: 115 | - task: MyCredScanTask@1 116 | - ${{ each job in parameters.jobs }}: # Then each job 117 | - ${{ each pair in job }}: # Insert all properties other than "dependsOn" 118 | ${{ if ne(pair.key, 'dependsOn') }}: 119 | ${{ pair.key }}: ${{ pair.value }} 120 | dependsOn: # Inject dependency 121 | - CredScan 122 | - ${{if job.dependsOn}}: 123 | - ${{ job.dependsOn }} 124 | ``` 125 | 126 | ## Syntax details 127 | 128 | ### Iterative sequence insertion 129 | 130 | Scenarios are listed above. 131 | 132 | Technical syntax example: 133 | 134 | ```yaml 135 | mySequence: 136 | - outer pre 137 | - ${{ each myItem in parameters.myCollection }}: 138 | - nested pre 139 | - ${{ myItem }} 140 | - nested post 141 | - outer post 142 | ``` 143 | 144 | ## Iterative mapping insertion 145 | 146 | A real scenario is detailed above. See the shortened example, for the first scenario. 147 | 148 | Technical syntax example: 149 | 150 | ```yaml 151 | parameters: 152 | myCollection: 153 | - key: myKey1 154 | value: my value 1 155 | - key: myKey2 156 | value: my value 2 157 | myMapping: 158 | outer pre: abc 159 | ${{ each myItem in parameters.myCollection }}: # Each key-value pair in the mapping 160 | pre_${{ myItem.key }}: pre ${{ myItem.value }} 161 | ${{ myItem.key }}: ${{ myItem.Value }} 162 | outer post: def 163 | ``` 164 | -------------------------------------------------------------------------------- /design/readonly-variables.md: -------------------------------------------------------------------------------- 1 | # Read-only variables 2 | 3 | Pipeline variables are little bits of data which help tasks/scripts in a pipeline decide what work to perform. 4 | For example, we make the triggering reason available as a variable called `Build.Reason`. 5 | Variables are exposed to tasks/scripts by making them available as environment variables. 6 | Scripts and tasks can also create new variables or update the contents of existing ones. 7 | 8 | ## The problems with variables 9 | 10 | Creating new variables is not a problem. 11 | Mutating existing ones, however, can be problematic: 12 | - A step can accidentally or maliciously clobber another step's variable, changing future steps' behavior 13 | - Similarly, a system variable can have its contents changed, altering behavior 14 | - Because of the mechanism for setting variables ([logging commands](https://docs.microsoft.com/azure/devops/pipelines/scripts/logging-commands)), using a feature like `set -x` can cause a variable to get its intended contents plus a spurious `'` at the end 15 | - Steps can accidentally or maliciously overwrite important shell variables like `TEMP`, altering the agent's behavior 16 | 17 | We can imagine scenarios where a poorly-secured step in a pipeline could allow for an injection attack against another step / job. 18 | It's important to close this gap as much as possible, without wrecking the ability to pass data between steps. 19 | 20 | ## Solution: make variables readonly 21 | 22 | Many of these problems can be fixed, or at least partially mitigated, by marking some variables as readonly. 23 | In fact, we document that this is the case for system-injected pipeline variables, but don't actually enforce it today. 24 | 25 | A readonly variable can be set once, then its contents cannot be changed using `##vso[task.setVariable]`. 26 | Any attempt to do so generates a warning. 27 | (This is to help with troubleshooting, in case there are tasks/scripts relying on the older behavior.) 28 | The warning will be something like: 29 | > `VariableName` is read-only and can't be changed. 30 | 31 | Azure Pipelines itself can still change variables. 32 | For instance, if the build name is changed using the appropriate logging command, then `Build.BuildNumber` may be changed to reflect that. 33 | 34 | All of the following should be readonly: 35 | - All system variables (whether from the server or agent) - this includes `System.`, `Agent.`, `Build.`, and so on 36 | - Output variables (those set using `isOutput=true`) 37 | - Queue-time variables 38 | - Task- and script-created variables with a new setting, `isReadonly=true` 39 | - YAML-described variables created with a new property, `readonly: true` 40 | 41 | ## Example 42 | 43 | This example YAML shows all the kinds of readonly variables. 44 | 45 | ```yaml 46 | variables: 47 | - name: first 48 | value: one 49 | readonly: true # new syntax marking this variable readonly 50 | 51 | steps: 52 | - script: echo "##vso[task.setVariable variable=second;isReadonly=true]two" 53 | displayName: Set a readonly variable from a script/task 54 | - script: echo "##vso[task.setVariable variable=third;isOutput=true]three" 55 | displayName: Output variables are automatically readonly 56 | - script: echo Another readonly variable is $(Build.SourcesDirectory) 57 | displayName: System variables are readonly 58 | # not pictured: queue-time variables that came from the server 59 | # are also readonly 60 | ``` 61 | 62 | ## Enforcement 63 | 64 | For variables marked readonly, the agent should refuse to update them. 65 | Additionally, if the server is asked to update these variables by the agent, it should also refuse. 66 | 67 | ## Not covered 68 | 69 | This feature does not address the scenario where a pipeline variable overwrites an important shell environment variable (think `TEMP`/`TMP`, `PATH`, etc.). 70 | This feature only covers which pipeline variables are readonly. 71 | A sufficiently paranoid pipeline author could implement a pattern like the following: 72 | 73 | ```yaml 74 | steps: 75 | - script: echo "##vso[task.setVariable variable=PATH;isReadonly=true]$PATH" 76 | displayName: Preserve the initial value of PATH 77 | ``` 78 | 79 | Then all subsequent steps will get the frozen copy of `PATH`. 80 | 81 | A future feature may be needed to address not clobbering existing environment variables. 82 | 83 | ## Safe deployment 84 | 85 | There's not a lot of support or telemetry for detecting how widely our customers depend on current behavior. 86 | Therefore, we need to put this behind a feature flag and roll it out safely. 87 | Ideally this is a server-side feature flag, but if we need an agent-side setting as well, that's acceptable. 88 | 89 | Plan: 90 | - Sprint _X_: When the flag is on, warn in situations where a variable would be overwritten. 91 | Turning off the feature flag turns off this warning (to fix situations where a customer is overwhelmed with warnings). 92 | - Sprint _X+1_: When the flag is on, variables cannot be overwritten. 93 | Turning off the flag turns off the behavior but does _not_ turn off the warning. 94 | - Sprint _X+2_: Remove feature flag. 95 | 96 | ## On-prem considerations 97 | 98 | An older TFS server may be using a newer agent. 99 | The older TFS server knows nothing about readonly variables. 100 | Those variables may remain mutable since the agent is taking its cues from the server. 101 | -------------------------------------------------------------------------------- /design/sidecar-containers.md: -------------------------------------------------------------------------------- 1 | # "Sidecar" or "multi-container" support 2 | 3 | An emerging pattern is the need for a "sidecar container". 4 | Run your build and tests in a container, and they need the support of one or more other containers to complete. 5 | Concrete scenario: Django is a Python-based web framework. 6 | They support 5 different database backends out of the box. 7 | So, a complete run of their test suite needs to happen against Postgres, MySQL, Microsoft SQL Server, Oracle, and MariaDB. 8 | 9 | This is one of the problems that [Docker Compose](https://docs.docker.com/compose/) exists to solve. 10 | We could let the user specify a Docker Compose file directly, but Docker Compose is too general. 11 | We don't need multiple networks, deployment constraints, replicas, and restart policies. 12 | Instead, we embed a mini-config inspired by Docker Compose v3 but without the overly general parts. 13 | 14 | ## Syntax 15 | 16 | This example assumes we introduce inline container resource definitions. 17 | We would also support our existing, separate-resource syntax. 18 | 19 | ```yaml 20 | jobs: 21 | - job: MyJob 22 | pool: 23 | container: 24 | image: python:latest 25 | name: python-builder 26 | 27 | ## Multi-container support here 28 | services: 29 | redis: 30 | image: redis:alpine 31 | ports: 32 | - "6379" 33 | postgres: 34 | image: postgres:9.4 35 | volumes: 36 | - db-data:/var/lib/postgresql/data 37 | env: 38 | FOO: bar 39 | ## End multi-container 40 | 41 | steps: 42 | - script: ... 43 | displayName: Run tests 44 | ``` 45 | We would spin up the sidecar containers and make sure they're networked together with the main `python-builder` container. 46 | 47 | ### Syntax note 48 | 49 | Docker Compose allows a second syntax for environment variables and uses the key `environment`. 50 | For consistency, I elected to stick with what we use everywhere else in Azure Pipelines: `env`. 51 | 52 | ## Requirements 53 | 54 | We support spinning up one or more sidecar containers when the job starts. 55 | The job itself may be in a container as well, or it may be running on the host. 56 | Tasks and scripts must be able to access the network services offered by the sidecar containers. 57 | All the containers that Azure Pipelines knows about will be on a shared Docker network. 58 | Also, we must be able to map in somewhat arbitrary bind mounts. 59 | 60 | ### Networking for container jobs 61 | 62 | A container job can access the other containers by hostname. 63 | Ports specified in the container are automatically exposed. 64 | `redis-cli -h redis ping` should work, assuming there's a container whose resource name is `redis`. 65 | 66 | ### Networking for host jobs 67 | 68 | For a host job, we can't rely on Docker DNS for name resolution. 69 | Customers can specify host:container port maps and then use the host port: 70 | If the portspec is `- "8080:80"`, `curl localhost:8080` should work. 71 | 72 | If multiple agents run on a single host, hard mapping to ports can cause conflicts. 73 | Instead, customers should use ephemeral port maps: `- 6379` which will cause the host to pick a random, unused port number. 74 | We'll provide environment variables which let the host know where to access services. 75 | `redis-cli -h localhost -p $(agent.services.redis.ports.6379) ping` would be expected to work. 76 | 77 | ### Storage 78 | 79 | Of the many volume formats Docker Compose offers, we'll implement three: 80 | - Bind mounts from host: `/opt/data:/var/lib/mysql` 81 | - Randomly-named bind mounts: `/var/lib/mysql` - fewer use cases for this, but no reason to block it 82 | - Named volumes: `datavolume:/var/lib/mysql` - good for sharing data across several containers as well as persisting data on a private agent 83 | 84 | There is no need to implement the "long" syntax. 85 | 86 | ## Industry considerations 87 | 88 | CircleCI has something like this. 89 | ```yaml 90 | version: 2 91 | jobs: 92 | build: 93 | working_directory: ~/mern-starter 94 | # The primary container is an instance of the first image listed. The job's commands run in this container. 95 | docker: 96 | - image: circleci/node:4.8.2-jessie 97 | # The secondary container is an instance of the second listed image which is run in a common network where ports exposed on the primary container are available on localhost. 98 | - image: mongo:3.4.4-jessie 99 | ``` 100 | 101 | Drone.io's config file is ["a superset of Docker Compose"](http://docs.drone.io/getting-started/#configuration), so they get this for free. 102 | ```yaml 103 | pipeline: 104 | build: 105 | image: golang 106 | commands: 107 | - go get 108 | - go build 109 | - go test 110 | 111 | services: 112 | postgres: 113 | image: postgres:9.4.5 114 | environment: 115 | - POSTGRES_USER=myapp 116 | ``` 117 | 118 | ## Other notes 119 | In the future, we could support Docker Compose files directly. 120 | Starting small allows us to constrain the capabilities and retain flexibility. 121 | For simple scenarios, something inline in the pipeline is preferable. 122 | 123 | The need for multiple containers starts to break into deployment scenarios as well. 124 | While this is a pure build'n'test play, we assume the deployment side will also want to read like a Docker Compose file. 125 | This maintains a common vocabulary and fewer distinct concepts. 126 | -------------------------------------------------------------------------------- /templates/deploy-to-existing-kubernetes-cluster.yml: -------------------------------------------------------------------------------- 1 | # Deploy to Azure Kubernetes Service 2 | # Build and push image to Azure Container Registry; Deploy to Azure Kubernetes Service 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - {{ branch }} 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | 13 | # Container registry service connection established during pipeline creation 14 | dockerRegistryServiceConnection: '{{ containerRegistryConnection.Id }}' 15 | imageRepository: '{{#toAlphaNumericString imageRepository 50}}{{/toAlphaNumericString}}' 16 | containerRegistry: '{{ containerRegistryConnection.Authorization.Parameters.loginServer }}' 17 | dockerfilePath: '**/Dockerfile' 18 | tag: '$(Build.BuildId)' 19 | imagePullSecret: '{{#toAlphaNumericString containerRegistryConnection.Name 50}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}-auth' 20 | 21 | # Agent VM image name 22 | vmImageName: 'ubuntu-latest' 23 | 24 | {{#if reviewApp}} 25 | # Name of the new namespace being created to deploy the PR changes. 26 | k8sNamespaceForPR: 'review-app-$(System.PullRequest.PullRequestId)' 27 | {{/if}} 28 | 29 | stages: 30 | - stage: Build 31 | displayName: Build stage 32 | jobs: 33 | - job: Build 34 | displayName: Build 35 | pool: 36 | vmImage: $(vmImageName) 37 | steps: 38 | - task: Docker@2 39 | displayName: Build and push an image to container registry 40 | inputs: 41 | command: buildAndPush 42 | repository: $(imageRepository) 43 | dockerfile: $(dockerfilePath) 44 | containerRegistry: $(dockerRegistryServiceConnection) 45 | tags: | 46 | $(tag) 47 | 48 | - upload: manifests 49 | artifact: manifests 50 | 51 | - stage: Deploy 52 | displayName: Deploy stage 53 | dependsOn: Build 54 | 55 | jobs: 56 | - deployment: Deploy 57 | {{#if reviewApp}} 58 | condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/'))) 59 | {{/if}} 60 | displayName: Deploy 61 | pool: 62 | vmImage: $(vmImageName) 63 | environment: '{{ k8sResource.EnvironmentReference.Name }}.{{ k8sResource.Name }}' 64 | strategy: 65 | runOnce: 66 | deploy: 67 | steps: 68 | - task: KubernetesManifest@0 69 | displayName: Create imagePullSecret 70 | inputs: 71 | action: createSecret 72 | secretName: $(imagePullSecret) 73 | dockerRegistryEndpoint: $(dockerRegistryServiceConnection) 74 | 75 | - task: KubernetesManifest@0 76 | displayName: Deploy to Kubernetes cluster 77 | inputs: 78 | action: deploy 79 | manifests: | 80 | $(Pipeline.Workspace)/manifests/deployment.yml 81 | $(Pipeline.Workspace)/manifests/service.yml 82 | imagePullSecrets: | 83 | $(imagePullSecret) 84 | containers: | 85 | $(containerRegistry)/$(imageRepository):$(tag) 86 | 87 | {{#if reviewApp}} 88 | - deployment: DeployPullRequest 89 | displayName: Deploy Pull request 90 | condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/pull/')) 91 | pool: 92 | vmImage: $(vmImageName) 93 | 94 | environment: '{{ k8sResource.EnvironmentReference.Name }}.$(k8sNamespaceForPR)' 95 | strategy: 96 | runOnce: 97 | deploy: 98 | steps: 99 | - reviewApp: {{ k8sResource.Name }} 100 | 101 | - task: Kubernetes@1 102 | displayName: 'Create a new namespace for the pull request' 103 | inputs: 104 | command: apply 105 | useConfigurationFile: true 106 | inline: '{ "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "$(k8sNamespaceForPR)" }}' 107 | 108 | - task: KubernetesManifest@0 109 | displayName: Create imagePullSecret 110 | inputs: 111 | action: createSecret 112 | secretName: $(imagePullSecret) 113 | namespace: $(k8sNamespaceForPR) 114 | dockerRegistryEndpoint: $(dockerRegistryServiceConnection) 115 | 116 | - task: KubernetesManifest@0 117 | displayName: Deploy to the new namespace in the Kubernetes cluster 118 | inputs: 119 | action: deploy 120 | namespace: $(k8sNamespaceForPR) 121 | manifests: | 122 | $(Pipeline.Workspace)/manifests/deployment.yml 123 | $(Pipeline.Workspace)/manifests/service.yml 124 | imagePullSecrets: | 125 | $(imagePullSecret) 126 | containers: | 127 | $(containerRegistry)/$(imageRepository):$(tag) 128 | 129 | - task: Kubernetes@1 130 | name: get 131 | displayName: 'Get services in the new namespace' 132 | continueOnError: true 133 | inputs: 134 | command: get 135 | namespace: $(k8sNamespaceForPR) 136 | arguments: svc 137 | outputFormat: jsonpath='http://{.items[0].status.loadBalancer.ingress[0].ip}:{.items[0].spec.ports[0].port}' 138 | 139 | # Getting the IP of the deployed service and writing it to a variable for posing comment 140 | - script: | 141 | url="$(get.KubectlOutput)" 142 | message="Your review app has been deployed" 143 | if [ ! -z "$url" -a "$url" != "http://:" ] 144 | then 145 | message="${message} and is available at $url.

[Learn More](https://aka.ms/testwithreviewapps) about how to test and provide feedback for the app." 146 | fi 147 | echo "##vso[task.setvariable variable=GITHUB_COMMENT]$message" 148 | {{/if}} 149 | -------------------------------------------------------------------------------- /design/step-target.md: -------------------------------------------------------------------------------- 1 | # Step Target 2 | 3 | Today, steps run either on the agent host or, if the step is part of a [container job](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops&tabs=yaml), inside of the container specified on the job. 4 | All steps in a job run in one context; separate steps cannot target different contexts throughout the job. 5 | Additionally, steps cannot target [services containers](./sidecar-containers.md) at all. 6 | 7 | The goal of a step target is to give the pipeline author flexibility in where a given step in the job runs. 8 | This provides additional flexibility for pipeline authors and template authors. 9 | 10 | ## Scenarios 11 | 12 | 1. Some of my steps need to run on the host, while others need to run in a job container for isolation purposes. 13 | (For more on isolation, see the [step target "safe mode"](step-target-safe-mode.md) spec.) 14 | 2. I need to run specific steps inside specific service containers for configuration purposes. 15 | 16 | ## YAML syntax 17 | 18 | **Example:** a container job using [services](./sidecar-containers.md) with one step targeting a service container. 19 | 20 | In the example below we see a single job that defined two service containers as well as a job container. 21 | 22 | * The first step in the job targets the `postgres` service container in order to run some sort of configuration script. 23 | * The second step runs in the `python-builder` container that is specified as part of the `container:` property for the job. 24 | * The third step targets the host machine where the agent is running. 25 | 26 | ```yaml 27 | jobs: 28 | - job: MyJob 29 | container: 30 | image: python:latest 31 | name: python-builder 32 | 33 | ## Multi-container support here 34 | services: 35 | redis: 36 | image: redis:alpine 37 | ports: 38 | - "6379" 39 | postgres: 40 | image: postgres:9.4 41 | volumes: 42 | - db-data:/var/lib/postgresql/data 43 | env: 44 | FOO: bar 45 | ## End multi-container 46 | 47 | steps: 48 | - script: ... 49 | displayName: Configure postrgres 50 | target: postgres # targets a service container 51 | - script: ... 52 | displayName: Run tests 53 | # no target, so targets the default (in this case, job container) 54 | - script: ... 55 | target: host # targets the host - another reserved word like "self" 56 | 57 | ``` 58 | 59 | ## A more complicated example 60 | 61 | In this example, we run some steps on the host and others in a container. 62 | The user's template is considered "untrusted" code, so all passed-in steps are rewritten to target the container. 63 | Also, we pre-configure the container so that it has no network access. 64 | 65 | * The `azure-pipelines.yml` pipeline file specifies one job that is derived from a job template. Here we see the consumer of that template passing in an enture job definition including a contaienr and two steps. 66 | * The `jobs-template.yml` loops over all of the parameters in the template and all of the jobs and applies the `job-template.yml` to it. 67 | * In the `job-template.yml` we take all of the parameters and steps from the job and remap them into a job that has a step that targets the host that first removes the network from the job container in order to make sure that when the steps run they do not have access to any external network resources. Then we loop over each step and create new steps from he properties explicilty omitting the `target:` property so they are limited to only targeting the job container. 68 | 69 | ```yaml 70 | ## job-template.yml 71 | parameters: 72 | # Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job 73 | cancelTimeoutInMinutes: '' 74 | condition: '' 75 | continueOnError: false 76 | container: '' 77 | dependsOn: '' 78 | displayName: '' 79 | steps: [] 80 | pool: '' 81 | strategy: '' 82 | timeoutInMinutes: '' 83 | variables: [] 84 | workspace: '' 85 | 86 | - jobs 87 | job: ${{ parameters.name }} 88 | # common jobs parameters 89 | steps: 90 | # remove the docker network from the job container need an easy way to determine the job network and job container 91 | - script: docker network disconnect 92 | target: host # reserved name for the host the worker is running on 93 | 94 | # copy each proerty from each step skipping the target property 95 | - ${{ each step in parameters.steps }}: 96 | ${{ each property in step }}: 97 | ${{ if ne(property.key, 'target') }}: 98 | ${{ property.key }}: ${{ property.value }} 99 | 100 | ## jobs-template.yml 101 | parameters: 102 | jobs: [] 103 | 104 | jobs: 105 | - ${{ each job in parameters.jobs }}: 106 | - template: ./job-template.yml 107 | parameters: 108 | # pass along parameters 109 | ${{ each parameter in parameters }}: 110 | ${{ if ne(parameter.key, 'jobs') }}: 111 | ${{ parameter.key }}: ${{ parameter.value }} 112 | 113 | # pass along job properties 114 | ${{ each property in job }}: 115 | ${{ if ne(property.key, 'job') }}: 116 | ${{ property.key }}: ${{ property.value }} 117 | 118 | name: ${{ job.job }} 119 | 120 | ## azure-pipelines.yml 121 | 122 | resources: 123 | containers: 124 | - container: mycontianer 125 | image: org/mycontainer:stable 126 | 127 | jobs: 128 | - template: /templates/jobs-template.yml 129 | parameters: 130 | jobs: 131 | - job: MyBuild 132 | container: mycontainer 133 | steps: 134 | - script: ... 135 | - script: ... 136 | ``` 137 | 138 | 139 | 140 | ## Future work 141 | 142 | - Need a standard way to get the name of the job network and the job container (probably pipeline variables) 143 | - Service containers need a way to map the `workspace` like the job container does 144 | -------------------------------------------------------------------------------- /templates/docker-container-to-aks.yml: -------------------------------------------------------------------------------- 1 | # Docker image, Azure Container Registry, and Azure Kubernetes Service 2 | # Build a Docker image, push it to an Azure Container Registry, and deploy it to Azure Kubernetes Service. 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - {{ branch }} 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | # ======================================================================== 13 | # Mandatory variables 14 | # ======================================================================== 15 | 16 | # Update Azure.ResourceGroupName value with Azure resource group name. 17 | Azure.ResourceGroupName: '{{#toAlphaNumericString repositoryName 50}}{{/toAlphaNumericString}}' 18 | 19 | # Update Azure.ServiceConnectionId value with AzureRm service endpoint. 20 | Azure.ServiceConnectionId: '{{ azureServiceConnectionId }}' 21 | 22 | # Update Azure.Location value with Azure Location. 23 | Azure.Location: 'eastus' 24 | 25 | # Update ACR.Name value with ACR name. Please note ACR names should be all lower-case and alphanumeric only. 26 | ACR.Name: '{{#toAlphaNumericString repositoryName 46}}{{/toAlphaNumericString}}{{#shortGuid}}{{/shortGuid}}' 27 | 28 | # Update AKS.ClusterName value Azure kubernetes cluster name. 29 | AKS.ClusterName: '{{#toAlphaNumericString repositoryName 32}}{{/toAlphaNumericString}}' 30 | 31 | # Docker Container port 32 | Container.Port: 5000 33 | 34 | # ======================================================================== 35 | # Optional variables 36 | # ======================================================================== 37 | 38 | ACR.RepositoryName: '$(ACR.Name)' 39 | ACR.ImageName: '$(ACR.Name):$(Build.BuildId)' 40 | ACR.FullName: '$(ACR.Name).azurecr.io' 41 | ACR.Sku: 'Standard' 42 | AKS.KubeDeploymentYaml: '$(System.DefaultWorkingDirectory)/KubeDeployment.yml' # Update AKS.KubeDeploymentYaml if you want to use deployment file from repo instead of generated file. 43 | AKS.DeploymentPort: '$(Container.Port)' 44 | Azure.CreateResources: 'true' # Update Azure.CreateResources to false if you have already created resources like resource group, azure container registry and azure kubernetes cluster. 45 | System.Debug: 'false' 46 | 47 | jobs: 48 | 49 | - job: CreateResources 50 | displayName: Create resources 51 | condition: and(succeeded(), eq(variables['Azure.CreateResources'], 'true')) 52 | 53 | pool: 54 | {{ pool }} 55 | 56 | steps: 57 | - task: AzureResourceGroupDeployment@2 58 | displayName: 'Azure Deployment:Create ACR and AKS' 59 | inputs: 60 | azureSubscription: '$(Azure.ServiceConnectionId)' 61 | resourceGroupName: '$(Azure.ResourceGroupName)' 62 | location: '$(Azure.Location)' 63 | templateLocation: 'URL of the file' 64 | addSpnToEnvironment: true 65 | csmFileLink: 'https://raw.githubusercontent.com/Microsoft/azure-pipelines-yaml/master/templates/resources/arm/aks.json' 66 | overrideParameters: '-registryName "$(ACR.Name)" -registryLocation "$(Azure.Location)" -servicePrincipalId $servicePrincipalId -servicePrincipalKey $servicePrincipalKey -clusterName "$(AKS.ClusterName)" -clusterLocation "$(Azure.Location)"' 67 | 68 | - job: BuildImage 69 | displayName: Build 70 | dependsOn: CreateResources 71 | condition: or(succeeded(), ne(variables['Azure.CreateResources'], 'true')) 72 | 73 | pool: 74 | {{ pool }} 75 | 76 | steps: 77 | - task: Docker@1 78 | displayName: 'Build an image' 79 | inputs: 80 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 81 | azureContainerRegistry: '$(ACR.FullName)' 82 | imageName: '$(ACR.ImageName)' 83 | command: build 84 | dockerFile: '**/Dockerfile' 85 | 86 | - task: Docker@1 87 | displayName: 'Push an image' 88 | inputs: 89 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 90 | azureContainerRegistry: '$(ACR.FullName)' 91 | imageName: '$(ACR.ImageName)' 92 | command: push 93 | 94 | - job: DeployApp 95 | displayName: Deploy 96 | dependsOn: BuildImage 97 | condition: succeeded() 98 | 99 | pool: 100 | {{ pool }} 101 | 102 | steps: 103 | - bash: | 104 | if [ -f $(AKS.KubeDeploymentYaml) ]; then 105 | echo "##vso[task.setvariable variable=AKS.KubeDeploymentYamlExists;]true" 106 | else 107 | echo "##vso[task.setvariable variable=AKS.KubeDeploymentYamlExists;]false" 108 | fi 109 | displayName: 'Check kubernetes deployment yaml exists' 110 | 111 | - bash: | 112 | echo "apiVersion : apps/v1beta1 113 | kind: Deployment 114 | metadata: 115 | name: $(ACR.RepositoryName) 116 | spec: 117 | replicas: 1 118 | template: 119 | metadata: 120 | labels: 121 | app: $(ACR.RepositoryName) 122 | spec: 123 | containers: 124 | - name: $(ACR.RepositoryName) 125 | image: $(ACR.FullName)/$(ACR.ImageName) 126 | ports: 127 | - containerPort: $(AKS.DeploymentPort) 128 | --- 129 | apiVersion: v1 130 | kind: Service 131 | metadata: 132 | name: $(ACR.RepositoryName) 133 | spec: 134 | type: LoadBalancer 135 | ports: 136 | - port: $(AKS.DeploymentPort) 137 | selector: 138 | app: $(ACR.RepositoryName)" > $(AKS.KubeDeploymentYaml) 139 | displayName: 'Generate kubernetes deployment yaml' 140 | condition: and(succeeded(), eq(variables['AKS.KubeDeploymentYamlExists'], 'False')) 141 | 142 | - task: Kubernetes@1 143 | displayName: 'kubectl apply' 144 | inputs: 145 | azureSubscriptionEndpoint: '$(Azure.ServiceConnectionId)' 146 | azureResourceGroup: '$(Azure.ResourceGroupName)' 147 | kubernetesCluster: '$(AKS.ClusterName)' 148 | arguments: '-f $(AKS.KubeDeploymentYaml)' 149 | command: 'apply' 150 | -------------------------------------------------------------------------------- /design/deployment-strategies.md: -------------------------------------------------------------------------------- 1 | ## Deployment strategies 2 | 3 | One of the key advantages of continuous delivery of application updates is the ability to quickly push updates into production for specific microservices, giving developers the ability to respond rapidly to changing business requirements. 4 | 5 | With Azure DevOps, environments are first-class constructs, handling the underlying orchestration such as **deployment strategies**, verifying health checks, disabling old version and rolling out new version, traceability down artifact versions are key to continuous value delivery. 6 | 7 | This proposal contains a number of interconnected elements. For example, an [environment](environment.md), a [deployment](deployment.md) job, primitive building blocks, and simplified YAML syntax to enable **safe deployments** in the majority of cases. Our approach to building a deployment orchestration language for **safe deployments** will be to get the fundamental building blocks right. 8 | 9 | **runOnce**: default strategy, if not defined. 10 | 11 | ```yaml 12 | jobs: 13 | - deployment: 14 | environment: musicCarnivalProd 15 | pool: 16 | name: musicCarnivalProdPool 17 | strategy: 18 | runOnce: 19 | deploy: 20 | steps: 21 | - script: echo deploy web app... 22 | ``` 23 | 24 | When deploying application updates it is important that the technique used to deliver update enables initialization, deploying the update, testing the updated version after routing traffic and in case of failure, run steps to restore to last known good version. We achieve this using fundamental building blocks viz., hooks where you can run your steps during deployment lifecycle. Each of the lifecycle hooks will be resolved into an agent, or a [server](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#server-jobs), (*or a container or validation job in future*). The lifecycle job type would be determined by the `pool` attribute. By default the lifecycle jobs will inherit the pool type specified by the `deployment`. 25 | 26 | Following are descriptions of the lifecycle events where you can run a hook during a deployment lifecycle 27 | 28 | **preDeploy** – Use to run tasks before the deploy step is executed. 29 | 30 | **Deploy** – Use to run the deploy tasks. The results of a lifecycle hook event can trigger a rollback. 31 | 32 | **routeTraffic** – Use to run tasks that serves the traffic to the updated version. The results of a lifecycle hook event can trigger a rollback. 33 | 34 | **postRouteTraffic** - Use to run the tasks after the traffic is routed. Typically these tasks monitor the health of the updated version for defined interval. The results of a lifecycle hook event can trigger a rollback. 35 | 36 | **on: Failure or Success** - Use to run the task to peform rollback actions or clean-up. The results of a lifecycle hook event doesn't trigger a rollback. 37 | 38 | Using the lifecycle hooks, we can achieve complex deployment events such as - 39 | 40 | - Canary 41 | - Rolling 42 | 43 | **Canary**: reduce the risk by slowly rolling out the change to a small subset of users. 44 | As you gain more confidence in the new version, you can start releasing it to more servers in your infrastructure 45 | and routing more users to it. 46 | 47 | Consider the following Azure Pipelines pipeline defined in YAML 48 | 49 | ```yaml 50 | jobs: 51 | - deployment: 52 | environment: musicCarnivalProd 53 | pool: 54 | name: musicCarnivalProdPool 55 | strategy: 56 | canary: 57 | increments: [10,20] 58 | preDeploy: 59 | steps: 60 | - script: initialize, cleanup.... 61 | deploy: 62 | steps: 63 | - script: echo deploy updates... 64 | routeTraffic: 65 | steps: 66 | - script: echo routing traffic... 67 | postRouteTaffic: 68 | pool: server 69 | steps: 70 | - script: echo monitor application health... 71 | on: 72 | failure: 73 | steps: 74 | - script: echo perform rollback actions. 75 | success: 76 | steps: 77 | - script: echo checks passed, notify... 78 | ``` 79 | 80 | 81 | **Rolling**: A rolling deployment replaces instances of the previous version of an application with instances of the new version of the application on a fixed set of machines (rolling set) in each iteration. 82 | 83 | For example, a rolling deployment typically waits for new pods to become ready via a readiness check before scaling down the old components. If a significant issue occurs, the rolling deployment can be aborted. 84 | 85 | ```yaml 86 | jobs: 87 | - deployment: 88 | environment: musicCarnivalProd 89 | pool: 90 | name: musicCarnivalProdPool 91 | strategy: 92 | rolling: 93 | maxParallel: 5 94 | preDeploy: 95 | steps: 96 | - script: echo initialize, cleanup, install certs... 97 | deploy: 98 | steps: 99 | - script: echo deploy web app... 100 | routeTraffic: 101 | steps: 102 | - script: echo swap slots... 103 | postRouteTaffic: 104 | pool: server 105 | steps: 106 | - task: appInsightsAlerts . 107 | on: 108 | failure: 109 | steps: 110 | - script: echo swap slots back.. 111 | success: 112 | steps: 113 | - script: echo checks passed... 114 | ``` 115 | 116 | #### Variables 117 | During execution, the jobs would have access to pre-defined system variables. For example, $(strategy), $(environment.staging), $(environment.prod), $(action), $(increments) etc. 118 | 119 | ### Virtual Machine deployment (for discussion) 120 | 121 | Azure Pipelimes supports **push** using remote script such as SSH and **pull** deployments using local agents to Virtual Machines. The proposal involves supporting **push** based deployments as first class option using remote PowerShell or SSH to Virtual Machines in the environment. 122 | 123 | 124 | --------------------------------------------------------------------------------