Ensure can be use to indicate a resource exist in an specific state, therefore implies a Test and a series of operations that are might be invoked to get the desired state, for this it will do the best effort.
An example is Ensure-DiskAvaialbleSpace 100GB , it will check if the desired space is available and if not it can start executing a series of operations trying to get the desired state.
Please let me know if there is a better way to express this with existing or other verbs.
There are other verbs that do come close, but none of the ones I can think of deal with idempotence -- not modifying the resource if it's already in the desired state.
Arguably, some of this is the purview of DSC, but I think we could still use some cmdlets that work in a similar fashion. 馃憤
How about Assert
?
Verb AliasPrefix Group Description
---- ----------- ----- -----------
Assert as Lifecycle Affirms the state of a resource
At least from what I've seen, Assert
typically indicates the cmdlet will throw an error if the resource is not in the desired state, rather than attempting to do anything to fix it, no?
The few example of cmdlet using assert behaves as @vexx32 indicates, wondering if there are some examples that can help me to understand other valid uses of assert
I've seen that in the context of tests, but not much otherwise. Afaik there's no official guidance suggesting that should be the case, and tbh it kind of seems like a waste of the verb.
It is in line with how _assert_ is used in debugging in other languages and in Pester's Assert-*
commands, but even generally I think the following distinction makes sense:
To _assert_ means to assume that a given state is _already in effect_, to make a claim about _what should already be true_, without implying action toward making it so.
To _ensure_ means to _take action if needed_ so that a desired state is reached.
Yeah I can't really argue with any of that. I'm just not super excited to see another verb added that is only used a handful of times. The nature of the verb system is that sometimes they don't match up 100%, but keeping the verb pool low means increased discoverability. Assert
is close enough imo.
Understood re concern about verb proliferation, but I've personally cringed at having to shoehorn _ensure_ functionality into Assert-
as a command _author_; as a command _user_ , I find it problematic to be faced with Assert-Foo
without knowing whether it will abort, if Foo isn't already true, or if it will make it true for me - two very different things.
Sure, the help will (hopefully) tell you, but to me the point of a standardized verb is to convey variations of _fundamentally_ the same action, and I don't think that is the case here.
As @vexx32 mentioned, we are getting into DSC territory. What would be the guidance to authors to help them know when to write an Ensure-
function or cmdlet instead of a DSC configuration? I guess for small scenarios that don't warrant the overhead of DSC, but that could get subjective..?
@tommeadon I've not seen or heard much guidance in the way of DSC for pwsh at present. Some pieces are in place. But as yet I don't think there's currently a supported way to apply any DSC configuration when using PowerShell 6+, is there?
More generally, I think, desired-state logic is useful in _any_ domain, not just in the domain of _computer configuration_ (DSC), so I don't see an either-or there (though it couldn't hurt to mention DSC in the documentation for the new verb).
Ensure-DiskAvaialbleSpace vs Test-DiskAvaialbleSpace - second looks well.
I'm afraid I don't understand your comment, @iSazonov; what looks good?
(Test-DiskSpaceAvailable
would return a Boolean reflecting whether enough space is available, Assert-DiskSpaceAvailable
would fail if not enough space is available, and Ensure-DiskSpaceAvailable
would try to make enough space available automatically.)
Original request is to execute a script if a condition is true/false - Ensure-DiskAvaialbleSpace 100Gb -ScriptBlock { Clear-OldFiles }
It is the same as:
if (-not Test-DiskAvaialbleSpace 100Gb)
{
Clear-OldFiles
}
At least from what I've seen,
Assert
typically indicates the cmdlet will throw an error if the resource is not in the desired state, rather than attempting to do anything to fix it, no?
well yes, Assuming you did Ensure-DiskAvaialbleSpace 100GB - what else would the cmdet do but to throw an error is not enough space were available? Unless that cmdlet is really really smart, it's not likely to make more space available on full drive!
Ensure-DiskAvaialbleSpace could begin to delete/archive files such as logs, cached files, VHD, videos, etc until it achieves the desired stated without having to wipe everything, so it is not a simple
if (test) { action }
Also the intention is to be reusable, so the it avoids repetition in several Jobs.
Throwing an exception still a possible result after trying and not achieving the expected state, for example some file types might have be in use or have a minimum retention before it can be moved or deleted..
Yeah. I can see that being really well received. How does this cmdlet even begin to know which log files, are good to remove or which corporate videos get nuked for every organisation.
I have 2TB collection of Grateful Dead live shows that has taken me over 35 years to accumulate. I would have negative thoughts were a cmdlet to delete that data. And suppose that there were no 'temp' files to remove - what then? Would you expect the cmdlet to order, install, and provision a new disk, or a new computer with more disks etc etc? I jest of course but there are some real risks here.
The question of what constitutes deletable files is going to provide different answers for every customers. An Assert-Freespace might be useful - just leave it to the customer to catch the issue and deal accordingly.
I think we're getting off topic here: the proposal is to add a new approved verb, Ensure
, and I think there's consensus on its _general logic_ (though not everyone agrees that we need a new verb).
I don't think we need to discuss the specifics of a hypothetical command that uses this verb; it's just an example (and @iSazonov even fleshed out a variant that should allay your concerns).
I am unconvinced the new verb is needed. Ensure as a verb suggests that something is done to do the ensuring. Ensure-memory, ensure-filespace, etc - all suggest cmdlets are going to be able to do something to do the ensuring.
I'd prefer to use Assert, then catch any exception assert raises. That gives responsibility to the user as to what to if the assertion fails.
Ensure as a verb suggests that something is done to do the ensuring.
Indeed; the distinction between Assert
and Ensure
was summarized above.
@SeeminglyScience's concern is verb proliferation, whereas you seem to think there's no value in implementing commands that do something to ensure a desired state?
I can think of many cases where a desired state can more self-evidently be ensured (think Ensure-ConnectionInitialized
, Ensure-ModuleInstalled
, ...), which is a useful technique, so to give it expression via a dedicated verb makes perfect sense to me. Note that we're not necessarily talking about _standard_ cmdlets using the new verb, but (also) about giving users a way to name self-authored commands that exhibit desired-state logic in an expressive manner that is also compliant.
Here the intention is to allow PSScriptAnalyzer to highlight unwanted verbs, but in this case we haven't found a good alternative to Ensure.
Throwing exceptions seems to over complicate the code,
Browsing I see other examples such as:
Ensure-IISAppPoolStarted
Ensure-FileEnconding
Ensure-PSDrive
Ensure-RegistryKey
Which helps to make code easier.
No sure if there is an alternative in PSScriptAnalyzer to expand the accepted verbs.
For many/.some of those other verbs work too.
0
Because -ea 0
(-ErrorAction SilentlyContinue
) is no substitute for desired-state logic: for instance, if New-RegistryKey
fails not because the key already exists, but _because you lack permission to create it_, your code will malfunction.
Also, a Start-*
command shouldn't be assumed to be idempotent.
What is the main benefit of adding this verb? In a Ensure
vs Set
cmdlet all I see Ensure
is going to run a Test
then Set
if needed. The same logic can performed in whatever script is calling that cmlet like in @iSazonov example.
The main benefit is DRY: you can write _wrapper_ commands you can call from multiple places that ensure that a desired state has been achieved, by wrapping the logic of testing for the state and performing what's necessary to achieve the state only once.
You could similarly ask what the benefit of DSC is: It is the convenience of being able to ask a command / engine / framework to achieve a desired target state, whether by no-op (if already in the target state) or by whatever operations are necessary, and to only be notified in case of the inability to achieve the target state; even though the syntax wouldn't be declarative (the way it is in DSC), calling an Ensure-*
command would have the same benefits.
I'll review current implementation based on @doctordns suggestions.
In general, the pattern is
function Ensure_X {
if ( test_X ) { return }
Action_1
....
if ( test_X ) { return }
Action_n
if ( test_X ) { return }
throw "Failed to achieve Test_X"
}
Verbs such as Set
and New
indicates the action is going to be executed without previous validation.
Certainly the code can be unwind in the caller function, but will make it less clear and reusable
Another example I see
Ensure-File -path $path -Source @($src1, @src2) # src can be UNC, http or git.
Can be argued that Copy-File
is an option, but it hides the intend of checking first and the do the best effort to try to get the resource from any of the indicated sources
I think part of my issue with this verb is that it mainly makes sense to use for internal functions. This may not be everyone's philosophy, but to me it doesn't matter what you name internal functions, as long as they describe what they do. The approved verb system is for discoverability, and you aren't discovering internal functions in the same way you do public functions (even as a new contributor).
I say it makes sense mainly for internal functions because it doesn't seem productive to export a Test
, Set
and also an Ensure
that just wraps the two. It doesn't feel like saving an if
statement is enough value to warrant exporting a whole extra command. If you really want to add a "test and fix" option, I'd just add a -Fix
switch to the Test
command.
The verb also makes sense for external "controller" scripts, but I feel the same way there. If the point of the script is to be ran from a scheduled task and not to provide utility to other scripts, there's nothing wrong with EnsureDiskSpace.ps1
.
it mainly makes sense to use for internal functions.
I don't disagree about the _mainly_ part; I personally think it can also make sense for _published_ commands, but I don't think we need to discuss this aspect further, at least yet.
Also, there are different degrees of "internality"; if it's not a published command as such, but something you share with colleagues (whether as function source code for their profiler or as an external script), you'd still want it to be properly named for the sake of _discoverability_.
This also applies to your example (Ensure-DiskSpace
not EnsureDiskspace
).
As for truly module-/script-internal helper functions/scripts:
This may not be everyone's philosophy, but to me it doesn't matter what you name internal functions, as long as they describe what they do
I'd say whether to use compliant naming there is a matter of personal preference.
Especially If something is a proper advanced function - even if used only internally - I personally prefer compliant names. If you use nonstandard verbs, _PSSA will haunt you_, as @JohnLeyva has stated.
(There's also the issue of something that may start out internal becoming public later; if you start out with compliant names, no refactoring is needed.)
There is currently no good solution:
You can either live with the annoyance of PSSA warnings that you know don't apply.
Or you can turn PSSA verb warnings off altogether, and lose warnings you may care about.
(I suppose making the UseApprovedVerbs
PSSA rule configurable with exclusion lists would _technically_ be a solution, but I don't think encouraging "private sub-languages" is a good idea; Ensure
deserves _public_ recognition, for the reasons discussed).
Also, there are different degrees of "internality"; if it's not a published command as such, but something you share with colleagues (whether as function source code for their profiler or as an external script), you'd still want it to be properly named for the sake of _discoverability_.
I'm using the term internal similar to the access modifier in C#. More specifically, when I say internal I'm referring to situations where you would not (easily) be able to retrieve the function via Get-Command -Verb Ensure
.
I'd say whether to use compliant naming there is a matter of personal preference.
Especially If something is a proper advanced function - even if used only internally - I personally prefer compliant names. If you use nonstandard verbs, _PSSA will haunt you_, as @JohnLeyva has stated.
I definitely agree that it's personal preference whether you'd like to extend the compliance guidline to include non-public commands. As for PSSA, yeah it's unfortunate that PSSA can't determine if a command is exported. Personally I use VerbNoun
naming in non-public commands mostly so it's easy to tell at a glance what is public and what isn't; not setting off PSSA is a plus as well.
(There's also the issue of something that may start out internal becoming public later; if you start out with compliant names, no refactoring is needed.)
I get what you're saying but no refactoring seems unlikely. I don't think the majority of folks build out an internal function in the same way they would a public one. Plus in this scenario, this internal function is internal because you're just wrapping existing Test
and Set
commands to keep DRY.
There is currently no good solution:
- You can either live with the annoyance of PSSA warnings that you know don't apply.
- Or you can turn PSSA verb warnings off altogether, and lose warnings you may care about.
Or use a different naming scheme for non-public commands. The list of approved verbs is pretty tailored to public API's imo. I don't think it makes sense to try to shoe horn incredibly specific and narrow functions into the same naming conventions as general use API's, and I don't think it makes sense to expand the pool of verbs approved for public API's to include every verb you might want for internal commands.
(I suppose making the
UseApprovedVerbs
configurable with exclusion lists would _technically_ be a solution, but I don't think encouraging "private sub-languages" is a good idea
Definitely agree there. It would be really cool if PSSA could be expanded to infer what is exported and what isn't, but that's pretty difficult statically.
Indeed the intention at some point is to make them public, if convenient .
For the time being we are going to try to make compromises by changing Ensure to other verbs as it is more important to allow the PSSA to execute all checks.
One that is very often used in Powershell on vscode so devs can quickly ensure are ready to perform operations in azure.
function Ensure-AzContext {
$azSubscription = $deployManifest.Subscription
$azContext = Get-AzContext
if (-not $azContext) {
Connect-AzAccount -Subscription $azSubscription | Out-Null
}
else {
Set-AzContext -Subscription $azSubscription | Out-Null
}
}
I'm using the term internal similar to the access modifier in C#. More specifically, when I say internal I'm referring to situations where you would not (easily) be able to retrieve the function via Get-Command -Verb Ensure.
The list of approved verbs is pretty tailored to public APIs imo. I don't think it makes sense to try to shoe horn incredibly specific and narrow functions into the same naming conventions as general use APIs
We can reasonably conceive of _public_ as _used by other people_, so I don't think an approved verb necessarily has to meet the bar of _suitable for built-in cmdlets and similar foundational cmdlets_.
Providing _higher-level convenience commands_ of general utility to be shared with other is a use case well worth supporting with an approved verb (@JohnLeyva's Ensure-AzContext
above is an example).
(And in terms of semantics I think we agree that _ensure_ is a broad, widely applicable concept.)
In your own example, having to name your external script EnsureDiskspace
rather than Ensure-DiskSpace
, just so you can avoid the presently unapproved to me makes matters worse. (And, if it is placed in $env:PATH
, you wouldn't be able to find it with Get-Command -Verb Ensure
.)
We can reasonably conceive of _public_ as _used by other people_, so I don't think an approved verb necessarily has to meet the bar of _suitable for built-in cmdlets and similar foundational cmdlets_.
I'm not referring to "a thing used by the public" I'm referring to specifically a "public API". If it's not a public API (i.e. is not exported in anyway), the concept of approved verbs is not as important.
In your own example, having to name your external script
EnsureDiskspace
rather thanEnsure-DiskSpace
, just so you can avoid the presently unapproved to me makes matters worse.
That's not the purpose of it, just a side effect. Either way, PSSA being unable to determine whether a command is exported is not a good reason to include a verb that is mostly suitable for internal only use.
(And, if it is placed in
$env:PATH
, you wouldn't be able to find it withGet-Command -Verb Ensure
.)
If it was in the path, then it would be an exported command and I would instead recommend a module that exports a Set
and a Test
version instead.
I'm not referring to "a thing used by the public"
I understand. I'm saying that it is _useful and desirable to apply approved verbs to such things too_.
If it was in the path, then it would be an exported command and I would instead recommend a module that exports a Set and a Test version instead
I think it's reasonable to want to and desirable to be able to provide used-by-others higher-level convenience Ensure-
commands - whether they are semi-public (used in a department / company) or even published to the PS gallery.
If what such commands do is to _ensure a desired state_, it should be possible to name them that - violating the naming guidelines more fundamentally with something like EnsureAzContent
just to avoid PSSA warnings, at the expense of discoverability, is undesirable.
To be clear: I'm not suggesting we open the floodgates for many new verbs. The benefits of Ensure
specifically are hopefully evident from the discussion.
I hope that clarifies the difference in opinion - which comes down to what the scope of approved verbs is - and I am happy to leave it that.
I hope the following clears up any confusion and presents clear, actionable choices.
We've identified three layers of commands by the scope of their audience:
(a) _Private_ (internal) commands: module- or script-internal scripts and functions. They can be considered _implementation details_.
(b) Non-public _shared commands_: Commands that aren't published publicly (internet-wide) - at least not yet - but are shared by a group of users, such as shared scripts and functions and commands from self-authored modules used in a given organization; users could be other scripters who build on these commands as well as end users.
Such commands are likely to include organization-specific wrappers for given commands as well as higher-level convenience commands that wrap multiple other commands.
(c) _Public_ (official and quasi-official) commands: Built-in commands, commands from official modules, (exemplary) modules published to public repositories.
The _general_ question it comes down to:
This is separate from the question whether Ensure-*
commands have their place in (c) as well - which could be a new discussion if the consensus is that approved verbs should only apply to (c)
If the consensus ends up being that (b) is in scope as well, the _specific_ question is:
Ensure
as a verb, as described above, meet the bar of being both expressive and widely applicable?Also, a Start-* command shouldn't be assumed to be idempotent.
Does "idempotent" as a description usefully apply to commands with side effects?
Is it your intent that Ensure- commands could be assumed to be "idempotent"? And if so, what does that mean? There's several cases mixed up; "runs once, only if needed. Not more than once" like start-process where you don't want many of them. "Runs at least once, as many times as you like, it's safe", like the Azure Context (Enter-AzContext
?). "can run as many times as you like, but preferably none because it's slow", like the free disk space one. All wrapped in using exceptions for control flow (try to get this state, throw if you can't).
And breaking the Noun part of Verb-Noun; new-process, get-process, start-process, but what does Ensure-Process actually ensure? You have to add a description to the end Ensure-ProcessHasCorrectLogonCredentials, Ensure-ProcessIsRunning, Ensure-ProcessExists. Would the pattern be to make parameters for what you are ensuring?
Throwing exceptions seems to over complicate the code,
@JohnLeyva if there are no exceptions, you could call Ensure-AzContext
and still have no context afterwards. That seems unhelpful, having an ensure which doesn't.
Does "idempotent" as a description usefully apply to commands with side effects?
Yes, with respect to the _end result_.
And if so, what does that mean?
It means: ensure the desired state, doing whatever is necessary, if anything; fail only if the desired state cannot be achieved.
You have to add a description to the end Ensure-
Yes.
Would the pattern be to make parameters for what you are ensuring?
Yes, just as with any command.
I agree that Ensure is needed and it is to support desired state. Every resource requires different logic to get to the desired state. It might be to delete and recreate, it might be to update it.
Having Ensure as a Verb allows module authors to create a module that wraps the complexity of getting something into the desired state.
New or Set with ignoring errors is a real bad form as it hides other errors that occur. You don't want to ignore all errors, just those due to the resource existing. Having Ensure makes code and intent much more explicit.
New implies I want a new one, but I don't want a New one if it already exists, I want the existing one.
Ensure provides that semantics
+1
Wonder how to bump this to get the discussion going, seems to have died off. We still see this as very valuable
Most helpful comment
I agree that Ensure is needed and it is to support desired state. Every resource requires different logic to get to the desired state. It might be to delete and recreate, it might be to update it.
Having Ensure as a Verb allows module authors to create a module that wraps the complexity of getting something into the desired state.
New or Set with ignoring errors is a real bad form as it hides other errors that occur. You don't want to ignore all errors, just those due to the resource existing. Having Ensure makes code and intent much more explicit.
New implies I want a new one, but I don't want a New one if it already exists, I want the existing one.
Ensure provides that semantics