Skip to content

Commit 739e427

Browse files
committed
update opt feats RFC for enable/disable scenarios
1 parent 7c9743a commit 739e427

File tree

1 file changed

+126
-59
lines changed

1 file changed

+126
-59
lines changed

1-Draft/RFCNNNN-Optional-Features.md

Lines changed: 126 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -34,119 +34,190 @@ so that I can leverage new functionality that could break existing scripts.
3434
## User experience
3535

3636
```powershell
37-
# Create a module manifest, specifically enabling one or more optional features in the manifest
38-
New-ModuleManifest -Path ./test.psd1 -OptionalFeatures @('OptionalFeature1','OptionalFeature2') -PassThru | Get-Content
37+
# Create a module manifest, specifically enabling or disabling one or more optional features in the manifest
38+
New-ModuleManifest -Path ./test.psd1 -OptionalFeatures @('OptionalFeature1',@{Name='OptionalFeature2';Enabled=$false}) -PassThru | Get-Content
3939
4040
# Output:
4141
#
4242
# @{
4343
#
4444
# <snip>
4545
#
46-
# # Optional features enabled in this module.
46+
# # Optional features enabled or disabled in this module.
4747
# OptionalFeatures = @(
4848
# 'OptionalFeature1'
49-
# 'OptionalFeature2'
49+
# @{Name='OptionalFeature2';Enabled=$false}
5050
# )
5151
#
5252
# }
5353
54-
# Create a script file, enabling one or more optional features in the file
54+
# Create a script file, enabling or disabling one or more optional features in the file
5555
@'
56-
#requires -OptionalFeature OptionalFeature1,OptionalFeature2
56+
#requires -OptionalFeature OptionalFeature1,@{Name='OptionalFeature2';Enabled=$false}
5757
5858
<snip>
5959
'@ | Out-File -FilePath ./test.ps1
6060
61-
# Get a list of optional features that are available
62-
Get-OptionalFeature
61+
# Get the current optional feature configuration for the current user and all users
62+
Get-OptionalFeatureConfiguration
6363
6464
# Output:
6565
#
66-
# EnabledIn: Script
66+
# Scope: AllUsers
67+
#
68+
# Name Session NewManifest
69+
# ---- ------ -----------
70+
# OptionalFeature1 False True
71+
# OptionalFeature2 True False
72+
# OptionalFeature4 True True
73+
# OptionalFeature5 False False
6774
#
68-
# Name Source Description
69-
# ---- ------ -----------
70-
# OptionalFeature1 PSEngine Description of optional feature 1
71-
# OptionalFeature2 PSEngine Description of optional feature 2
7275
#
76+
# Scope: CurrentUser
7377
#
74-
# EnabledIn: NotEnabled
78+
# Name Session NewManifest
79+
# ---- ------ -----------
80+
# OptionalFeature2 False True
81+
# OptionalFeature3 False True
82+
#
83+
84+
# Get a list of optional features, their source, and their descriptions
85+
Get-OptionalFeature
86+
87+
# Output:
7588
#
7689
# Name Source Description
7790
# ---- ------ -----------
91+
# OptionalFeature1 PSEngine Description of optional feature 1
92+
# OptionalFeature2 PSEngine Description of optional feature 2
7893
# OptionalFeature3 PSEngine Description of optional feature 3
94+
# OptionalFeature4 PSEngine Description of optional feature 4
95+
96+
# Enable an optional feature in current and future PowerShell sessions for all
97+
# users in PowerShell.
98+
Enable-OptionalFeature -Name OptionalFeature1 -UserScope AllUsers
99+
100+
# Output:
101+
# None
79102
80-
# Enable an optional feature by default in PowerShell
81-
Enable-OptionalFeature -Name OptionalFeature1
103+
# Disable an optional feature in current and future PowerShell sessions for the
104+
# current user in PowerShell.
105+
Disable-OptionalFeature -Name OptionalFeature1 -UserScope CurrentUser
82106
83107
# Output:
84-
# This works just like Enable-ExperimentalFeature, turning the optional
85-
# feature on by default for all future sessions in PowerShell.
108+
# None, unless the feature was explicitly enabled for all users and is being
109+
# disabled only for the current user as an override, as is the case here,
110+
# in which case they get prompted to confirm.
86111
87-
# Disable an optional feature by default in PowerShell
88-
Disable-OptionalFeature -Name OptionalFeature1
112+
# Enable an optional feature in all new module manifests created with
113+
# New-ModuleManifest in the current and future PowerShell sessions for any user
114+
# in PowerShell.
115+
Enable-OptionalFeature -Name OptionalFeature2 -NewModuleManifests -UserScope AllUsers
89116
90117
# Output:
91-
# This works ust like Disable-ExperimentalFeature, turning the optional
92-
# feature off by default for all future sessions in PowerShell.
118+
# None
119+
120+
# Enable an optional feature in all new module manifests created with
121+
# New-ModuleManifest in the current and future PowerShell sessions for the
122+
# current user in PowerShell.
123+
Disable-OptionalFeature -Name OptionalFeature3 -NewModuleManifests
93124
94-
# Enable an optional feature by default in all new module manifests
95-
# created with New-ModuleManifest in all future sessions in PowerShell.
96-
Enable-OptionalFeature -Name OptionalFeature1 -NewModuleManifests
125+
# Output:
126+
# None
97127
98-
# Disable an optional feature by default in all new module manifests
99-
# created with New-ModuleManifest in all future sessions in PowerShell.
100-
Disable-OptionalFeature -Name OptionalFeature1 -NewModuleManifests
128+
# Enable an optional feature the duration of the script block being invoked.
129+
Use-OptionalFeature -Name OptionalFeature1 -ScriptBlock {
130+
# Do things using OptionalFeature1 here
131+
}
101132
```
102133

