[{"data":1,"prerenderedAt":21},["ShallowReactive",2],{"blog-post-en-ai-agents-databricks-data-access":3},{"slug":4,"lang":5,"title":6,"description":7,"heading":8,"translationKey":9,"date":10,"keywords":11,"readingMinutes":17,"html":18,"alternates":19},"ai-agents-databricks-data-access","en","AI Agents and Databricks: Table and Column Access Control","How to give AI agents read access to Databricks without exposing personal data: structured queries, table and column allowlists, value masking.","AI agents and Databricks: access control for tables and columns","databricks-data-access","2026-09-04",[12,13,14,15,16],"AI agent Databricks access control","Databricks AI agent","column level access AI agent","PII masking AI","Databricks MCP",6,"\u003Cp>Data is where many AI agent projects become serious. Answering a question such as &quot;how many\norders failed payment last week, per country&quot; is a task an agent can do well, and it saves an\nanalyst a small but constant stream of requests. At the same time, a data platform such as\nDatabricks holds exactly the information that should not leave the company without a reason:\ncustomer records, payment details, employee data. An agent with a broad warehouse token can read\nall of it, and whatever it reads can end up in a prompt sent to a model provider.\u003C\u002Fp>\n\u003Cp>This post describes how agent access to Databricks can be limited to specific tables and columns,\nwhy raw SQL is the wrong interface for agents, and how personal data can be masked before it\nreaches the model. It is part of our series on \u003Ca href=\"\u002Fblog\u002Fai-agents-software-development-lifecycle\">AI agents in the software development lifecycle\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2>Why direct warehouse access is a problem\u003C\u002Fh2>\n\u003Cp>The usual way to connect an agent to Databricks is a personal access token or a service principal\nwith read rights on a catalog, together with a tool that executes SQL. This is quick to set up and\nworks well in a demo. It has three weaknesses in production.\u003C\u002Fp>\n\u003Cp>First, the scope is too wide. Warehouse permissions are usually granted per catalog or schema, for\npeople who need to explore data. An agent that only has to answer questions about orders inherits\naccess to every table in that schema, including those with personal data.\u003C\u002Fp>\n\u003Cp>Second, raw SQL is a very expressive interface. An agent can join tables, write subqueries and\nselect any column. Even with good intentions, it can combine data in ways nobody reviewed. If\nparts of the query come from user input or from text the agent read elsewhere, there is also the\nclassic risk of injection.\u003C\u002Fp>\n\u003Cp>Third, the result goes to the model. Every row the agent reads becomes part of its context, and\nwith a hosted model this means it is sent to a third party. From a data protection perspective\n(for example under the GDPR), the question is therefore not only who may query a table, but which\nvalues may leave the network at all.\u003C\u002Fp>\n\u003Ch2>A narrower interface: structured, read-only queries\u003C\u002Fh2>\n\u003Cp>Vordix takes a different approach. Instead of accepting SQL, it offers four read-only operations:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>get_tables\u003C\u002Fcode> lists the tables the agent may use,\u003C\u002Fli>\n\u003Cli>\u003Ccode>describe_table\u003C\u002Fcode> returns the columns of an allowed table,\u003C\u002Fli>\n\u003Cli>\u003Ccode>query_table\u003C\u002Fcode> selects rows with filters, sorting and a row limit (capped by the gateway),\u003C\u002Fli>\n\u003Cli>\u003Ccode>aggregate_table\u003C\u002Fcode> computes grouped aggregates (count, sum, average, minimum, maximum).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The agent describes what it wants in structured parameters (table, columns, filters, grouping),\nand Vordix builds the SQL statement itself. Filter values are passed as bound parameters, never\ninserted into the query text. Joins, subqueries and raw SQL are not supported. Write operations do\nnot exist for Databricks at all.\u003C\u002Fp>\n\u003Cfigure class=\"post-figure\">\n\u003Csvg viewBox=\"0 0 760 256\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" role=\"img\" aria-labelledby=\"dbx-query-title\">\n  \u003Ctitle id=\"dbx-query-title\">The agent sends a structured request; Vordix checks table and columns, builds the SQL with bound parameters and runs a read-only query.\u003C\u002Ftitle>\n  \u003Crect class=\"fig-box fig-box--ai\" x=\"16\" y=\"40\" width=\"180\" height=\"180\" rx=\"10\"\u002F>\n  \u003Ctext class=\"fig-h fig-c-ai\" x=\"32\" y=\"66\">AGENT REQUEST\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"32\" y=\"96\">op: aggregate_table\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"32\" y=\"116\">table: sales.orders\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"32\" y=\"136\">group_by: country\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"32\" y=\"156\">filter: status =\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"32\" y=\"176\">        \"failed\"\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"32\" y=\"196\">metric: count\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--primary\" x=\"226\" y=\"20\" width=\"330\" height=\"222\" rx=\"14\"\u002F>\n  \u003Ctext class=\"fig-h fig-c-primary\" x=\"242\" y=\"46\">VORDIX · DATABRICKS\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"242\" y=\"58\" width=\"298\" height=\"40\" rx=\"8\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"256\" y=\"83\">01\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"280\" y=\"75\">Table allowed?\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"280\" y=\"91\">sales.orders ✓\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"242\" y=\"102\" width=\"298\" height=\"40\" rx=\"8\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"256\" y=\"127\">02\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"280\" y=\"119\">Columns allowed?\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"280\" y=\"135\">country, status ✓\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"242\" y=\"146\" width=\"298\" height=\"40\" rx=\"8\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"256\" y=\"171\">03\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"280\" y=\"163\">Build SQL\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"280\" y=\"179\">bound parameters\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"242\" y=\"190\" width=\"298\" height=\"40\" rx=\"8\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"256\" y=\"215\">04\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"280\" y=\"207\">Read only\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"280\" y=\"223\">no joins, no writes\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"586\" y=\"97\" width=\"158\" height=\"68\" rx=\"10\"\u002F>\n  \u003Ctext class=\"fig-t\" x=\"665\" y=\"126\" text-anchor=\"middle\">Databricks\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"665\" y=\"145\" text-anchor=\"middle\">SQL warehouse\u003C\u002Ftext>\n  \u003Cpath class=\"fig-flow fig-flow--ai\" d=\"M196 131 H226\"\u002F>\n  \u003Cpath class=\"fig-flow\" d=\"M556 131 H586\"\u002F>\n\u003C\u002Fsvg>\n\u003Cfigcaption>A structured request passes four checks in Vordix before a read-only query reaches the Databricks SQL warehouse.\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\n\u003Cp>This is clearly less flexible than free SQL, and that is the intended trade-off. The interface\ncovers the questions agents are usually asked (filtered lists, counts and sums per group) while making\nit impossible to query tables or columns outside the configuration.\u003C\u002Fp>\n\u003Ch2>Scoping by table and column\u003C\u002Fh2>\n\u003Cp>Tables are addressed with a full key of the form \u003Ccode>connection.catalog.schema.table\u003C\u002Fcode>. The connection\npart allows several named Databricks connections, for example one per workspace or environment.\nVordix connects with OAuth machine-to-machine authentication through a service principal, so no\npersonal token is involved.\u003C\u002Fp>\n\u003Cp>Access is configured in two levels:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Tables.\u003C\u002Fstrong> The administrator allows specific tables, at organisation level as a ceiling and\nthen per project. A table that is not allowed does not appear in \u003Ccode>get_tables\u003C\u002Fcode>, and a query\nagainst it is denied.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Columns per table.\u003C\u002Fstrong> Under each allowed table, the columns are an allowlist of their own. The\nallowed columns are checked against a stored scan of the warehouse schema. The value \u003Ccode>*\u003C\u002Fcode> means\nall columns of that table, which is practical for tables without sensitive content. For a\ncustomer table, a typical configuration would allow \u003Ccode>country\u003C\u002Fcode>, \u003Ccode>created_at\u003C\u002Fcode> and \u003Ccode>segment\u003C\u002Fcode>, but\nnot \u003Ccode>email\u003C\u002Fcode>, \u003Ccode>name\u003C\u002Fcode> or \u003Ccode>iban\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Because columns are allowed per table, the same column name can be allowed in one table and\nblocked in another. The organisation-wide ceiling limits what a project can allow; a project cannot\ngrant a table or column the organisation has not allowed.\u003C\u002Fp>\n\u003Ch2>Masking personal data in results\u003C\u002Fh2>\n\u003Cp>Column scoping removes the obvious fields, but personal data also appears in unexpected places: a\nfree-text comment that contains an email address, a reference field with an IBAN, a log column\nwith IP addresses. For these cases, Vordix applies data policies to the response before it is\nreturned to the agent.\u003C\u002Fp>\n\u003Cp>A data policy works in three layers:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>field-path rules\u003C\u002Fstrong> for known fields (for example always mask \u003Ccode>customer.email\u003C\u002Fcode>),\u003C\u002Fli>\n\u003Cli>\u003Cstrong>built-in detectors\u003C\u002Fstrong> for common patterns: email addresses, phone numbers, IBANs, credit card\nnumbers (validated with the Luhn check), IP addresses, and national ID numbers for Germany,\nAustria and Switzerland,\u003C\u002Fli>\n\u003Cli>\u003Cstrong>organisation term lists\u003C\u002Fstrong> for internal terms such as project code names.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>For each match, the policy decides what happens: the value is masked, pseudonymised (replaced by a\nstable placeholder, so that the same customer still appears as the same entity), redacted,\ndropped, or the whole response is blocked. A preview tool shows what a policy does to a sample\nbefore it is enabled.\u003C\u002Fp>\n\u003Cfigure class=\"post-figure\">\n\u003Csvg viewBox=\"0 0 760 270\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" role=\"img\" aria-labelledby=\"dbx-mask-title\">\n  \u003Ctitle id=\"dbx-mask-title\">A customer row passes the column allowlist and the data policy; the agent sees only allowed columns, with the email in the note masked.\u003C\u002Ftitle>\n  \u003Ctext class=\"fig-h\" x=\"16\" y=\"36\">WAREHOUSE ROW\u003C\u002Ftext>\n  \u003Ctext class=\"fig-h fig-c-ok\" x=\"464\" y=\"36\">WHAT THE AGENT SEES\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"16\" y=\"54\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"28\" y=\"72\">country\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"112\" y=\"73\">AT\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"16\" y=\"88\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"28\" y=\"106\">segment\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"112\" y=\"107\">SMB\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"16\" y=\"122\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"28\" y=\"140\">created_at\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"112\" y=\"141\">2026-03-14\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--deny\" x=\"16\" y=\"156\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"28\" y=\"174\">email\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"112\" y=\"175\">anna@example.com\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--deny\" x=\"16\" y=\"190\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"28\" y=\"208\">name\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"112\" y=\"209\">Anna Beispiel\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"16\" y=\"224\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"28\" y=\"242\">note\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"112\" y=\"243\">via max@example.com\u003C\u002Ftext>\n  \u003Cpath class=\"fig-flow\" d=\"M296 120 H310\"\u002F>\n  \u003Cpath class=\"fig-flow\" d=\"M382 144 V170\"\u002F>\n  \u003Cpath class=\"fig-flow\" d=\"M454 194 H464\"\u002F>\n  \u003Crect class=\"fig-box\" x=\"310\" y=\"96\" width=\"144\" height=\"48\" rx=\"8\"\u002F>\n  \u003Ctext class=\"fig-t\" x=\"382\" y=\"117\" text-anchor=\"middle\">Column allowlist\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"382\" y=\"134\" text-anchor=\"middle\">drops email, name\u003C\u002Ftext>\n  \u003Crect class=\"fig-box\" x=\"310\" y=\"170\" width=\"144\" height=\"48\" rx=\"8\"\u002F>\n  \u003Ctext class=\"fig-t\" x=\"382\" y=\"191\" text-anchor=\"middle\">Data policy\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s\" x=\"382\" y=\"208\" text-anchor=\"middle\">masks patterns\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--ok\" x=\"464\" y=\"54\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"476\" y=\"72\">country\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"560\" y=\"73\">AT\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--ok\" x=\"464\" y=\"88\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"476\" y=\"106\">segment\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"560\" y=\"107\">SMB\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--ok\" x=\"464\" y=\"122\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"476\" y=\"140\">created_at\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"560\" y=\"141\">2026-03-14\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--muted fig-box--dashed\" x=\"464\" y=\"156\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"476\" y=\"174\">email\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s fig-c-deny\" x=\"560\" y=\"174\">removed\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--muted fig-box--dashed\" x=\"464\" y=\"190\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"476\" y=\"208\">name\u003C\u002Ftext>\n  \u003Ctext class=\"fig-s fig-c-deny\" x=\"560\" y=\"208\">removed\u003C\u002Ftext>\n  \u003Crect class=\"fig-box fig-box--primary\" x=\"464\" y=\"224\" width=\"280\" height=\"28\" rx=\"6\"\u002F>\n  \u003Ctext class=\"fig-s\" x=\"476\" y=\"242\">note\u003C\u002Ftext>\n  \u003Ctext class=\"fig-t\" x=\"560\" y=\"243\">via ***@***\u003C\u002Ftext>\n\u003C\u002Fsvg>\n\u003Cfigcaption>The same customer row before and after Vordix: blocked columns are removed, and an email address inside a free-text note is masked.\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\n\u003Cp>In addition, response fields can be filtered: only allowlisted fields of a response are returned,\nand if the filter cannot be applied, the call fails closed instead of returning unfiltered data.\u003C\u002Fp>\n\u003Ch2>Audit and rate limits\u003C\u002Fh2>\n\u003Cp>Every query is written to the audit log, including denied ones with the reason (for example &quot;table\nnot allowed&quot; or &quot;column not allowed&quot;). Parameters are redacted before they are written, so the log\nitself does not become a second copy of sensitive filter values. Rate limits per key prevent an\nagent from turning into an unplanned bulk export. The post \u003Ca href=\"\u002Fblog\u002Fai-agent-audit-trail-eu-ai-act\">What an audit trail for AI agents\nshould contain\u003C\u002Fa> covers the log in more detail.\u003C\u002Fp>\n\u003Ch2>Trade-offs and limitations\u003C\u002Fh2>\n\u003Cp>The approach has clear limits, and it is better to know them before a project starts:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>No joins.\u003C\u002Fstrong> Questions that need data from several tables cannot be answered in one call. The\nagent can query tables one after another, but for regular analyses a prepared table that already\ncombines the data is the better option. This moves some work to the data team.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>No raw SQL, no writes.\u003C\u002Fstrong> Advanced analytics, window functions or data changes are out of scope.\nThe interface is meant for answering questions, not for data engineering.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Detectors are pattern based.\u003C\u002Fstrong> Email addresses or IBANs are recognised reliably; personal names\nin free text are not in the list of built-in detectors. Columns with names should therefore be\nexcluded by the column allowlist or covered by field-path rules, not left to detection.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Configuration effort.\u003C\u002Fstrong> Choosing tables and columns is a data governance decision that needs\nsomeone who knows the data. The column wildcard \u003Ccode>*\u003C\u002Fcode> saves time, but it should only be used for\ntables where every current and future column is acceptable.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Conclusion\u003C\u002Fh2>\n\u003Cp>Giving AI agents access to Databricks is useful, but a warehouse token combined with free SQL gives\nthem far more than the task needs. A narrower interface with structured, read-only queries, an\nallowlist of tables and of columns per table, and masking of personal data in results keeps the\nuseful part (answers to routine data questions) and removes most of the exposure. The remaining\nlimits, mainly the missing joins and pattern-based detection, are manageable if they are planned\nfor.\u003C\u002Fp>\n\u003Cp>More in this series: \u003Ca href=\"\u002Fblog\u002Fwhat-is-an-mcp-gateway\">What is an MCP gateway?\u003C\u002Fa> and\n\u003Ca href=\"\u002Fblog\u002Fai-agent-audit-trail-eu-ai-act\">What an audit trail for AI agents should contain\u003C\u002Fa>. If you\nwant to test the Databricks controls against your own warehouse, you can request a demo or read the\nDatabricks integration page in the Vordix documentation.\u003C\u002Fp>\n",{"de":20,"en":4},"ki-agenten-databricks-datenzugriff",1790588073004]