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.manifests.json",
"assertion.dsc.resource.json",
"featureondemand.dsc.resource.json",
"group.dsc.resource.json",
Expand Down
8 changes: 4 additions & 4 deletions lib/dsc-lib-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,8 +745,8 @@ fn convert_value_data_to_offline(value_data: &RegistryValueData) -> Result<(u32,

/// Decode a null-terminated UTF-16LE byte slice to a String.
fn decode_utf16_bytes(data: &[u8]) -> String {
let u16_slice: Vec<u16> = data.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
let u16_slice: Vec<u16> = data.as_chunks::<2>().0.iter()
.map(|chunk| u16::from_le_bytes(*chunk))
.collect();
// Strip trailing null
let len = u16_slice.iter().position(|&c| c == 0).unwrap_or(u16_slice.len());
Expand All @@ -761,8 +761,8 @@ fn encode_utf16_bytes(s: &str) -> Vec<u8> {

/// Decode REG_MULTI_SZ: double-null-terminated list of null-terminated UTF-16LE strings.
fn decode_multi_sz(data: &[u8]) -> Vec<String> {
let u16_slice: Vec<u16> = data.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
let u16_slice: Vec<u16> = data.as_chunks::<2>().0.iter()
.map(|chunk| u16::from_le_bytes(*chunk))
.collect();
let mut strings = Vec::new();
let mut start = 0;
Expand Down
9 changes: 4 additions & 5 deletions lib/dsc-lib/src/functions/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,15 @@ impl Function for Int {

fn invoke(&self, args: &[Value], _context: &Context) -> Result<Value, DscError> {
let arg = &args[0];
let value: i64;
if arg.is_string() {
let value: i64 = if arg.is_string() {
let input = arg.as_str().ok_or(DscError::FunctionArg("int".to_string(), t!("functions.int.invalidInput").to_string()))?;
let result = input.parse::<f64>().map_err(|_| DscError::FunctionArg("int".to_string(), t!("functions.int.parseStringError").to_string()))?;
value = NumCast::from(result).ok_or(DscError::FunctionArg("int".to_string(), t!("functions.int.castError").to_string()))?;
NumCast::from(result).ok_or(DscError::FunctionArg("int".to_string(), t!("functions.int.castError").to_string()))?
} else if arg.is_number() {
value = arg.as_i64().ok_or(DscError::FunctionArg("int".to_string(), t!("functions.int.parseNumError").to_string()))?;
arg.as_i64().ok_or(DscError::FunctionArg("int".to_string(), t!("functions.int.parseNumError").to_string()))?
} else {
return Err(DscError::FunctionArg("int".to_string(), t!("functions.invalidArgType").to_string()));
}
};
Ok(Value::Number(value.into()))
}
}
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.manifests.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 }
Loading
Loading