D365 Audit

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

For teams with their own solutions

Pipeline subscription Starter

€79 / month
net, plus 19% VAT (€94.01 gross), cancel monthly
10 checks per month, which is €7.90 per check
BUILD-LOGJUNITSARIFEXCEL

Benefit from the integration options of the D365 Audit analysis solution. You receive all check results directly in your pipeline for further processing. Keep an eye on changes to your company solutions and let deployments be stopped when critical thresholds are exceeded or there are security concerns.

What you get

  • Every build checked with over 155 rules, the same as in the one-off report
  • Findings directly in the build log of Azure DevOps or GitHub Actions, optionally as JUnit test results or SARIF code scanning alerts
  • Quality gate: the build fails only for new findings at or above the threshold you choose (default: high)
  • Comparison with the previous run: new and resolved findings per project
  • The first run only warns and sets the baseline, so existing findings do not block you
  • List of findings as Excel, retrievable for 7 days via the link in the log
  • Any number of solutions and projects per publisher

Limits

  • 10 checks per month
  • 1 publisher(s) per subscription, set by the first runs
  • No PDF or Word report and no flow documentation
  • .zip up to 60 MB, at most 30 uploads per 10 minutes
Starter
10 checks per month
Team
30 checks per month
Enterprise
100 checks per month

Payment is processed by Stripe. You can enter your VAT ID for the invoice at checkout.

Two checks · no payment details

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

  1. Your deployment pipeline exports the solution as it does today (Power Platform Build Tools or pac solution export).
  2. One command sends the .zip together with your API key to the D365 Audit API.
  3. 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).

  1. Store the key as a secret variable. Edit pipeline → Variables → New variable, name D365AUDIT_API_KEY, tick "Keep this value secret".
  2. 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
  3. Enter the path of the .zip. Replace $(Build.ArtifactStagingDirectory)/MeineLoesung.zip with 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:

- script: | curl --fail-with-body -sS --create-dirs -H "Authorization: Bearer $(D365AUDIT_API_KEY)" -o "$(Common.TestResultsDirectory)/d365audit.xml" "$(D365AUDIT_JUNIT_URL)" displayName: D365 Audit - JUnit condition: and(succeededOrFailed(), variables['D365AUDIT_JUNIT_URL']) - task: PublishTestResults@2 displayName: D365 Audit - Tests condition: and(succeededOrFailed(), variables['D365AUDIT_JUNIT_URL']) inputs: testResultsFormat: JUnit testResultsFiles: $(Common.TestResultsDirectory)/d365audit.xml testRunTitle: D365 Audit
Adjust: threshold, list, project name

As parameters in the address, e.g. /api/v1/scan?ci=azure&threshold=medium&mode=delta

thresholdcritical, high (default), medium, low, none (only report)
modeWhich 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.
projectName for the comparison with the previous run (default: solution name)
first_runfail = the first run fails like every other (default warn)
only_new0 = all findings count for the gate, not only new ones
langen = 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:

# Linux, macOS (bash) - 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 env: D365AUDIT_API_KEY: $(D365AUDIT_API_KEY) # Windows (cmd.exe) - 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 env: D365AUDIT_API_KEY: $(D365AUDIT_API_KEY)

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.

- name: D365 Audit shell: bash run: | curl --fail-with-body -sS -H "Authorization: Bearer $D365AUDIT_API_KEY" -F "file=@out/MeineLoesung.zip" "https://www.d365audit.de/api/v1/scan?ci=github" env: D365AUDIT_API_KEY: ${{ secrets.D365AUDIT_API_KEY }}

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).

curl --fail-with-body -sS -H "Authorization: Bearer $D365AUDIT_API_KEY" -F "file=@MeineLoesung.zip" "https://www.d365audit.de/api/v1/scan"

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/analysesUpload .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>/junitJUnit-XML
GET /api/v1/analyses/<id>/sarifSARIF 2.1.0
GET /api/v1/analyses/<id>/summaryMarkdown
GET /api/v1/accountPlan, status, quota
POST /api/v1/keys/rotateNew 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 401Key unknown or replaced: request a new key
HTTP 402Subscription not active (payment failed or cancelled): please check your subscription here: Manage subscription.
HTTP 429Monthly 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 422Not an error: the quality gate failed, the findings are in the log.
HTTP 400No .zip in the field file, not a solution export, or a parameter is misspelt; the message says which.
curl: (26) … readThe .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 unknowncurl older than 7.76 (some older self-hosted agents). curl --version shows the version; update curl.
curl: (6) or (7), could not resolve or connectSelf-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 504The analysis took longer than 90 seconds. It keeps running; the message names the address of the result.