Nefarius.Tools.SDCM 1.1.0

Prefix Reserved
dotnet tool install --global Nefarius.Tools.SDCM --version 1.1.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Nefarius.Tools.SDCM --version 1.1.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Nefarius.Tools.SDCM&version=1.1.0
                    
nuke :add-package Nefarius.Tools.SDCM --version 1.1.0
                    

Surface Dev Center Manager (SDCM)

.NET Requirements Nuget Nuget Assisted by Cursor AI

Surface Dev Center Manager (SDCM) is a .NET tool that automates common Microsoft Hardware Dev Center (Partner Center) tasks around driver and firmware submissions, using the Hardware Dashboard API.

sdcm lets you create Attestation and WHQL products and submissions, upload and download packages, manage shipping labels to release drivers on Windows Update, and submit packages for preproduction signing.

This is nefarius/SDCM, a modernized fork of microsoft/SDCM: .NET 10, dependency injection, System.CommandLine, MSAL instead of the now end-of-life ADAL, and a dotnet tool distribution. Backward compatibility with the original CLI was not a design goal here - see Migrating from sdcm 1.x below if you're coming from the upstream tool.

Installation

Requires the .NET 10 runtime or later.

As a global tool (recommended - puts sdcm on your PATH):

dotnet tool install -g Nefarius.Tools.SDCM
sdcm --help

As a local tool (pinned per-repository via a tool manifest):

dotnet new tool-manifest # if you don't already have one
dotnet tool install Nefarius.Tools.SDCM
dotnet sdcm --help

To update: dotnet tool update -g Nefarius.Tools.SDCM.

Documentation

Setting up credentials

The preferred path is a Partner Center API key (the Key on a Microsoft Entra application created under User management). That key is sdcm's --auth client-secret profile key.

sdcm config set --tenant-id <tenant-guid> --client-id <client-guid> --key
sdcm product list

--key prompts for the Partner Center secret (or reads one line from stdin). Do not put the key on the command line.

Walkthrough, config layers, and the other auth modes: docs/authentication.md. sdcm config path shows which file was resolved.

Command reference

sdcm
├─ product
│  ├─ create              --input <file>
│  ├─ list                [--product-id <id>]   (id form deprecated; prefer get)
│  └─ get                 --product-id <id>
├─ submission
│  ├─ create              --product-id --input <file>
│  ├─ list                --product-id <id> [--submission-id <id>]   (id form deprecated)
│  ├─ get                 --product-id --submission-id
│  ├─ status              --product-id --submission-id
│  ├─ commit              --product-id --submission-id     (idempotent)
│  ├─ upload              --product-id --submission-id --package <path>
│  ├─ download            --product-id --submission-id --output-file <path>
│  │                      [--overwrite]
│  ├─ wait                --product-id --submission-id [--wait-metadata]
│  │                      [--poll-interval <sec>] [--wait-timeout <sec>]
│  └─ metadata
│     ├─ download         --product-id --submission-id --output-file <path>
│     │                   [--overwrite]
│     └─ create           --product-id --submission-id
├─ preprod-submission
│  ├─ submit              --package <path>
│  ├─ status              --package-id <id>
│  ├─ assets              --package-id <id> [--asset-id <id>]
│  ├─ download            --package-id <id> --asset-id <id> --output-file <path>
│  │                      [--overwrite]
│  └─ wait                --package-id <id> [--poll-interval <sec>] [--wait-timeout <sec>]
├─ shipping-label
│  ├─ create              --product-id --submission-id --input <file> [--partner-id]
│  ├─ list                --product-id --submission-id [--shipping-label-id]
│  └─ wait                --product-id --submission-id --shipping-label-id
│                         [--poll-interval <sec>] [--wait-timeout <sec>]
├─ partner-submission
│  ├─ list                --publisher-id --product-id --submission-id
│  └─ translate           --publisher-id --product-id --submission-id
├─ audience list
└─ config
   ├─ path
   ├─ init                [--force]
   └─ set                 [--tenant-id] [--client-id] [--key]

Global options, valid anywhere in the tree: --profile, --auth, --aad, --config, --timeout (HTTP timeout in seconds, default 300), --output text|json (default text), -v/--verbose (diagnostic logging on stderr), and --replay <fixtures.json> (or SDCM_REPLAY) for an offline fake backend. See Submission states.

Run sdcm <command> --help (or sdcm <noun> <verb> --help) for the full option list of any command.

Input file schema

--input takes the bare payload for the type being created - no wrapper object. This deserializes directly into the underlying library's NewProduct, NewSubmission or NewShippingLabel types.

Creating a product

{
  "productName": "ProductName_HLK",
  "testHarness": "HLK",
  "announcementDate": "2023-01-01T00:00:00",
  "firmwareVersion": "0",
  "deviceType": "external",
  "isTestSign": false,
  "isFlightSign": false,
  "selectedProductTypes": {
    "windows_v100_RS4": "Unclassified"
  },
  "requestedSignatures": [
    "WINDOWS_v100_X64_RS4_FULL"
  ]
}

For an Attestation submission, set testHarness to Attestation.

Creating a submission

