dp-deploy push takes a deployment MSI bundle and a list of host names, connects to each host over Windows Remote Management, and either installs DPLens where it is absent or updates the configuration where it is already running. It needs nothing on the hosts beyond remote management being enabled, and it leaves nothing behind: no agent listener, no scheduled task, no persistent connection.
It is the right tool for a few hundred hosts at a time, for a fleet you manage without a directory, and for pushing a configuration change to machines that were installed any other way.
Before you start
On every target:
- Windows Remote Management is enabled (
Enable-PSRemotingon the host, or the equivalent Group Policy). - The account you run the push as is a local administrator on the host.
Domain-joined targets need nothing more. The push authenticates with Kerberos as the account you are signed in as, and no credential is asked for.
Workgroup targets (or any target you cannot reach with Kerberos) need three things, because a password is going to cross the network:
- An HTTPS listener for remote management on the target, port 5986, with a certificate. The push connects over TLS only; an unauthenticated listener is refused.
- A way to trust that certificate. Either the certificate is issued by an authority your workstation trusts and names the host, or you pin it: a file of
hostname thumbprintlines, one per target, passed as--thumbprints. Before connecting, the push fetches the listener's certificate, compares its thumbprint to the pinned one, and only on a match trusts that certificate for the duration of the action. - A local administrator credential. Run
Push-Credential.ps1 -Out C:\deploy\push.credfrom the bundle folder: it prompts once and saves the credential protected so that only your account on your workstation can read it; the push passes the file to each host action and deletes it at the end. If the account is a local administrator other than the built-inAdministrator, the target must haveLocalAccountTokenFilterPolicyset to1— without it Windows strips the administrator token from remote logons by local accounts and the push is refused as unprivileged.
Pinning has a cost worth knowing: while a pinned action is in flight, the target's certificate is trusted machine-wide on your workstation, not just by the push. It is removed when the action ends. Prefer Kerberos, or a certificate from an authority you already trust, wherever you can; pinning is for the hosts that have neither. Pinning needs the push to run elevated.
On your workstation: the signed deployment MSI bundle, the deployment key in your secrets manager, and dp-deploy itself. The push runs the two scripts it emits beside the bundle — Push-Host.ps1 and Push-Credential.ps1 — with -ExecutionPolicy Bypass, because they are files the tool writes and are not signed; that setting applies to those invocations only and changes nothing on the machine. Sign them under your own policy if you need to.
The targets file
One host name per line. Blank lines and lines beginning # are ignored; a host listed twice is refused. Up to 5,000 hosts.
# finance floor
fin-ws-001.corp.example
fin-ws-002.corp.example
# the lab (workgroup)
lab-01
For pinned hosts, the thumbprints file:
lab-01 5F98A0C1…40 hex characters
Running it
Domain hosts:
Get-Content .\deploy.key | dp-deploy push --bundle C:\deploy\bundle-1 --targets C:\deploy\hosts.txt
Workgroup hosts, pinned:
Get-Content .\deploy.key | dp-deploy push --bundle C:\deploy\bundle-1 --targets C:\deploy\lab.txt `
--https --thumbprints C:\deploy\pins.txt --credential-file C:\deploy\push.cred
The key arrives on standard input, once. It is never on the command line, in the results, or in a file dp-deploy writes. On the wire it travels inside the encrypted remoting session as a protected string, and on the target it is written straight to its staging location with administrators-only permissions and deleted by the installer.
| Option | Meaning |
|---|---|
--bundle DIR | The bundle folder from build-msi. It is verified — every hash, and the rows read back out of the MSI — before any host is contacted. |
--targets FILE | The targets file. |
--share \\files\dplens\bundle-1 | Let each target pull the MSI from this share as its own machine account, instead of copying it through the session. The share must be readable by the targets' computer accounts. Faster for large fleets; the target still verifies the package's hash before running it. |
--canary N | How many hosts go first. Every canary host must succeed (or be skipped) before the rest start. Default 5. |
--max-failures PCT | Stop when failed hosts exceed this percentage of hosts processed so far. Default 10. It never trips on a single failure — one bad host out of the first two does not stop a 500-host run. |
--concurrency N | Hosts in flight at once after the canary batch. Default 8, at most 32. |
--https [--port N] | Connect over TLS, port 5986 unless given. Required for --thumbprints and --credential-file. |
--thumbprints FILE | The pinned certificates, above. |
--credential-file FILE | The saved local-administrator credential. Without it, --https runs Push-Credential.ps1 to prompt you and deletes the file afterwards. |
--key-source ProgramData|Registry | Where to stage the key on the target. Default ProgramData. |
--healthy-seconds N | How long the service must stay running after an install or update before the host counts as healthy. Default 10. |
What happens on each host
- Query. Which version is installed, whether the service is running, and which bundle was last applied.
- Decide. Nothing installed → install. This bundle already applied → skip. Anything else → update. A host that was installed but failed is not marked as applied, so a re-run retries it.
- Install. The MSI is copied into
%ProgramData%\DPLens\push-…\— a folder created with administrators-only permissions; a pre-existing%ProgramData%\DPLenscreated by anyone else is refused — or pulled from the share into that folder. Its SHA-256 must equal the one in the bundle manifest, checked on the target immediately beforemsiexecruns it. The key is staged,msiexec /i … /qn /norestartruns with a log, and the service must be running for--healthy-seconds. - Update. The payload and licence list are copied, the key staged, the service stopped, the agent's own apply step run in update mode, the service started, health checked. The working licence is kept unless a key in the list verifies on that host (see Licensing).
- Report. One row per host.
Each host action has fifteen minutes. A host that overruns is killed and reported as failed; if it was a pinned HTTPS host, the trust established for it is removed afterwards — the row says so — but a key staged on that host may remain. Remove it by hand (Stage-DeployKey.ps1 will tell you it is there the next time), or let the next push's installer consume and delete it.
The push prints one line per host as it goes and a summary at the end, and exits 0 only when nothing failed and nothing was aborted.
The results file
push-results-<timestamp>.json in the current directory: the bundle id, the options used, the counts, and one entry per host — the action taken, whether it succeeded, the state before and after, the message, the duration, and the installer's exit code where there was one. Non-matching licence keys are listed in the host's message. Nothing in it is secret.
Re-running, rolling back
Re-run the same push after fixing whatever failed: hosts that are done are skipped, hosts that failed are tried again.
To roll back, push the previous bundle. There is no host-side snapshot; the previous bundle is the rollback, which is why you should keep every bundle you have pushed. The wizard offers the previous bundle as a rollback choice for this reason.
Verifying on a host
Get-Service dplens
Get-Content C:\ProgramData\DPLens\state\deploy.json # the bundle id and when it was applied
Get-Content C:\ProgramData\DPLens\state\audit\audit.jsonl | Select-String deploy-payload-applied
Test-Path C:\ProgramData\DPLens\deploy\deploy.key # False: the installer deleted it
What is logged, and what is not
The push was measured against PowerShell's own logging with every option turned on — script block logging, module logging, transcription, and process command-line auditing. The deployment key appeared in none of them. The scripts' text does, as you would expect; the key does not.