103134
## Specification
104135

105-
Aside from closely (but not exactly, see below) mirroring what is already in place internally for experimental features in PowerShell, this RFC includes a few additional enhancements that will be useful for optional features, as follows:
136+
Unlike experimental features, which can only be enabled or disabled in PowerShell sessions created after enabling or disabling them, optional features can be enabled or disabled in the current PowerShell session as well as in future PowerShell sessions. This is necessary to allow certain functionality to be "lit up" in packaged modules or scripts.
137+
138+
Below you will find details describing how this functionality will be implemented.
139+
140+
### System and User scope powershell.config.json
141+
142+
Enabling optional features automatically in future PowerShell sessions requires creating or updating one of two `powershell.config.json` configuration files that are read on startup of a new PowerShell session:
143+
144+
* one in `$PSHOME`, which applies to all user sessions
145+
* one in `$HOME\Documents\PowerShell\powershell.config.json` on Windows or `$HOME/.config/powershell/powershell.config.json` on Linux and macOS, which applies only to current user sessions.
146+
147+
This RFC will enable optional feature defaults to be read from these configuration files, with current user configuration taking precedence over system (all users) configuration. System config is not policy so this should be acceptable and expected.
106148

107149
### Add parameter to New-ModuleManifest
108150

109-
`[-OptionalFeatures <string[]>]`
151+
`[-OptionalFeatures <object[]>]`
110152

111-
This new parameter would assign specific optional features to new modules. Note that these would be in addition to optional features that are enabled by default in manifests created with `New-ModuleManifest`.
153+
This parameter would configure specific optional features in the new module manifest that is generated.
112154

113-
### Add parameter to #requires statement
155+
The values provided to this parameter would be combined with optional features that are enabled or disabled by default according to the session configuration files, with the values specified in the `New-ModuleManifest` command overriding the settings for optional features with the same name that are configured in the configuration files. Entries in this collection would either be string (the name of the optional feature to enable) or a hashtable with two keys: `name` (a string) and `enabled` (a boolean value). The hashtable allows an optional feature to be specifically disabled instead of enabled within a module, which is necessary if an older module does not support a newer optional feature yet.
114156

115-
`#requires -OptionalFeatures <string[]>`
157+
A terminating error is generated if the same optional feature name is used twice in the collection passed into the `-OptionalFeatures` parameter.
116158

117-
This new parameter would enable optional features in the current script file.
159+
### Add parameter set to #requires statement
118160

119-
### New command: Get-OptionalFeature
161+
`#requires -OptionalFeatures <object[]>`
162+
163+
This parameter set would enable optional features in the current script file.
164+
165+
Entries in this collection would either be string (the name of the optional feature to enable) or a hashtable with two keys: `name` and `enabled`. The hashtable allows an optional feature to be specifically disabled instead of enabled within a script.
166+
167+
A terminating error is generated if the same optional feature name is used twice in the collection passed into the `-OptionalFeatures` parameter.
168+
169+
### New command: Get-OptionalFeatureConfiguration
120170

