1"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[11725],{40366:(e,n,a)=>{a.r(n),a.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>i,metadata:()=>t,toc:()=>d});const t=JSON.parse('{"id":"guides/database-per-tenant/target-groups","title":"Defining Target Groups for Multi-Tenant Deployments","description":"Learn how to define target groups in Atlas for managing multiple tenant databases in a Database per Tenant architecture.","source":"@site/../md/guides/database-per-tenant/02-db-groups.mdx","sourceDirName":"guides/database-per-tenant","slug":"/guides/database-per-tenant/target-groups","permalink":"/guides/database-per-tenant/target-groups","draft":false,"unlisted":false,"tags":[{"inline":true,"label":"database-per-tenant","permalink":"/tags/database-per-tenant"},{"inline":true,"label":"multi-tenant","permalink":"/tags/multi-tenant"},{"inline":true,"label":"for_each","permalink":"/tags/for-each"}],"version":"current","sidebarPosition":2,"frontMatter":{"title":"Defining Target Groups for Multi-Tenant Deployments","id":"target-groups","slug":"/guides/database-per-tenant/target-groups","tags":["database-per-tenant","multi-tenant","for_each"],"description":"Learn how to define target groups in Atlas for managing multiple tenant databases in a Database per Tenant architecture.","keywords":["database-per-tenant","multi-tenant","target-groups","for_each meta-argument","env blocks","runtimevar"]},"sidebar":"guides","previous":{"title":"Introduction","permalink":"/guides/database-per-tenant/intro"},"next":{"title":"Deploying","permalink":"/guides/database-per-tenant/deploying"}}');var s=a(74848),r=a(71184);const i={title:"Defining Target Groups for Multi-Tenant Deployments",id:"target-groups",slug:"/guides/database-per-tenant/target-groups",tags:["database-per-tenant","multi-tenant","for_each"],description:"Learn how to define target groups in Atlas for managing multiple tenant databases in a Database per Tenant architecture.",keywords:["database-per-tenant","multi-tenant","target-groups","for_each meta-argument","env blocks","runtimevar"]},o=void 0,l={},d=[{value:"<code>env</code> blocks and <code>for_each</code> meta-arguments",id:"env-blocks-and-for_each-meta-arguments",level:2},{value:"Dynamically Computing URLs",id:"dynamically-computing-urls",level:2},{value:"Loading data from local JSON files",id:"loading-data-from-local-json-files",level:2},{value:"Loading Data from an API Endpoint",id:"loading-data-from-an-api-endpoint",level:2},{value:"Loading data from a Database Query",id:"loading-data-from-a-database-query",level:2},{value:"Fetching Tenant Metadata",id:"fetching-tenant-metadata",level:3},{value:"Incorporating Sensitive Data",id:"incorporating-sensitive-data",level:2},{value:"Next Steps",id:"next-steps",level:2}];function c(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",li:"li",mdxAdmonitionTitle:"mdxAdmonitionTitle",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(n.p,{children:["In Atlas, a ",(0,s.jsx)(n.strong,{children:"target group"})," is a collection of target databases whose schema is managed together. In a database-per-tenant\narchitecture, each tenant's database is a target database, and all tenant databases are grouped into a target group.\nHowever, you can also group databases by other criteria, such as environment (dev, staging, prod), region, or any other\ncriteria that makes sense for your application."]}),"\n",(0,s.jsx)(n.p,{children:"For example, you might group all databases in the same region into a target group to ensure that schema changes are\napplied consistently across all databases in that region, or to group free-tier databases separately from\npaid-tier databases."}),"\n",(0,s.jsx)(n.p,{children:"Target groups can be defined statically or dynamically loaded from an API endpoint or a database query."}),"\n",(0,s.jsxs)(n.p,{children:["Target groups are defined in the project's ",(0,s.jsx)(n.a,{href:"/atlas-schema/projects",children:(0,s.jsx)(n.code,{children:"atlas.hcl"})})," file and are later used by the Atlas CLI during the deployment\nprocess to determine which databases to deploy to."]}),"\n",(0,s.jsx)(n.p,{children:"Let's review some examples of how to define target groups in Atlas."}),"\n",(0,s.jsxs)(n.h2,{id:"env-blocks-and-for_each-meta-arguments",children:[(0,s.jsx)(n.code,{children:"env"})," blocks and ",(0,s.jsx)(n.code,{children:"for_each"})," meta-arguments"]}),"\n",(0,s.jsxs)(n.p,{children:["Before we jump into various techniques to define target groups, let's first understand the ",(0,s.jsx)(n.code,{children:"for_each"})," meta-argument for\nenvironment blocks in Atlas."]}),"\n",(0,s.jsxs)(n.p,{children:["Environment blocks (",(0,s.jsx)(n.code,{children:"env"})," blocks) are used in Atlas project files (",(0,s.jsx)(n.code,{children:"atlas.hcl"}),") to group configuration settings for\na specific environment. Normally, an ",(0,s.jsx)(n.code,{children:"env"})," block is used to define the URL of a single target database, like so:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'env "dev" {\n url = "postgres://root:pass@localhost:5432/dev"\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["However, using the ",(0,s.jsx)(n.code,{children:"for_each"})," meta-argument, it is possible to define multiple instances of a specific environment block\nby iterating over a list of values. For example:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'locals {\n target_db_urls = [\n "postgres://root:pass@host-1:5432",\n "postgres://root:pass@host-2:5432",\n ]\n}\n\nenv "targets" {\n for_each = toset(local.target_db_urls)\n url = each.value\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["When the ",(0,s.jsx)(n.code,{children:"for_each"})," meta-argument is used, the ",(0,s.jsx)(n.code,{children:"env"})," block is instantiated for each value in the list, and the ",(0,s.jsx)(n.code,{children:"each"}),"\nobject is used to access the current value. In our case, we will get two instances of the ",(0,s.jsx)(n.code,{children:"target"})," block, one for each\nURL in the ",(0,s.jsx)(n.code,{children:"target_db_urls"})," list."]}),"\n",(0,s.jsx)(n.h2,{id:"dynamically-computing-urls",children:"Dynamically Computing URLs"}),"\n",(0,s.jsxs)(n.p,{children:["A technique commonly used in ",(0,s.jsx)(n.code,{children:"atlas.hcl"})," files is to dynamically compile URLs by combining values from various sources.\nFor instance, the database instance URL might be provided as an input variable, with the database name added to it\n
1dynamically. Here's an example:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'variable "db_instance_url" {\n type = string\n}\n\nlocals {\n tenants = ["acme_corp", "widget_inc", "wayne_enterprises", "stark_industries"]\n}\n\nenv "tenants" {\n for_each = toset(local.tenants)\n url = urlsetpath(var.db_instance_url, each.value)\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Let's review the code snippet above:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["We define a variable ",(0,s.jsx)(n.code,{children:"db_instance_url"})," that will be used as the base URL for the database instances.\nThis variable is provided by the user when running the Atlas CLI by providing the ",(0,s.jsx)(n.code,{children:"--var"})," flag."]}),"\n",(0,s.jsxs)(n.li,{children:["We define a local variable ",(0,s.jsx)(n.code,{children:"tenants"})," that contains a list of tenant names."]}),"\n",(0,s.jsxs)(n.li,{children:["We define an ",(0,s.jsx)(n.code,{children:"env"})," block named ",(0,s.jsx)(n.code,{children:"tenants"})," that iterates over the ",(0,s.jsx)(n.code,{children:"tenants"})," list. For each tenant, we set the ",(0,s.jsx)(n.code,{children:"url"}),"\nattribute to the result of the ",(0,s.jsx)(n.code,{children:"urlsetpath"})," function, which combines the ",(0,s.jsx)(n.code,{children:"db_instance_url"})," with the tenant name."]}),"\n"]}),"\n",(0,s.jsxs)(n.admonition,{type:"info",children:[(0,s.jsxs)(n.mdxAdmonitionTitle,{children:["The ",(0,s.jsx)(n.code,{children:"urlsetpath"})," function"]}),(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"urlsetpath"}),' function is a helper function provided by Atlas that allows you to set the "path" part\nof a URL. For example:']}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'urlsetpath("postgres://root:pass@localhost:5432", "mydb")\n# \u21b3 Evaluates to "postgres://root:pass@localhost:5432/mydb"\n\nurlsetpath("mysql://localhost:3306", "mydb")\n# \u21b3 Evaluates to "mysql://localhost:3306/mydb"\n'})})]}),"\n",(0,s.jsx)(n.h2,{id:"loading-data-from-local-json-files",children:"Loading data from local JSON files"}),"\n",(0,s.jsxs)(n.p,{children:["Suppose our list of tenants is stored in a local file named ",(0,s.jsx)(n.code,{children:"tenants.json"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",metastring:'title="tenants.json"',children:'{\n "tenants": [\n "acme_corp",\n "widget_inc",\n "wayne_enterprises",\n "stark_industries"\n ]\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["We can load this data into our ",(0,s.jsx)(n.code,{children:"atlas.hcl"})," file using the ",(0,s.jsx)(n.code,{children:"file"})," and ",(0,s.jsx)(n.code,{children:"jsondecode"})," functions:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",metastring:'title="atlas.hcl"',children:'locals {\n f = file("tenants.json")\n decoded = jsondecode(local.f)\n tenants = local.decoded.tenants\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["Next, we define an environment block for this target group that consumes the ",(0,s.jsx)(n.code,{children:"tenants"})," local variable\ninto the ",(0,s.jsx)(n.code,{children:"for_each"})," argument:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",metastring:'title="atlas.hcl"',children:'env "tenants" {\n for_each = toset(local.tenants)\n url = urlsetpath("postgres://root:pass@localhost:5432", each.value)\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Let's review the code snippet above:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["We define a local variable ",(0,s.jsx)(n.code,{children:"f"})," that reads the contents of the ",(0,s.jsx)(n.code,{children:"tenants.json"})," file."]}),"\n",(0,s.jsxs)(n.li,{children:["Next, we use the ",(0,s.jsx)(n.code,{children:"jsondecode"})," function to parse the JSON content into a structured object."]}),"\n",(0,s.jsxs)(n.li,{children:["We extract the ",(0,s.jsx)(n.code,{children:"tenants"})," array from the decoded JSON object and store it in the ",(0,s.jsx)(n.code,{children:"tenants"})," local variable."]}),"\n",(0,s.jsxs)(n.li,{children:["Finally, we define an ",(0,s.jsx)(n.code,{children:"env"})," block named ",(0,s.jsx)(n.code,{children:"tenants"})," that iterates over the ",(0,s.jsx)(n.code,{children:"tenants"})," list. For each tenant, we set the ",(0,s.jsx)(n.code,{children:"url"}),"\nattribute to the result of the ",(0,s.jsx)(n.code,{children:"urlsetpath"})," function, which combines the base URL with the tenant name."]}),"\n"]}
1),"\n",(0,s.jsx)(n.h2,{id:"loading-data-from-an-api-endpoint",children:"Loading Data from an API Endpoint"}),"\n",(0,s.jsxs)(n.p,{children:["In some cases, you may want to load target groups dynamically from an API endpoint. For example, you might have a\nservice ",(0,s.jsx)(n.code,{children:"tenant-svc"})," that provides a list of tenant databases based on some criteria. Let's suppose this service's\nendpoints receive the target group ID in the path, such as ",(0,s.jsx)(n.code,{children:"https://tenant-svc/api/target-group/{id}"})," and return a\nsimple JSON payload:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",children:'{\n "databases": [\n "acme_corp",\n "widget_inc",\n "wayne_enterprises",\n "stark_industries"\n ]\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["You can use the ",(0,s.jsx)(n.code,{children:"runtimevar"})," data source with the ",(0,s.jsx)(n.code,{children:"http"})," scheme to fetch this data and use it to define\ntarget groups."]}),"\n",(0,s.jsx)(n.p,{children:"Here's an example of how you might load tenant databases from an API endpoint:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'var "group_id" {\n type = string\n}\n\ndata "runtimevar" "tenants" {\n url = "http://tenant-svc/api/target-group/${var.group_id}"\n}\n\nlocals {\n decoded = jsondecode(data.runtimevar.tenants)\n tenants = local.decoded.databases\n}\n\nenv "tenants" {\n for_each = toset(local.tenants)\n url = urlsetpath("postgres://root:pass@localhost:5432", each.value)\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Let's unpack this example:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["We define a variable ",(0,s.jsx)(n.code,{children:"group_id"})," that will be used to fetch the tenant databases from the API endpoint."]}),"\n",(0,s.jsxs)(n.li,{children:["We use the ",(0,s.jsx)(n.code,{children:"runtimevar"})," data source with the ",(0,s.jsx)(n.code,{children:"http"})," scheme to fetch the tenant databases from the API endpoint."]}),"\n",(0,s.jsxs)(n.li,{children:["We parse the JSON response using the ",(0,s.jsx)(n.code,{children:"jsondecode"})," function and extract the ",(0,s.jsx)(n.code,{children:"databases"})," array."]}),"\n",(0,s.jsxs)(n.li,{children:["We define an ",(0,s.jsx)(n.code,{children:"env"})," block named ",(0,s.jsx)(n.code,{children:"tenants"})," that iterates over the ",(0,s.jsx)(n.code,{children:"tenants"})," list. For each tenant, we set the ",(0,s.jsx)(n.code,{children:"url"}),"\nattribute to the result of the ",(0,s.jsx)(n.code,{children:"urlsetpath"})," function, which combines the base URL with the tenant name."]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["By using the ",(0,s.jsx)(n.code,{children:"runtimevar"})," data source with the ",(0,s.jsx)(n.code,{children:"http"})," scheme, you can dynamically load target groups from an API\nendpoint and use them to define target groups in your Atlas project."]}),"\n",(0,s.jsx)(n.h2,{id:"loading-data-from-a-database-query",children:"Loading data from a Database Query"}),"\n",(0,s.jsxs)(n.p,{children:["In some cases, you may want to load target groups dynamically from a database query. For example, you might have a\ndatabase schema for each tenant in some instance, and would like to retrieve the list from the database's native\n",(0,s.jsx)(n.code,{children:"information_schema"})," tables."]}),"\n",(0,s.jsxs)(n.p,{children:["You can utilize the ",(0,s.jsx)(n.code,{children:"sql"})," data source to fetch this data and use it to define target groups."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'var "url" {\n type = string\n}\n\nlocals {\n pattern = "tenant_%"\n}\n\ndata "sql" "tenants" {\n url = var.url\n query = <<EOS\nSELECT `schema_name`\n FROM `information_schema`.`schemata`\n WHERE `schema_name` LIKE ?\nEOS\n args = [local.pattern]\n}\n\nenv "prod" {\n for_each = toset(data.sql.tenants.values)\n url = urlsetpath(var.url, each.value)\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Let's break down this example:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["We define a variable ",(0,s.jsx)(n.code,{children:"url"})," that will be used to connect to the database."]}),"\n",(0,s.jsxs)(n.li,{children:["We define a local variable ",(0,s.jsx)(n.code,{children:"pattern"}
1)," that contains a pattern to match the tenant schemas. In this case, we're looking\nfor schemas that start with ",(0,s.jsx)(n.code,{children:"tenant_"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:["We use the ",(0,s.jsx)(n.code,{children:"sql"})," data source to execute a query against the database. The query selects the ",(0,s.jsx)(n.code,{children:"schema_name"})," from the\n",(0,s.jsx)(n.code,{children:"information_schema.schemata"})," table where the ",(0,s.jsx)(n.code,{children:"schema_name"})," matches the pattern."]}),"\n",(0,s.jsxs)(n.li,{children:["We define an ",(0,s.jsx)(n.code,{children:"env"})," block named ",(0,s.jsx)(n.code,{children:"prod"})," that iterates over the results of the query. For each schema name, we set the\n",(0,s.jsx)(n.code,{children:"url"})," attribute to the result of the ",(0,s.jsx)(n.code,{children:"urlsetpath"})," function, which combines the base URL with the schema name."]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"fetching-tenant-metadata",children:"Fetching Tenant Metadata"}),"\n",(0,s.jsxs)(n.p,{children:["For more advanced use cases, such as ",(0,s.jsx)(n.a,{href:"/guides/database-per-tenant/rollout",children:"deployment rollout strategies"}),", you may need\nto fetch additional metadata about each tenant (e.g., tier, region, or status). The ",(0,s.jsx)(n.code,{children:"sql"})," data source can return multiple\ncolumns per row, which are accessible as a map:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'data "sql" "tenants" {\n url = var.management_url\n query = <<EOS\nSELECT\n `org`.`name`,\n `org`.`schema`,\n `plan`.`tier`\nFROM `management`.`organizations` AS `org`\nLEFT JOIN `management`.`plans` AS `plan`\n ON `org`.`plan_id` = `plan`.`id`\nEOS\n}\n\nenv "prod" {\n for_each = toset(data.sql.tenants.values)\n url = urlsetpath(var.url, each.value.schema)\n // each.value.name and each.value.tier are also available\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["This metadata can be used to configure ",(0,s.jsx)(n.a,{href:"/guides/database-per-tenant/rollout",children:"staged rollouts"})," based on tenant\nattributes like tier, region, or priority."]}),"\n",(0,s.jsx)(n.h2,{id:"incorporating-sensitive-data",children:"Incorporating Sensitive Data"}),"\n",(0,s.jsxs)(n.p,{children:["Essentially, defining target groups in Atlas is about dynamically compiling a list of URLs that represent the target\ndatabases. Database URLs often contain sensitive information, such as passwords, that should not be hardcoded in the\n",(0,s.jsx)(n.code,{children:"atlas.hcl"})," file, which is typically checked into version control."]}),"\n",(0,s.jsxs)(n.p,{children:["To address this issue, Atlas provides mechanisms for loading credentials from external sources, such as environment\nvariables or secret management systems. This allows you to keep your database credentials secure while still being able\nto define target groups dynamically. Learn more about ",(0,s.jsx)(n.a,{href:"/guides/deploying/secrets",children:"working with secrets"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"For the purpose of this example, suppose our database password is stored in an AWS Secrets Manager, created\nusing the AWS CLI as follows:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'aws secretsmanager create-secret \\\n --name db-pass-demo \\\n --secret-string "p455w0rd"\n'})}),"\n",(0,s.jsx)(n.p,{children:"The AWS CLI returns:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",children:'{\n "ARN": "arn:aws:secretsmanager:us-east-1:1111111111:secret:db-pass-demo-aBiM5k",\n "Name": "db-pass-demo",\n "VersionId": "b702431d-174f-4a8f-aee5-b80e687b8bf1"\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["To retrieve the secret value we will use the ",(0,s.jsx)(n.code,{children:"runtimevar"})," data source in the ",(0,s.jsx)(n.code,{children:"atlas.hcl"})," file:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'var "db_instance_url" {\n type = string\n}\n\nvar "db_user" {\n type = string\n}\n\ndata "runtimevar" "pass" {\n url = "awssecretsmanager://db-pass-demo?region=us-east-1"\n}\n\nlocals {\n db_with_pass = urluserinfo(var.db_instance_url, var.db_user, data.runtimevar.pass.value)\n tenants = ["acme_corp", "widget_inc", "wayne_enterprises", "stark_industries"]\n}\n\nenv "tenants" {\n for_each = toset(local.tenants)\n url = urlsetpath(local.db_with_pass, each.value)\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Let's review what's going on here:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["We define two variables, ",(0,s.jsx)(n.code,{children:"db_instance_url"})," and ",(0,s.jsx)(n.code,{children:"db_user"}),", which are used to construct the database URL."]}),"\n",(0,s.jsxs)(n.li,{children:["We use the ",(0,s.jsx)(n.code,{children:"runtimevar"})," data source to fetch the password from AWS Secrets Manager. If you need to assume an\nIAM role, use AWS SDK v2 mode and append ",(0,s.jsx)(n.code,{children:"&awssdk=v2&role=arn:aws:iam::<account-id>:role/<role-name>"})," to the URL."]}),"\n",(0,s.jsxs)(n.li,{children:["We define a local variable ",(0,s.jsx)(n.code,{children:"db_with_pass"}
1)," that combines the database URL, the username, and the password."]}),"\n",(0,s.jsxs)(n.li,{children:["We define an ",(0,s.jsx)(n.code,{children:"env"})," block named ",(0,s.jsx)(n.code,{children:"tenants"})," that iterates over the ",(0,s.jsx)(n.code,{children:"tenants"})," list. For each tenant, we set the ",(0,s.jsx)(n.code,{children:"url"}),"\nattribute to the result of the ",(0,s.jsx)(n.code,{children:"urlsetpath"})," function, which combines the ",(0,s.jsx)(n.code,{children:"db_with_pass"})," with the tenant name."]}),"\n"]}),"\n",(0,s.jsxs)(n.admonition,{type:"info",children:[(0,s.jsxs)(n.mdxAdmonitionTitle,{children:["The ",(0,s.jsx)(n.code,{children:"urluserinfo"})," function"]}),(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"urluserinfo"}),' function is a helper function provided by Atlas that allows you to set the "userinfo" part\nof a URL. For example:']}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hcl",children:'urluserinfo("postgres://localhost:5432", "root", "p455w0rd")\n# \u21b3 Evaluates to "postgres://root:p455w0rd@localhost:5432"\n'})})]}),"\n",(0,s.jsx)(n.h2,{id:"next-steps",children:"Next Steps"}),"\n",(0,s.jsxs)(n.p,{children:["In this guide, we've explored various techniques for defining target groups in Atlas. By using ",(0,s.jsx)(n.code,{children:"env"})," blocks and\n",(0,s.jsx)(n.code,{children:"for_each"})," meta-arguments, you can dynamically compile a list of target databases based on various criteria, such as\ntenant names, regions, or other factors."]}),"\n",(0,s.jsx)(n.p,{children:"In the next section, we will show how to use these target groups in the deployment process to ensure that schema changes\nare applied consistently across all databases in the group."})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(c,{...e})}):c(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.