Repository navigation
What is the right way to implement deprecation in PowerShell? #11674
Description
Follow-up from #11662 (comment):
Context:
Note: Deprecation in the context of PowerShell is always "soft" deprecation in the sense that, given the commitment to backward compatibility, features are never removed, so that old code continues to function. Instead, deprecation means discouraging use of a given feature, for various reasons: having been superseded by a superior alternative, general obsoleteness, security concerns.
Deprecation can be implemented in the following, potentially complementary ways:
-
Documentation warnings: Clearly state that a given cmdlet / one of its parameters is deprecated and should not be used in new code.
-
Design-time warnings: Implement a PSScriptAnalyzer (PSSA) warning that is ideally also surfaced in Visual Studio Code, via the PowerShell extension's integrated PowerShell Editor Services (PSES).
This is currently mostly not implemented (see details below). -
Runtime warnings:
Currently, a warning (to stream3) is emitted when a deprecated feature is called.
The latter are currently implemented by a repurposing of the general System.ObsoleteAttribute attribute, whose purpose is to provide compile-time warnings (and optionally errors) about obsolete features: given that PowerShell code isn't compiled, the PowerShell engine explicitly surfaces Obsolete attributes on cmdlets or their parameters (but seemingly not SDK classes with the attribute) with a PowerShell warning at runtime.
Examples of features currently deprecated this way are Send-MailMessage and the -Raw parameter of the Format-Hex cmdlet; e.g.:
PS> 'foo' | Format-Hex -Raw
WARNING: Parameter 'Raw' is obsolete. Raw parameter is deprecated.
... # regular output3>$null or -WarningAction Ignore can be used to suppress the warning, but old code written pre-deprecation will show the warning.
As an aside: The combination of the boilerplate part of the warning (Parameter 'Raw' is obsolete) with the attribute-specific message (Raw parameter is deprecated) could use some revising.
Relevant questions are:
-
Should deprecation be signaled at runtime at all?
- If so, is the current approach the right one?
-
Is it sufficient to warn at design time, knowing that not every user will see those warnings, given that they may use an editor other than Visual Studio Code and not install and routinely run
Invoke-ScriptAnalyzerafter installing thePSScriptAnalyzermodule?-
As of this writing, PSSA (warning) rules seems to comprise just one relating to one specific deprecated feature,
AvoidUsingDeprecatedManifestFields, whereas several more deprecated features exist - full list of rules is here. -
Perhaps a more promising approach is to let PSSA directly surface
Obsoleteattributes encountered in code, if technically feasible.
-
Activity
SeeminglyScience commentedon Jan 24, 2020
Thanks Michael Klement (@mklement0)! Copying my comment here so I can mark the one in the other thread as off topic.
It definitely can break things (I know there is a difference between something breaking the change contract and a change technically breaking a thing, but bear with me):
This is part of why I don't really like the warning system at all. Most of the things I would want to warn about are better off as a non-terminating error anyway.
If something is writing a warning, I'm either silencing it, or throwing on it. If I'm throwing on it with
-WarningAction Stop, then addingObsoleteAttributewill cause my script to always throw.I know that if it's considered a breaking change than that makes the attribute useless, but that's how it feels currently. Personally I think
ObsoleteAttributeshould be moved to a design time warning only, maybe witherror: truebeing the exception. That's how C# works as well, anything already built keeps on chugging but you're gonna know about it if you try to use it in something new.That's true up until the point where something reads that metadata and changes behavior based on it. Adding
Mandatoryto aParameterAttributefor example is for sure a breaking change.SeeminglyScience commentedon Jan 24, 2020
Simply emitting a new warning message can also break systems that detect success/failure based on stdout/err.
For example, script based application detection methods in SCCM. The SCCM client will invoke a PowerShell script and use stdout/err to determine if the subject application was installed correctly. Here's a table (from About custom script detection methods):
Zero exit code
Non-zero exit code
So since warning counts as stdout, if a detection method uses a command that has been deprecated, all of those applications will start reporting as if they are already installed.
iSazonov commentedon Jan 24, 2020
GitHub search results:
I still believe that the transfer of the system to a new version involves testing. I don’t think anyone can easily jump from 5.1 version to 7.0.
To migrate a large system like SCCM MSFT team and consumers must test all used scripts.
I guess we need a special label for already tested scripts and engine could report still not tested scripts.
For warning messages we could play with preference level to ignore such messages in scripts and show in console session.
SeeminglyScience commentedon Jan 24, 2020
That's honestly way more than I expected to see. I would have guessed like low 300s.
Well yeah for sure. I've supported many breaking changes in the past and will continue to do so - when the benefit outweighs the risk.
You could argue (and I'd find it hard to disagree) that the risk is pretty small for adding a warning. That said, it's often discussed as zero risk, like it's an interactive only change. The risk is definitely higher than zero, and in my opinion higher than should be acceptable for the simple act of marking something as obsolete. I'm mainly suggesting that a design time warning should be sufficient.
dragonwolf83 commentedon Jan 25, 2020
A new
ObsoleteorDeprecatestream could be added that is only updated by theSystem.Obsoleteattribute. That prevents breaking changes with anyone using-WarningActionand separates out code changes from scripting logic.The SCCM scenario is not likely an issue.
WarningandVerboseare not redirected to standard out by default so it should not be breaking their detection logic. You can redirect those streams individually or redirect all streams to standard out, but thenVerbosewould break you way before anything else does. Anyone redirecting those streams likely is already handling error detection properly.SeeminglyScience commentedon Jan 25, 2020
That's a cool idea, but a lot of work. Problems with that approach aside, they're realistically not going to do that.
When I say stdout, I'm not referring to PowerShell's output stream. I'm referring to the console process standard output. Anything that writes to the console window and isn't classified as an error message is written to standard output.
Open up
cmd.exeand run thisIn the file will be "WARNING: visible".
dragonwolf83 commentedon Jan 25, 2020
I was too, but I see
$WarningPreferencedefaults toContinueso it is redirecting by default to standard out butVerboseoutput is not unless you specify it.Another option is to just write to the console only and not implement as a full stream. I don't believe the old
Write-Hostfrom before PS 5 wrote to standard out since the whole issue was that it wrote to the console directly. If that is the case, surfacing that logic shouldn't be too hard for this special case.SeeminglyScience commentedon Jan 25, 2020
The issue was that it wasn't going through a PowerShell controlled stream. It did still write to stdout though.
mklement0 commentedon Jan 25, 2020
To illustrate that with a quick example: The following
cmd.execommand has no output, because not justWrite-Host, but[Console]::WriteLine()goes to stdout too:As an aside: That all streams, including the error stream, go to stdout is, of course, a problem, but changing that would be a breaking change: #7989 (comment)
I agree; in order to implement that we'd have to do the following (I can't speak to the technical feasibility):
Make only PSSA, not the PS engine look for
Obsoleteattributes and automatically surface them as warnings.Make PSES and therefore Visual Studio Code (with the PowerShell extension) surface all deprecation warnings by default.
Through announcements and documentation, make it clear that the best PowerShell development experience is offered by Visual Studio Code (along the line of this, but not just from the perspective of obsolescent technology) and that use of a different editor should be complemented with integrating
Invoke-ScriptAnalyzerinto the development workflow.iSazonov commentedon Jan 25, 2020
I see the discussion is only about"Runtime warnings".
I think that the SCCM example is not successful, because SCCM starts an external process. Sometimes it seems to me that it has no design at all :-). Since PowerShell merges everything into one output stream, this will always be a problem, regardless of whether there are warnings. This mechanism is, by definition, completely unreliable.
Opposite look SC Orchestrator. It doesn't use external process, it hosts PowerShell and as result has not the issue.
Now we should look how deprecation is used. We have 2 main scenario - user console session and application session (I mean all without user interaction).
As user I'd accept informational messages that a cmdlet or feature will be removed from future version as non-secure (Invoke-Expression) or non-supported (Send-MailMessage).
But it is important that the message offers a replacement (link to docs) otherwise it will useless and annoying. User will switch to proposed alternative and will do not get warnings.
For applications the warnings are pointless. Main concern in the discussion is that warnings can break something. I believe we are thinking not in right direction because besides this minimal risk there are many, many changes that represent a real risk.
Running blindly any script, module or application on new PowerShell version is dangerous and may be destructively. Users should test their scripts in sandbox before run in live business environment.
Right direction is that we should protect users and enforce them to test scripts.
Proposal is to introduce PowerShell adoption mode to prevent users from running non-tested scripts.
mklement0 commentedon Jan 25, 2020
This is a separate problem, which can be tackled via the planned separate PowerShell executable for not loading
$PROFILEby default - see #8072.The right behavior is to send all streams other than the success output stream to stderr, which is the only other native stream available.
This is not just about SCCM. It is unrealistic and unreasonable to expect all external (build, CI / CD, ....) tooling to host PowerShell, so the problem will persist, even with the above fix in place.
Aside from that, even a host that checks streams separately could be thrown off by the appearance of new warnings.
Agreed, in interactively submitted individual commands (as opposed to scripts) that print to the host it would be helpful for the engine to surface deprecation warnings, along with helpful links.
Interesting - I suggest you create a new issue for that.
But if I interpret your comments correctly, you agree that there is no need for runtime deprecation warnings for unattended (update: and captured / redirected) execution, correct?
iSazonov commentedon Jan 25, 2020
I find it useful for users in interactive session and pointless for scenarios like task scheduler (here we could only write a log event and telemetry if we need).
[-]Is using the System.Obsolete attribute the right way to implement deprecation in PowerShell?[/-][+]What is the right way to implement deprecation in PowerShell?[/+]SeeminglyScience commentedon Jan 25, 2020
One option would be to only write the warning when
CommandOriginisRunspaceand-NonInteractivewas not specified. That would eliminate the majority of risk.31 remaining items
SteveL-MSFT commentedon Aug 19, 2020
@PowerShell/powershell-committee discussed this today. Although no conclusion, we did agree on a few things:
We believe this feature requires a RFC to be authored
iSazonov commentedon Aug 20, 2020
We could do not complicate and simply continue to use Experimental Features but have "Deprecate" in an experimental feature name.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 23, 2023
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.
xtqqczze commentedon Oct 7, 2025
Patrick Meinecke (@SeeminglyScience) Please reopen.