121171
```none
122-
Get-OptionalFeature [[-Name] <string[]>] [<CommonParameters>]
172+
Get-OptionalFeatureConfiguration [[-Name] <string[]>] [-UserScope { CurrentUser | AllUsers | Any }] [<CommonParameters>]
123173
```
124174

125-
This command would return the optional features that are available in PowerShell. The default output format would be of type table with the properties `Name`, `Source`, and `Description`, and with the results grouped by the value of the `EnableIn` property. All of those properties would be of type string except for `EnabledIn`, which would be an enumeration with the possible values of `NotEnabled`, `Session`, `Manifest`, `Script`, and `Scope`. This differs from experimental features where `Enabled` is a boolean value. Given the locations in which an optional feature can be enabled, it would be more informative to identify where it is enabled than simply showing `$true` or `$false`. The enumeration values have the following meaning:
175+
This command would return the current configuration of optional features that are available in PowerShell, read from the configuration files.
126176

127-
|Value|Description|How to set the feature up this way|
128-
|--|--|--|
129-
|NotEnabled|The optional feature is not enabled at all|Disable-OptionalFeature command|
130-
|Session|The optional feature is enabled by default in all PowerShell sessions|Enable-OptionalFeature command|
131-
|Manifest|The optional feature is enabled in the manifest for the current module|OptionalFeatures entry in module manifest|
132-
|Script|The optional feature is enabled in the current script|#requires entry in script file|
133-
|Scope|The optional feature is enabled the current scope|Use-OptionalFeature command|
177+
The properties on the `S.M.A.OptionalFeatureConfiguration` object would be `Name`, `Session`, `NewManifest`, and `Scope`, defined as follows:
134178

135-
### New command: Enable-OptionalFeature
179+
|Property Name|Description|
180+
|--|--|
181+
|`Name`|A string value that identifies the optional feature name|
182+
|`Session`|A boolean value that identifies whether the optional feature is enabled or disabled in the current and new PowerShell sessions|
183+
|`NewManifest`|A boolean value that identifies whether the optional feature is enabled or disabled in manifests created by new module manifests in the current and new PowerShell sessions|
184+
|`Scope`|An enumeration identifying whether the optional feature configuration was set up for the `CurrentUser` or `AllUsers`|
185+
186+
The default output format is of type table with the properties `Name`, `Session`, and `NewManifest` with the results grouped by `Scope`.
187+
188+
When this command is invoked with the `-UserScope` parameter, the results are automatically filtered for that scope. The default value for `-UserScope` is `Any`, showing configuration values from both configuration files.
189+
190+
### New command: Get-OptionalFeature
136191

137192
```none
138-
Enable-OptionalFeature [-Name] <string[]> [-NewModuleManifests] [-WhatIf] [-Confirm] [<CommonParameters>]
193+
Get-OptionalFeature [[-Name] <string[]>] [<CommonParameters>]
139194
```
140195

141-
This command would enable an optional feature either globally (if the `-NewModuleManifests` switch is not used) or only in new module manifests created by `New-ModuleManifest`.
196+
This command will return a list of the optional features that are available in PowerShell, along with their source and description.
197+
198+
The properties on the `S.M.A.OptionalFeature` object would be `Name`, `Source`, `Description`, defined as follows:
142199

143-
### New command: Disable-OptionalFeature
200+
|Property Name|Description|
201+
|--|--|
202+
|`Name`|A string value that identifies the optional feature name|
203+
|`Source`|A string value that identifies the area of PowerShell that is affected by this optional feature|
204+
|`Description`|A string value that describes the optional feature|
205+
206+
The default output format would be of type table with the properties `Name`, `Source`, and `Description`.
207+
208+
### Enabling and disabling optional features in current and future PowerShell sessions
144209

145210
```none
146-
Disable-OptionalFeature [-Name] <string[]> [-NewModuleManifests] [-WhatIf] [-Confirm] [<CommonParameters>]
211+
Enable-OptionalFeature [-Name] <string[]> [-NewModuleManifests] [-UserScope { CurrentUser | AllUsers }] [-WhatIf] [-Confirm] [<CommonParameters>]
212+
213+
Disable-OptionalFeature [-Name] <string[]> [-NewModuleManifests] [-UserScope { CurrentUser | AllUsers }] [-WhatIf] [-Confirm] [<CommonParameters>]
147214
```
148215

