[{"data":1,"prerenderedAt":720},["ShallowReactive",2],{"/en-us/blog/multi-account-aws-sam-deployments-with-gitlab-ci/":3,"navigation-en-us":39,"banner-en-us":466,"footer-en-us":483,"Forrest Brazeal":693,"next-steps-en-us":705},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":29,"_id":32,"_type":33,"title":34,"_source":35,"_file":36,"_stem":37,"_extension":38},"/en-us/blog/multi-account-aws-sam-deployments-with-gitlab-ci","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"How to set up multi-account AWS SAM deployments with GitLab CI/CD","Our guest author, an AWS Serverless hero, shares how to automate SAM deployments using GitLab CI/CD.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749666959/Blog/Hero%20Images/gitlab-aws-cover.png","https://about.gitlab.com/blog/multi-account-aws-sam-deployments-with-gitlab-ci","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to set up multi-account AWS SAM deployments with GitLab CI/CD\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Forrest Brazeal\"}],\n        \"datePublished\": \"2019-02-04\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Forrest Brazeal","2019-02-04","I've been working with [serverless](/topics/serverless/) applications in AWS\nfor about three years – that makes me an old salt in serverless terms! So I\nknow that deploying and maintaining a serverless app can be tricky; the\ntooling often has critical gaps.\n\n\nAWS's [SAM (Serverless Application\nModel)](https://aws.amazon.com/serverless/sam/) is an open source framework\nthat makes it easier to define AWS resources – such as Lambda functions, API\nGateway APIs and DynamoDB tables – commonly used in serverless applications.\nOnce you lay out your app in a SAM template, the next thing you need is a\nconsistent, repeatable way to get that template off your laptop and deployed\nin the cloud.\n\n\nYou need CI/CD.\n\n\nI've used several different [CI/CD systems](/topics/ci-cd/) to automate SAM\ndeployments, and I always look for the following features:\n\n\n- A single deployment pipeline that can build once and securely deploy to\nmultiple AWS accounts (dev, staging, prod).\n\n- Dynamic feature branch deployments, so serverless devs can collaborate in\nthe cloud without stepping on each other.\n\n- Automated cleanup of feature deployments.\n\n- Review of our SAM application directly integrated with the CI/CD tool's\nuser interface.\n\n- Manual confirmation before code is released into production.\n\n\nIn this post, we'll find out how [GitLab\nCI](/solutions/continuous-integration/) can check these boxes on its way to\ndelivering effective CI/CD for AWS SAM. You can follow along using [the\nofficial example code, available\nhere](https://gitlab.com/gitlab-examples/aws-sam).\n\n\n## Multi-account AWS deployments\n\n\nWe'll want to set up our deployment pipeline across multiple AWS accounts,\nbecause accounts are the only true security boundary in AWS. We don't want\nto run any risk of deploying prod data in dev, or vice versa. Our\nmulti-account setup will look something like this:\n\n\nAny time we work with multiple AWS accounts, we need cross-account [IAM\nroles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) in\norder to authorize deployments. We'll handle this task through the following\nsteps. (All referenced scripts are available in the [example\nrepo](https://gitlab.com/gitlab-examples/aws-sam))\n\n\n### 1\\. Establish three AWS accounts for development, staging, and\nproduction deployments\n\n\nYou can use existing AWS accounts if you have them, or [provision new ones\nunder an AWS\nOrganization](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_accounts_create.html).\n\n\n### 2\\. Set up GitLab IAM roles in each account\n\n\nRun the following AWS CLI call with admin credentials in each of the three\naccounts:\n\n\n```\n\naws cloudformation deploy --stack-name GitLabCIRoles --template-file\nsetup-templates/roles.yml --capabilities CAPABILITY_NAMED_IAM\n--parameter-overrides CIAccountID=\"\u003CAWS Account ID where your GitLab CI/CD\nrunner lives>\" CIAccountSTSCondition=\"\u003CThe aws:userid for the IAM principal\nused by the Gitlab runner>\"\n  ```\n\nReplace `CIAccountID` and `CIAccountSTSCondition` as indicated with values\nfrom the AWS account where your GitLab CI/CD runner exists. (Need help\nfinding the `aws:userid` for your runner’s IAM principal? Check out [this\nguide](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#principaltable).)\n\n\nThis CloudFormation template defines two roles: `SharedServiceRole` and\n`SharedDeploymentRole`. The `SharedServiceRole` is assumed by the GitLab\nCI/CD runner when calling the AWS CloudFormation service. This role trusts\nthe GitLab CI/CD runner's role. It has permissions to call the\nCloudFormation service, pass a role via IAM, and access S3 and CloudFront:\nnothing else. This role is not privileged enough to do arbitrary AWS\ndeployments on its own.\n\n\nThe `SharedDeploymentRole`, on the other hand, has full administrative\naccess to perform any AWS action. A such, it cannot be assumed directly by\nthe GitLab CI/CD runner. Instead, this role must be \"passed\" to\nCloudFormation using the service's `RoleArn` parameter. The CloudFormation\nservice trusts the `SharedDeploymentRole` and can use it to deploy whatever\nresources are needed as part of the pipeline.\n\n\n### 3\\. Create an S3 bucket for CI artifacts\n\n\nGrab the AWS account ID for each of your development, staging, and\nproduction accounts, then deploy this CloudFormation template **in the\naccount where your GitLab CI/CD Runner exists**:\n\n\n`aws cloudformation deploy --stack-name GitLabCIBucket --template-file\nsetup-templates/ci-bucket.yml --parameter-overrides DevAwsAccountId=\"\u003CAWS\nAccount ID for dev>\" StagingAwsAccountId=\"\u003CAWS Account ID for staging>\"\nProdAwsAccountId=\"\u003CAWS Account ID for prod>\" ArtifactBucketName=\"\u003CA unique\nname for your bucket>\"`\n\n\nThis CloudFormation template creates a centralized S3 bucket which holds the\nartifacts created during your pipeline run. Artifacts are created once for\neach branch push and reused between staging and production. The bucket\npolicy allows the development, test, and production accounts to reference\nthe same artifacts when deploying CloudFormation stacks -- checking off our\n\"build once, deploy many\" requirement.\n\n\n### 4\\. Assume the `SharedServiceRole` before making any cross-account AWS\ncalls\n\nWe have provided the script `assume-role.sh`, which will assume the provided\nrole and export temporary AWS credentials to the current shell. It is\nsourced in the various `.gitlab-ci.yml` build scripts.\n\n\n## Single deployment pipeline\n\n\nThat brings us to the `.gitlab-ci.yml` file you can see at the root of our\nexample repository. GitLab CI/CD is smart enough to dynamically create and\nexecute the pipeline based on that template when we push code to GitLab. The\nfile has a number of variables at the top that you can tweak based on your\nenvironment specifics.\n\n\n### Stages\n\n\nOur Gitlab CI/CD pipeline contains seven possible stages, defined as\nfollows:\n\n\n![Multi-account AWS SAM deployment model with GitLab\nCI](https://about.gitlab.com/images/blogimages/multi-account-aws-sam/deployment-model.png){:\n.shadow.medium.center}\n\n\n```yaml\n\nstages:\n - test\n - build-dev\n - deploy-dev\n - build-staging\n - deploy-staging\n - create-change-prod\n - execute-change-prod\n```\n\n\n![Deployment lifecycle\nstages](https://about.gitlab.com/images/blogimages/multi-account-aws-sam/deployment-lifecycle-stages.png){:\n.shadow.medium.center}\n\n\n\"Stages\" are used as a control flow mechanism when building the pipeline.\nMultiple build jobs within a stage will run in parallel, but all jobs in a\ngiven stage must complete before any jobs belonging to the next stage in the\nlist can be executed.\n\n\nAlthough seven stages are defined here, only certain ones will execute,\ndepending on what kind of Git action triggered our pipeline. We effectively\nhave three stages to any deployment: a \"test\" phase where we run unit tests\nand dependency scans against our code, a \"build\" phase that packages our SAM\ntemplate, and a \"deploy\" phase split into two parts: creating a\n[CloudFormation change\nset](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html)\nand then executing that change set in the target environment.\n\n\n#### Test\n\n\nOur `.gitlab-ci.yml` file currently runs two types of tests: unit tests\nagainst our code, and dependency scans against our third-party Python\npackages.\n\n\n##### Unit tests\n\n\nUnit tests run on every branch pushed to the remote repository. This\nbehavior is defined by the `only: branches` property in the job shown below:\n\n\n```yaml\n\ntest:unit:\n stage: test\n only:\n   - branches\n script: |\n   if test -f requirements.txt; then\n       pip install -r requirements.txt\n   fi\n   python -m pytest --ignore=functions/\n```\n\n\nEvery GitLab CI/CD job runs a script. Here, we install any dependencies,\nthen execute Python unit tests.\n\n\n##### Dependency scans\n\n\n[Dependency\nscans](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/),\nwhich can take a few minutes, run only on code pushed to the master branch;\nit would be counterproductive for developers to wait on them every time they\nwant to test code.\n\n\nThese scans use a hardcoded, standard Docker image to mount the code and run\n\"Docker in Docker\" checks against a database of known package\nvulnerabilities. If a vulnerability is found, the pipeline will log the\nerror without stopping the build (that's what the `allow-failure: true`\nproperty does).\n\n\n#### Build\n\n\nThe build stage turns our SAM template into CloudFormation and turns our\nPython code into a valid AWS Lambda deployment package. For example, here's\nthe `build:dev` job:\n\n\n```yaml\n\nbuild:dev:\n stage: build-dev\n \u003C\u003C: *build_script\n variables:\n   \u003C\u003C: *dev_variables\n artifacts:\n   paths:\n     - deployment.yml\n   expire_in: 1 week\n only:\n   - branches\n except:\n   - master\n```\n\n\nWhat's going on here? Note first the combination of `only` and `except`\nproperties to ensure that our development builds happen only on pushes to\nbranches that aren't `master`. We're referring to `dev_variables`, the set\nof development-specific variables defined at the top of `.gitlab-ci.yml`.\nAnd we're running a script, pointed to by `build_script`, which packages our\nSAM template and code for deployment using the `aws cloudformation package`\nCLI call.\n\n\nThe artifact `deployment.yml` is the CloudFormation template output by our\npackage command. It has all the implicit SAM magic expanded into\nCloudFormation resources. By managing it as an artifact, we can pass it\nalong to further steps in the build pipeline, even though it isn't committed\nto our repository.\n\n\n#### Deploy\n\nOur deployments use AWS CloudFormation to deploy the packaged application in\na target AWS environment.\n\n\nIn development and staging environments, we use the `aws cloudformation\ndeploy` command to create a change set and immediately execute it. In\nproduction, we put a manual \"wait\" in the pipeline at this point so you have\nthe opportunity to review the change set before moving onto the \"Execute\"\nstep, which actually calls `aws cloudformation execute-changeset` to update\nthe underlying stack.\n\n\nOur deployment jobs use a helper script, committed to the top level of the\nexample repository, called `cfn-wait.sh`. This script is needed because the\n`aws cloudformation` commands don't wait for results; they report success as\nsoon as the stack operation starts. To properly record the deployment\nresults in our job, we need a script that polls the CloudFormation service\nand throws an error if the deployment or update fails.\n\n\n## Dynamic feature branch deployments and Review Apps\n\n\n![Dynamic feature branch deployments and Review\nApps](https://about.gitlab.com/images/blogimages/multi-account-aws-sam/dynamic-feature-branch-deployments.png){:\n.shadow.medium.center}\n\n\nWhen a non-master branch is pushed to GitLab, our pipeline runs tests,\nbuilds the [updated source\ncode](/solutions/source-code-management/), and deploys and/or\nupdates the changed CloudFormation resources in the development AWS account.\nWhen the branch is merged into master, or if someone clicks the \"Stop\"\nbutton next to the branch's environment in GitLab CI, the CloudFormation\nstack will be torn down automatically.\n\n\nIt is perfectly possible, and indeed desirable, to have multiple development\nfeature branches simultaneously deployed as live environments for more\nefficient parallel feature development and QA. The serverless model makes\nthis a cost-effective strategy for collaborating in the cloud.\n\n\nIf we are dynamically deploying our application on every branch push, we\nmight like to view it as part of our interaction with the GitLab console\n(such as during a code review). GitLab supports this with a nifty feature\ncalled [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/). Review\nApps allow you to specify an \"environment\" as part of a deployment job, as\nseen in our `deploy:dev` job below:\n\n\n```yaml\n\ndeploy:dev:\n \u003C\u003C: *deploy_script\n stage: deploy-dev\n dependencies:\n   - build:dev\n variables:\n   \u003C\u003C: *dev_variables\n environment:\n   name: review/$CI_COMMIT_REF_NAME\n   url: https://${CI_COMMIT_REF_NAME}.${DEV_HOSTED_ZONE_NAME}/services\n   on_stop: stop:dev\n only:\n   - branches\n except:\n   - master\n```\n\n\nThe link specified in the `url` field of the `environment` property will be\naccessible in the `Environments` section of GitLab CI/CD or on any merge\nrequest of the associated branch. (In the case of the sample SAM application\nprovided with our example, since we don't have a front end to view, the link\njust takes you to a GET request for the `/services` API endpoint and should\ndisplay some raw JSON in your browser.)\n\n\n![Link to live\nenvironment](https://about.gitlab.com/images/blogimages/multi-account-aws-sam/link-live-environment.png){:\n.shadow.medium.center}\n\n\nThe `on_stop` property specifies what happens when you \"shut down\" the\nenvironment in GitLab CI. This can be done manually or by deleting the\nassociated branch. In the case above, we have stopped behavior for dev\nenvironments linked to a separate job called `stop:dev`:\n\n\n```yaml\n\nstop:dev:\n stage: deploy-dev\n variables:\n   GIT_STRATEGY: none\n   \u003C\u003C: *dev_variables\n \u003C\u003C: *shutdown_script\n when: manual\n environment:\n   name: review/$CI_COMMIT_REF_NAME\n   action: stop\n only:\n   - branches\n except:\n   - master\n```\n\n\nThis job launches the `shutdown_script` script, which calls `aws\ncloudformation teardown` to clean up the SAM deployment.\n\n\nFor safety's sake, there is no automated teardown of staging or production\nenvironments.\n\n\n## Production releases\n\n\n![Production\nreleases](https://about.gitlab.com/images/blogimages/multi-account-aws-sam/production-releases.png){:\n.shadow.medium.center}\n\n\nWhen a change is merged into the master branch, the code is built, tested\n(including dependency scans) and deployed to the staging environment. This\nis a separate, stable environment that developers, QA, and others can use to\nverify changes before attempting to deploy in production.\n\n\n![Staging\nenvironment](https://about.gitlab.com/images/blogimages/multi-account-aws-sam/staging-environment.png){:\n.shadow.medium.center}\n\n\nAfter deploying code to the staging environment, the pipeline will create a\nchange set for the production stack, and then pause for a manual\nintervention. A human user must click a button in the Gitlab CI/CD\n\"Environments\" view to execute the final change set.\n\n\n## Now what?\n\n\nStep back and take a deep breath – that was a lot of information! Let's not\nlose sight of what we've done here: we've defined a secure, multi-account\nAWS deployment pipeline in our GitLab repo, integrated tests, builds and\ndeployments, and successfully rolled a SAM-defined serverless app to the\ncloud. Not bad for a few lines of config!\n\n\nThe next step is to try this on your own. If you'd like to start with our\nsample \"AWS News\" application, you can simply run `sam init --location\ngit+https://gitlab.com/gitlab-examples/aws-sam` to download the project on\nyour local machine. The AWS News app contains a stripped-down,\nsingle-account version of the `gitlab-ci.yml` file discussed in this post,\nso you can try out deployments with minimal setup needed.\n\n\n## Further reading\n\n\nWe have barely scratched the surface of GitLab CI/CD and AWS SAM in this\npost. Here are some interesting readings if you would like to take your work\nto the next level:\n\n\n### SAM\n\n\n- [Implementing safe AWS Lambda deployments with AWS SAM and\nCodeDeploy](https://aws.amazon.com/blogs/compute/implementing-safe-aws-lambda-deployments-with-aws-codedeploy/)\n\n- [Running and debugging serverless applications locally using the AWS SAM\nCLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-test-and-debug.html)\n\n\n### GitLab CI\n\n\n- [Setting up a GitLab Runner on\nEC2](https://hackernoon.com/configuring-gitlab-ci-on-aws-ec2-using-docker-7c359d513a46)\n\n- [Scheduled\npipelines](https://docs.gitlab.com/ee/ci/pipelines/schedules.html)\n\n- [ChatOps](https://docs.gitlab.com/ee/ci/chatops/)\n\n\nPlease [let me know](https://twitter.com/forrestbrazeal) if you have further\nquestions!\n\n\n### About the guest author\n\n\nForrest Brazeal is an [AWS Serverless\nHero](https://aws.amazon.com/developer/community/heroes/forrest-brazeal/).\nHe currently works as a senior cloud architect at\n[Trek10](https://trek10.com), an AWS Advanced Consulting Partner. You can\n[read more about Trek10's GitLab journey here](/customers/trek10/).\n","engineering",[23,24,25,26,27,28],"CI/CD","demo","integrations","open source","production","user stories",{"slug":30,"featured":6,"template":31},"multi-account-aws-sam-deployments-with-gitlab-ci","BlogPost","content:en-us:blog:multi-account-aws-sam-deployments-with-gitlab-ci.yml","yaml","Multi Account Aws Sam Deployments With Gitlab Ci","content","en-us/blog/multi-account-aws-sam-deployments-with-gitlab-ci.yml","en-us/blog/multi-account-aws-sam-deployments-with-gitlab-ci","yml",{"_path":40,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"data":42,"_id":462,"_type":33,"title":463,"_source":35,"_file":464,"_stem":465,"_extension":38},"/shared/en-us/main-navigation","en-us",{"logo":43,"freeTrial":48,"sales":53,"login":58,"items":63,"search":393,"minimal":424,"duo":443,"pricingDeployment":452},{"config":44},{"href":45,"dataGaName":46,"dataGaLocation":47},"/","gitlab logo","header",{"text":49,"config":50},"Get free trial",{"href":51,"dataGaName":52,"dataGaLocation":47},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":54,"config":55},"Talk to sales",{"href":56,"dataGaName":57,"dataGaLocation":47},"/sales/","sales",{"text":59,"config":60},"Sign in",{"href":61,"dataGaName":62,"dataGaLocation":47},"https://gitlab.com/users/sign_in/","sign in",[64,108,205,210,314,374],{"text":65,"config":66,"cards":68,"footer":91},"Platform",{"dataNavLevelOne":67},"platform",[69,75,83],{"title":65,"description":70,"link":71},"The most comprehensive AI-powered DevSecOps Platform",{"text":72,"config":73},"Explore our Platform",{"href":74,"dataGaName":67,"dataGaLocation":47},"/platform/",{"title":76,"description":77,"link":78},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":79,"config":80},"Meet GitLab Duo",{"href":81,"dataGaName":82,"dataGaLocation":47},"/gitlab-duo/","gitlab duo ai",{"title":84,"description":85,"link":86},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":87,"config":88},"Learn more",{"href":89,"dataGaName":90,"dataGaLocation":47},"/why-gitlab/","why gitlab",{"title":92,"items":93},"Get started with",[94,99,104],{"text":95,"config":96},"Platform Engineering",{"href":97,"dataGaName":98,"dataGaLocation":47},"/solutions/platform-engineering/","platform engineering",{"text":100,"config":101},"Developer Experience",{"href":102,"dataGaName":103,"dataGaLocation":47},"/developer-experience/","Developer experience",{"text":105,"config":106},"MLOps",{"href":107,"dataGaName":105,"dataGaLocation":47},"/topics/devops/the-role-of-ai-in-devops/",{"text":109,"left":110,"config":111,"link":113,"lists":117,"footer":187},"Product",true,{"dataNavLevelOne":112},"solutions",{"text":114,"config":115},"View all Solutions",{"href":116,"dataGaName":112,"dataGaLocation":47},"/solutions/",[118,142,166],{"title":119,"description":120,"link":121,"items":126},"Automation","CI/CD and automation to accelerate deployment",{"config":122},{"icon":123,"href":124,"dataGaName":125,"dataGaLocation":47},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[127,130,134,138],{"text":23,"config":128},{"href":129,"dataGaLocation":47,"dataGaName":23},"/solutions/continuous-integration/",{"text":131,"config":132},"AI-Assisted Development",{"href":81,"dataGaLocation":47,"dataGaName":133},"AI assisted development",{"text":135,"config":136},"Source Code Management",{"href":137,"dataGaLocation":47,"dataGaName":135},"/solutions/source-code-management/",{"text":139,"config":140},"Automated Software Delivery",{"href":124,"dataGaLocation":47,"dataGaName":141},"Automated software delivery",{"title":143,"description":144,"link":145,"items":150},"Security","Deliver code faster without compromising security",{"config":146},{"href":147,"dataGaName":148,"dataGaLocation":47,"icon":149},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[151,156,161],{"text":152,"config":153},"Application Security Testing",{"href":154,"dataGaName":155,"dataGaLocation":47},"/solutions/application-security-testing/","Application security testing",{"text":157,"config":158},"Software Supply Chain Security",{"href":159,"dataGaLocation":47,"dataGaName":160},"/solutions/supply-chain/","Software supply chain security",{"text":162,"config":163},"Software Compliance",{"href":164,"dataGaName":165,"dataGaLocation":47},"/solutions/software-compliance/","software compliance",{"title":167,"link":168,"items":173},"Measurement",{"config":169},{"icon":170,"href":171,"dataGaName":172,"dataGaLocation":47},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[174,178,182],{"text":175,"config":176},"Visibility & Measurement",{"href":171,"dataGaLocation":47,"dataGaName":177},"Visibility and Measurement",{"text":179,"config":180},"Value Stream Management",{"href":181,"dataGaLocation":47,"dataGaName":179},"/solutions/value-stream-management/",{"text":183,"config":184},"Analytics & Insights",{"href":185,"dataGaLocation":47,"dataGaName":186},"/solutions/analytics-and-insights/","Analytics and insights",{"title":188,"items":189},"GitLab for",[190,195,200],{"text":191,"config":192},"Enterprise",{"href":193,"dataGaLocation":47,"dataGaName":194},"/enterprise/","enterprise",{"text":196,"config":197},"Small Business",{"href":198,"dataGaLocation":47,"dataGaName":199},"/small-business/","small business",{"text":201,"config":202},"Public Sector",{"href":203,"dataGaLocation":47,"dataGaName":204},"/solutions/public-sector/","public sector",{"text":206,"config":207},"Pricing",{"href":208,"dataGaName":209,"dataGaLocation":47,"dataNavLevelOne":209},"/pricing/","pricing",{"text":211,"config":212,"link":214,"lists":218,"feature":301},"Resources",{"dataNavLevelOne":213},"resources",{"text":215,"config":216},"View all resources",{"href":217,"dataGaName":213,"dataGaLocation":47},"/resources/",[219,251,273],{"title":220,"items":221},"Getting started",[222,227,232,237,242,247],{"text":223,"config":224},"Install",{"href":225,"dataGaName":226,"dataGaLocation":47},"/install/","install",{"text":228,"config":229},"Quick start guides",{"href":230,"dataGaName":231,"dataGaLocation":47},"/get-started/","quick setup checklists",{"text":233,"config":234},"Learn",{"href":235,"dataGaLocation":47,"dataGaName":236},"https://university.gitlab.com/","learn",{"text":238,"config":239},"Product documentation",{"href":240,"dataGaName":241,"dataGaLocation":47},"https://docs.gitlab.com/","product documentation",{"text":243,"config":244},"Best practice videos",{"href":245,"dataGaName":246,"dataGaLocation":47},"/getting-started-videos/","best practice videos",{"text":248,"config":249},"Integrations",{"href":250,"dataGaName":25,"dataGaLocation":47},"/integrations/",{"title":252,"items":253},"Discover",[254,259,263,268],{"text":255,"config":256},"Customer success stories",{"href":257,"dataGaName":258,"dataGaLocation":47},"/customers/","customer success stories",{"text":260,"config":261},"Blog",{"href":262,"dataGaName":5,"dataGaLocation":47},"/blog/",{"text":264,"config":265},"Remote",{"href":266,"dataGaName":267,"dataGaLocation":47},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":269,"config":270},"TeamOps",{"href":271,"dataGaName":272,"dataGaLocation":47},"/teamops/","teamops",{"title":274,"items":275},"Connect",[276,281,286,291,296],{"text":277,"config":278},"GitLab Services",{"href":279,"dataGaName":280,"dataGaLocation":47},"/services/","services",{"text":282,"config":283},"Community",{"href":284,"dataGaName":285,"dataGaLocation":47},"/community/","community",{"text":287,"config":288},"Forum",{"href":289,"dataGaName":290,"dataGaLocation":47},"https://forum.gitlab.com/","forum",{"text":292,"config":293},"Events",{"href":294,"dataGaName":295,"dataGaLocation":47},"/events/","events",{"text":297,"config":298},"Partners",{"href":299,"dataGaName":300,"dataGaLocation":47},"/partners/","partners",{"backgroundColor":302,"textColor":303,"text":304,"image":305,"link":309},"#2f2a6b","#fff","Insights for the future of software development",{"altText":306,"config":307},"the source promo card",{"src":308},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":310,"config":311},"Read the latest",{"href":312,"dataGaName":313,"dataGaLocation":47},"/the-source/","the source",{"text":315,"config":316,"lists":318},"Company",{"dataNavLevelOne":317},"company",[319],{"items":320},[321,326,332,334,339,344,349,354,359,364,369],{"text":322,"config":323},"About",{"href":324,"dataGaName":325,"dataGaLocation":47},"/company/","about",{"text":327,"config":328,"footerGa":331},"Jobs",{"href":329,"dataGaName":330,"dataGaLocation":47},"/jobs/","jobs",{"dataGaName":330},{"text":292,"config":333},{"href":294,"dataGaName":295,"dataGaLocation":47},{"text":335,"config":336},"Leadership",{"href":337,"dataGaName":338,"dataGaLocation":47},"/company/team/e-group/","leadership",{"text":340,"config":341},"Team",{"href":342,"dataGaName":343,"dataGaLocation":47},"/company/team/","team",{"text":345,"config":346},"Handbook",{"href":347,"dataGaName":348,"dataGaLocation":47},"https://handbook.gitlab.com/","handbook",{"text":350,"config":351},"Investor relations",{"href":352,"dataGaName":353,"dataGaLocation":47},"https://ir.gitlab.com/","investor relations",{"text":355,"config":356},"Trust Center",{"href":357,"dataGaName":358,"dataGaLocation":47},"/security/","trust center",{"text":360,"config":361},"AI Transparency Center",{"href":362,"dataGaName":363,"dataGaLocation":47},"/ai-transparency-center/","ai transparency center",{"text":365,"config":366},"Newsletter",{"href":367,"dataGaName":368,"dataGaLocation":47},"/company/contact/","newsletter",{"text":370,"config":371},"Press",{"href":372,"dataGaName":373,"dataGaLocation":47},"/press/","press",{"text":375,"config":376,"lists":377},"Contact us",{"dataNavLevelOne":317},[378],{"items":379},[380,383,388],{"text":54,"config":381},{"href":56,"dataGaName":382,"dataGaLocation":47},"talk to sales",{"text":384,"config":385},"Get help",{"href":386,"dataGaName":387,"dataGaLocation":47},"/support/","get help",{"text":389,"config":390},"Customer portal",{"href":391,"dataGaName":392,"dataGaLocation":47},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":394,"login":395,"suggestions":402},"Close",{"text":396,"link":397},"To search repositories and projects, login to",{"text":398,"config":399},"gitlab.com",{"href":61,"dataGaName":400,"dataGaLocation":401},"search login","search",{"text":403,"default":404},"Suggestions",[405,407,411,413,417,421],{"text":76,"config":406},{"href":81,"dataGaName":76,"dataGaLocation":401},{"text":408,"config":409},"Code Suggestions (AI)",{"href":410,"dataGaName":408,"dataGaLocation":401},"/solutions/code-suggestions/",{"text":23,"config":412},{"href":129,"dataGaName":23,"dataGaLocation":401},{"text":414,"config":415},"GitLab on AWS",{"href":416,"dataGaName":414,"dataGaLocation":401},"/partners/technology-partners/aws/",{"text":418,"config":419},"GitLab on Google Cloud",{"href":420,"dataGaName":418,"dataGaLocation":401},"/partners/technology-partners/google-cloud-platform/",{"text":422,"config":423},"Why GitLab?",{"href":89,"dataGaName":422,"dataGaLocation":401},{"freeTrial":425,"mobileIcon":430,"desktopIcon":435,"secondaryButton":438},{"text":426,"config":427},"Start free trial",{"href":428,"dataGaName":52,"dataGaLocation":429},"https://gitlab.com/-/trials/new/","nav",{"altText":431,"config":432},"Gitlab Icon",{"src":433,"dataGaName":434,"dataGaLocation":429},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":431,"config":436},{"src":437,"dataGaName":434,"dataGaLocation":429},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":439,"config":440},"Get Started",{"href":441,"dataGaName":442,"dataGaLocation":429},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":444,"mobileIcon":448,"desktopIcon":450},{"text":445,"config":446},"Learn more about GitLab Duo",{"href":81,"dataGaName":447,"dataGaLocation":429},"gitlab duo",{"altText":431,"config":449},{"src":433,"dataGaName":434,"dataGaLocation":429},{"altText":431,"config":451},{"src":437,"dataGaName":434,"dataGaLocation":429},{"freeTrial":453,"mobileIcon":458,"desktopIcon":460},{"text":454,"config":455},"Back to pricing",{"href":208,"dataGaName":456,"dataGaLocation":429,"icon":457},"back to pricing","GoBack",{"altText":431,"config":459},{"src":433,"dataGaName":434,"dataGaLocation":429},{"altText":431,"config":461},{"src":437,"dataGaName":434,"dataGaLocation":429},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":467,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"title":468,"button":469,"image":474,"config":478,"_id":480,"_type":33,"_source":35,"_file":481,"_stem":482,"_extension":38},"/shared/en-us/banner","is now in public beta!",{"text":470,"config":471},"Try the Beta",{"href":472,"dataGaName":473,"dataGaLocation":47},"/gitlab-duo/agent-platform/","duo banner",{"altText":475,"config":476},"GitLab Duo Agent Platform",{"src":477},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":479},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":484,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"data":485,"_id":689,"_type":33,"title":690,"_source":35,"_file":691,"_stem":692,"_extension":38},"/shared/en-us/main-footer",{"text":486,"source":487,"edit":493,"contribute":498,"config":503,"items":508,"minimal":681},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":488,"config":489},"View page source",{"href":490,"dataGaName":491,"dataGaLocation":492},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":494,"config":495},"Edit this page",{"href":496,"dataGaName":497,"dataGaLocation":492},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":499,"config":500},"Please contribute",{"href":501,"dataGaName":502,"dataGaLocation":492},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":504,"facebook":505,"youtube":506,"linkedin":507},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[509,532,588,617,651],{"title":65,"links":510,"subMenu":515},[511],{"text":512,"config":513},"DevSecOps platform",{"href":74,"dataGaName":514,"dataGaLocation":492},"devsecops platform",[516],{"title":206,"links":517},[518,522,527],{"text":519,"config":520},"View plans",{"href":208,"dataGaName":521,"dataGaLocation":492},"view plans",{"text":523,"config":524},"Why Premium?",{"href":525,"dataGaName":526,"dataGaLocation":492},"/pricing/premium/","why premium",{"text":528,"config":529},"Why Ultimate?",{"href":530,"dataGaName":531,"dataGaLocation":492},"/pricing/ultimate/","why ultimate",{"title":533,"links":534},"Solutions",[535,540,542,544,549,554,558,561,565,570,572,575,578,583],{"text":536,"config":537},"Digital transformation",{"href":538,"dataGaName":539,"dataGaLocation":492},"/topics/digital-transformation/","digital transformation",{"text":152,"config":541},{"href":154,"dataGaName":152,"dataGaLocation":492},{"text":141,"config":543},{"href":124,"dataGaName":125,"dataGaLocation":492},{"text":545,"config":546},"Agile development",{"href":547,"dataGaName":548,"dataGaLocation":492},"/solutions/agile-delivery/","agile delivery",{"text":550,"config":551},"Cloud transformation",{"href":552,"dataGaName":553,"dataGaLocation":492},"/topics/cloud-native/","cloud transformation",{"text":555,"config":556},"SCM",{"href":137,"dataGaName":557,"dataGaLocation":492},"source code management",{"text":23,"config":559},{"href":129,"dataGaName":560,"dataGaLocation":492},"continuous integration & delivery",{"text":562,"config":563},"Value stream management",{"href":181,"dataGaName":564,"dataGaLocation":492},"value stream management",{"text":566,"config":567},"GitOps",{"href":568,"dataGaName":569,"dataGaLocation":492},"/solutions/gitops/","gitops",{"text":191,"config":571},{"href":193,"dataGaName":194,"dataGaLocation":492},{"text":573,"config":574},"Small business",{"href":198,"dataGaName":199,"dataGaLocation":492},{"text":576,"config":577},"Public sector",{"href":203,"dataGaName":204,"dataGaLocation":492},{"text":579,"config":580},"Education",{"href":581,"dataGaName":582,"dataGaLocation":492},"/solutions/education/","education",{"text":584,"config":585},"Financial services",{"href":586,"dataGaName":587,"dataGaLocation":492},"/solutions/finance/","financial services",{"title":211,"links":589},[590,592,594,596,599,601,603,605,607,609,611,613,615],{"text":223,"config":591},{"href":225,"dataGaName":226,"dataGaLocation":492},{"text":228,"config":593},{"href":230,"dataGaName":231,"dataGaLocation":492},{"text":233,"config":595},{"href":235,"dataGaName":236,"dataGaLocation":492},{"text":238,"config":597},{"href":240,"dataGaName":598,"dataGaLocation":492},"docs",{"text":260,"config":600},{"href":262,"dataGaName":5,"dataGaLocation":492},{"text":255,"config":602},{"href":257,"dataGaName":258,"dataGaLocation":492},{"text":264,"config":604},{"href":266,"dataGaName":267,"dataGaLocation":492},{"text":277,"config":606},{"href":279,"dataGaName":280,"dataGaLocation":492},{"text":269,"config":608},{"href":271,"dataGaName":272,"dataGaLocation":492},{"text":282,"config":610},{"href":284,"dataGaName":285,"dataGaLocation":492},{"text":287,"config":612},{"href":289,"dataGaName":290,"dataGaLocation":492},{"text":292,"config":614},{"href":294,"dataGaName":295,"dataGaLocation":492},{"text":297,"config":616},{"href":299,"dataGaName":300,"dataGaLocation":492},{"title":315,"links":618},[619,621,623,625,627,629,631,635,640,642,644,646],{"text":322,"config":620},{"href":324,"dataGaName":317,"dataGaLocation":492},{"text":327,"config":622},{"href":329,"dataGaName":330,"dataGaLocation":492},{"text":335,"config":624},{"href":337,"dataGaName":338,"dataGaLocation":492},{"text":340,"config":626},{"href":342,"dataGaName":343,"dataGaLocation":492},{"text":345,"config":628},{"href":347,"dataGaName":348,"dataGaLocation":492},{"text":350,"config":630},{"href":352,"dataGaName":353,"dataGaLocation":492},{"text":632,"config":633},"Sustainability",{"href":634,"dataGaName":632,"dataGaLocation":492},"/sustainability/",{"text":636,"config":637},"Diversity, inclusion and belonging (DIB)",{"href":638,"dataGaName":639,"dataGaLocation":492},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":355,"config":641},{"href":357,"dataGaName":358,"dataGaLocation":492},{"text":365,"config":643},{"href":367,"dataGaName":368,"dataGaLocation":492},{"text":370,"config":645},{"href":372,"dataGaName":373,"dataGaLocation":492},{"text":647,"config":648},"Modern Slavery Transparency Statement",{"href":649,"dataGaName":650,"dataGaLocation":492},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":652,"links":653},"Contact Us",[654,657,659,661,666,671,676],{"text":655,"config":656},"Contact an expert",{"href":56,"dataGaName":57,"dataGaLocation":492},{"text":384,"config":658},{"href":386,"dataGaName":387,"dataGaLocation":492},{"text":389,"config":660},{"href":391,"dataGaName":392,"dataGaLocation":492},{"text":662,"config":663},"Status",{"href":664,"dataGaName":665,"dataGaLocation":492},"https://status.gitlab.com/","status",{"text":667,"config":668},"Terms of use",{"href":669,"dataGaName":670,"dataGaLocation":492},"/terms/","terms of use",{"text":672,"config":673},"Privacy statement",{"href":674,"dataGaName":675,"dataGaLocation":492},"/privacy/","privacy statement",{"text":677,"config":678},"Cookie preferences",{"dataGaName":679,"dataGaLocation":492,"id":680,"isOneTrustButton":110},"cookie preferences","ot-sdk-btn",{"items":682},[683,685,687],{"text":667,"config":684},{"href":669,"dataGaName":670,"dataGaLocation":492},{"text":672,"config":686},{"href":674,"dataGaName":675,"dataGaLocation":492},{"text":677,"config":688},{"dataGaName":679,"dataGaLocation":492,"id":680,"isOneTrustButton":110},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[694],{"_path":695,"_dir":696,"_draft":6,"_partial":6,"_locale":7,"content":697,"config":700,"_id":702,"_type":33,"title":18,"_source":35,"_file":703,"_stem":704,"_extension":38},"/en-us/blog/authors/forrest-brazeal","authors",{"name":18,"config":698},{"headshot":7,"ctfId":699},"fbrazeal",{"template":701},"BlogAuthor","content:en-us:blog:authors:forrest-brazeal.yml","en-us/blog/authors/forrest-brazeal.yml","en-us/blog/authors/forrest-brazeal",{"_path":706,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"header":707,"eyebrow":708,"blurb":709,"button":710,"secondaryButton":714,"_id":716,"_type":33,"title":717,"_source":35,"_file":718,"_stem":719,"_extension":38},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":49,"config":711},{"href":712,"dataGaName":52,"dataGaLocation":713},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":54,"config":715},{"href":56,"dataGaName":57,"dataGaLocation":713},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758326262260]