{
  "name": "ProductName_HLK_Submission",
  "type": "initial"
}

Creating a shipping label

{
  "publishingSpecifications": {
    "goLiveDate": "2023-01-01T00:00:00.000Z",
    "visibleToAccounts": [],
    "isAutoInstallDuringOSUpgrade": true,
    "isAutoInstallOnApplicableSystems": true,
    "manualAcquisition": false,
    "isDisclosureRestricted": true,
    "publishToWindows10s": false,
    "additionalInfoForMsApproval": {
      "microsoftContact": "contact@microsoft.com",
      "validationsPerformed": "TBD",
      "affectedOems": ["Your Company"],
      "isRebootRequired": true,
      "isCoEngineered": true,
      "isForUnreleasedHardware": true,
      "hasUiSoftware": false,
      "businessJustification": "Driver Update"
    }
  },
  "targeting": {
    "hardwareIds": [
      {
        "bundleId": "0",
        "infId": "empty.inf",
        "operatingSystemCode": "WINDOWS_v100_RS4_FULL",
        "pnpString": "empty pnp"
      }
    ],
    "chids": [
      { "chid": "guid", "distributionState": "pendingAdd" }
    ],
    "restrictedToAudiences": [],
    "inServicePublishInfo": { "flooring": "19H1", "ceiling": "19H1" }
  },
  "name": "ProductName_HLK_ShippingLabel",
  "destination": "windowsUpdate"
}

A file still using the old {"createType": ..., "createProduct"/"createSubmission"/"createShippingLabel": {...}} envelope from sdcm 1.x fails fast with an explicit message pointing back to this section, instead of a confusing null-reference deeper in the call stack.

Basic operations

Create a product:

sdcm product create --input product.json

Get it back by id, or list every product:

sdcm product get --product-id 12345
sdcm product list

Create and inspect a submission:

sdcm submission create --product-id 12345 --input submission.json
sdcm submission get --product-id 12345 --submission-id 67890
sdcm submission status --product-id 12345 --submission-id 67890

Upload the package (must be signed by the Extended Validation Certificate (EV Cert) registered on your Hardware account), commit, and wait for processing:

sdcm submission upload --product-id 12345 --submission-id 67890 --package test.hlkx
sdcm submission commit --product-id 12345 --submission-id 67890
sdcm submission wait --product-id 12345 --submission-id 67890

Download the signed result:

sdcm submission download --product-id 12345 --submission-id 67890 --output-file signed.zip

Get a package signed for preproduction testing instead - no product/submission needed, just the EV-signed package itself:

$packageId = (sdcm preprod-submission submit --package test.cab --output json | ConvertFrom-Json).id
sdcm preprod-submission wait --package-id $packageId
sdcm preprod-submission assets --package-id $packageId
sdcm preprod-submission download --package-id $packageId --asset-id <asset-id-from-assets> --output-file signed.zip

Add --output json to any command to get machine-readable results for scripting, instead of regexing human-readable text:

$id = (sdcm product create --input product.json --output json | ConvertFrom-Json).id

Exit codes

Code Meaning
0 Success
1 InvalidArguments - parse errors, malformed or missing --input file
2 AuthenticationFailed - no usable credentials, token acquisition failed
3 ApiRequestFailed - generic Hardware Dev Center API error
4 NotFound - the requested entity doesn't exist
5 InvalidState - the request is invalid for the entity's current state
6 RateLimited - HTTP 429 from the service
7 WorkflowFailed - the submission or shipping label failed server-side
8 IoError - output path missing, or destination already exists
9 Canceled - Ctrl+C, or a --wait-timeout was exceeded
10 UnhandledException

Automation scripts

End-to-end local scripts (sdcm must be on PATH). For CI submit/wait/download, see docs/ci-signing.md.

Migrating from sdcm 1.x

sdcm 1.x sdcm 2.x (this fork)
-create <product json> product create --input
-create <submission json> -productid submission create --product-id --input
-create <shippingLabel json> -productid -submissionid shipping-label create --product-id --submission-id --input
-commit submission commit
-list product\|submission\|shippinglabel\|partnersubmission product list / submission list / shipping-label list / partner-submission list
-upload submission upload --package
-download submission download --output-file
-metadata submission metadata download --output-file
-createmetadata submission metadata create
-wait (with/without -shippinglabelid) submission wait / shipping-label wait
-a / -audience audience list
-translate partner-submission translate
-partnerid shipping-label create --partner-id
-server <int> --profile <name>
-creds <mode> --auth <mode>

Also new:

  • --input files no longer use the {"createType": ..., "createXxx": {...}} envelope - see Input file schema.
  • authconfig.json moved from an ordinal array to named profiles, and now lives in a per-user config directory by default rather than next to the executable - see Authentication.
  • ErrorCodes (48 negative values) was replaced by ten positive exit codes.
  • -v used to be dead code; it now actually raises the log level.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
1.1.0 116 9/10/2026
1.0.0-pre004 195 9/10/2026
1.0.0-pre003 111 9/7/2026
1.0.0-pre002 113 9/7/2026
1.0.0-pre001 113 9/7/2026