D365 Audit in your deployment pipeline
Integrate the analysis tools of D365 Audit directly into your pipeline. Get notified about new findings or let your pipeline fail when critical findings appear, for example when credentials would travel unencrypted, and gain valuable insights into the changes from version to version.
Plans
Free trial
See for yourself. Integrate our API into your own pipeline. The trial key is valid for two checks and then deactivates itself.
You will receive your API key by email at the address entered below. Each company domain and solution publisher can test our interface once free of charge.
How it works
- Your deployment pipeline exports the solution as it does today (Power Platform Build Tools or pac solution export).
- One command sends the .zip together with your API key to the D365 Audit API.
- The findings appear in the build log, optionally as test results (JUnit) or code scanning alerts (SARIF).
Security for IT admins
- No code loaded at run time. The pipeline runs curl, which is part of the agent, and sends one HTTPS request with the .zip. Nothing from d365audit.de is executed on your agent; the answer is plain text for the log.
- Only the solution export leaves the pipeline, never access to your environment. What is stored: the uploaded file is deleted right after the analysis; the list of findings (Excel) remains available for 7 days. For the binding to your publisher (two with Enterprise) only a fingerprint of the name of each publisher is kept (SHA-256 with the salt of your account), until 30 days after the subscription ends. For the comparison with the previous run only a fingerprint per finding is kept (SHA-256 over a random salt of your account, rule, table and component name), no names and no content. The fingerprints are kept for 400 days, those of the latest run per project as the baseline for the next run until 30 days after the subscription ends.
- The key is stored only as a hash. Renew it at any time: link by email or POST /api/v1/keys/rotate; the old key stops working immediately.
- Link in the log. Whoever can read the build log can open the list of findings (Excel) through the link for 7 days. JUnit, SARIF and summary need the API key.
- Values from the solution cannot inject log commands: names and notes are sent without line breaks, "##" and "::".
- Proxy: curl uses HTTPS_PROXY (and NO_PROXY) of the machine, or --proxy in the line. Allow www.d365audit.de on port 443.
Already a customer or lost your key?
You can manage your subscription here in the Stripe customer portal (sign in with the email address of your subscription). There you can see your invoices, manage payment methods or cancel.
If you need a new API key, you can request one here with the email address of your subscription; the previous key then stops working.
Integration with Azure DevOps
For an existing pipeline that already exports the solution (Power Platform Build Tools or pac CLI). One HTTPS call with curl, which is already on every agent: no extension, no script, no code loaded at run time. You need the API key from your subscription (plans above).
- Store the key as a secret variable.
Edit pipeline → Variables → New variable, name
D365AUDIT_API_KEY, tick "Keep this value secret". - Put the step right after the export step.
- script: | curl --fail-with-body -sS -H "Authorization: Bearer $(D365AUDIT_API_KEY)" -F "file=@$(Build.ArtifactStagingDirectory)/MeineLoesung.zip" "https://www.d365audit.de/api/v1/scan?ci=azure" displayName: D365 Audit
- Enter the path of the .zip.
Replace
$(Build.ArtifactStagingDirectory)/MeineLoesung.zipwith the same path as SolutionOutputFile of your export step (PowerPlatformExportSolution@2), for example$(Build.ArtifactStagingDirectory)/$(SolutionName).zip.
What happens
- The step waits for the analysis (usually a few seconds) and writes the result to the build log: summary, findings and the link to the list of findings as Excel.
- First run: findings become the baseline. The build only warns (partially succeeded), it does not fail.
- From then on the build fails only for NEW findings of severity high or critical: the server answers HTTP 422, curl ends with exit code 22, the step turns red and the findings are in the log.
- Runs on windows-latest (cmd.exe), ubuntu-latest and macos-latest (bash) and on self-hosted agents with curl 7.76 or newer.
Optional: findings in the Tests tab
The D365 Audit step sets the variable D365AUDIT_JUNIT_URL. Two more steps fetch the JUnit XML and publish it:
Adjust: threshold, list, project name
As parameters in the address, e.g. /api/v1/scan?ci=azure&threshold=medium&mode=delta
threshold | critical, high (default), medium, low, none (only report) |
mode | Which findings the log lists, the gate stays the same: critical (default) = new findings at or above the threshold, the ones that fail the build; delta = new and fixed findings since the previous run, all severities; all = every finding of the current version. |
project | Name for the comparison with the previous run (default: solution name) |
first_run | fail = the first run fails like every other (default warn) |
only_new | 0 = all findings count for the gate, not only new ones |
lang | en = English reports and messages |
Stricter: the key only as an environment variable
Azure DevOps fills $(D365AUDIT_API_KEY) into the script before it runs and masks the value in the log; that is documented and works. Microsoft recommends mapping secrets into environment variables instead. Then the key is not part of the temporary script file. The syntax differs between cmd.exe and bash, so there is one line per agent type:
In both variants the shell hands the key to curl as a command-line argument while the step runs. On Microsoft-hosted agents every job gets a fresh machine; on shared self-hosted agents other processes of the same machine can see command lines.
Integration with GitHub Actions
Store the key as repository secret D365AUDIT_API_KEY and put the step after the export. shell: bash makes the same line work on Windows runners too.
Findings appear as annotations (::error:: and ::warning::); GitHub shows the first 10 of each kind per step, the rest is in the log.
Integration with other CI systems
Any system that can run curl (GitLab CI, Jenkins, Bitbucket, TeamCity …). Without ci= the answer has no log commands. Exit code 0 = gate passed, 22 = gate failed or error (the text says which).
API documentation
Authorization: Bearer d365a_…
POST /api/v1/scan | .zip in field file, analysis in the same request, text for the build log. Parameters ci (azure, github, plain), threshold, mode, project, first_run, only_new, lang. 200 = gate passed, 422 = gate failed. |
POST /api/v1/analyses | Upload .zip (field file, optional project, lang). Returns 202 with id; then poll. |
GET /api/v1/analyses/<id> | Status and result as JSON. Parameters threshold, only_new, first_run and mode (delta, critical, all; without mode the full list). |
GET /api/v1/analyses/<id>/junit | JUnit-XML |
GET /api/v1/analyses/<id>/sarif | SARIF 2.1.0 |
GET /api/v1/analyses/<id>/summary | Markdown |
GET /api/v1/account | Plan, status, quota |
POST /api/v1/keys/rotate | New key, the old one stops working |
Troubleshooting
| "No API key arrived" (HTTP 401) | The secret variable D365AUDIT_API_KEY is missing or the name is misspelt. |
| HTTP 401 | Key unknown or replaced: request a new key |
| HTTP 402 | Subscription not active (payment failed or cancelled): please check your subscription here: Manage subscription. |
| HTTP 429 | Monthly checks used up; they reset on the 1st of the next month (UTC). Or too many uploads in 10 minutes, the message says when to retry. Or another check of the same subscription is still running (scan_running); send again once it has finished. Uploads rejected for another publisher or without a .zip do not count towards the quota. |
| HTTP 422 | Not an error: the quality gate failed, the findings are in the log. |
| HTTP 400 | No .zip in the field file, not a solution export, or a parameter is misspelt; the message says which. |
| curl: (26) … read | The .zip is not at that path: the step runs before the export, or the path in the curl line differs from SolutionOutputFile. |
| curl: option --fail-with-body: is unknown | curl older than 7.76 (some older self-hosted agents). curl --version shows the version; update curl. |
| curl: (6) or (7), could not resolve or connect | Self-hosted agent behind a proxy or firewall: allow www.d365audit.de on port 443. curl uses HTTPS_PROXY of the machine; or add --proxy http://proxy:8080 to the line. |
| Proxy with sign-in (HTTP 407) | Add --proxy-user "user:password" to the line, the password best as a secret variable, e.g. --proxy "$(Agent.ProxyUrl)" --proxy-user "$(Agent.ProxyUsername):$(Agent.ProxyPassword)" for the proxy of the agent. |
| HTTP 504 | The analysis took longer than 90 seconds. It keeps running; the message names the address of the result. |