{"id":111052,"date":"2026-07-06T12:00:00","date_gmt":"2026-07-06T12:00:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=111052"},"modified":"2026-06-15T09:12:56","modified_gmt":"2026-06-15T09:12:56","slug":"ai-llm-app-why-ai-still-cant-be-trusted-for-security","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/ai\/ai-llm-app-why-ai-still-cant-be-trusted-for-security\/","title":{"rendered":"AI can build the pipeline, but don&#8217;t trust it for security (building an app with an AI LLM, part three)"},"content":{"rendered":"\n<p><strong>Building a database deployment pipeline with AI assistance sounds straightforward&#8230;until the AI starts treating temporary security shortcuts as permanent architecture. <\/strong><\/p>\n\n\n\n<p><strong>In part three of his <a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/how-i-built-an-app-just-by-talking-to-an-ai-llm\/\" target=\"_blank\" rel=\"noreferrer noopener\">series on building an app and database almost entirely through an LLM<\/a>, Grant Fritchey walks through using GitHub Copilot and VSCode to configure Redgate Flyway for PostgreSQL on Azure Flexible Server, provision a self-hosted GitHub Actions runner inside a private VNet, and wire it all together into an automated CI\/CD pipeline. <\/strong><\/p>\n\n\n\n<p><strong>The result is impressively functional \u2014 but the process surfaces a critical lesson: AI coding assistants are fast, capable, and dangerously confident about security decisions they have no business making alone.<\/strong><\/p>\n\n\n\n<p><em>Find the full repo, including every prompt, <a href=\"https:\/\/github.com\/ScaryDBA\/dbRosetta\" target=\"_blank\" rel=\"noreferrer noopener\">on GitHub<\/a>.<\/em><br><br>Since I&#8217;m starting development on the database side of dbRosetta, and since I&#8217;m much more comfortable with databases than with code, I&#8217;m going to stay in the database sphere for a bit. <\/p>\n\n\n\n<p>My next step is to work with the AI to get a pipeline in place to deploy our database code to <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/postgresql\/getting-started-with-azure-database-for-postgresql-flexible-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">Azure Flexible Server<\/a>. I&#8217;m using <a href=\"https:\/\/www.red-gate.com\/products\/flyway\/\" target=\"_blank\" rel=\"noreferrer noopener\">Redgate Flyway<\/a> as the database automation tool because I&#8217;m comfortable with it (also, because it&#8217;s the best DB deployment tool out there \u2014 fight me).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-setting-up-flyway\">Setting up Flyway<\/h2>\n\n\n\n<p>I went straight to <a href=\"https:\/\/copilot.microsoft.com\/\">Microsoft Copilot<\/a> &#8211; my main AI companion for this experiment &#8211; and asked it to generate the prompt I&#8217;d need for <a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">VSCode<\/a>. However, I did a bad job on my own prompt. See if you can spot the problem:<br><br><em>Can you give me a prompt for VSCode, using the agreed prompt structure that you helped me build, to create a Flyway Enterprise baseline and initial set of migrations based on reverse engineering using Flyway Compare technology, to capture the existing database we created earlier through the Flyway CLI?<br><\/em><br>It might not be obvious why this prompt is bad. In fact, Copilot generated a perfectly reasonable VSCode prompt from that &#8211; versioned migration naming, non-destructive baseline, CI-friendly output, validation step. So, what&#8217;s wrong?<\/p>\n\n\n\n<p>Well, I didn&#8217;t specify what I wanted for development versus production environments &#8211; so it&#8217;s treating them all the same. This is something we&#8217;ll need to fix later. Isn&#8217;t it great that, even with AI, we can still introduce code debt!<\/p>\n\n\n\n<section id=\"my-first-block-block_7a6d41a41e364ad2d6d94e79fc855e79\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Deploy with confidence with Redgate Flyway<br \/>\r\n<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            The best-in-class database migration and version control tool &#8211; designed for for stable, governed database changes.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/products\/flyway\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Learn more &amp; try for free: Deploy with confidence with Redgate Flyway\">Learn more &amp; try for free<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-giving-the-bad-prompt-to-vscode\">Giving the bad prompt to VSCode<\/h3>\n\n\n\n<p>I fed the prompt into VSCode. It went to work. It knows how to connect up to my Azure Flexible Server, so it ran `pg_dump` to get the SQL file for the baseline (I&#8217;d assumed it would use Flyway for this &#8211; it surprised me). And it then built out the `*.toml` file for controlling Flyway, and ran a baseline command against my database using the migrations it had created. <\/p>\n\n\n\n<p>It didn&#8217;t work flawlessly. It made a mistake in the configuration. Then, since it had provided multiple test steps, it fixed it. I&#8217;m not kidding. So, yeah, I&#8217;ve got a Flyway migration harness ready to go. <\/p>\n\n\n\n<p>There may be more that&#8217;s needed down the road to automate this fully, but I&#8217;m not going to lie &#8211; I&#8217;m surprised at what I&#8217;ve got so far. Pleasantly so.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-creating-a-pipeline\">Creating a pipeline<\/h2>\n\n\n\n<p>When I&#8217;ve set these up myself, I get Flyway configured and functional first, then figure out how to get it to run inside the pipeline I create. Talking with Copilot, it wanted me to prompt it, to enable it to build the <a href=\"https:\/\/github.com\/features\/actions\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub Actions<\/a>.<em> <\/em>It <em>is<\/em> weird talking to one to talk to the other, but it&#8217;s working gangbusters so far!<\/p>\n\n\n\n<p>I did have to remind it that, only earlier, it had told me we&#8217;d need to set up a <a href=\"https:\/\/azure.microsoft.com\/en-us\/resources\/cloud-computing-dictionary\/what-is-a-virtual-machine\" target=\"_blank\" rel=\"noreferrer noopener\">VM (virtual machine)<\/a> inside my VNet (virtual network) to be a self-hosted GitHub runner&#8230;<br><br>I&#8217;ve noticed these things don&#8217;t do a flawless job of maintaining context. It falls back on us &#8211; at least in my experience so far &#8211; to be extremely thorough in our prompts in order to get the best results. However, it remembers <em>again<\/em> that we&#8217;re behind a firewall. <\/p>\n\n\n\n<p>(And yes, before anyone asks, I&#8217;m using the paid tier for this adventure. I have to imagine the free tier may be even more forgetful.)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-copilot-eventually-produced\">What Copilot eventually produced<\/h3>\n\n\n\n<p>The prompt Copilot eventually produced was a self-hosted runner spec: cheapest viable Azure VM (Standard_B1s, ~$7.50\/month), no public IP, system-managed identity, secrets fetched from Key Vault at runtime via <a href=\"https:\/\/www.microsoft.com\/en-us\/security\/business\/security-101\/what-is-openid-connect-oidc\" target=\"_blank\" rel=\"noreferrer noopener\">OpenID Connect (OIDC)<\/a>. Sensible. Over to VSCode&#8230;<br><br>This time it chose to create everything as <a href=\"https:\/\/www.w3schools.com\/bash\/bash_script.php\" target=\"_blank\" rel=\"noreferrer noopener\">bash scripts<\/a>. The first one ran cleanly and provisioned the VM, security group, network interface and VNet. On to the next script&#8230;<\/p>\n\n\n\n<p>This one involved a <em>lot<\/em> of setup within the VM, where VSCode can&#8217;t help me as an agent \u2014 so I did a lot of typing from VSCode prompts. Now I&#8217;m the AI. Ha! <\/p>\n\n\n\n<p>The interesting thing was the troubleshooting. The AI didn&#8217;t get everything right the first time, although I may have introduced a typo or three. Troubleshooting, however, was actually pretty outstanding. I&#8217;d feed it errors and it would feed me solutions (or additional checks followed by solutions.)<br><br>Most of the rest of this was very interactive. Some work was readily done by GitHub Copilot acting as an agent inside VSCode. And some was me getting certificates and other things that couldn&#8217;t readily be retrieved through Copilot.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-when-temporary-becomes-permanent-the-security-issues-triggered-by-the-ai\">When temporary becomes permanent (the security issues triggered by the AI)<\/h2>\n\n\n\n<p>It was interesting how much I had to remind the AI of things. For example, it wanted to test what we&#8217;d done, but only supported a `Development` or `Production` branch. Its first inclination was to add the branch we were working on, but that branch wouldn&#8217;t be permanent, so I had to remind it that we could do it for a test but it had to be easily undone. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-then-we-hit-a-security-problem-on-our-postgresql-cluster\">Then, we hit a security problem on our <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/postgresql\/\" target=\"_blank\" rel=\"noreferrer noopener\">PostgreSQL<\/a> cluster.<\/h4>\n\n\n\n<p>It wanted to use the public IP address we had temporarily added to our VM. It was like, once a thing was done and available, that thing was permanently part of the project &#8211; <em>despite<\/em> the stated purpose being a temporary thing for setup.<br><br>The conversation went something like this:<br><br><em><strong>Me:<\/strong> Instead of the public IP address, shouldn&#8217;t we do something within Azure to let the resource access it since the IP address is temporary for the setup?<br><br><strong>Copilot:<\/strong> Excellent point! You&#8217;re absolutely right. Since both the VM and PostgreSQL are in Azure, we should use VNet integration or Azure service endpoints instead of relying on the public IP.<\/em><\/p>\n\n\n\n<p><em>[Copilot then investigated and pivoted to suggesting the `0.0.0.0` &#8220;AllowAzureServices&#8221; firewall rule.]<br><br><strong>Copilot: <\/strong>Perfect! The firewall rule `0.0.0.0` is a special Azure rule that allows all Azure services (including VMs in the same subscription) to connect to the PostgreSQL server. This is the Azure-native way to allow internal Azure resources to communicate.<\/em><\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-the-latter-is-the-kind-of-suggestion-you-want-to-pay-close-attention-to-but-why\">The latter is the kind of suggestion you want to pay close attention to &#8211; but why?<\/h4>\n\n\n\n<p><strong>Well, the <a href=\"https:\/\/www.techtarget.com\/searchnetworking\/answer\/What-is-the-IP-address-0000-used-for\" target=\"_blank\" rel=\"noreferrer noopener\">0.0.0.0 rule<\/a> allows access from *any* Azure subscription, not just yours. It&#8217;s one of the more commonly cited misconfigurations in cloud security write-ups. <\/strong><\/p>\n\n\n\n<p>For a hobby project with no production data, the risk is small. For an engineer in their first job, however, copying this pattern into a real codebase because the AI called it the &#8220;Azure-native way,&#8221; the risk is most certainly <em>not<\/em> small. <\/p>\n\n\n\n<p>In this instance, I pushed back, but only because I <em>knew<\/em> to push back. We ended up with a sensible VNet-based path instead. <br><br>This was actually a common pattern across the whole step. Once the AI had something working &#8211; a temporary IP rule, a test branch in a deployment config, a one-off setup script &#8211; its instinct was to fold it into the architecture rather than flag it for removal. <\/p>\n\n\n\n<p><strong>That&#8217;s simply because scaffolding and architecture look identical to an LLM (large language model). Both are *code that exists in the project*. The distinction lives in your head, not the model&#8217;s.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-summary-i-m-very-impressed-but-security-continues-to-be-an-issue\">Summary: I&#8217;m very impressed &#8211; but security continues to be an issue<\/h2>\n\n\n\n<p><strong>Overall, I&#8217;m blown away. Most snags were <em>not<\/em> because the AI <a href=\"https:\/\/www.ibm.com\/think\/topics\/ai-hallucinations\" target=\"_blank\" rel=\"noreferrer noopener\">hallucinated<\/a>, or was horribly wrong, but instead from licensing, permissions, and stuff I didn&#8217;t define while I was writing prompts to both AIs. The silly thing was pretty darn helpful. <\/strong><\/p>\n\n\n\n<p>I let it do troubleshooting I was perfectly capable of doing myself, because I wanted to see it work things out. And it did. Quite well. <\/p>\n\n\n\n<p>AI is coming for your job&#8230; IF you&#8217;re not <em>real good<\/em> at your job. I could have done every bit of this work, but using GitHub Copilot made it a lot faster and easier.<\/p>\n\n\n\n<p><strong>However, I had to be on top of it. I had to track everything, because it didn&#8217;t always. It was more than ready to compromise security, multiple times. I couldn&#8217;t have just let it run.<\/strong> <strong>And this is a point I keep coming back to. The AI is a particularly brilliant, but extremely junior, developer on my team.<\/strong><\/p>\n\n\n\n<p>It&#8217;s lightning fast. It&#8217;s often correct. But it&#8217;s confidently wrong in exactly the places that matter most &#8211; and especially around the things that are &#8220;temporary for now&#8221;, but that no junior developer has the experience to flag. <\/p>\n\n\n\n<p>You don&#8217;t let a junior developer near production secrets without supervision. You don&#8217;t let them make the call on networking topology. The same goes here, with the AI. That&#8217;s why humans are still so valuable (at least for the moment). We&#8217;re not just employed &#8211; we&#8217;re necessary!<\/p>\n\n\n\n<p>In the next article, we&#8217;ll begin actually building the application.\u00a0See you there.<\/p>\n\n\n\n<section id=\"my-first-block-block_5bbdd5ed3ac2cabacb8b926dde123881\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Enjoying this article? Subscribe to the Simple Talk newsletter<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Get selected articles, event information, podcasts and other industry content delivered straight to your inbox.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/simple-talk\/subscribe\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Subscribe now: Enjoying this article? Subscribe to the Simple Talk newsletter\">Subscribe now<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: AI can build the pipeline, but don&#039;t trust it for security (building an app with an AI LLM, part three)<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What is Redgate Flyway and why is it used for database deployments?<\/h3>\n            <div class=\"faq-answer\">\n                <p><a href=\"https:\/\/www.red-gate.com\/products\/flyway\/\" target=\"_blank\" rel=\"noopener\">Redgate Flyway<\/a> is a database migration and version control tool. It manages schema changes through numbered migration scripts, making deployments repeatable and auditable. In this project, Flyway Enterprise is used alongside Azure PostgreSQL Flexible Server to create a baseline from an existing database and apply controlled migrations through a CI\/CD pipeline.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. Can GitHub Copilot set up a GitHub Actions pipeline automatically?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Partially. Copilot can generate the YAML, bash scripts, and configuration files needed for a pipeline, and can troubleshoot errors when you feed them back. However, it requires careful human oversight \u2014 in this project it needed reminders about VNet constraints, self-hosted runner requirements, and security configurations it was getting wrong.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. What security risks can AI assistants introduce in cloud infrastructure setup?<\/h3>\n            <div class=\"faq-answer\">\n                <p>A common pattern is treating temporary scaffolding as permanent architecture. In this case, Copilot suggested using the <code class=\"bg-text-200\/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]\">0.0.0.0<\/code> PostgreSQL firewall rule \u2014 which grants access to all Azure subscriptions, not just your own \u2014 and described it as the &#8220;Azure-native&#8221; approach. A developer without prior cloud security experience could easily copy that pattern into production. The correct solution was a VNet-based private connection.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. Do AI coding tools maintain context across a long conversation?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Not reliably. In this project, Copilot would forget earlier constraints (such as the self-hosted runner requirement or the temporary nature of a public IP) and needed to be re-prompted. Writing thorough, explicit prompts and tracking decisions yourself remains essential.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">5. Is AI going to replace database engineers?<\/h3>\n            <div class=\"faq-answer\">\n                <p>No &#8211; not experienced ones. AI functions like a fast, confident junior developer \u2014 useful for acceleration, but dangerous without supervision. It can&#8217;t distinguish scaffolding from production architecture, and it lacks the instinct to flag security or design decisions for review. Human expertise remains the necessary check.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>Learn what happens when you let GitHub Copilot and VSCode set up database migrations and a GitHub Actions pipeline on Azure &#8211; featuring significant security risks.&hellip;<\/p>\n","protected":false},"author":221792,"featured_media":106674,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[159169,143526,53,46],"tags":[159075,4359,159395,5765],"coauthors":[6785],"class_list":["post-111052","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-development","category-featured","category-data-security-privacy-compliance","tag-ai","tag-development","tag-how-i-built-an-app-ai-llm","tag-security-and-compliance"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111052","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221792"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=111052"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111052\/revisions"}],"predecessor-version":[{"id":111316,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111052\/revisions\/111316"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/106674"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=111052"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=111052"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=111052"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=111052"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}