-
Notifications
You must be signed in to change notification settings - Fork 58
Add Microsoft.Windows/FeatureOnDemandList resource
#1433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
SteveL-MSFT
wants to merge
3
commits into
PowerShell:main
Choose a base branch
from
SteveL-MSFT:featureondemand-resource
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,321
−191
Draft
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| { | ||
| "$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json", | ||
| "description": "Manage Windows Features on Demand (capabilities) using the DISM API.", | ||
| "tags": [ | ||
| "windows", | ||
| "dism", | ||
| "capability", | ||
| "featureondemand", | ||
| "fod" | ||
| ], | ||
| "type": "Microsoft.Windows/FeatureOnDemandList", | ||
| "version": "0.1.0", | ||
| "get": { | ||
| "executable": "dism_dsc", | ||
| "args": [ | ||
| "get", | ||
| "feature-on-demand" | ||
| ], | ||
| "input": "stdin", | ||
| "requireSecurityContext": "elevated" | ||
| }, | ||
| "export": { | ||
| "executable": "dism_dsc", | ||
| "args": [ | ||
| "export", | ||
| "feature-on-demand" | ||
| ], | ||
| "input": "stdin", | ||
| "requireSecurityContext": "elevated" | ||
| }, | ||
| "set": { | ||
| "executable": "dism_dsc", | ||
| "args": [ | ||
| "set", | ||
| "feature-on-demand" | ||
| ], | ||
| "input": "stdin", | ||
| "implementsPretest": false, | ||
| "return": "state", | ||
| "requireSecurityContext": "elevated" | ||
| }, | ||
| "schema": { | ||
| "embedded": { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "$id": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/resources/Microsoft.Windows/FeatureOnDemandList/v0.1.0/schema.json", | ||
| "title": "Windows Feature on Demand", | ||
| "description": "Manage Windows Features on Demand (capabilities) using the DISM API. Supports get, set, and export operations.\n\nhttps://learn.microsoft.com/powershell/dsc/reference/microsoft.windows/featureondemandlist/resource\n", | ||
| "markdownDescription": "The `Microsoft.Windows/FeatureOnDemandList` resource enables you to manage Windows Features on Demand (capabilities) using the DISM API.\n\n[Online documentation][01]\n\n[01]: https://learn.microsoft.com/powershell/dsc/reference/microsoft.windows/featureondemandlist/resource\n", | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": [ | ||
| "capabilities" | ||
| ], | ||
| "properties": { | ||
| "_restartRequired": { | ||
| "type": "array", | ||
| "title": "Restart required", | ||
| "description": "Indicates that a system restart is required to complete the state change. Returned by the set operation when DISM reports that a reboot is needed.", | ||
| "items": { | ||
| "type": "object", | ||
| "additionalProperties": true | ||
| } | ||
| }, | ||
| "capabilities": { | ||
| "type": "array", | ||
| "title": "Capabilities", | ||
| "description": "An array of Feature on Demand (capability) filters or information objects.", | ||
| "items": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "name": { | ||
| "type": "string", | ||
| "title": "Capability name", | ||
| "description": "The name of the Windows capability (Feature on Demand). Required for get operation. For export operation, this is optional and wildcards (*) are supported for case-insensitive filtering." | ||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| "_exist": { | ||
| "type": "boolean", | ||
| "title": "Exists", | ||
| "description": "Indicates whether the capability exists on the system. Set to false when the requested capability name is not recognized by DISM." | ||
| }, | ||
| "state": { | ||
| "type": "string", | ||
| "enum": [ | ||
| "NotPresent", | ||
| "UninstallPending", | ||
| "Staged", | ||
| "Removed", | ||
| "Installed", | ||
| "InstallPending", | ||
| "Superseded", | ||
| "PartiallyInstalled" | ||
| ], | ||
| "title": "Capability state", | ||
| "description": "The current state of the capability. For set operations, only Installed and NotPresent are accepted; other states are returned by get and export operations and are not valid inputs for set." | ||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| "displayName": { | ||
| "type": "string", | ||
| "title": "Display name", | ||
| "description": "The display name of the capability. Returned by get and export operations. For export, wildcards (*) are supported for case-insensitive filtering." | ||
| }, | ||
| "description": { | ||
| "type": "string", | ||
| "title": "Description", | ||
| "description": "The description of the capability. Returned by get and export operations. For export, wildcards (*) are supported for case-insensitive filtering." | ||
| }, | ||
| "downloadSize": { | ||
| "type": "integer", | ||
| "title": "Download size", | ||
| "description": "The download size of the capability in bytes. Returned by get and export operations." | ||
| }, | ||
| "installSize": { | ||
| "type": "integer", | ||
| "title": "Install size", | ||
| "description": "The install size of the capability in bytes. Returned by get and export operations." | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| use rust_i18n::t; | ||
|
|
||
| use crate::optional_feature::dism::DismSessionHandle; | ||
| use crate::feature_on_demand::types::{CapabilityState, FeatureOnDemandInfo, FeatureOnDemandList}; | ||
| use crate::util::{matches_wildcard, WildcardFilterable}; | ||
|
|
||
| pub fn handle_export(input: &str) -> Result<String, String> { | ||
| let filters: Vec<FeatureOnDemandInfo> = if input.trim().is_empty() { | ||
| vec![FeatureOnDemandInfo::default()] | ||
| } else { | ||
| let list: FeatureOnDemandList = serde_json::from_str(input) | ||
| .map_err(|e| t!("fod_export.failedParseInput", err = e.to_string()).to_string())?; | ||
| list.capabilities | ||
| }; | ||
|
|
||
| let session = DismSessionHandle::open()?; | ||
| let all_basics = session.get_all_capability_basics()?; | ||
|
|
||
| // Check if any filter requires full info (displayName, description, downloadSize, installSize) | ||
| let needs_full_info = filters.iter().any(|f| { | ||
| f.display_name.is_some() || f.description.is_some() | ||
| || f.download_size.is_some() || f.install_size.is_some() | ||
| }); | ||
|
|
||
| let mut results = Vec::new(); | ||
|
|
||
| let (filters_with_name, filters_without_name): (Vec<&FeatureOnDemandInfo>, Vec<&FeatureOnDemandInfo>) = | ||
| if needs_full_info { | ||
| filters.iter().partition(|f| f.name.is_some()) | ||
| } else { | ||
| (Vec::new(), Vec::new()) | ||
| }; | ||
|
|
||
| for (name, state_val) in &all_basics { | ||
| let state = CapabilityState::from_dism(*state_val); | ||
|
|
||
| if needs_full_info { | ||
| let mut should_get_full = !filters_without_name.is_empty(); | ||
| if !should_get_full { | ||
| for f in &filters_with_name { | ||
| if let Some(ref filter_name) = f.name { | ||
| if matches_wildcard(name, filter_name) { | ||
| should_get_full = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if !should_get_full { | ||
| continue; | ||
| } | ||
|
|
||
| let info = match session.get_capability_info(name) { | ||
| Ok(raw) if !raw.unknown => FeatureOnDemandInfo { | ||
| name: Some(raw.name), | ||
| exist: None, | ||
| state: CapabilityState::from_dism(raw.state), | ||
| display_name: Some(raw.display_name), | ||
| description: Some(raw.description), | ||
| download_size: Some(raw.download_size), | ||
| install_size: Some(raw.install_size), | ||
| }, | ||
| _ => FeatureOnDemandInfo { | ||
| name: Some(name.clone()), | ||
| exist: None, | ||
| state, | ||
| display_name: None, | ||
| description: None, | ||
| download_size: None, | ||
| install_size: None, | ||
| }, | ||
| }; | ||
|
|
||
| if info.matches_any_filter(&filters) { | ||
| results.push(info); | ||
| } | ||
| } else { | ||
| // Fast path: only need name and state for filtering, skip expensive | ||
| // per-capability DismGetCapabilityInfo calls to match dism /online /get-capabilities speed. | ||
| let basic = FeatureOnDemandInfo { | ||
| name: Some(name.clone()), | ||
| state: state.clone(), | ||
| ..FeatureOnDemandInfo::default() | ||
| }; | ||
|
|
||
| if basic.matches_any_filter(&filters) { | ||
| results.push(basic); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let output = FeatureOnDemandList { restart_required_meta: None, capabilities: results }; | ||
| serde_json::to_string(&output) | ||
| .map_err(|e| t!("fod_export.failedSerializeOutput", err = e.to_string()).to_string()) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| use rust_i18n::t; | ||
|
|
||
| use crate::optional_feature::dism::DismSessionHandle; | ||
| use crate::feature_on_demand::types::{CapabilityState, FeatureOnDemandInfo, FeatureOnDemandList}; | ||
|
|
||
| pub fn handle_get(input: &str) -> Result<String, String> { | ||
| let capability_list: FeatureOnDemandList = serde_json::from_str(input) | ||
| .map_err(|e| t!("fod_get.failedParseInput", err = e.to_string()).to_string())?; | ||
|
|
||
| if capability_list.capabilities.is_empty() { | ||
| return Err(t!("fod_get.capabilitiesArrayEmpty").to_string()); | ||
| } | ||
|
|
||
| let session = DismSessionHandle::open()?; | ||
| let mut results = Vec::new(); | ||
|
|
||
| for cap_input in &capability_list.capabilities { | ||
| let name = cap_input | ||
| .name | ||
| .as_ref() | ||
| .ok_or_else(|| t!("fod_get.nameRequired").to_string())?; | ||
|
|
||
| let raw = session.get_capability_info(name)?; | ||
| let info = if raw.unknown { | ||
| FeatureOnDemandInfo { | ||
| name: Some(name.clone()), | ||
| exist: Some(false), | ||
| ..FeatureOnDemandInfo::default() | ||
| } | ||
| } else { | ||
| FeatureOnDemandInfo { | ||
| name: Some(raw.name), | ||
| state: CapabilityState::from_dism(raw.state), | ||
| display_name: Some(raw.display_name), | ||
| description: Some(raw.description), | ||
| download_size: Some(raw.download_size), | ||
| install_size: Some(raw.install_size), | ||
| ..FeatureOnDemandInfo::default() | ||
| } | ||
| }; | ||
| results.push(info); | ||
| } | ||
|
|
||
| let output = FeatureOnDemandList { restart_required_meta: None, capabilities: results }; | ||
| serde_json::to_string(&output) | ||
| .map_err(|e| t!("fod_get.failedSerializeOutput", err = e.to_string()).to_string()) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| mod types; | ||
| mod get; | ||
| mod set; | ||
| mod export; | ||
|
|
||
| pub use get::handle_get; | ||
| pub use set::handle_set; | ||
| pub use export::handle_export; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.