Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/skills/create-dsc-resource/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Management tasks or operations are specific to the resource type, but may includ
- **Resource manifest**: A JSON file that defines the resource type name, supported operations (including executable and arguments), and JSON schema for input parameters
- **Dependency management**: All crates must be listed in Cargo.toml specifying to use workspace dependencies. The root level Cargo.toml should be updated to include the new crates or associated to the DSC resource project.
- **Project files**: A `.project.data.json` file in the root of the project folder defines properties of the project and non-code files to include during build
- **Release packaging**: Add every resource binary and manifest to the applicable platform list under `PackageFiles` in the root `data.build.json`. Project discovery alone does not include a resource in released packages.
- **Localization**: For Rust-based resources, all user-facing strings must use `rust-i18n` for internationalization. For script-based resources (such as PowerShell), follow the existing localization and string-handling patterns used by those scripts or any repository-specific localization guidance.
- **Copyright headers**: Every source file must start with the copyright header:
```
Expand All @@ -37,6 +38,7 @@ Management tasks or operations are specific to the resource type, but may includ
- Create a resource manifest JSON file named `<resource_name>.dsc.resource.json` in the same directory using `./resources/windows_service/windows_service.dsc.resource.json` as an example
- Create a `.project.data.json` file in the root of the resource project directory
- Create a `locales/en-us.toml` file for localized strings
- Add the resource executable and manifest to the applicable `PackageFiles` platform list in the root `data.build.json` (for example, `<resource_name>.exe` and `<resource_name>.dsc.resource.json` under `PackageFiles.Windows`)

### 2. .project.data.json

Expand Down Expand Up @@ -261,6 +263,7 @@ someError = "Failed to do something: %{error}"

#### Build and Deployment

- Verify the root `data.build.json` lists the resource binary and every manifest under each applicable `PackageFiles` platform. A `.project.data.json` entry controls building and copying artifacts but does not by itself add those files to a released package.
- The resource should be built using `build.ps1 -project <resource_name>` from the root of the repository, which will handle building the Rust code and ensure it is found in PATH for testing

## What-If support
Expand Down Expand Up @@ -560,4 +563,3 @@ When asked to add what-if to a new resource, perform these steps in order:
7. Add `whatIf*` localized strings under `[<resource>_helper]` and `args.configArgsWhatIfHelp` in `locales/en-us.toml`.
8. Create `<resource>.config.whatif.tests.ps1` (and the list variant if applicable) following the test template; cover create, update, delete-via-`_exist`, and `delete -w`.
9. Build with `./build.ps1 -project <resource_name>` and run the new Pester file.

11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ members = [
"resources/windows_firewall",
"resources/windows_service",
"resources/WindowsUpdate",
"resources/environment_variable",
"tools/dsctest",
"tools/test_group_resource",
"xtask",
Expand Down Expand Up @@ -57,6 +58,7 @@ default-members = [
"resources/windows_firewall",
"resources/windows_service",
"resources/WindowsUpdate",
"resources/environment_variable",
"tools/dsctest",
"tools/test_group_resource",
"xtask",
Expand Down Expand Up @@ -90,6 +92,7 @@ Windows = [
"resources/windows_firewall",
"resources/windows_service",
"resources/WindowsUpdate",
"resources/environment_variable",
"tools/dsctest",
"tools/test_group_resource",
"xtask",
Expand Down
2 changes: 2 additions & 0 deletions data.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
"dsc-bicep-ext.exe",
"dscecho.exe",
"echo.dsc.resource.json",
"environment_variable.exe",
"environment_variable.dsc.resource.json",
"assertion.dsc.resource.json",
"featureondemand.dsc.resource.json",
"group.dsc.resource.json",
Expand Down
14 changes: 14 additions & 0 deletions resources/environment_variable/.project.data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"Name": "environment_variable",
"Kind": "Resource",
"IsRust": true,
"SupportedPlatformOS": "Windows",
"Binaries": [
"environment_variable"
],
"CopyFiles": {
"Windows": [
"environment_variable.dsc.resource.json"
]
}
}
18 changes: 18 additions & 0 deletions resources/environment_variable/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "environment_variable"
version = "0.1.0"
edition = "2024"

[package.metadata.i18n]
available-locales = ["en-us"]
default-locale = "en-us"
load-path = "locales"

[dependencies]
rust-i18n = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }

[target.'cfg(windows)'.dependencies]
dsc-lib-registry = { workspace = true }
dsc-lib-security_context = { workspace = true }
124 changes: 124 additions & 0 deletions resources/environment_variable/environment_variable.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
{
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json",
"type": "Microsoft.Windows/EnvironmentVariableList",
"description": "Manage user and machine environment variables stored in the Windows registry.",
"tags": [
"Windows",
"Environment"
],
"version": "0.1.0",
"get": {
"executable": "environment_variable",
"args": [
"get",
{
"jsonInputArg": "--input",
"mandatory": true
}
]
},
"set": {
"executable": "environment_variable",
"args": [
"set",
{
"jsonInputArg": "--input",
"mandatory": true
}
],
"implementsPretest": false,
"handlesExist": true,
"return": "state"
},
"exitCodes": {
"0": "Success",
"1": "Invalid arguments",
"2": "Invalid input",
"3": "Environment variable resource error",
"4": "Elevation required: Setting or removing AllUsers environment variables requires an elevated process"
},
"schema": {
"embedded": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Windows Environment Variable List",
"description": "Manage user and machine environment variables stored in the Windows registry.",
"type": "object",
"additionalProperties": false,
"required": [
"environmentVariables"
],
"properties": {
"environmentVariables": {
"type": "array",
"title": "Environment variables",
"description": "The environment variables to get or set.",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"not": {
"required": [
"value",
"pathValue"
]
},
"properties": {
"scope": {
"type": "string",
"title": "Scope",
"description": "The registry scope for the environment variable.",
"default": "CurrentUser",
"enum": [
"AllUsers",
"CurrentUser"
]
},
"name": {
"type": "string",
"title": "Name",
"description": "The environment variable name.",
"minLength": 1
},
"value": {
"type": "string",
"title": "Value",
"description": "The environment variable value."
},
"pathValue": {
"type": "array",
"title": "Path value",
"description": "The semicolon-delimited environment variable value represented as path entries.",
"items": {
"type": "string",
"minLength": 1,
"pattern": "^[^;]+$"
}
},
"pathAction": {
"type": "string",
"title": "Path action",
"description": "How pathValue entries are combined with the current value.",
"writeOnly": true,
"default": "clobber",
"enum": [
"prepend",
"append",
"clobber"
]
},
"_exist": {
"type": "boolean",
"title": "Exists",
"description": "Whether the environment variable should exist. Set to false to remove it.",
"default": true
}
}
}
}
}
}
}
}
31 changes: 31 additions & 0 deletions resources/environment_variable/locales/en-us.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
_version = 1

[main]
missingOperation = "Missing operation. Usage: environment_variable get --input <json> | set --input <json>"
unknownOperation = "Unknown operation: '%{operation}'. Expected: get or set"
missingInput = "Missing --input argument"
missingInputValue = "Missing value for --input argument"
invalidJson = "Invalid JSON input: %{error}"
serializeError = "Failed to serialize resource output: %{error}"
windowsOnly = "The Microsoft.Windows/EnvironmentVariableList resource is only supported on Windows"
registryError = "Failed to access environment variable '%{name}' in scope '%{scope}': %{error}"

[validation]
emptyList = "The environmentVariables array must contain at least one environment variable"
emptyName = "Environment variable name must not be empty"
invalidName = "Environment variable name '%{name}' contains an invalid null character"
duplicate = "Environment variable '%{name}' is specified more than once in scope '%{scope}'"
valueConflict = "Environment variable '%{name}' cannot specify both value and pathValue"
pathActionWithoutValue = "Environment variable '%{name}' can only specify pathAction with pathValue"
invalidPathEntry = "Environment variable '%{name}' has a pathValue entry that is empty or contains a semicolon or null character"
missingValue = "Environment variable '%{name}' must specify value or pathValue when _exist is true"

[get]
readError = "Failed to read environment variable '%{name}' in scope '%{scope}': %{error}"
unsupportedType = "Environment variable '%{name}' in scope '%{scope}' uses an unsupported registry value type; expected REG_SZ or REG_EXPAND_SZ"

[set]
elevationRequired = "Setting or removing AllUsers environment variables requires elevation. Run DSC from an elevated process."
readError = "Failed to read environment variable '%{name}' in scope '%{scope}' before setting it: %{error}"
writeError = "Failed to set environment variable '%{name}' in scope '%{scope}': %{error}"
removeError = "Failed to remove environment variable '%{name}' in scope '%{scope}': %{error}"
Loading
Loading