[{"data":1,"prerenderedAt":2074},["ShallowReactive",2],{"/blog/managing-multiple-environments-with-terraform-and-gitlab-ci":3,"navigation-en-us":1278,"banner-en-us":1706,"footer-en-us":1716,"blog-post-authors-en-us-Sophia Manicor|Noah Ing":1959,"blog-related-posts-en-us-managing-multiple-environments-with-terraform-and-gitlab-ci":1986,"blog-promotions-en-us":2011,"next-steps-en-us":2064},{"id":4,"title":5,"authors":6,"body":9,"category":1257,"date":1258,"description":1259,"extension":1260,"externalUrl":1261,"faq":1261,"featured":1262,"heroImage":1263,"meta":1264,"navigation":125,"path":1265,"seo":1266,"slug":1270,"stem":1271,"tags":1272,"template":1276,"updatedDate":1261,"__hash__":1277},"blogPosts/en-us/blog/managing-multiple-environments-with-terraform-and-gitlab-ci.md","Managing multiple environments with Terraform and GitLab CI",[7,8],"Sophia Manicor","Noah Ing",{"type":10,"value":11,"toc":1245},"minimark",[12,21,26,42,46,49,60,64,67,71,76,79,87,548,552,560,1041,1045,1052,1063,1196,1200,1203,1230,1234,1241],[13,14,15,16,20],"p",{},"Using multiple environments ensures that your infrastructure as code (IaC) is rigorously tested before it is deployed. This tutorial will show a setup of how to manage ",[17,18,19],"strong",{},"three different environments in one project"," using GitLab CI and Terraform.",[22,23,25],"h2",{"id":24},"prerequisites","Prerequisites",[27,28,29,33,36,39],"ul",{},[30,31,32],"li",{},"Working knowledge of GitLab CI/CD",[30,34,35],{},"An AWS / GCP account (where you will deploy to)",[30,37,38],{},"Working knowledge of Terraform",[30,40,41],{},"5 minutes",[22,43,45],{"id":44},"multiple-environments","Multiple environments",[13,47,48],{},"In this tutorial, we have three environments set up: dev, staging, and production.",[27,50,51,54,57],{},[30,52,53],{},"Dev: This should be where all the experimental changes go. This environment is intended to develop new features and/or test out new changes.",[30,55,56],{},"Staging: After you have confirmed your changes in dev, this environment should have parity with the production environment.",[30,58,59],{},"Production: This environment has the latest versions of infrastructure and applications are live.",[22,61,63],{"id":62},"file-structure","File structure",[13,65,66],{},"For each environment we set up a corresponding folder at the root level: folders are named dev, staging, and production respectively. Each folder stores all the Terraform infrastructure configuration for the corresponding environment. Within each of these folders, we created a CI file for that environment.",[22,68,70],{"id":69},"gitlab-ciyml",".gitlab-ci.yml",[72,73,75],"h3",{"id":74},"environment-specific-gitlab-ciyml","Environment-specific .gitlab-ci.yml",[13,77,78],{},"The file below is for the dev environment and is in the dev folder. Note that there is a rule with each job that only allows the jobs to run when a file in the dev folder is changed. There is a corresponding file in the staging and production folders that has the same rules to only allow jobs when those specific folders are changed. To keep each CI file running the same jobs we have made use of a helper file.",[13,80,81],{},[82,83,86],"a",{"href":84,"rel":85},"https://gitlab.com/demos/infrastructure/terraform-multi-env/-/blob/main/dev/.gitlab-ci.yml",[],"Environment-specific GitLab CI",[88,89,94],"pre",{"className":90,"code":91,"language":92,"meta":93,"style":93},"language-yaml shiki shiki-themes github-light","include:\n  - 'helper.yml'\n\nvariables:\n  TF_ROOT: ./dev  # The relative path to the root directory of the Terraform project\n  TF_STATE_NAME: default      # The name of the state file used by the GitLab-managed Terraform state backend\n  SECURE_ANALYZERS_PREFIX: \"$CI_TEMPLATE_REGISTRY_HOST/security-products\"\n  SAST_IMAGE_SUFFIX: \"\"\n  SAST_EXCLUDED_PATHS: \"spec, test, tests, tmp\"\n  PLAN: plan.cache\n  PLAN_JSON: plan.json\n\n\ncache:\n  key: \"${TF_ROOT}\"\n  paths:\n    - ${TF_ROOT}/.terraform/\n\nfmt-dev:\n  extends: .fmt\n  rules:\n      - changes:\n          - dev/**/*\n\nvalidate-dev:\n  extends: .validate\n  rules:\n      - changes:\n          - dev/**/*\n\nbuild-dev:\n  extends: .build\n  rules:\n      - changes:\n          - dev/**/*\n\nkics-iac-sast-dev:\n  extends: .kics-iac-sast\n  rules:\n      - changes:\n          - dev/**/*\n\ndeploy-dev:\n  extends: .deploy\n  rules:\n      - changes:\n          - dev/**/*\n\ndestroy-dev:\n  extends: .destroy\n  rules:\n      - changes:\n          - dev/**/*\n\n","yaml","",[95,96,97,110,120,127,135,151,165,176,187,198,209,220,225,230,238,249,257,266,271,279,290,298,309,318,323,331,341,348,357,364,369,377,387,394,403,410,415,423,433,440,449,456,461,469,479,486,495,502,507,515,525,532,541],"code",{"__ignoreMap":93},[98,99,102,106],"span",{"class":100,"line":101},"line",1,[98,103,105],{"class":104},"shJU0","include",[98,107,109],{"class":108},"sgsFI",":\n",[98,111,113,116],{"class":100,"line":112},2,[98,114,115],{"class":108},"  - ",[98,117,119],{"class":118},"sYBdl","'helper.yml'\n",[98,121,123],{"class":100,"line":122},3,[98,124,126],{"emptyLinePlaceholder":125},true,"\n",[98,128,130,133],{"class":100,"line":129},4,[98,131,132],{"class":104},"variables",[98,134,109],{"class":108},[98,136,138,141,144,147],{"class":100,"line":137},5,[98,139,140],{"class":104},"  TF_ROOT",[98,142,143],{"class":108},": ",[98,145,146],{"class":118},"./dev",[98,148,150],{"class":149},"sAwPA","  # The relative path to the root directory of the Terraform project\n",[98,152,154,157,159,162],{"class":100,"line":153},6,[98,155,156],{"class":104},"  TF_STATE_NAME",[98,158,143],{"class":108},[98,160,161],{"class":118},"default",[98,163,164],{"class":149},"      # The name of the state file used by the GitLab-managed Terraform state backend\n",[98,166,168,171,173],{"class":100,"line":167},7,[98,169,170],{"class":104},"  SECURE_ANALYZERS_PREFIX",[98,172,143],{"class":108},[98,174,175],{"class":118},"\"$CI_TEMPLATE_REGISTRY_HOST/security-products\"\n",[98,177,179,182,184],{"class":100,"line":178},8,[98,180,181],{"class":104},"  SAST_IMAGE_SUFFIX",[98,183,143],{"class":108},[98,185,186],{"class":118},"\"\"\n",[98,188,190,193,195],{"class":100,"line":189},9,[98,191,192],{"class":104},"  SAST_EXCLUDED_PATHS",[98,194,143],{"class":108},[98,196,197],{"class":118},"\"spec, test, tests, tmp\"\n",[98,199,201,204,206],{"class":100,"line":200},10,[98,202,203],{"class":104},"  PLAN",[98,205,143],{"class":108},[98,207,208],{"class":118},"plan.cache\n",[98,210,212,215,217],{"class":100,"line":211},11,[98,213,214],{"class":104},"  PLAN_JSON",[98,216,143],{"class":108},[98,218,219],{"class":118},"plan.json\n",[98,221,223],{"class":100,"line":222},12,[98,224,126],{"emptyLinePlaceholder":125},[98,226,228],{"class":100,"line":227},13,[98,229,126],{"emptyLinePlaceholder":125},[98,231,233,236],{"class":100,"line":232},14,[98,234,235],{"class":104},"cache",[98,237,109],{"class":108},[98,239,241,244,246],{"class":100,"line":240},15,[98,242,243],{"class":104},"  key",[98,245,143],{"class":108},[98,247,248],{"class":118},"\"${TF_ROOT}\"\n",[98,250,252,255],{"class":100,"line":251},16,[98,253,254],{"class":104},"  paths",[98,256,109],{"class":108},[98,258,260,263],{"class":100,"line":259},17,[98,261,262],{"class":108},"    - ",[98,264,265],{"class":118},"${TF_ROOT}/.terraform/\n",[98,267,269],{"class":100,"line":268},18,[98,270,126],{"emptyLinePlaceholder":125},[98,272,274,277],{"class":100,"line":273},19,[98,275,276],{"class":104},"fmt-dev",[98,278,109],{"class":108},[98,280,282,285,287],{"class":100,"line":281},20,[98,283,284],{"class":104},"  extends",[98,286,143],{"class":108},[98,288,289],{"class":118},".fmt\n",[98,291,293,296],{"class":100,"line":292},21,[98,294,295],{"class":104},"  rules",[98,297,109],{"class":108},[98,299,301,304,307],{"class":100,"line":300},22,[98,302,303],{"class":108},"      - ",[98,305,306],{"class":104},"changes",[98,308,109],{"class":108},[98,310,312,315],{"class":100,"line":311},23,[98,313,314],{"class":108},"          - ",[98,316,317],{"class":118},"dev/**/*\n",[98,319,321],{"class":100,"line":320},24,[98,322,126],{"emptyLinePlaceholder":125},[98,324,326,329],{"class":100,"line":325},25,[98,327,328],{"class":104},"validate-dev",[98,330,109],{"class":108},[98,332,334,336,338],{"class":100,"line":333},26,[98,335,284],{"class":104},[98,337,143],{"class":108},[98,339,340],{"class":118},".validate\n",[98,342,344,346],{"class":100,"line":343},27,[98,345,295],{"class":104},[98,347,109],{"class":108},[98,349,351,353,355],{"class":100,"line":350},28,[98,352,303],{"class":108},[98,354,306],{"class":104},[98,356,109],{"class":108},[98,358,360,362],{"class":100,"line":359},29,[98,361,314],{"class":108},[98,363,317],{"class":118},[98,365,367],{"class":100,"line":366},30,[98,368,126],{"emptyLinePlaceholder":125},[98,370,372,375],{"class":100,"line":371},31,[98,373,374],{"class":104},"build-dev",[98,376,109],{"class":108},[98,378,380,382,384],{"class":100,"line":379},32,[98,381,284],{"class":104},[98,383,143],{"class":108},[98,385,386],{"class":118},".build\n",[98,388,390,392],{"class":100,"line":389},33,[98,391,295],{"class":104},[98,393,109],{"class":108},[98,395,397,399,401],{"class":100,"line":396},34,[98,398,303],{"class":108},[98,400,306],{"class":104},[98,402,109],{"class":108},[98,404,406,408],{"class":100,"line":405},35,[98,407,314],{"class":108},[98,409,317],{"class":118},[98,411,413],{"class":100,"line":412},36,[98,414,126],{"emptyLinePlaceholder":125},[98,416,418,421],{"class":100,"line":417},37,[98,419,420],{"class":104},"kics-iac-sast-dev",[98,422,109],{"class":108},[98,424,426,428,430],{"class":100,"line":425},38,[98,427,284],{"class":104},[98,429,143],{"class":108},[98,431,432],{"class":118},".kics-iac-sast\n",[98,434,436,438],{"class":100,"line":435},39,[98,437,295],{"class":104},[98,439,109],{"class":108},[98,441,443,445,447],{"class":100,"line":442},40,[98,444,303],{"class":108},[98,446,306],{"class":104},[98,448,109],{"class":108},[98,450,452,454],{"class":100,"line":451},41,[98,453,314],{"class":108},[98,455,317],{"class":118},[98,457,459],{"class":100,"line":458},42,[98,460,126],{"emptyLinePlaceholder":125},[98,462,464,467],{"class":100,"line":463},43,[98,465,466],{"class":104},"deploy-dev",[98,468,109],{"class":108},[98,470,472,474,476],{"class":100,"line":471},44,[98,473,284],{"class":104},[98,475,143],{"class":108},[98,477,478],{"class":118},".deploy\n",[98,480,482,484],{"class":100,"line":481},45,[98,483,295],{"class":104},[98,485,109],{"class":108},[98,487,489,491,493],{"class":100,"line":488},46,[98,490,303],{"class":108},[98,492,306],{"class":104},[98,494,109],{"class":108},[98,496,498,500],{"class":100,"line":497},47,[98,499,314],{"class":108},[98,501,317],{"class":118},[98,503,505],{"class":100,"line":504},48,[98,506,126],{"emptyLinePlaceholder":125},[98,508,510,513],{"class":100,"line":509},49,[98,511,512],{"class":104},"destroy-dev",[98,514,109],{"class":108},[98,516,518,520,522],{"class":100,"line":517},50,[98,519,284],{"class":104},[98,521,143],{"class":108},[98,523,524],{"class":118},".destroy\n",[98,526,528,530],{"class":100,"line":527},51,[98,529,295],{"class":104},[98,531,109],{"class":108},[98,533,535,537,539],{"class":100,"line":534},52,[98,536,303],{"class":108},[98,538,306],{"class":104},[98,540,109],{"class":108},[98,542,544,546],{"class":100,"line":543},53,[98,545,314],{"class":108},[98,547,317],{"class":118},[72,549,551],{"id":550},"helperyml","helper.yml",[13,553,554,555,559],{},"This helper file was created at the root level so that it could be referenced by all of the environment-specific files. The ",[82,556,551],{"href":557,"rel":558},"https://gitlab.com/demos/infrastructure/terraform-multi-env/-/blob/main/helper.yml",[]," is where all the heavy lifting is happening. This will make sure that all the jobs throughout the environment-specific file's configuration stays up to date and consistent. In the environment-specific files we 'included' the helper file and extended the jobs outlined below.",[88,561,563],{"className":90,"code":562,"language":92,"meta":93,"style":93},".fmt:\n  stage: validate\n  script:\n    - cd \"${TF_ROOT}\"\n    - gitlab-terraform fmt\n  allow_failure: true\n\n\n.validate:\n  stage: validate\n  script:\n    - cd \"${TF_ROOT}\"\n    - gitlab-terraform validate\n\n\n.build:\n  stage: build\n  before_script:\n    - apk --no-cache add jq\n    - alias convert_report=\"jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\\\"create\\\":(map(select(.==\\\"create\\\"))|length),\\\"update\\\":(map(select(.==\\\"update\\\"))|length),\\\"delete\\\":(map(select(.==\\\"delete\\\"))|length)}'\"\n  script:\n    - cd \"${TF_ROOT}\"\n    - gitlab-terraform plan -out=$PLAN\n    - gitlab-terraform plan-json | convert_report > $PLAN_JSON\n  resource_group: ${TF_STATE_NAME}\n  artifacts:\n    paths:\n      - ${TF_ROOT}/plan.cache\n    reports:\n      terraform: ${TF_ROOT}/$PLAN_JSON\n\n.kics-iac-sast:\n  stage: test\n  artifacts:\n    reports:\n      sast: gl-sast-report.json\n  image:\n    name: \"$SAST_ANALYZER_IMAGE\"\n  variables:\n    SEARCH_MAX_DEPTH: 4\n    SAST_ANALYZER_IMAGE_TAG: 3\n    SAST_ANALYZER_IMAGE: \"$SECURE_ANALYZERS_PREFIX/kics:$SAST_ANALYZER_IMAGE_TAG$SAST_IMAGE_SUFFIX\"\n  allow_failure: true\n  script:\n    - /analyzer run\n\n\n.deploy:\n  stage: deploy\n  script:\n    - cd \"${TF_ROOT}\"\n    - gitlab-terraform apply\n  resource_group: ${TF_STATE_NAME}\n  when: manual\n  rules:\n      - changes:\n          - ${TF_ENVIRONMENT}/**/*\n\n.destroy:\n  stage: cleanup\n  script:\n    - cd \"${TF_ROOT}\"\n    - gitlab-terraform destroy\n  resource_group: ${TF_STATE_NAME}\n  when: manual\n\n",[95,564,565,572,582,589,596,603,614,618,622,629,637,643,649,656,660,664,671,680,687,694,701,707,713,720,727,737,744,751,758,765,775,779,786,795,801,807,817,824,834,841,851,861,871,879,885,892,896,900,907,916,922,928,935,943,954,961,970,978,983,991,1001,1008,1015,1023,1032],{"__ignoreMap":93},[98,566,567,570],{"class":100,"line":101},[98,568,569],{"class":104},".fmt",[98,571,109],{"class":108},[98,573,574,577,579],{"class":100,"line":112},[98,575,576],{"class":104},"  stage",[98,578,143],{"class":108},[98,580,581],{"class":118},"validate\n",[98,583,584,587],{"class":100,"line":122},[98,585,586],{"class":104},"  script",[98,588,109],{"class":108},[98,590,591,593],{"class":100,"line":129},[98,592,262],{"class":108},[98,594,595],{"class":118},"cd \"${TF_ROOT}\"\n",[98,597,598,600],{"class":100,"line":137},[98,599,262],{"class":108},[98,601,602],{"class":118},"gitlab-terraform fmt\n",[98,604,605,608,610],{"class":100,"line":153},[98,606,607],{"class":104},"  allow_failure",[98,609,143],{"class":108},[98,611,613],{"class":612},"sYu0t","true\n",[98,615,616],{"class":100,"line":167},[98,617,126],{"emptyLinePlaceholder":125},[98,619,620],{"class":100,"line":178},[98,621,126],{"emptyLinePlaceholder":125},[98,623,624,627],{"class":100,"line":189},[98,625,626],{"class":104},".validate",[98,628,109],{"class":108},[98,630,631,633,635],{"class":100,"line":200},[98,632,576],{"class":104},[98,634,143],{"class":108},[98,636,581],{"class":118},[98,638,639,641],{"class":100,"line":211},[98,640,586],{"class":104},[98,642,109],{"class":108},[98,644,645,647],{"class":100,"line":222},[98,646,262],{"class":108},[98,648,595],{"class":118},[98,650,651,653],{"class":100,"line":227},[98,652,262],{"class":108},[98,654,655],{"class":118},"gitlab-terraform validate\n",[98,657,658],{"class":100,"line":232},[98,659,126],{"emptyLinePlaceholder":125},[98,661,662],{"class":100,"line":240},[98,663,126],{"emptyLinePlaceholder":125},[98,665,666,669],{"class":100,"line":251},[98,667,668],{"class":104},".build",[98,670,109],{"class":108},[98,672,673,675,677],{"class":100,"line":259},[98,674,576],{"class":104},[98,676,143],{"class":108},[98,678,679],{"class":118},"build\n",[98,681,682,685],{"class":100,"line":268},[98,683,684],{"class":104},"  before_script",[98,686,109],{"class":108},[98,688,689,691],{"class":100,"line":273},[98,690,262],{"class":108},[98,692,693],{"class":118},"apk --no-cache add jq\n",[98,695,696,698],{"class":100,"line":281},[98,697,262],{"class":108},[98,699,700],{"class":118},"alias convert_report=\"jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\\\"create\\\":(map(select(.==\\\"create\\\"))|length),\\\"update\\\":(map(select(.==\\\"update\\\"))|length),\\\"delete\\\":(map(select(.==\\\"delete\\\"))|length)}'\"\n",[98,702,703,705],{"class":100,"line":292},[98,704,586],{"class":104},[98,706,109],{"class":108},[98,708,709,711],{"class":100,"line":300},[98,710,262],{"class":108},[98,712,595],{"class":118},[98,714,715,717],{"class":100,"line":311},[98,716,262],{"class":108},[98,718,719],{"class":118},"gitlab-terraform plan -out=$PLAN\n",[98,721,722,724],{"class":100,"line":320},[98,723,262],{"class":108},[98,725,726],{"class":118},"gitlab-terraform plan-json | convert_report > $PLAN_JSON\n",[98,728,729,732,734],{"class":100,"line":325},[98,730,731],{"class":104},"  resource_group",[98,733,143],{"class":108},[98,735,736],{"class":118},"${TF_STATE_NAME}\n",[98,738,739,742],{"class":100,"line":333},[98,740,741],{"class":104},"  artifacts",[98,743,109],{"class":108},[98,745,746,749],{"class":100,"line":343},[98,747,748],{"class":104},"    paths",[98,750,109],{"class":108},[98,752,753,755],{"class":100,"line":350},[98,754,303],{"class":108},[98,756,757],{"class":118},"${TF_ROOT}/plan.cache\n",[98,759,760,763],{"class":100,"line":359},[98,761,762],{"class":104},"    reports",[98,764,109],{"class":108},[98,766,767,770,772],{"class":100,"line":366},[98,768,769],{"class":104},"      terraform",[98,771,143],{"class":108},[98,773,774],{"class":118},"${TF_ROOT}/$PLAN_JSON\n",[98,776,777],{"class":100,"line":371},[98,778,126],{"emptyLinePlaceholder":125},[98,780,781,784],{"class":100,"line":379},[98,782,783],{"class":104},".kics-iac-sast",[98,785,109],{"class":108},[98,787,788,790,792],{"class":100,"line":389},[98,789,576],{"class":104},[98,791,143],{"class":108},[98,793,794],{"class":118},"test\n",[98,796,797,799],{"class":100,"line":396},[98,798,741],{"class":104},[98,800,109],{"class":108},[98,802,803,805],{"class":100,"line":405},[98,804,762],{"class":104},[98,806,109],{"class":108},[98,808,809,812,814],{"class":100,"line":412},[98,810,811],{"class":104},"      sast",[98,813,143],{"class":108},[98,815,816],{"class":118},"gl-sast-report.json\n",[98,818,819,822],{"class":100,"line":417},[98,820,821],{"class":104},"  image",[98,823,109],{"class":108},[98,825,826,829,831],{"class":100,"line":425},[98,827,828],{"class":104},"    name",[98,830,143],{"class":108},[98,832,833],{"class":118},"\"$SAST_ANALYZER_IMAGE\"\n",[98,835,836,839],{"class":100,"line":435},[98,837,838],{"class":104},"  variables",[98,840,109],{"class":108},[98,842,843,846,848],{"class":100,"line":442},[98,844,845],{"class":104},"    SEARCH_MAX_DEPTH",[98,847,143],{"class":108},[98,849,850],{"class":612},"4\n",[98,852,853,856,858],{"class":100,"line":451},[98,854,855],{"class":104},"    SAST_ANALYZER_IMAGE_TAG",[98,857,143],{"class":108},[98,859,860],{"class":612},"3\n",[98,862,863,866,868],{"class":100,"line":458},[98,864,865],{"class":104},"    SAST_ANALYZER_IMAGE",[98,867,143],{"class":108},[98,869,870],{"class":118},"\"$SECURE_ANALYZERS_PREFIX/kics:$SAST_ANALYZER_IMAGE_TAG$SAST_IMAGE_SUFFIX\"\n",[98,872,873,875,877],{"class":100,"line":463},[98,874,607],{"class":104},[98,876,143],{"class":108},[98,878,613],{"class":612},[98,880,881,883],{"class":100,"line":471},[98,882,586],{"class":104},[98,884,109],{"class":108},[98,886,887,889],{"class":100,"line":481},[98,888,262],{"class":108},[98,890,891],{"class":118},"/analyzer run\n",[98,893,894],{"class":100,"line":488},[98,895,126],{"emptyLinePlaceholder":125},[98,897,898],{"class":100,"line":497},[98,899,126],{"emptyLinePlaceholder":125},[98,901,902,905],{"class":100,"line":504},[98,903,904],{"class":104},".deploy",[98,906,109],{"class":108},[98,908,909,911,913],{"class":100,"line":509},[98,910,576],{"class":104},[98,912,143],{"class":108},[98,914,915],{"class":118},"deploy\n",[98,917,918,920],{"class":100,"line":517},[98,919,586],{"class":104},[98,921,109],{"class":108},[98,923,924,926],{"class":100,"line":527},[98,925,262],{"class":108},[98,927,595],{"class":118},[98,929,930,932],{"class":100,"line":534},[98,931,262],{"class":108},[98,933,934],{"class":118},"gitlab-terraform apply\n",[98,936,937,939,941],{"class":100,"line":543},[98,938,731],{"class":104},[98,940,143],{"class":108},[98,942,736],{"class":118},[98,944,946,949,951],{"class":100,"line":945},54,[98,947,948],{"class":104},"  when",[98,950,143],{"class":108},[98,952,953],{"class":118},"manual\n",[98,955,957,959],{"class":100,"line":956},55,[98,958,295],{"class":104},[98,960,109],{"class":108},[98,962,964,966,968],{"class":100,"line":963},56,[98,965,303],{"class":108},[98,967,306],{"class":104},[98,969,109],{"class":108},[98,971,973,975],{"class":100,"line":972},57,[98,974,314],{"class":108},[98,976,977],{"class":118},"${TF_ENVIRONMENT}/**/*\n",[98,979,981],{"class":100,"line":980},58,[98,982,126],{"emptyLinePlaceholder":125},[98,984,986,989],{"class":100,"line":985},59,[98,987,988],{"class":104},".destroy",[98,990,109],{"class":108},[98,992,994,996,998],{"class":100,"line":993},60,[98,995,576],{"class":104},[98,997,143],{"class":108},[98,999,1000],{"class":118},"cleanup\n",[98,1002,1004,1006],{"class":100,"line":1003},61,[98,1005,586],{"class":104},[98,1007,109],{"class":108},[98,1009,1011,1013],{"class":100,"line":1010},62,[98,1012,262],{"class":108},[98,1014,595],{"class":118},[98,1016,1018,1020],{"class":100,"line":1017},63,[98,1019,262],{"class":108},[98,1021,1022],{"class":118},"gitlab-terraform destroy\n",[98,1024,1026,1028,1030],{"class":100,"line":1025},64,[98,1027,731],{"class":104},[98,1029,143],{"class":108},[98,1031,736],{"class":118},[98,1033,1035,1037,1039],{"class":100,"line":1034},65,[98,1036,948],{"class":104},[98,1038,143],{"class":108},[98,1040,953],{"class":118},[72,1042,1044],{"id":1043},"root-level-gitlab-ciyml","Root-level .gitlab-ci.yml",[13,1046,1047],{},[82,1048,1051],{"href":1049,"rel":1050},"https://gitlab.com/demos/infrastructure/terraform-multi-env/-/blob/main/.gitlab-ci.yml",[],"Root-level GitLab CI",[13,1053,1054,1055,1059,1060,1062],{},"The file that brings everything above together is the root-level CI file. This will be what the pipeline initially references when run. The ",[82,1056,1058],{"href":1049,"rel":1057},[],"root-level GitLab CI"," is where all of the stages and container images are defined. Note that they are inheriting ",[95,1061,70],{}," from each of the individual folders themselves.",[88,1064,1066],{"className":90,"code":1065,"language":92,"meta":93,"style":93},"include:\n  - 'dev/.gitlab-ci.yml'\n  - 'staging/.gitlab-ci.yml'\n  - 'production/.gitlab-ci.yml'\n\n  \nimage:\n  name: \"$CI_TEMPLATE_REGISTRY_HOST/gitlab-org/terraform-images/releases/1.1:v0.43.0\"\n\nstages:          \n  - validate\n  - build\n  - test\n  - deploy\n  - cleanup\n\nvariables:\n  # If not using GitLab's HTTP backend, remove this line and specify TF_HTTP_* variables\n  TF_STATE_NAME: default\n  TF_CACHE_KEY: default\n\n",[95,1067,1068,1074,1081,1088,1095,1099,1104,1111,1121,1125,1133,1139,1145,1151,1157,1163,1167,1173,1178,1187],{"__ignoreMap":93},[98,1069,1070,1072],{"class":100,"line":101},[98,1071,105],{"class":104},[98,1073,109],{"class":108},[98,1075,1076,1078],{"class":100,"line":112},[98,1077,115],{"class":108},[98,1079,1080],{"class":118},"'dev/.gitlab-ci.yml'\n",[98,1082,1083,1085],{"class":100,"line":122},[98,1084,115],{"class":108},[98,1086,1087],{"class":118},"'staging/.gitlab-ci.yml'\n",[98,1089,1090,1092],{"class":100,"line":129},[98,1091,115],{"class":108},[98,1093,1094],{"class":118},"'production/.gitlab-ci.yml'\n",[98,1096,1097],{"class":100,"line":137},[98,1098,126],{"emptyLinePlaceholder":125},[98,1100,1101],{"class":100,"line":153},[98,1102,1103],{"class":108},"  \n",[98,1105,1106,1109],{"class":100,"line":167},[98,1107,1108],{"class":104},"image",[98,1110,109],{"class":108},[98,1112,1113,1116,1118],{"class":100,"line":178},[98,1114,1115],{"class":104},"  name",[98,1117,143],{"class":108},[98,1119,1120],{"class":118},"\"$CI_TEMPLATE_REGISTRY_HOST/gitlab-org/terraform-images/releases/1.1:v0.43.0\"\n",[98,1122,1123],{"class":100,"line":189},[98,1124,126],{"emptyLinePlaceholder":125},[98,1126,1127,1130],{"class":100,"line":200},[98,1128,1129],{"class":104},"stages",[98,1131,1132],{"class":108},":          \n",[98,1134,1135,1137],{"class":100,"line":211},[98,1136,115],{"class":108},[98,1138,581],{"class":118},[98,1140,1141,1143],{"class":100,"line":222},[98,1142,115],{"class":108},[98,1144,679],{"class":118},[98,1146,1147,1149],{"class":100,"line":227},[98,1148,115],{"class":108},[98,1150,794],{"class":118},[98,1152,1153,1155],{"class":100,"line":232},[98,1154,115],{"class":108},[98,1156,915],{"class":118},[98,1158,1159,1161],{"class":100,"line":240},[98,1160,115],{"class":108},[98,1162,1000],{"class":118},[98,1164,1165],{"class":100,"line":251},[98,1166,126],{"emptyLinePlaceholder":125},[98,1168,1169,1171],{"class":100,"line":259},[98,1170,132],{"class":104},[98,1172,109],{"class":108},[98,1174,1175],{"class":100,"line":268},[98,1176,1177],{"class":149},"  # If not using GitLab's HTTP backend, remove this line and specify TF_HTTP_* variables\n",[98,1179,1180,1182,1184],{"class":100,"line":273},[98,1181,156],{"class":104},[98,1183,143],{"class":108},[98,1185,1186],{"class":118},"default\n",[98,1188,1189,1192,1194],{"class":100,"line":281},[98,1190,1191],{"class":104},"  TF_CACHE_KEY",[98,1193,143],{"class":108},[98,1195,1186],{"class":118},[22,1197,1199],{"id":1198},"merge-request-promotion-through-environments","Merge request + promotion through environments",[13,1201,1202],{},"With the project set up and GitLab CI’s triggering only based off changes to the individual environment folders, you can now safely promote changes using merge requests. When you want to make a change:",[1204,1205,1206,1209,1218,1221,1224,1227],"ol",{},[30,1207,1208],{},"First create a merge request in the dev environment with your *.tf files.",[30,1210,1211,1212,1217],{},"Review the ",[82,1213,1216],{"href":1214,"rel":1215},"https://docs.gitlab.com/user/infrastructure/iac/mr_integration/",[],"Terraform integration in merge requests"," to see X changes, X to Add, and X to Remove.",[30,1219,1220],{},"If your changes are as expected, request your team members to review the changes and Terraform code.",[30,1222,1223],{},"Apply the changes to your dev environment and merge in the merge request.",[30,1225,1226],{},"If everything worked as intended, then make the same merge up into the staging environment.",[30,1228,1229],{},"If the staging environment remains stable, make a merge request up into the production environment.",[22,1231,1233],{"id":1232},"results","Results",[13,1235,1236,1237,1240],{},"Voila, and there you have it! ",[17,1238,1239],{},"A single project to manage three different infrastructure environments"," in a safe way to ensure that your changes to production are tested, reviewed, and approved by the rest of your team members.",[1242,1243,1244],"style",{},"html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}",{"title":93,"searchDepth":112,"depth":112,"links":1246},[1247,1248,1249,1250,1255,1256],{"id":24,"depth":112,"text":25},{"id":44,"depth":112,"text":45},{"id":62,"depth":112,"text":63},{"id":69,"depth":112,"text":70,"children":1251},[1252,1253,1254],{"id":74,"depth":122,"text":75},{"id":550,"depth":122,"text":551},{"id":1043,"depth":122,"text":1044},{"id":1198,"depth":112,"text":1199},{"id":1232,"depth":112,"text":1233},"security","2023-06-14","This tutorial shows how to set up and manage three different environments in one project using GitLab CI and Terraform.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663779/Blog/Hero%20Images/cicd-2018_blogimage.jpg",{},"/en-us/blog/managing-multiple-environments-with-terraform-and-gitlab-ci",{"title":5,"description":1259,"ogTitle":5,"ogDescription":1259,"noIndex":1262,"ogImage":1263,"ogUrl":1267,"ogSiteName":1268,"ogType":1269,"canonicalUrls":1267},"https://about.gitlab.com/blog/managing-multiple-environments-with-terraform-and-gitlab-ci","https://about.gitlab.com","article","managing-multiple-environments-with-terraform-and-gitlab-ci","en-us/blog/managing-multiple-environments-with-terraform-and-gitlab-ci",[1273,1274,1275,1257],"DevOps","CI","DevSecOps","BlogPost","euBfgyRmG0h44f7KZWZxKLx7uJ4Wbi8pkKS9U6j_OT0",{"logo":1279,"freeTrial":1284,"sales":1289,"login":1294,"items":1299,"search":1626,"minimal":1657,"duo":1676,"switchNav":1685,"pricingDeployment":1696},{"config":1280},{"href":1281,"dataGaName":1282,"dataGaLocation":1283},"/","gitlab logo","header",{"text":1285,"config":1286},"Get free trial",{"href":1287,"dataGaName":1288,"dataGaLocation":1283},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":1290,"config":1291},"Request a demo",{"href":1292,"dataGaName":1293,"dataGaLocation":1283},"/sales/?contact-topic=request-demo","sales",{"text":1295,"config":1296},"Sign in",{"href":1297,"dataGaName":1298,"dataGaLocation":1283},"https://gitlab.com/users/sign_in/","sign in",[1300,1329,1429,1434,1548,1604],{"text":1301,"config":1302,"menu":1304},"Platform",{"dataNavLevelOne":1303},"platform",{"type":1305,"columns":1306},"cards",[1307,1313,1321],{"title":1301,"description":1308,"link":1309},"The intelligent orchestration platform for DevSecOps",{"text":1310,"config":1311},"Explore our Platform",{"href":1312,"dataGaName":1303,"dataGaLocation":1283},"/platform/",{"title":1314,"description":1315,"link":1316},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":1317,"config":1318},"Meet GitLab Duo",{"href":1319,"dataGaName":1320,"dataGaLocation":1283},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1322,"description":1323,"link":1324},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":1325,"config":1326},"Learn more",{"href":1327,"dataGaName":1328,"dataGaLocation":1283},"/why-gitlab/","why gitlab",{"text":1330,"left":125,"config":1331,"menu":1333},"Product",{"dataNavLevelOne":1332},"solutions",{"type":1334,"link":1335,"columns":1339,"feature":1408},"lists",{"text":1336,"config":1337},"View all Solutions",{"href":1338,"dataGaName":1332,"dataGaLocation":1283},"/solutions/",[1340,1364,1387],{"title":1341,"description":1342,"link":1343,"items":1348},"Automation","CI/CD and automation to accelerate deployment",{"config":1344},{"icon":1345,"href":1346,"dataGaName":1347,"dataGaLocation":1283},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[1349,1353,1356,1360],{"text":1350,"config":1351},"CI/CD",{"href":1352,"dataGaLocation":1283,"dataGaName":1350},"/solutions/continuous-integration/",{"text":1314,"config":1354},{"href":1319,"dataGaLocation":1283,"dataGaName":1355},"gitlab duo agent platform - product menu",{"text":1357,"config":1358},"Source Code Management",{"href":1359,"dataGaLocation":1283,"dataGaName":1357},"/solutions/source-code-management/",{"text":1361,"config":1362},"Automated Software Delivery",{"href":1346,"dataGaLocation":1283,"dataGaName":1363},"Automated software delivery",{"title":1365,"description":1366,"link":1367,"items":1372},"Security","Deliver code faster without compromising security",{"config":1368},{"href":1369,"dataGaName":1370,"dataGaLocation":1283,"icon":1371},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[1373,1377,1382],{"text":1374,"config":1375},"Application Security Testing",{"href":1369,"dataGaName":1376,"dataGaLocation":1283},"Application security testing",{"text":1378,"config":1379},"Software Supply Chain Security",{"href":1380,"dataGaLocation":1283,"dataGaName":1381},"/solutions/supply-chain/","Software supply chain security",{"text":1383,"config":1384},"Software Compliance",{"href":1385,"dataGaName":1386,"dataGaLocation":1283},"/solutions/software-compliance/","software compliance",{"title":1388,"link":1389,"items":1394},"Measurement",{"config":1390},{"icon":1391,"href":1392,"dataGaName":1393,"dataGaLocation":1283},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[1395,1399,1403],{"text":1396,"config":1397},"Visibility & Measurement",{"href":1392,"dataGaLocation":1283,"dataGaName":1398},"Visibility and Measurement",{"text":1400,"config":1401},"Value Stream Management",{"href":1402,"dataGaLocation":1283,"dataGaName":1400},"/solutions/value-stream-management/",{"text":1404,"config":1405},"Analytics & Insights",{"href":1406,"dataGaLocation":1283,"dataGaName":1407},"/solutions/analytics-and-insights/","Analytics and insights",{"title":1409,"type":1334,"items":1410},"GitLab for",[1411,1417,1423],{"text":1412,"config":1413},"Enterprise",{"icon":1414,"href":1415,"dataGaLocation":1283,"dataGaName":1416},"Building","/enterprise/","enterprise",{"text":1418,"config":1419},"Small Business",{"icon":1420,"href":1421,"dataGaLocation":1283,"dataGaName":1422},"Work","/small-business/","small business",{"text":1424,"config":1425},"Public Sector",{"icon":1426,"href":1427,"dataGaLocation":1283,"dataGaName":1428},"Organization","/solutions/public-sector/","public sector",{"text":1430,"config":1431},"Pricing",{"href":1432,"dataGaName":1433,"dataGaLocation":1283,"dataNavLevelOne":1433},"/pricing/","pricing",{"text":1435,"config":1436,"menu":1438},"Resources",{"dataNavLevelOne":1437},"resources",{"type":1334,"link":1439,"columns":1443,"feature":1537},{"text":1440,"config":1441},"View all resources",{"href":1442,"dataGaName":1437,"dataGaLocation":1283},"/resources/",[1444,1477,1504],{"title":1445,"items":1446},"Getting started",[1447,1452,1457,1462,1467,1472],{"text":1448,"config":1449},"Install",{"href":1450,"dataGaName":1451,"dataGaLocation":1283},"/install/","install",{"text":1453,"config":1454},"Quick start guides",{"href":1455,"dataGaName":1456,"dataGaLocation":1283},"/get-started/","quick setup checklists",{"text":1458,"config":1459},"Learn",{"href":1460,"dataGaLocation":1283,"dataGaName":1461},"https://university.gitlab.com/","learn",{"text":1463,"config":1464},"Product documentation",{"href":1465,"dataGaName":1466,"dataGaLocation":1283},"https://docs.gitlab.com/","product documentation",{"text":1468,"config":1469},"Best practice videos",{"href":1470,"dataGaName":1471,"dataGaLocation":1283},"/getting-started-videos/","best practice videos",{"text":1473,"config":1474},"Integrations",{"href":1475,"dataGaName":1476,"dataGaLocation":1283},"/integrations/","integrations",{"title":1478,"items":1479},"Discover",[1480,1485,1490,1495,1499],{"text":1481,"config":1482},"Customer success stories",{"href":1483,"dataGaName":1484,"dataGaLocation":1283},"/customers/","customer success stories",{"text":1486,"config":1487},"Blog",{"href":1488,"dataGaName":1489,"dataGaLocation":1283},"/blog/","blog",{"text":1491,"config":1492},"Demo Hub",{"href":1493,"dataGaName":1494,"dataGaLocation":1283},"/demo-hub/","demo hub",{"text":1496,"config":1497},"The Source",{"href":1498,"dataGaName":1489,"dataGaLocation":1283},"/the-source/",{"text":1500,"config":1501},"Remote",{"href":1502,"dataGaName":1503,"dataGaLocation":1283},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1505,"items":1506},"Connect",[1507,1512,1517,1522,1527,1532],{"text":1508,"config":1509},"GitLab Services",{"href":1510,"dataGaName":1511,"dataGaLocation":1283},"/services/","services",{"text":1513,"config":1514},"Contribute",{"href":1515,"dataGaName":1516,"dataGaLocation":1283},"https://contributors.gitlab.com","contribute",{"text":1518,"config":1519},"Community",{"href":1520,"dataGaName":1521,"dataGaLocation":1283},"/community/","community",{"text":1523,"config":1524},"Forum",{"href":1525,"dataGaName":1526,"dataGaLocation":1283},"https://forum.gitlab.com/","forum",{"text":1528,"config":1529},"Events",{"href":1530,"dataGaName":1531,"dataGaLocation":1283},"/events/","events",{"text":1533,"config":1534},"Partners",{"href":1535,"dataGaName":1536,"dataGaLocation":1283},"/partners/","partners",{"config":1538,"title":1541,"text":1542,"link":1543},{"background":1539,"textColor":1540},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":1544,"config":1545},"Read the latest",{"href":1546,"dataGaName":1547,"dataGaLocation":1283},"/whats-new/","whats new",{"text":1549,"config":1550,"menu":1552},"Company",{"dataNavLevelOne":1551},"company",{"type":1334,"columns":1553},[1554],{"items":1555},[1556,1561,1567,1569,1574,1579,1584,1589,1594,1599],{"text":1557,"config":1558},"About",{"href":1559,"dataGaName":1560,"dataGaLocation":1283},"/company/","about",{"text":1562,"config":1563,"footerGa":1566},"Jobs",{"href":1564,"dataGaName":1565,"dataGaLocation":1283},"/jobs/","jobs",{"dataGaName":1565},{"text":1528,"config":1568},{"href":1530,"dataGaName":1531,"dataGaLocation":1283},{"text":1570,"config":1571},"Leadership",{"href":1572,"dataGaName":1573,"dataGaLocation":1283},"/company/team/e-group/","leadership",{"text":1575,"config":1576},"Handbook",{"href":1577,"dataGaName":1578,"dataGaLocation":1283},"https://handbook.gitlab.com/","handbook",{"text":1580,"config":1581},"Investor relations",{"href":1582,"dataGaName":1583,"dataGaLocation":1283},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1585,"config":1586},"Trust Center",{"href":1587,"dataGaName":1588,"dataGaLocation":1283},"/security/","trust center",{"text":1590,"config":1591},"AI Transparency Center",{"href":1592,"dataGaName":1593,"dataGaLocation":1283},"/ai-transparency-center/","ai transparency center",{"text":1595,"config":1596},"Newsletter",{"href":1597,"dataGaName":1598,"dataGaLocation":1283},"/company/contact/#contact-forms","newsletter",{"text":1600,"config":1601},"Press",{"href":1602,"dataGaName":1603,"dataGaLocation":1283},"/press/","press",{"text":1605,"config":1606,"menu":1607},"Contact us",{"dataNavLevelOne":1551},{"type":1334,"columns":1608},[1609],{"items":1610},[1611,1616,1621],{"text":1612,"config":1613},"Talk to sales",{"href":1614,"dataGaName":1615,"dataGaLocation":1283},"/sales/","talk to sales",{"text":1617,"config":1618},"Support portal",{"href":1619,"dataGaName":1620,"dataGaLocation":1283},"https://support.gitlab.com/hc/en-us","support portal",{"text":1622,"config":1623},"Customer portal",{"href":1624,"dataGaName":1625,"dataGaLocation":1283},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1627,"login":1628,"suggestions":1635},"Close",{"text":1629,"link":1630},"To search repositories and projects, login to",{"text":1631,"config":1632},"gitlab.com",{"href":1297,"dataGaName":1633,"dataGaLocation":1634},"search login","search",{"text":1636,"default":1637},"Suggestions",[1638,1640,1644,1646,1650,1654],{"text":1314,"config":1639},{"href":1319,"dataGaName":1314,"dataGaLocation":1634},{"text":1641,"config":1642},"Code Suggestions (AI)",{"href":1643,"dataGaName":1641,"dataGaLocation":1634},"/solutions/code-suggestions/",{"text":1350,"config":1645},{"href":1352,"dataGaName":1350,"dataGaLocation":1634},{"text":1647,"config":1648},"GitLab on AWS",{"href":1649,"dataGaName":1647,"dataGaLocation":1634},"/partners/technology-partners/aws/",{"text":1651,"config":1652},"GitLab on Google Cloud",{"href":1653,"dataGaName":1651,"dataGaLocation":1634},"/partners/technology-partners/google-cloud-platform/",{"text":1655,"config":1656},"Why GitLab?",{"href":1327,"dataGaName":1655,"dataGaLocation":1634},{"freeTrial":1658,"mobileIcon":1663,"desktopIcon":1668,"secondaryButton":1671},{"text":1659,"config":1660},"Start free trial",{"href":1661,"dataGaName":1288,"dataGaLocation":1662},"https://gitlab.com/-/trials/new/","nav",{"altText":1664,"config":1665},"Gitlab Icon",{"src":1666,"dataGaName":1667,"dataGaLocation":1662},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1664,"config":1669},{"src":1670,"dataGaName":1667,"dataGaLocation":1662},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1672,"config":1673},"Get Started",{"href":1674,"dataGaName":1675,"dataGaLocation":1662},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1677,"mobileIcon":1681,"desktopIcon":1683},{"text":1678,"config":1679},"Learn more about GitLab Duo",{"href":1319,"dataGaName":1680,"dataGaLocation":1662},"gitlab duo",{"altText":1664,"config":1682},{"src":1666,"dataGaName":1667,"dataGaLocation":1662},{"altText":1664,"config":1684},{"src":1670,"dataGaName":1667,"dataGaLocation":1662},{"button":1686,"mobileIcon":1691,"desktopIcon":1693},{"text":1687,"config":1688},"/switch",{"href":1689,"dataGaName":1690,"dataGaLocation":1662},"#contact","switch",{"altText":1664,"config":1692},{"src":1666,"dataGaName":1667,"dataGaLocation":1662},{"altText":1664,"config":1694},{"src":1695,"dataGaName":1667,"dataGaLocation":1662},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1697,"mobileIcon":1702,"desktopIcon":1704},{"text":1698,"config":1699},"Back to pricing",{"href":1432,"dataGaName":1700,"dataGaLocation":1662,"icon":1701},"back to pricing","GoBack",{"altText":1664,"config":1703},{"src":1666,"dataGaName":1667,"dataGaLocation":1662},{"altText":1664,"config":1705},{"src":1670,"dataGaName":1667,"dataGaLocation":1662},{"title":1707,"titleMobile":1708,"button":1709,"config":1714},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":1325,"config":1710},{"href":1711,"dataGaName":1712,"dataGaLocation":1713},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1715,"disabled":1262},"release",{"data":1717},{"text":1718,"source":1719,"edit":1725,"contribute":1730,"config":1735,"items":1740,"minimal":1948},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1720,"config":1721},"View page source",{"href":1722,"dataGaName":1723,"dataGaLocation":1724},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1726,"config":1727},"Edit this page",{"href":1728,"dataGaName":1729,"dataGaLocation":1724},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1731,"config":1732},"Please contribute",{"href":1733,"dataGaName":1734,"dataGaLocation":1724},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1736,"facebook":1737,"youtube":1738,"linkedin":1739},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1741,1788,1840,1884,1916],{"title":1430,"links":1742,"subMenu":1757},[1743,1747,1752],{"text":1744,"config":1745},"View plans",{"href":1432,"dataGaName":1746,"dataGaLocation":1724},"view plans",{"text":1748,"config":1749},"Why Premium?",{"href":1750,"dataGaName":1751,"dataGaLocation":1724},"/pricing/premium/","why premium",{"text":1753,"config":1754},"Why Ultimate?",{"href":1755,"dataGaName":1756,"dataGaLocation":1724},"/pricing/ultimate/","why ultimate",[1758],{"title":1759,"links":1760},"Contact Us",[1761,1764,1766,1768,1773,1778,1783],{"text":1762,"config":1763},"Contact sales",{"href":1614,"dataGaName":1293,"dataGaLocation":1724},{"text":1617,"config":1765},{"href":1619,"dataGaName":1620,"dataGaLocation":1724},{"text":1622,"config":1767},{"href":1624,"dataGaName":1625,"dataGaLocation":1724},{"text":1769,"config":1770},"Status",{"href":1771,"dataGaName":1772,"dataGaLocation":1724},"https://status.gitlab.com/","status",{"text":1774,"config":1775},"Terms of use",{"href":1776,"dataGaName":1777,"dataGaLocation":1724},"/terms/","terms of use",{"text":1779,"config":1780},"Privacy statement",{"href":1781,"dataGaName":1782,"dataGaLocation":1724},"/privacy/","privacy statement",{"text":1784,"config":1785},"Cookie preferences",{"dataGaName":1786,"dataGaLocation":1724,"id":1787,"isOneTrustButton":125},"cookie preferences","ot-sdk-btn",{"title":1330,"links":1789,"subMenu":1798},[1790,1794],{"text":1791,"config":1792},"DevSecOps platform",{"href":1312,"dataGaName":1793,"dataGaLocation":1724},"devsecops platform",{"text":1795,"config":1796},"AI-Assisted Development",{"href":1319,"dataGaName":1797,"dataGaLocation":1724},"ai-assisted development",[1799],{"title":1800,"links":1801},"Topics",[1802,1807,1812,1816,1821,1825,1830,1835],{"text":1803,"config":1804},"CICD",{"href":1805,"dataGaName":1806,"dataGaLocation":1724},"/topics/ci-cd/","cicd",{"text":1808,"config":1809},"GitOps",{"href":1810,"dataGaName":1811,"dataGaLocation":1724},"/topics/gitops/","gitops",{"text":1273,"config":1813},{"href":1814,"dataGaName":1815,"dataGaLocation":1724},"/topics/devops/","devops",{"text":1817,"config":1818},"Version Control",{"href":1819,"dataGaName":1820,"dataGaLocation":1724},"/topics/version-control/","version control",{"text":1275,"config":1822},{"href":1823,"dataGaName":1824,"dataGaLocation":1724},"/topics/devsecops/","devsecops",{"text":1826,"config":1827},"Cloud Native",{"href":1828,"dataGaName":1829,"dataGaLocation":1724},"/topics/cloud-native/","cloud native",{"text":1831,"config":1832},"AI for Coding",{"href":1833,"dataGaName":1834,"dataGaLocation":1724},"/topics/devops/ai-for-coding/","ai for coding",{"text":1836,"config":1837},"Agentic AI",{"href":1838,"dataGaName":1839,"dataGaLocation":1724},"/topics/agentic-ai/","agentic ai",{"title":1841,"links":1842},"Solutions",[1843,1845,1847,1852,1856,1859,1863,1866,1868,1871,1874,1879],{"text":1374,"config":1844},{"href":1369,"dataGaName":1374,"dataGaLocation":1724},{"text":1363,"config":1846},{"href":1346,"dataGaName":1347,"dataGaLocation":1724},{"text":1848,"config":1849},"Agile development",{"href":1850,"dataGaName":1851,"dataGaLocation":1724},"/solutions/agile-delivery/","agile delivery",{"text":1853,"config":1854},"SCM",{"href":1359,"dataGaName":1855,"dataGaLocation":1724},"source code management",{"text":1803,"config":1857},{"href":1352,"dataGaName":1858,"dataGaLocation":1724},"continuous integration & delivery",{"text":1860,"config":1861},"Value stream management",{"href":1402,"dataGaName":1862,"dataGaLocation":1724},"value stream management",{"text":1808,"config":1864},{"href":1865,"dataGaName":1811,"dataGaLocation":1724},"/solutions/gitops/",{"text":1412,"config":1867},{"href":1415,"dataGaName":1416,"dataGaLocation":1724},{"text":1869,"config":1870},"Small business",{"href":1421,"dataGaName":1422,"dataGaLocation":1724},{"text":1872,"config":1873},"Public sector",{"href":1427,"dataGaName":1428,"dataGaLocation":1724},{"text":1875,"config":1876},"Education",{"href":1877,"dataGaName":1878,"dataGaLocation":1724},"/solutions/education/","education",{"text":1880,"config":1881},"Financial services",{"href":1882,"dataGaName":1883,"dataGaLocation":1724},"/solutions/finance/","financial services",{"title":1435,"links":1885},[1886,1888,1890,1892,1895,1897,1900,1902,1904,1906,1908,1910,1912,1914],{"text":1448,"config":1887},{"href":1450,"dataGaName":1451,"dataGaLocation":1724},{"text":1453,"config":1889},{"href":1455,"dataGaName":1456,"dataGaLocation":1724},{"text":1458,"config":1891},{"href":1460,"dataGaName":1461,"dataGaLocation":1724},{"text":1463,"config":1893},{"href":1465,"dataGaName":1894,"dataGaLocation":1724},"docs",{"text":1486,"config":1896},{"href":1488,"dataGaName":1489,"dataGaLocation":1724},{"text":1898,"config":1899},"What's new",{"href":1546,"dataGaName":1547,"dataGaLocation":1724},{"text":1481,"config":1901},{"href":1483,"dataGaName":1484,"dataGaLocation":1724},{"text":1500,"config":1903},{"href":1502,"dataGaName":1503,"dataGaLocation":1724},{"text":1508,"config":1905},{"href":1510,"dataGaName":1511,"dataGaLocation":1724},{"text":1513,"config":1907},{"href":1515,"dataGaName":1516,"dataGaLocation":1724},{"text":1518,"config":1909},{"href":1520,"dataGaName":1521,"dataGaLocation":1724},{"text":1523,"config":1911},{"href":1525,"dataGaName":1526,"dataGaLocation":1724},{"text":1528,"config":1913},{"href":1530,"dataGaName":1531,"dataGaLocation":1724},{"text":1533,"config":1915},{"href":1535,"dataGaName":1536,"dataGaLocation":1724},{"title":1549,"links":1917},[1918,1920,1922,1924,1926,1928,1932,1937,1939,1941,1943],{"text":1557,"config":1919},{"href":1559,"dataGaName":1551,"dataGaLocation":1724},{"text":1562,"config":1921},{"href":1564,"dataGaName":1565,"dataGaLocation":1724},{"text":1570,"config":1923},{"href":1572,"dataGaName":1573,"dataGaLocation":1724},{"text":1575,"config":1925},{"href":1577,"dataGaName":1578,"dataGaLocation":1724},{"text":1580,"config":1927},{"href":1582,"dataGaName":1583,"dataGaLocation":1724},{"text":1929,"config":1930},"Sustainability",{"href":1931,"dataGaName":1929,"dataGaLocation":1724},"/sustainability/",{"text":1933,"config":1934},"Diversity, inclusion and belonging (DIB)",{"href":1935,"dataGaName":1936,"dataGaLocation":1724},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1585,"config":1938},{"href":1587,"dataGaName":1588,"dataGaLocation":1724},{"text":1595,"config":1940},{"href":1597,"dataGaName":1598,"dataGaLocation":1724},{"text":1600,"config":1942},{"href":1602,"dataGaName":1603,"dataGaLocation":1724},{"text":1944,"config":1945},"Modern Slavery Transparency Statement",{"href":1946,"dataGaName":1947,"dataGaLocation":1724},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1949},[1950,1953,1956],{"text":1951,"config":1952},"Terms",{"href":1776,"dataGaName":1777,"dataGaLocation":1724},{"text":1954,"config":1955},"Cookies",{"dataGaName":1786,"dataGaLocation":1724,"id":1787,"isOneTrustButton":125},{"text":1957,"config":1958},"Privacy",{"href":1781,"dataGaName":1782,"dataGaLocation":1724},[1960,1974],{"id":1961,"title":7,"body":1261,"config":1962,"content":1964,"description":1261,"extension":1968,"meta":1969,"navigation":125,"path":1970,"seo":1971,"stem":1972,"__hash__":1973},"blogAuthors/en-us/blog/authors/sophia-manicor.yml",{"template":1963},"BlogAuthor",{"name":7,"config":1965},{"headshot":1966,"ctfId":1967},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749665576/Blog/Author%20Headshots/sophie_manicor_headshot.png","79Msqcc9YZrC0IvTggfQ5y","yml",{},"/en-us/blog/authors/sophia-manicor",{},"en-us/blog/authors/sophia-manicor","i6tZEYh0BGVe56GYn5Z59J7fVNHk3-yVCcPBXBlfXJY",{"id":1975,"title":8,"body":1261,"config":1976,"content":1977,"description":1261,"extension":1968,"meta":1981,"navigation":125,"path":1982,"seo":1983,"stem":1984,"__hash__":1985},"blogAuthors/en-us/blog/authors/noah-ing.yml",{"template":1963},{"name":8,"config":1978},{"headshot":1979,"ctfId":1980},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664410/Blog/Author%20Headshots/noahing.png","noahing",{},"/en-us/blog/authors/noah-ing",{},"en-us/blog/authors/noah-ing","NHgV_yTX8kHX01mE2SSxC6hcOMw3BCWiblr8m6csLkw",[1987,1995,2003],{"title":1988,"description":1989,"heroImage":1990,"category":1257,"date":1991,"authors":1992,"slug":1994,"externalUrl":1261},"How GitLab tracks vulnerabilities through refactors and reformatting","Learn how GitLab's improved Scope+Offset fingerprinting keeps vulnerability tracking stable across comments, blank lines, and reformatting.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1759320418/xjmqcozxzt4frx0hori3.png","2026-08-12",[1993],"Julian Thome","improved-scope-offset-fingerprinting",{"title":1996,"description":1997,"heroImage":1990,"category":1257,"date":1998,"authors":1999,"slug":2002,"externalUrl":1261},"GitLab Secrets Manager adds ESO, Terraform, API support","Simplify credential management across your stack. GitLab Secrets Manager provides secure retrieval in Kubernetes, Terraform, and external workflows.","2026-08-06",[2000,2001],"Erick Bajao","Joe Randazzo","gitlab-secrets-manager-add-eso-terraform-api-support",{"title":2004,"description":2005,"heroImage":2006,"category":1257,"date":2007,"authors":2008,"slug":2010,"externalUrl":1261},"Secure every commit to production with Claude and GitLab","Claude Security catches vulnerabilities inside a coding session. GitLab picks up from there, scanning, enforcing policy, and producing audit evidence for the software lifecycle. ","https://res.cloudinary.com/about-gitlab-com/image/upload/v1756122536/akivvcnafog9c4dhhzkp.png","2026-08-03",[2009],"Alisa Ho","claude-security-and-gitlab",{"promotions":2012},[2013,2027,2039,2050],{"id":2014,"categories":2015,"header":2017,"text":2018,"button":2019,"image":2024},"ai-modernization",[2016],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":2020,"config":2021},"Get your AI maturity score",{"href":2022,"dataGaName":2023,"dataGaLocation":1489},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":2025},{"src":2026},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":2028,"categories":2029,"header":2031,"text":2018,"button":2032,"image":2036},"devops-modernization",[2030,1824],"product","Are you just managing tools or shipping innovation?",{"text":2033,"config":2034},"Get your DevOps maturity score",{"href":2035,"dataGaName":2023,"dataGaLocation":1489},"/assessments/devops-modernization-assessment/",{"config":2037},{"src":2038},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":2040,"categories":2041,"header":2042,"text":2018,"button":2043,"image":2047},"security-modernization",[1257],"Are you trading speed for security?",{"text":2044,"config":2045},"Get your security maturity score",{"href":2046,"dataGaName":2023,"dataGaLocation":1489},"/assessments/security-modernization-assessment/",{"config":2048},{"src":2049},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":2051,"paths":2052,"header":2055,"text":2056,"button":2057,"image":2062},"github-azure-migration",[2053,2054],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":2058,"config":2059},"See how GitLab compares to GitHub",{"href":2060,"dataGaName":2061,"dataGaLocation":1489},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":2063},{"src":2038},{"header":2065,"blurb":2066,"button":2067,"secondaryButton":2072},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":2068,"config":2069},"Get your free trial",{"href":2070,"dataGaName":1288,"dataGaLocation":2071},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1762,"config":2073},{"href":1614,"dataGaName":1293,"dataGaLocation":2071},1786803751073]