.. _cicd: ****************************** Continuous Delivery and GitOps ****************************** Bowtie's access configuration, including policies, resources, resource groups, DNS, and sites, can be managed as code with the :ref:`Bowtie Terraform provider ` and applied through your existing delivery pipeline. This page shows a GitOps workflow that plans changes on a pull request and applies them on merge, along with a pre-deploy policy check that verifies access decisions before they ship. Managing access as code *********************** Keep your ``bowtie_*`` Terraform in version control alongside the rest of your infrastructure. A typical GitOps flow runs ``terraform plan`` on every pull request so reviewers see the proposed change, and runs ``terraform apply`` once the change merges to your main branch. Store Terraform state in a shared backend so the pipeline and your team share one source of truth. Provider setup, authentication, the ``terraform import`` workflow, and an apply-ordering pattern that waits for a Controller's API to become reachable are covered under :ref:`terraform-provider`. The pipelines below assume the provider is configured from the ``BOWTIE_HOST``, ``BOWTIE_USERNAME``, and ``BOWTIE_PASSWORD`` environment variables, supplied as pipeline secrets. GitHub Actions ************** This workflow validates and plans on a pull request, and applies on a push to ``main``. .. code:: yaml name: bowtie on: pull_request: push: branches: [main] jobs: terraform: runs-on: ubuntu-latest env: BOWTIE_HOST: ${{ secrets.BOWTIE_HOST }} BOWTIE_USERNAME: ${{ secrets.BOWTIE_USERNAME }} BOWTIE_PASSWORD: ${{ secrets.BOWTIE_PASSWORD }} steps: - uses: actions/checkout@v4 - uses: hashicorp/setup-terraform@v3 - run: terraform init - run: terraform fmt -check - run: terraform plan -input=false - if: github.ref == 'refs/heads/main' run: terraform apply -auto-approve -input=false GitLab CI ********* The same flow expressed as a GitLab pipeline, with the secrets set as masked CI/CD variables. .. code:: yaml default: image: hashicorp/terraform:latest stages: [validate, deploy] plan: stage: validate script: - terraform init - terraform fmt -check - terraform plan -input=false apply: stage: deploy script: - terraform init - terraform apply -auto-approve -input=false rules: - if: $CI_COMMIT_BRANCH == "main" Pre-deploy policy checks ************************ A Controller can return the access decision for a hypothetical connection before you ship a change. The ``POST /-net/api/v0/policy/explain`` endpoint takes a source, a destination, a protocol, and an optional port, and returns the verdict that the enforcement engine would apply. Because it calls the same decision logic that enforces traffic in the kernel, the answer is the real policy outcome rather than a separate simulation. This makes it a pre-deploy policy gate: assert in your pipeline that intended access works and that unintended access is denied, and fail the job when a verdict does not match. Authenticate as described under :ref:`api-keys`, then call the endpoint: .. code:: shell # Does alice-laptop reach the finance app over HTTPS? curl -sS -X POST "https://$BOWTIE_HOST/-net/api/v0/policy/explain" \ -H "Content-Type: application/json" \ -H "Authorization: $BOWTIE_API_KEY" \ -d '{ "party_one": "alice-laptop", "party_two": "finance.internal.example.com", "protocol": "TCP", "party_two_port": 443 }' \ | jq '.responses[].verdict' The response carries one verdict per resolved address pair. Read the verdict and fail the pipeline when it does not match the decision you expect, so a policy change that would open or close the wrong path is caught before it merges. The source and destination accept a device name, an IP address, or a DNS name, so you can express checks in the same terms your policies use. .. admonition:: Reference status :class: note The policy evaluator is currently in beta. Its request and response shapes may change between releases, so pin your assertions to the fields you read and verify them against your Controller's responses.