149-
This command would disable an optional feature either globally (if the `-NewModuleManifests` switch is not used) or only in new module manifests created by `New-ModuleManifest`. If the optional feature is not enabled that way in the first place, nothing would happen.
216+
It is important to note up front that there are three default states for an optional feature: enabled by default, implicitly disabled by default, and explicitly disabled by default. The only time an optional feature needs to be explicitly disabled by default is if it is enabled by default in the AllUsers configuration file and a specific user wants to disable it for their sessions. This impacts how `Disable-OptionalFeature` works.
217+
218+
The `Enable-OptionalFeature` command will enable an optional feature in current and future PowerShell sessions either globally (if the `-NewModuleManifests` switch is not used) or only in manifests created by `New-ModuleManifest`.
219+
220+
The `Disable-OptionalFeature` command will disable an optional feature in current and future PowerShell sessions either globally (if the `-NewModuleManifests` switch is not used) or only in manifests created by `New-ModuleManifest`. If the `AllUsers` scope is used and the optional feature is completely disabled in that scope as a result of this command, the entry is removed from the configuration file. If the `AllUsers` scope is used and there is no entry in the system (all users) configuration file, nothing happens. If the `CurrentUser` scope is used there is no entry in the system (all users) or current user configuration files, nothing happens. If the `CurrentUser` scope is used and the optional feature is enabled in the `AllUsers` configuration file, users will be informed that this feature is enabled for all users and asked to confirm that they want to explicitly disable this feature for the current user in the current and future PowerShell sessions. They can always re-enable it later.
150221

151222
### New command: Use-OptionalFeature
152223

@@ -156,16 +227,12 @@ Use-OptionalFeature [-Name] <string[]> [-ScriptBlock] <ScriptBlock> [-Confirm] [
156227

157228
This command would enable an optional feature for the duration of the `ScriptBlock` identified in the `-ScriptBlock` parameter, and return the feature to its previous state afterwards. This allows for easy use of an optional feature over a small section of code.
158229

159-
## Alternate proposals and considerations
230+
### Checking optional feature states within the PowerShell runtime
160231

161-
### Extend experimental features to support the enhancements defined in this RFC
162-
163-
Experimental features and optional features are very similar to one another, so much so that they really only differ in name. Given the model for how both of these types of features are used, it may make sense to have them both use the same functionality when it comes to enabling/disabling them in scripts and modules. The downside I see to this approach is that optional features are permanent features in PowerShell while experimental features are not, so it may not be a good idea to support more permanent ways to enable experimental features such as `#requires` or enabling an experimental feature in a new module manifest.
164-
165-
### Supporting a `-Scope` parameter like the experimental feature cmdlets do
232+
Optional features can be enabled or disabled in a session, module, script, or script block. Since enabling or disabling an optional feature can happen at different levels, the current state of an optional feature should be maintained in a stack, where the validation logic simply peeks at the top of the stack to see if the feature is enabled or not, popping the top of the stack off when appropriate (when leaving the scope of the module, script, or script block where the feature is enabled).
166233

167-
The `Enable-OptionalFeature` and `Disable-OptionalFeature` cmdlets could support a `-Scope` parameter like their experimental feature cmdlet counterparts do. I felt it was better to remove this for optional features, because it may be risky to allow a command to enable an optional feature in a scope above the one in which it is invoked, influencing behaviour elsewhere.
234+
## Alternate proposals and considerations
168235

169-
### Allow optional features to be disabled at a certain level
236+
### Extend experimental features to support the enhancements defined in this RFC
170237

171-
Certain optional features may be so important that PowerShell should be installed with them to be on by default. In cases where this happens, scripters should be able to indicate that they want the opposite behaviour in a script file or module, so that they can ensure any compatibility issues are addressed before the feature is enabled in that module/script. With this in mind, we could either allow optional features to be disabled at a certain level, or stick with enable only, inversing how the optional feature is designed such that turning it on effectively disables a breaking fix that was deemed important enough to have fixed by default.
238+
At a glance, experimental features and optional features are very similar to one another, so it was proposed that it may make sense to have them both use the same functionality when it comes to enabling/disabling them in scripts and modules; however, experimental features have a completely different intent (to try out new functionality in a PowerShell session), are only for future PowerShell sessions, and they only have a single on/off state. On the other hand, optional features are for the current and future PowerShell sessions, and may be enabled or disabled in various scopes within those sessions. For that reason, this approach doesn't seem like a viable solution. If we want to change that, perhaps someone should file an RFC against experimental features to make that change.

0 commit comments

Comments
 (0)