diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml
deleted file mode 100644
index cf53ec8..0000000
--- a/.github/workflows/terraform.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-name: "terraform"
-
-# for security should only run on push to bors branches (staging/trying)
-on:
-  push:
-    branches:
-      - staging
-      - trying
-
-jobs:
-  terraform-deploy:
-    if: github.repository == 'nix-community/infra'
-    runs-on: ubuntu-latest
-    defaults:
-      run:
-        working-directory: terraform
-    env:
-      SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
-      TF_TOKEN_app_terraform_io: ${{ secrets.TF_TOKEN_APP_TERRAFORM_IO }}
-      TF_IN_AUTOMATION: true
-      TF_INPUT: 0
-    steps:
-      - uses: actions/checkout@v3
-      - uses: dorny/paths-filter@v2
-        id: changes
-        with:
-          filters: |
-            terraform:
-              - 'terraform/**'
-      - uses: cachix/install-nix-action@v20
-        if: steps.changes.outputs.terraform == 'true'
-        with:
-          extra_nix_config: |
-            experimental-features = nix-command flakes
-            accept-flake-config = true
-      - name: init
-        if: steps.changes.outputs.terraform == 'true'
-        run: nix develop -c terraform init
-      - name: validate
-        if: steps.changes.outputs.terraform == 'true'
-        run: nix develop -c terraform validate
-      - name: fmt
-        if: steps.changes.outputs.terraform == 'true'
-        run: nix develop -c terraform fmt -check
-      - name: plan
-        if: steps.changes.outputs.terraform == 'true' && github.ref == 'refs/heads/trying'
-        run: nix develop -c terraform plan
-      - name: apply
-        if: steps.changes.outputs.terraform == 'true' && github.ref == 'refs/heads/staging'
-        run: nix develop -c terraform apply -auto-approve
diff --git a/bors.toml b/bors.toml
index 4d909ac..369554d 100644
--- a/bors.toml
+++ b/bors.toml
@@ -3,5 +3,4 @@ status = [
   "ci/hercules/derivations",
   "ci/hercules/effects",
   "ci/hercules/evaluation",
-  "terraform-deploy",
 ]
diff --git a/ci.nix b/ci.nix
index 8dff876..325b62f 100644
--- a/ci.nix
+++ b/ci.nix
@@ -4,14 +4,38 @@
 }:
 let
   self = builtins.getFlake (toString ./.);
-  terraform = builtins.getFlake (toString ./terraform/.);
   inherit (self.inputs.nixpkgs) lib;
   stripDomain = name: lib.head (builtins.match "(.*).nix-community.org" name);
+
+  effects = self.inputs.hercules-ci-effects.lib.withPkgs self.inputs.nixpkgs.legacyPackages.x86_64-linux;
+  terraform-deploy =
+    effects.runIf (src.ref == "refs/heads/trying" || src.ref == "refs/heads/staging")
+      (effects.mkEffect {
+        name = "terraform-deploy";
+        inputs = [ (builtins.getFlake (toString ./terraform/.)).outputs.devShells.x86_64-linux.default.nativeBuildInputs ];
+        src = lib.cleanSource ./.;
+        secretsMap.tf-secrets = "tf-secrets";
+        effectScript = ''
+          export TF_IN_AUTOMATION=1
+          export TF_INPUT=0
+          export SOPS_AGE_KEY="$(readSecretString tf-secrets .SOPS_AGE_KEY)"
+          export TF_TOKEN_app_terraform_io="$(readSecretString tf-secrets .TF_TOKEN_app_terraform_io)"
+
+          pushd terraform
+          terraform init
+          terraform validate
+          if [[ ${src.ref} == "refs/heads/staging" ]]; then
+            terraform apply -auto-approve
+          else
+            terraform plan
+          fi
+        '';
+      });
 in
 (lib.mapAttrs' (name: config: lib.nameValuePair "nixos-${stripDomain name}" config.config.system.build.toplevel) self.outputs.nixosConfigurations) //
 {
   # FIXME: maybe find a more generic solution here?
   devShell-x86_64 = self.outputs.devShells.x86_64-linux.default;
   devShell-aarch64 = self.outputs.devShells.aarch64-linux.default;
-  devShell-terraform-x86_64 = terraform.outputs.devShells.x86_64-linux.default;
+  inherit terraform-deploy;
 } // self.outputs.checks.x86_64-linux # mainly for treefmt at the moment...
diff --git a/flake.lock b/flake.lock
index daaf351..1149ca5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -40,6 +40,30 @@
         "type": "github"
       }
     },
+    "hercules-ci-effects": {
+      "inputs": {
+        "flake-parts": [
+          "flake-parts"
+        ],
+        "hercules-ci-agent": [],
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1676558019,
+        "narHash": "sha256-obUHCMMWbffb3k0b9YIChsJ2Z281BcDYnTPTbJRP6vs=",
+        "owner": "hercules-ci",
+        "repo": "hercules-ci-effects",
+        "rev": "fdbc15b55db8d037504934d3af52f788e0593380",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "hercules-ci-effects",
+        "type": "github"
+      }
+    },
     "nixpkgs": {
       "locked": {
         "lastModified": 1678662842,
@@ -161,6 +185,7 @@
       "inputs": {
         "disko": "disko",
         "flake-parts": "flake-parts",
+        "hercules-ci-effects": "hercules-ci-effects",
         "nixpkgs": "nixpkgs",
         "nixpkgs-update": "nixpkgs-update",
         "nixpkgs-update-github-releases": "nixpkgs-update-github-releases",
diff --git a/flake.nix b/flake.nix
index 08f2fdc..c801d9c 100644
--- a/flake.nix
+++ b/flake.nix
@@ -33,6 +33,11 @@
     disko.url = "github:nix-community/disko";
     disko.inputs.nixpkgs.follows = "nixpkgs";
 
+    hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects";
+    hercules-ci-effects.inputs.flake-parts.follows = "flake-parts";
+    hercules-ci-effects.inputs.hercules-ci-agent.follows = "";
+    hercules-ci-effects.inputs.nixpkgs.follows = "nixpkgs";
+
     treefmt-nix.url = "github:numtide/treefmt-nix";
     treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
   };