{"id":27627,"date":"2025-01-03T08:55:44","date_gmt":"2025-01-03T08:55:44","guid":{"rendered":"https:\/\/cloud-cod.com\/?p=27627"},"modified":"2025-01-12T13:45:18","modified_gmt":"2025-01-12T13:45:18","slug":"azure-devops-pipeline-for-deploying-aviatrix-controller-and-copilot","status":"publish","type":"post","link":"https:\/\/cloud-cod.com\/index.php\/2025\/01\/03\/azure-devops-pipeline-for-deploying-aviatrix-controller-and-copilot\/","title":{"rendered":"Azure DevOps Pipeline with Terraform State Backend in Azure Storage &#8211; Part 1"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"27627\" class=\"elementor elementor-27627\" data-elementor-post-type=\"post\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-73a6791 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"73a6791\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-ed92f02\" data-id=\"ed92f02\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-4af3764 elementor-widget elementor-widget-image\" data-id=\"4af3764\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<style>\/*! elementor - v3.17.0 - 01-11-2023 *\/\n.elementor-widget-image{text-align:center}.elementor-widget-image a{display:inline-block}.elementor-widget-image a img[src$=\".svg\"]{width:48px}.elementor-widget-image img{vertical-align:middle;display:inline-block}<\/style>\t\t\t\t\t\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"567\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-1024x726.png\" class=\"attachment-large size-large wp-image-27805\" alt=\"Azure DevOps Pipeline with Terraform and Azure Storage\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-1024x726.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-300x213.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-768x545.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-1536x1090.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy.png 2013w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-598a8d0 elementor-toc--minimized-on-tablet elementor-widget elementor-widget-table-of-contents\" data-id=\"598a8d0\" data-element_type=\"widget\" data-settings=\"{&quot;exclude_headings_by_selector&quot;:[],&quot;marker_view&quot;:&quot;bullets&quot;,&quot;headings_by_tags&quot;:[&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;],&quot;icon&quot;:{&quot;value&quot;:&quot;fas fa-circle&quot;,&quot;library&quot;:&quot;fa-solid&quot;},&quot;minimize_box&quot;:&quot;yes&quot;,&quot;minimized_on&quot;:&quot;tablet&quot;,&quot;hierarchical_view&quot;:&quot;yes&quot;,&quot;min_height&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;min_height_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;min_height_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}\" data-widget_type=\"table-of-contents.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<style>\/*! elementor-pro - v3.17.0 - 01-11-2023 *\/\n.elementor-widget-table-of-contents .elementor-toc__header-title{color:var(--header-color)}.elementor-widget-table-of-contents.elementor-toc--collapsed .elementor-toc__toggle-button--collapse,.elementor-widget-table-of-contents:not(.elementor-toc--collapsed) .elementor-toc__toggle-button--expand{display:none}.elementor-widget-table-of-contents .elementor-widget-container{min-height:var(--box-min-height);border:var(--box-border-width,1px) solid var(--box-border-color,#9da5ae);border-radius:var(--box-border-radius,3px);background-color:var(--box-background-color);transition:min-height .4s;overflow:hidden}.elementor-toc__header{display:flex;align-items:center;justify-content:space-between;padding:var(--box-padding,20px);background-color:var(--header-background-color);border-bottom:var(--separator-width,1px) solid var(--box-border-color,#9da5ae)}.elementor-toc__header-title{font-size:18px;margin:0;color:var(--header-color)}.elementor-toc__toggle-button{cursor:pointer;display:inline-flex}.elementor-toc__toggle-button i{color:var(--toggle-button-color)}.elementor-toc__toggle-button svg{height:1em;width:1em;fill:var(--toggle-button-color)}.elementor-toc__spinner-container{text-align:center}.elementor-toc__spinner{font-size:2em}.elementor-toc__spinner.e-font-icon-svg{height:1em;width:1em}.elementor-toc__body{padding:var(--box-padding,20px);max-height:var(--toc-body-max-height);overflow-y:auto}.elementor-toc__body::-webkit-scrollbar{width:7px}.elementor-toc__body::-webkit-scrollbar-thumb{background-color:#babfc5;border-radius:10px}.elementor-toc__list-wrapper{list-style:none;padding:0}.elementor-toc__list-item{margin-bottom:.5em}.elementor-toc__list-item.elementor-item-active{font-weight:700}.elementor-toc__list-item .elementor-toc__list-wrapper{margin-top:.5em;margin-left:var(--nested-list-indent,1em)}.elementor-toc__list-item-text:hover{color:var(--item-text-hover-color);-webkit-text-decoration:var(--item-text-hover-decoration);text-decoration:var(--item-text-hover-decoration)}.elementor-toc__list-item-text.elementor-item-active{color:var(--item-text-active-color);-webkit-text-decoration:var(--item-text-active-decoration);text-decoration:var(--item-text-active-decoration)}.elementor-toc__list-item-text-wrapper{display:flex;align-items:center}.elementor-toc__list-item-text-wrapper:before,.elementor-toc__list-item-text-wrapper i{margin-right:8px;color:var(--marker-color)}.elementor-toc__list-item-text-wrapper svg{margin-right:8px;fill:var(--marker-color);height:var(--marker-size,.5em);width:var(--marker-size,.5em)}.elementor-toc__list-item-text-wrapper i{font-size:var(--marker-size,.5em)}.elementor-toc__list-item-text-wrapper:before{font-size:var(--marker-size,1em)}.elementor-toc--content-ellipsis .elementor-toc__list-item-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.elementor-toc__list-items--collapsible>.elementor-toc__list-wrapper>.elementor-toc__list-item>.elementor-toc__list-wrapper{display:none}.elementor-toc__heading-anchor{position:absolute}.elementor-toc__body .elementor-toc__list-item-text{color:var(--item-text-color);-webkit-text-decoration:var(--item-text-decoration);text-decoration:var(--item-text-decoration)}.elementor-toc__body .elementor-toc__list-item-text:hover{color:var(--item-text-hover-color);-webkit-text-decoration:var(--item-text-hover-decoration);text-decoration:var(--item-text-hover-decoration)}.elementor-toc__body .elementor-toc__list-item-text.elementor-item-active{color:var(--item-text-active-color);-webkit-text-decoration:var(--item-text-active-decoration);text-decoration:var(--item-text-active-decoration)}ol.elementor-toc__list-wrapper{counter-reset:item}ol.elementor-toc__list-wrapper .elementor-toc__list-item{counter-increment:item}ol.elementor-toc__list-wrapper .elementor-toc__list-item-text-wrapper:before{content:counters(item,\".\") \". \"}<\/style>\t\t<div class=\"elementor-toc__header\">\n\t\t\t<h5 class=\"elementor-toc__header-title\">\n\t\t\t\tSITEMAP\t\t\t<\/h5>\n\t\t\t\t\t\t\t<div class=\"elementor-toc__toggle-button elementor-toc__toggle-button--expand\" role=\"button\" tabindex=\"0\" aria-controls=\"elementor-toc__598a8d0\" aria-expanded=\"true\" aria-label=\"Open table of contents\"><i aria-hidden=\"true\" class=\"fas fa-chevron-down\"><\/i><\/div>\n\t\t\t\t<div class=\"elementor-toc__toggle-button elementor-toc__toggle-button--collapse\" role=\"button\" tabindex=\"0\" aria-controls=\"elementor-toc__598a8d0\" aria-expanded=\"true\" aria-label=\"Close table of contents\"><i aria-hidden=\"true\" class=\"fas fa-chevron-up\"><\/i><\/div>\n\t\t\t\t\t<\/div>\n\t\t<div id=\"elementor-toc__598a8d0\" class=\"elementor-toc__body\">\n\t\t\t<div class=\"elementor-toc__spinner-container\">\n\t\t\t\t<i class=\"elementor-toc__spinner eicon-animation-spin eicon-loading\" aria-hidden=\"true\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9c967c6 elementor-widget elementor-widget-heading\" data-id=\"9c967c6\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<style>\/*! elementor - v3.17.0 - 01-11-2023 *\/\n.elementor-heading-title{padding:0;margin:0;line-height:1}.elementor-widget-heading .elementor-heading-title[class*=elementor-size-]>a{color:inherit;font-size:inherit;line-height:inherit}.elementor-widget-heading .elementor-heading-title.elementor-size-small{font-size:15px}.elementor-widget-heading .elementor-heading-title.elementor-size-medium{font-size:19px}.elementor-widget-heading .elementor-heading-title.elementor-size-large{font-size:29px}.elementor-widget-heading .elementor-heading-title.elementor-size-xl{font-size:39px}.elementor-widget-heading .elementor-heading-title.elementor-size-xxl{font-size:59px}<\/style><h2 class=\"elementor-heading-title elementor-size-default\">Terraform Backend in Azure Storage<\/h2>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-335a1e5 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"335a1e5\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-3643ea1\" data-id=\"3643ea1\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-e4975c3 elementor-widget elementor-widget-text-editor\" data-id=\"e4975c3\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<style>\/*! elementor - v3.17.0 - 01-11-2023 *\/\n.elementor-widget-text-editor.elementor-drop-cap-view-stacked .elementor-drop-cap{background-color:#69727d;color:#fff}.elementor-widget-text-editor.elementor-drop-cap-view-framed .elementor-drop-cap{color:#69727d;border:3px solid;background-color:transparent}.elementor-widget-text-editor:not(.elementor-drop-cap-view-default) .elementor-drop-cap{margin-top:8px}.elementor-widget-text-editor:not(.elementor-drop-cap-view-default) .elementor-drop-cap-letter{width:1em;height:1em}.elementor-widget-text-editor .elementor-drop-cap{float:left;text-align:center;line-height:1;font-size:50px}.elementor-widget-text-editor .elementor-drop-cap-letter{display:inline-block}<\/style>\t\t\t\t<p>Terraform supports multiple backend options (<strong>local<\/strong> or <strong>remote<\/strong>) for storing the Terraform state file, each with its benefits and drawbacks. Terraform uses a backend called <strong>local<\/strong> by default. The <strong>local<\/strong> backend type stores the state as a local file on disk. <strong>Remote<\/strong> backends refer to any non-local backend, e.g., Terraform Cloud, AWS S3, Azure Storage, Google Cloud Storage, etc.<\/p><p>The article presents the basic pipeline that deploys the<strong> Azure Storage Blob Container<\/strong> to store the remote Terraform Backend. The Azure DevOps pipeline must access the Terraform state file(s). The pipeline could do that in different ways. From a high-level perspective, we could think about public access (over the Internet and leveraging the Public Endpoint) or private access (utilizing the Private Endpoint).<\/p><p>The article focuses on the Public Endpoint protected by the Azure Storage Networking Firewall feature. Other security mechanisms, such as Azure Storage keys, Shared Access Signatures (SAS), or Azure Entra authentication, could also be leveraged, but the article does not cover them.<\/p><p>The article will present how to allow the Azure DevOps Pipeline to connect with the Azure Storage Blob Container using a Private Endpoint and Self-hosted Azure DevOps Agent.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-8c61986 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"8c61986\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-4f69bce\" data-id=\"4f69bce\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-1806fd7 elementor-widget elementor-widget-heading\" data-id=\"1806fd7\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Pre-Task: Azure DevOps Repo and Azure Storage creation<\/h2>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-b4d8342 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"b4d8342\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-1c5d842\" data-id=\"1c5d842\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-bb41aeb elementor-widget elementor-widget-text-editor\" data-id=\"bb41aeb\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>Let&#8217;s start by creating the Azure DevOps Project and Git Repo to host the Pipeline(s) and the Azure Storage Blob Container, which will host our Terraform state file. I am going to use Terraform to do that. The Terraform State file will be placed in Terraform Cloud (you could store the state anywhere or even create all the components &#8220;manually&#8221;).<\/p><p>The structure of my Terraform files:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-328cca8 elementor-widget elementor-widget-image\" data-id=\"328cca8\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/terraform_files_structure-3.png\" data-elementor-open-lightbox=\"no\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"487\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/terraform_files_structure-3-768x487.png\" class=\"attachment-medium_large size-medium_large wp-image-27814\" alt=\"Terraform Files Structure and Azure Resources\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/terraform_files_structure-3-768x487.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/terraform_files_structure-3-300x190.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/terraform_files_structure-3-1024x649.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/terraform_files_structure-3-1536x974.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/terraform_files_structure-3.png 1579w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6a09093 elementor-widget elementor-widget-heading\" data-id=\"6a09093\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">The azure_devops.tf file<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ea4ff5e elementor-widget elementor-widget-text-editor\" data-id=\"ea4ff5e\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The <strong>azure_devops.tf<\/strong> file contains a few resources: Project, Repo, Build, Service Endpoint:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-9e06858 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"9e06858\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-e8cc096\" data-id=\"e8cc096\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-83f2bac elementor-widget elementor-widget-code-highlight\" data-id=\"83f2bac\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp># Create a Project in your Organization\r\n# You must have a Personal Access Token\r\nresource \"azuredevops_project\" \"avx-ctrl-u22-tf\" {\r\n  name               = \"Aviatrix-Ctrl-U22-TF\"\r\n  description        = \"Project that contains Aviatrix Controller\"\r\n  visibility         = \"private\"\r\n  version_control    = \"Git\"\r\n  work_item_template = \"Basic\"\r\n\r\n  features = {\r\n    \"testplans\"    = \"disabled\"\r\n    \"artifacts\"    = \"disabled\"\r\n    \"boards\"       = \"disabled\"\r\n    \"repositories\" = \"enabled\"\r\n    \"pipelines\"    = \"enabled\"\r\n  }\r\n}\r\n\r\n# Repository that contains code for Aviatrix Controller and Copilot\r\nresource \"azuredevops_git_repository\" \"avx-ctrl-u22-tf-gitrepo\" {\r\n  project_id = azuredevops_project.avx-ctrl-u22-tf.id\r\n  name       = \"Aviatrix-Mgmt-Git-Repo\"\r\n  initialization {\r\n    init_type = \"Clean\"\r\n  }\r\n  lifecycle {\r\n    ignore_changes = [\r\n      initialization,\r\n    ]\r\n  }\r\n}\r\n\r\nresource \"azuredevops_build_definition\" \"avx-ctrl-pipeline-deploy\" {\r\n  project_id = azuredevops_project.avx-ctrl-u22-tf.id\r\n  name       = \"avx-ctrl-pipeline-deploy\"\r\n\r\n  ci_trigger {\r\n    use_yaml = true\r\n  }\r\n\r\n  repository {\r\n    repo_type   = \"TfsGit\"\r\n    repo_id     = azuredevops_git_repository.avx-ctrl-u22-tf-gitrepo.id\r\n    branch_name = azuredevops_git_repository.avx-ctrl-u22-tf-gitrepo.default_branch\r\n    yml_path    = \"azure-pipelines.yml\"\r\n  }\r\n\r\n}\r\n\r\nresource \"azuredevops_build_definition\" \"avx-ctrl-pipeline-destroy\" {\r\n  project_id = azuredevops_project.avx-ctrl-u22-tf.id\r\n  name       = \"avx-ctrl-pipeline-destroy\"\r\n\r\n  ci_trigger {\r\n    use_yaml = true\r\n  }\r\n\r\n  repository {\r\n    repo_type   = \"TfsGit\"\r\n    repo_id     = azuredevops_git_repository.avx-ctrl-u22-tf-gitrepo.id\r\n    branch_name = azuredevops_git_repository.avx-ctrl-u22-tf-gitrepo.default_branch\r\n    yml_path    = \"azure-pipelines-destroy.yml\"\r\n  }\r\n}\r\n\r\nresource \"azuredevops_serviceendpoint_azurerm\" \"avx-ado-svcendpoint-azurerm\" {\r\n  project_id                             = azuredevops_project.avx-ctrl-u22-tf.id\r\n  service_endpoint_name                  = \"AzureRM Service Endpoint for Aviatrix deployment\"\r\n  service_endpoint_authentication_scheme = \"ServicePrincipal\"\r\n  azurerm_spn_tenantid                   = \"<Your-Tenant-ID>\"\r\n  azurerm_subscription_id                = \"<Your-Subscription-ID>\"\r\n  azurerm_subscription_name              = \"<Your-Subscription-Name>\"\r\n}\r\n\r\nresource \"azuredevops_resource_authorization\" \"avx-ado-resource-auth\" {\r\n  project_id  = azuredevops_project.avx-ctrl-u22-tf.id\r\n  resource_id = azuredevops_serviceendpoint_azurerm.avx-ado-svcendpoint-azurerm.id\r\n  authorized  = true\r\n}\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-374867d elementor-widget elementor-widget-text-editor\" data-id=\"374867d\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The new Project with Repos and Pipelines enabled:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-edf5f34 elementor-widget elementor-widget-image\" data-id=\"edf5f34\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_project-1.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure DevOps Project\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc2NTAsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfZGV2b3BzX3Byb2plY3QtMS5wbmcifQ%3D%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"379\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_project-1-768x379.png\" class=\"attachment-medium_large size-medium_large wp-image-27650\" alt=\"Azure DevOps Project\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_project-1-768x379.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_project-1-300x148.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_project-1-1024x506.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_project-1.png 1166w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-039d6d6 elementor-widget elementor-widget-text-editor\" data-id=\"039d6d6\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The Service Endpoint is there:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-07d327d elementor-widget elementor-widget-image\" data-id=\"07d327d\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_service_endpoint.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure DevOps Service Endpoint\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc2NDgsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfZGV2b3BzX3NlcnZpY2VfZW5kcG9pbnQucG5nIn0%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"555\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_service_endpoint-768x555.png\" class=\"attachment-medium_large size-medium_large wp-image-27648\" alt=\"Azure DevOps Service Endpoint\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_service_endpoint-768x555.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_service_endpoint-300x217.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_service_endpoint-1024x740.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_service_endpoint.png 1319w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-011ff15 elementor-widget elementor-widget-text-editor\" data-id=\"011ff15\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>Please note that when you create a new Project, Azure DevOps will automatically create a Repo (with the Project name) for you. This is the reason we can see two Repos (the one created automatically and the one created by use through TF):<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9568a47 elementor-widget elementor-widget-image\" data-id=\"9568a47\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repos.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure DevOps Repos\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc2NDksInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfZGV2b3BzX3JlcG9zLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"206\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repos-768x206.png\" class=\"attachment-medium_large size-medium_large wp-image-27649\" alt=\"Azure DevOps Repos\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repos-768x206.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repos-300x81.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repos-1024x275.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repos-1536x412.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repos.png 1852w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1add2d6 elementor-widget elementor-widget-heading\" data-id=\"1add2d6\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">The azure_storage.tf file<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8328871 elementor-widget elementor-widget-text-editor\" data-id=\"8328871\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The <strong>azure_storage.tf<\/strong> file contains resources for the Resource Group, Storage Account, and the Container. For now, I will restrict access to the Storage Account only to my Public IP address (I do not want my Terraform State file to be Public).<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-afc29f3 elementor-widget elementor-widget-code-highlight\" data-id=\"afc29f3\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp># Create a Storage Account Container for Azure DevOpS Pipeline TF backend\r\nresource \"azurerm_resource_group\" \"avx-mgmt-tf-storage-rg\" {\r\n  name     = \"avx-mgmt-storage-rg\"\r\n  location = \"westeurope\"\r\n}\r\n\r\nresource \"azurerm_storage_account\" \"avx-mgmt-tf-storage-account\" {\r\n  name                     = \"avxmgmttfstorageaccount\"  # name can only consist of lowercase letters and numbers, and must be between 3 and 24 characters long\r\n  resource_group_name      = azurerm_resource_group.avx-mgmt-tf-storage-rg.name\r\n  location                 = \"westeurope\"\r\n  account_tier             = \"Standard\"\r\n  account_replication_type = \"LRS\"   # LRS - Locally Redundant Storage is ok for Test\/Non-Prod Storage Account\r\n  \r\n  network_rules {\r\n    default_action             = \"Deny\"\r\n    ip_rules = [\"<your-Public-IPs>\"]\r\n  }\r\n}\r\n\r\nresource \"azurerm_storage_container\" \"avx-mgmt-tf-storage-container\" {\r\n  name                 = \"avx-mgmt-tf-storage-container\"\r\n  storage_account_id   = azurerm_storage_account.avx-mgmt-tf-storage-account.id\r\n}<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-d3a21d0 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"d3a21d0\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-6230000\" data-id=\"6230000\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-137d69a elementor-widget elementor-widget-text-editor\" data-id=\"137d69a\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The result:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8d92a26 elementor-widget elementor-widget-image\" data-id=\"8d92a26\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_container_network_rules-1.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure Storage Network Rules\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc4MjEsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfc3RvcmFnZV9jb250YWluZXJfbmV0d29ya19ydWxlcy0xLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"532\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_container_network_rules-1-768x532.png\" class=\"attachment-medium_large size-medium_large wp-image-27821\" alt=\"Azure Storage Network Rules\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_container_network_rules-1-768x532.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_container_network_rules-1-300x208.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_container_network_rules-1-1024x709.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_container_network_rules-1-1536x1064.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_container_network_rules-1-2048x1418.png 2048w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-23392bf elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"23392bf\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-ef7817d\" data-id=\"ef7817d\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-40b88c3 elementor-widget elementor-widget-heading\" data-id=\"40b88c3\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">The provider.tf, variables.tf, and versions.tf files<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-698de42 elementor-widget elementor-widget-text-editor\" data-id=\"698de42\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The content of the remaining files is presented below.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ceca3b3 elementor-widget elementor-widget-text-editor\" data-id=\"ceca3b3\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The providers.tf file:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-36bb116 elementor-widget elementor-widget-code-highlight\" data-id=\"36bb116\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>provider \"azurerm\" {\r\n  features {}\r\n}\r\n\r\nprovider \"azuredevops\" {\r\n  org_service_url = var.ado_org_service_url\r\n}<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-878f760 elementor-widget elementor-widget-text-editor\" data-id=\"878f760\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The variables.tf file (of course, you could use more variables for different DevOps Project and Repo, and Azure Storage arguments):<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e457e61 elementor-widget elementor-widget-code-highlight\" data-id=\"e457e61\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>variable \"ado_org_service_url\" {\r\n  type        = string\r\n  description = \"Organization Service URL\"  \r\n  default     = \"https:\/\/dev.azure.com\/<your-org>\"\r\n}<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-580fa5e elementor-widget elementor-widget-text-editor\" data-id=\"580fa5e\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The versions.tf file:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-423e0ea elementor-widget elementor-widget-code-highlight\" data-id=\"423e0ea\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>terraform { \r\n  required_providers {\r\n    azuredevops = {\r\n      source  = \"microsoft\/azuredevops\"\r\n      version = \">=0.1.0\"\r\n    }\r\n\r\n    azurerm = {\r\n      source  = \"hashicorp\/azurerm\"\r\n      version = \">= 2.39\"\r\n    }\r\n  }\r\n\r\n# Backend for TF STATE FILE - only for Aviatrix Controller\/Copilot\r\n  cloud { \r\n    organization = \"<Your-TF_Org>\" \r\n\r\n    workspaces { \r\n      name = \"<Your-workspace>\" \r\n    } \r\n  } \r\n}<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9ec9857 elementor-widget elementor-widget-heading\" data-id=\"9ec9857\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Azure DevOps Project caveats<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-faa1a6b elementor-widget elementor-widget-text-editor\" data-id=\"faa1a6b\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p><strong>Azure DevOps Project Service connection (endpoint)<\/strong>. We have created the Service endpoint with Autnetication Scheme = Service Principal. If you want to follow the latest recommendation, you can leverage Workload identity federation instead.<\/p><p><strong>Azure DevOps Pipeline Authorization<\/strong> resource. This resource will be deprecated and removed in the future. Please use `azuredevops_pipeline_authorization` instead.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7c7ac54 elementor-widget elementor-widget-heading\" data-id=\"7c7ac54\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Azure Storage caveats<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fadc8e5 elementor-widget elementor-widget-text-editor\" data-id=\"fadc8e5\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p><strong>Storage Account Replication Type<\/strong>. The Storage Account created by our code is of Replication Type = LRS (Locally Redundant Storage). It is enough for Test\/Non-Prod environments. Based on the criticality of the data, availability requirements, and budget considerations you could consider using a different type.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e8bebed elementor-widget elementor-widget-image\" data-id=\"e8bebed\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_comparison_table.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure Storage Replication Types\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc2NjMsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfc3RvcmFnZV9jb21wYXJpc29uX3RhYmxlLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"382\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_comparison_table-768x382.png\" class=\"attachment-medium_large size-medium_large wp-image-27663\" alt=\"Azure Storage Replication Types\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_comparison_table-768x382.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_comparison_table-300x150.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_comparison_table.png 977w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d7d9a44 elementor-widget elementor-widget-text-editor\" data-id=\"d7d9a44\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p><strong>Storage Account Access<\/strong>. The Storage Account we created is accessible from the public internet. We have created the Network Rules to control access and protect it. However, there are some important topics to consider:<\/p><ul><li>Azure DevOps Microsoft-hosted Agents are using different Public IP addresses. Tons of IP addresses. These addresses are specific to Azure geography. Ideally, the Agents should run in the Azure Geography of your Azure DevOps Organization. <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/devops\/pipelines\/agents\/hosted?view=azure-devops&amp;tabs=yaml#networking\">https:\/\/learn.microsoft.com\/en-us\/azure\/devops\/pipelines\/agents\/hosted?view=azure-devops&amp;tabs=yaml#networking<\/a><\/li><li>You can download (there is no API) the list of IP addresses used by Microsoft Services here (the ones used by Azure Agents are under AzureCloud sections): <a href=\"https:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=56519\">https:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=56519<\/a><\/li><\/ul>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-618b379 elementor-widget elementor-widget-image\" data-id=\"618b379\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_agents_fallback.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure DevOps Agents Fallback Info\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc2NjQsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfZGV2b3BzX2FnZW50c19mYWxsYmFjay5wbmcifQ%3D%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"327\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_agents_fallback-768x327.png\" class=\"attachment-medium_large size-medium_large wp-image-27664\" alt=\"Azure DevOps Agents Fallback Info\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_agents_fallback-768x327.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_agents_fallback-300x128.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_agents_fallback-1024x436.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_agents_fallback-1536x653.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_agents_fallback.png 1815w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-13529ef elementor-widget elementor-widget-text-editor\" data-id=\"13529ef\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<ul><li>But it could happen (see the Note above taken from the Microsoft webpage) that your Pipeline will leverage Agents from other Regions due to contention and resource utilization. You could think, &#8220;OK, I will also add IP addresses from other Regions.&#8221; But no, you cannot do that. The Azure Storage Network Firewall is <strong>limited to 400 rules<\/strong> only. If you look at the list of IP addresses, you can see that this limit is OK for one Region, but it is already too low for two Regions. Based on my tests, the Microsoft-hosted Agents used by my Pipeline were assigned the IP addresses from 3 different Azure Regions: 10 times from <strong>North Europe<\/strong>, 6 times from <strong>West Europe<\/strong>, and 3 times from <strong>Central France<\/strong>. Adding hundreds of IP prefixes to the Network Firewall might not always be a valid solution.<\/li><\/ul><p>Some other solutions could be:<\/p><ul><li>Get the Microsoft-hosted Agent&#8217;s IP every time the Pipeline runs and update the Storage Account Network Firewall rule (please notice that each Pipeline Stage leverages a different Agent, so your Pipeline could use multiple Agents and different Public IPs). &lt;- This is the solution I will present below in this blog article.<\/li><li>Use self-hosted Agents (with a Static\/fixed Public IP). The Storage Account will use a public endpoint.<\/li><li>Use self-hosted Agents and Service Endpoints. The Storage Account still uses a public endpoint, but we limit access to authorized VNETs.<\/li><li>Use self-hosted Agents and Private Endpoints. The Private Endpoint will use a dedicated private IP address in our VNET.<\/li><\/ul>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ad680c0 elementor-widget elementor-widget-heading\" data-id=\"ad680c0\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Creating Azure Pipelines for your environment deployment<\/h2>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5814573 elementor-widget elementor-widget-text-editor\" data-id=\"5814573\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>After applying the code above, we are ready to go with the central part: Azure Pipelines for deploying our infrastructure. My infrastructure is the Aviatrix Controller and CoPilot, created in a dedicated VNET.\u00a0<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-003990b elementor-widget elementor-widget-text-editor\" data-id=\"003990b\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>There are two YAML files with the Azure Pipelines:<\/p><ul><li><strong>azure-pipelines.yml<\/strong> &#8211; the pipeline to create all the resources (Resource Group, VNET, Subnet, NSG, VM instances and so on). It executes terraform init, terraform plan, terraform apply commands.<\/li><li><strong>azure-pipelines-destroy.yml<\/strong> &#8211; the pipeline to destroy the entire infrastructure. It executes the terraform destroy command.<\/li><\/ul><p>The pipeline will use the following Terraform files to create the Resources:<\/p><ul><li><strong>main.tf<\/strong> &#8211; creates Resource Groups, VNET, Subnet, NSGs (one for the Aviatrix Controller and the other one for the Aviatrix CoPilot), Public IPs (one for the Controller and the other one for the CoPilot), NICs, NIC-associations, Azure Linux VM instances, managed disk and managed disk association for the Aviatrix CoPilot.<\/li><li><strong>outputs.tf<\/strong> &#8211; lists the Public and Private IPs assigned to the Aviatrix Controller and CoPilot VM instances.<\/li><li><strong>providers.tf<\/strong> &#8211; only azurerm provider is used<\/li><li><strong>variables.tf<\/strong> &#8211; defines all the variables<\/li><li><strong>versions.tf<\/strong> &#8211; specifies the Terraform state file to be stored in the Azure Storage Container we created in Part #1<\/li><\/ul><p>Additionally, all the values for the variables are specified within the file:<\/p><ul><li><strong>terraform.auto.tfvars<\/strong><\/li><\/ul>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-896a35b elementor-widget elementor-widget-text-editor\" data-id=\"896a35b\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>You can access my GitHub Repo to get all the files:\u00a0<a href=\"https:\/\/github.com\/JakubD-AVX\/pipeline-terraform-backend-in-azure-storage-firewall-rule\">https:\/\/github.com\/JakubD-AVX\/pipeline-terraform-backend-in-azure-storage-firewall-rule<\/a><\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-97a20a7 elementor-widget elementor-widget-text-editor\" data-id=\"97a20a7\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>This is how your Repo should look like:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3298ec5 elementor-widget elementor-widget-image\" data-id=\"3298ec5\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repo_files.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure DevOps Repo Files\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3MjEsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfZGV2b3BzX3JlcG9fZmlsZXMucG5nIn0%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"393\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repo_files-768x393.png\" class=\"attachment-medium_large size-medium_large wp-image-27721\" alt=\"Azure DevOps Repo Files\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repo_files-768x393.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repo_files-300x154.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repo_files-1024x524.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repo_files-1536x787.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_repo_files-2048x1049.png 2048w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c07969a elementor-widget elementor-widget-text-editor\" data-id=\"c07969a\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>And the Pipelines:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-48ba257 elementor-widget elementor-widget-image\" data-id=\"48ba257\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_pipelines.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure DevOps Pipelines\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3MjUsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfZGV2b3BzX3BpcGVsaW5lcy5wbmcifQ%3D%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"351\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_pipelines-768x351.png\" class=\"attachment-medium_large size-medium_large wp-image-27725\" alt=\"Azure DevOps Pipelines\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_pipelines-768x351.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_pipelines-300x137.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_pipelines-1024x468.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_devops_pipelines.png 1329w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-678b670 elementor-widget elementor-widget-heading\" data-id=\"678b670\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Prerequisite - Subscribe to Azure Marketplace offering<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b1262ae elementor-widget elementor-widget-text-editor\" data-id=\"b1262ae\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>Please remember that to deploy the Aviatrix Controller and the Aviatrix Copilot, you must subscribe to the respective Azure Marketplace offerings. Before running the pipelines please execute the following Azure CLI commands:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-00efd94 elementor-widget elementor-widget-text-editor\" data-id=\"00efd94\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>For the Aviatrix Controller:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5f356fa elementor-widget elementor-widget-code-highlight\" data-id=\"5f356fa\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>az vm image accept-terms --urn aviatrix-systems:aviatrix-controller:aviatrix-controller-g3:latest<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4327482 elementor-widget elementor-widget-text-editor\" data-id=\"4327482\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>For the Aviatrix CoPilot:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-97d73b5 elementor-widget elementor-widget-code-highlight\" data-id=\"97d73b5\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>az vm image accept-terms --urn aviatrix-systems:aviatrix-copilot:avx-cplt-byol-02:latest<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c02e8fa elementor-widget elementor-widget-heading\" data-id=\"c02e8fa\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Azure Deploy-Pipeline Structure<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-778d8d7 elementor-widget elementor-widget-text-editor\" data-id=\"778d8d7\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>There are plenty of Terraform resources deployed by the pipeline:<\/p><ul><li>Resource Group<\/li><li>VNET with Subnet<\/li><li>2x NSGs<\/li><li>2x Public IPs<\/li><li>2x NICs<\/li><li>2x VM instances (Aviatrix Controller and Aviatrix CoPilot)<\/li><li>Managed DIsk (for CoPilot)<\/li><\/ul>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-26d4ff0 elementor-widget elementor-widget-image\" data-id=\"26d4ff0\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/aviatrix_components.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Aviatrix components\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3NTgsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXZpYXRyaXhfY29tcG9uZW50cy5wbmcifQ%3D%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"475\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/aviatrix_components-768x475.png\" class=\"attachment-medium_large size-medium_large wp-image-27758\" alt=\"Aviatrix components\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/aviatrix_components-768x475.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/aviatrix_components-300x186.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/aviatrix_components-1024x634.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/aviatrix_components-1536x950.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/aviatrix_components.png 1794w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0523ce4 elementor-widget elementor-widget-text-editor\" data-id=\"0523ce4\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The Deploy-Pipeline consists of three Stages and several Tasks. Each Stage is run by a different Agent (and different Public IP).<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-97594b4 elementor-widget elementor-widget-image\" data-id=\"97594b4\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/deploy-pipeline-1.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Deploy Pipeline\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3OTMsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvZGVwbG95LXBpcGVsaW5lLTEucG5nIn0%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"558\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/deploy-pipeline-1-768x558.png\" class=\"attachment-medium_large size-medium_large wp-image-27793\" alt=\"Deploy Pipeline\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/deploy-pipeline-1-768x558.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/deploy-pipeline-1-300x218.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/deploy-pipeline-1-1024x744.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/deploy-pipeline-1-1536x1117.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/deploy-pipeline-1.png 1575w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6ba6e41 elementor-widget elementor-widget-text-editor\" data-id=\"6ba6e41\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>There are three things related to Pipeline that are worth discussing:<\/p><ul><li>Adjusting firewall Network Rules (of Azure Storage)<\/li><li>Sleep command used by the pipeline<\/li><li>Manual Approval task<\/li><\/ul>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3eda9c8 elementor-widget elementor-widget-heading\" data-id=\"3eda9c8\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h4 class=\"elementor-heading-title elementor-size-default\">Adjusting Firewall Network Rules for every Public IP used by the Agent<\/h4>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-81918fc elementor-widget elementor-widget-text-editor\" data-id=\"81918fc\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>As mentioned above (in the Azure Storage Caveats section), the Azure Storage that hosts the Terraform State leverages the Azure Storage Public Endpoint. It means it is exposed to the Internet. However, I used Firewall network rules (of the Azure Storage) to allow only the legitimate (specified by me) Public IPs to access my Storage Account Blob Container.<\/p><p>Of course, enabling access for all Public IPs (or having a rule with a 0.0.0.0\/0 IP Range) is not a secure option.<\/p><p>Adding all the Microsoft-hosted Agents&#8217; IP addresses to the list is impossible because there could be only up to 400 rules (and the agents use many more Public IPs).<\/p><p>The solution used by my pipeline is to <strong>adjust the list with the Agent&#8217;s IP address every time the Tasks of a specific Stage start and remove the IP as the last Task of the Stage<\/strong>. It is necessary to update the Network Rules for Azure Storage Firewall for every Stage because each Stage is executed by a different Agent (with a different Public IP address).<\/p><p>The task that modifies the Firewall Network Rules:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-513829e elementor-widget elementor-widget-code-highlight\" data-id=\"513829e\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>- task: AzureCLI@2\r\n            displayName: addFirewallRule\r\n            inputs:\r\n              azureSubscription: 'AzureRM Service Endpoint for Aviatrix deployment'\r\n              scriptType: 'bash'\r\n              scriptLocation: 'inlineScript'\r\n              inlineScript: |\r\n                AGENT_IP=$(curl https:\/\/api.ipify.org\/?format=json)\r\n                GET_IP=$(echo $AGENT_IP | jq -r '.ip')\r\n                echo \"The IP address used by the Agent is  $GET_IP\"\r\n                az storage account network-rule add -g $(bckstorageresourcegroup) --account-name $(bckstorage) --ip-address $GET_IP\r\n                echo \"Updating Firewall rule with IP $GET_IP and waiting for 25 seconds\"\r\n                sleep 25\r\n                echo \"Wait-period has been finished. Resuming...\"<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0acac43 elementor-widget elementor-widget-text-editor\" data-id=\"0acac43\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The output that shows the Public IP used by the Agent:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-73b331a elementor-widget elementor-widget-image\" data-id=\"73b331a\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_getip-1.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Task GET-IP\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3OTcsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvdGFza19nZXRpcC0xLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"299\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_getip-1-768x299.png\" class=\"attachment-medium_large size-medium_large wp-image-27797\" alt=\"Task GET-IP\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_getip-1-768x299.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_getip-1-300x117.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_getip-1-1024x399.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_getip-1-1536x598.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_getip-1.png 1539w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-04b40b1 elementor-widget elementor-widget-text-editor\" data-id=\"04b40b1\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The output when the Network Rule gets added:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-85d993b elementor-widget elementor-widget-image\" data-id=\"85d993b\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_addnetworkrule-1.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Task Add Network Rule\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3OTgsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvdGFza19hZGRuZXR3b3JrcnVsZS0xLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"260\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_addnetworkrule-1-768x260.png\" class=\"attachment-medium_large size-medium_large wp-image-27798\" alt=\"Task Add Network Rule\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_addnetworkrule-1-768x260.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_addnetworkrule-1-300x102.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_addnetworkrule-1-1024x347.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_addnetworkrule-1.png 1144w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3a14840 elementor-widget elementor-widget-text-editor\" data-id=\"3a14840\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The snapshot from the Azure Portal showing that the Agent&#8217;s Public IP has been added (temporarily) to the Firewall Rules:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0c15d7e elementor-widget elementor-widget-image\" data-id=\"0c15d7e\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_firewall_add_rule-1.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure Storage Firewall - Rule Added\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc4MjMsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvYXp1cmVfc3RvcmFnZV9maXJld2FsbF9hZGRfcnVsZS0xLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"648\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_firewall_add_rule-1-768x648.png\" class=\"attachment-medium_large size-medium_large wp-image-27823\" alt=\"Azure Storage Firewall - Rule Added\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_firewall_add_rule-1-768x648.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_firewall_add_rule-1-300x253.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_firewall_add_rule-1-1024x864.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/azure_storage_firewall_add_rule-1.png 1357w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-73bb9a1 elementor-widget elementor-widget-text-editor\" data-id=\"73bb9a1\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The output when the Network Rule gets removed:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-63f309a elementor-widget elementor-widget-image\" data-id=\"63f309a\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_removenetworkrule-1.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Task Remove Network Rule\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3OTksInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvdGFza19yZW1vdmVuZXR3b3JrcnVsZS0xLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"310\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_removenetworkrule-1-768x310.png\" class=\"attachment-medium_large size-medium_large wp-image-27799\" alt=\"Task Remove Network Rule\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_removenetworkrule-1-768x310.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_removenetworkrule-1-300x121.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/task_removenetworkrule-1.png 923w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-250fc26 elementor-widget elementor-widget-heading\" data-id=\"250fc26\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h4 class=\"elementor-heading-title elementor-size-default\">Sleep command<\/h4>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9fb7a8f elementor-widget elementor-widget-text-editor\" data-id=\"9fb7a8f\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>There is one more important thing. I had to add the &#8220;sleep&#8221; command. It is required to give the Azure API time (after executing the Azure CLI &#8220;az storage account network-rule add&#8221; command) to update the Azure Storage Firewall Network Rules properly. Without the &#8220;sleep&#8221; time, the pipeline would usually fail, as the pipeline is much faster than the Azure API. We have to give Azure some time to update the rules.<\/p><p>And why 25 seconds? You could think it is too much. However, based on my tests, <strong>everything below 20-25 seconds led (more or less often) to errors in the pipeline<\/strong>. Please notice that even 25 seconds of sleep time is not enough occasionally. If you want to use this approach in the production environment, this could be a significant issue.<br \/>The error you would see if your &#8220;sleep&#8221; time is too short:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-18d2b48 elementor-widget elementor-widget-code-highlight\" data-id=\"18d2b48\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>\u2502 Error: Failed to get existing workspaces: containers.Client#ListBlobs: Failure responding to request: StatusCode=403 -- Original Error: autorest\/azure: Service returned an error. Status=403 Code=\"AuthorizationFailure\" Message=\"This request is not authorized to perform this operation.\"\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-447e649 elementor-widget elementor-widget-heading\" data-id=\"447e649\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h4 class=\"elementor-heading-title elementor-size-default\">Manual Approval Task<\/h4>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0a6a873 elementor-widget elementor-widget-text-editor\" data-id=\"0a6a873\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>I have decided to use the Manual Approval Task. The administrator will be notified via e-mail that a task is awaiting his\/her approval. If the Task is not approved, it will be rejected when the timer expires.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2e042c2 elementor-widget elementor-widget-text-editor\" data-id=\"2e042c2\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The manual approval stage:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8431768 elementor-widget elementor-widget-code-highlight\" data-id=\"8431768\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>  - stage: approval\r\n    condition: succeeded('tfplan')\r\n    dependsOn: tfplan\r\n    jobs:\r\n      - job: manualapproval\r\n        pool: server\r\n        steps:\r\n        - task: ManualValidation@1\r\n          inputs:\r\n              notifyUsers: 'your-email@dot.com'\r\n              approvers: 'your-email@dot.com'\r\n              instructions: 'Please Approve'<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-68c3775 elementor-widget elementor-widget-text-editor\" data-id=\"68c3775\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The Manual Approval e-mail:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-67694d0 elementor-widget elementor-widget-image\" data-id=\"67694d0\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_email.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Manual Approval E-mail\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3NDcsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvbWFudWFsX2FwcHJvdmFsX2VtYWlsLnBuZyJ9\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"436\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_email-768x436.png\" class=\"attachment-medium_large size-medium_large wp-image-27747\" alt=\"Manual Approval E-mail\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_email-768x436.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_email-300x170.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_email-1024x581.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_email-1536x872.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_email.png 1600w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-84fb51c elementor-widget elementor-widget-text-editor\" data-id=\"84fb51c\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The Manual Approval Task in the Azure DevOps Pipeline:<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b3dcb52 elementor-widget elementor-widget-image\" data-id=\"b3dcb52\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_pipeline-2.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Azure DevOps Pipeline Manual Approval\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc4MDEsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvbWFudWFsX2FwcHJvdmFsX3BpcGVsaW5lLTIucG5nIn0%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"386\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_pipeline-2-768x386.png\" class=\"attachment-medium_large size-medium_large wp-image-27801\" alt=\"Azure DevOps Pipeline Manual Approval\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_pipeline-2-768x386.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_pipeline-2-300x150.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_pipeline-2-1024x514.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_pipeline-2-1536x771.png 1536w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/manual_approval_pipeline-2.png 2021w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9b3cad6 elementor-widget elementor-widget-heading\" data-id=\"9b3cad6\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Azure Destroy-Pipeline Structure<\/h3>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ce99248 elementor-widget elementor-widget-text-editor\" data-id=\"ce99248\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The Azure Destroy-Pipeline leverages the same concept as the Deploy-Pipeline when modifying the Azure Storage Firewall Network Rules and &#8220;Sleep&#8221; command. However, it consists of only one STAGE.<\/p><p>One thing that could be added (it is not there now) is to include the Manual Approval step (the same way the Deploy-Pipeline uses it).<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c8da4b9 elementor-widget elementor-widget-image\" data-id=\"c8da4b9\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"http:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/destroy-pipeline.png\" data-elementor-open-lightbox=\"yes\" data-elementor-lightbox-title=\"Destroy Pipeline\" data-e-action-hash=\"#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6Mjc3NjYsInVybCI6Imh0dHBzOlwvXC9jbG91ZC1jb2QuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI1XC8wMVwvZGVzdHJveS1waXBlbGluZS5wbmcifQ%3D%3D\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"667\" src=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/destroy-pipeline-768x667.png\" class=\"attachment-medium_large size-medium_large wp-image-27766\" alt=\"Destroy Pipeline\" srcset=\"https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/destroy-pipeline-768x667.png 768w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/destroy-pipeline-300x261.png 300w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/destroy-pipeline-1024x889.png 1024w, https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/destroy-pipeline.png 1081w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7c74c82 elementor-widget elementor-widget-heading\" data-id=\"7c74c82\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Summary<\/h2>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-15138f0 elementor-widget elementor-widget-text-editor\" data-id=\"15138f0\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>The pipeline works well. However, as always, there are some spaces for improvement.<\/p><p>The main advantage:<\/p><ul><li>The Azure Storage Firewall network rules are very restrictive and allow only specific IPs. Moreover, the Agents&#8217; Public IPs are deleted at the end of every Stage.<\/li><\/ul><p>The main drawbacks:<\/p><ul><li>The Azure Storage leverages Public Endpoint.<\/li><li>The pipeline depends heavily on the &#8220;sleep&#8221; command, which introduces a lot of wait time.<\/li><li>Regardless of &#8220;sleep&#8221; time, tasks can fail occasionally.<\/li><\/ul>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fa08978 elementor-widget elementor-widget-heading\" data-id=\"fa08978\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Update - Part 2 - Using Self-Hosted Agent<\/h2>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4913376 elementor-widget elementor-widget-text-editor\" data-id=\"4913376\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>How do you create a Self-hosted Azure Pipeline Agent (Ubuntu)? How do you access the Terraform TF State in Azure Storage Container through a Private Endpoint? Let&#8217;s find out!<br \/><a href=\"https:\/\/cloud-cod.com\/index.php\/2025\/01\/10\/azure-pipeline-self-hosted-agents-and-azure-storage-for-terraform-state\/\">https:\/\/cloud-cod.com\/index.php\/2025\/01\/10\/azure-pipeline-self-hosted-agents-and-azure-storage-for-terraform-state\/<\/a><\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-e0a4981 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"e0a4981\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-6a64c50\" data-id=\"6a64c50\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>The article explains how to create the Azure Storage Blob Container to store the Terraform State file and provides the pipeline to deploy your infrastructure.<\/p>\n","protected":false},"author":2,"featured_media":27805,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[14,18,29,30,17],"tags":[],"class_list":["post-27627","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aviatrix","category-azure","category-devops","category-pipeline","category-terraform"],"uagb_featured_image_src":{"full":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy.png",2013,1428,false],"thumbnail":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-150x150.png",150,150,true],"medium":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-300x213.png",300,213,true],"medium_large":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-768x545.png",768,545,true],"large":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-1024x726.png",800,567,true],"1536x1536":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-1536x1090.png",1536,1090,true],"2048x2048":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy.png",2013,1428,false],"onepress-blog-small":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-300x150.png",300,150,true],"onepress-small":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-480x300.png",480,300,true],"onepress-medium":["https:\/\/cloud-cod.com\/wp-content\/uploads\/2025\/01\/blog_post19-Copy-640x400.png",640,400,true]},"uagb_author_info":{"display_name":"Jakub","author_link":"https:\/\/cloud-cod.com\/index.php\/author\/jakub\/"},"uagb_comment_info":7,"uagb_excerpt":"The article explains how to create the Azure Storage Blob Container to store the Terraform State file and provides the pipeline to deploy your infrastructure.","_links":{"self":[{"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/posts\/27627","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/comments?post=27627"}],"version-history":[{"count":168,"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/posts\/27627\/revisions"}],"predecessor-version":[{"id":27956,"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/posts\/27627\/revisions\/27956"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/media\/27805"}],"wp:attachment":[{"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/media?parent=27627"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/categories?post=27627"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloud-cod.com\/index.php\/wp-json\/wp\/v2\/tags?post=27627"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}