Skip to content
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

Several suggestions to make it better #162

Merged
merged 29 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f19883d
Make .NET written consistently the same throughout
richy58729 Jan 28, 2023
8555e21
Change 'addition' to 'additional'.
richy58729 Jan 28, 2023
b9dd96e
Change some inconsistent punctuation marks.
richy58729 Jan 28, 2023
410532a
Change 'Windows PowerShell' to 'PowerShell'.
richy58729 Jan 28, 2023
10e35e6
Change 'Null' to 'null' to keep it consistent.
richy58729 Jan 28, 2023
5f7cf65
Change "it's" to "its" as it is used possessively.
richy58729 Jan 28, 2023
59b660d
Change 'Powershell' to 'PowerShell'.
richy58729 Jan 28, 2023
7539a35
Change '.Net' to '.NET'.
richy58729 Jan 28, 2023
ecd84a1
Add missing comma to make the sentence correct.
richy58729 Jan 28, 2023
c7defc8
Change '.net' to '.NET'.
richy58729 Jan 28, 2023
04bcaf2
Change '...' to '.'.
richy58729 Jan 28, 2023
454dfe4
Change 'when' to 'which'.
richy58729 Jan 28, 2023
59ac646
Change non-existent words.
richy58729 Jan 28, 2023
fbb9f55
Add dot to the end of a sentence.
richy58729 Jan 28, 2023
f0dc492
Add 'controller'.
richy58729 Jan 28, 2023
58e9ab6
Delete 'on the other hand'.
richy58729 Jan 28, 2023
2e1d936
Delete inconsistent line spacing.
richy58729 Jan 28, 2023
a28f24f
Add dot to sentences.
richy58729 Jan 28, 2023
fa28cea
Correct 'in regards'.
richy58729 Jan 28, 2023
dfb06f4
Write 'pipeline' consistently throughout.
richy58729 Jan 29, 2023
845f55e
Change few typos
richy58729 Apr 16, 2023
4ef6c31
Update Output-and-Formatting.md
richy58729 Apr 16, 2023
d82198f
Update Output-and-Formatting.md
richy58729 Apr 16, 2023
670a534
Update Performance.md
richy58729 Apr 16, 2023
9477842
Update Performance.md
richy58729 Apr 16, 2023
e06936a
Update Security.md
richy58729 Apr 16, 2023
6fbe21f
Update TODO.md
richy58729 Apr 16, 2023
3a00c90
Update Writing-Parameter-Blocks.md
richy58729 Apr 16, 2023
3f9f225
Merge branch 'master' into master
Jaykul Apr 20, 2023
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
19 changes: 6 additions & 13 deletions Best-Practices/Building-Reusable-Tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@

For this discussion, it's important to have some agreed-upon terminology. While the terminology here isn't used universally, the community generally agrees that several types of "script" exist:

1. Some scripts contain tools, when are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of re-use.
2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands
1. Some scripts contain tools, which are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of reuse.
2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A controller script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands.

For example, you might write a "New-CorpUser" script, which provisions new users. In it, you might call numerous commands and functions to create a user account, mailbox-enable them, provision a home folder, and so on. Those discrete tasks might also be used in other processes, so you build them as functions. The script is only intended to automate that one process, and so it doesn't need to exhibit reusability concepts. It's a standalone thing.

Controllers, on the other hand, often produce output directly to the screen (when designed for interactive use), or may log to a file (when designed to run unattended).

Controllers often produce output directly to the screen (when designed for interactive use), or may log to a file (when designed to run unattended).

# TOOL-02 Make your code modular

Generally, people tend to feel that most working code - that is, your code which does things - should be modularized into functions and ideally stored in script modules.

That makes those functions more easily re-used. Those functions should exhibit a high level of reusability, such as accepting input only via parameters and producing output only as objects to the pipeline

That makes those functions more easily reused. Those functions should exhibit a high level of reusability, such as accepting input only via parameters and producing output only as objects to the pipeline.

# TOOL-03 Make tools as re-usable as possible

Expand All @@ -29,9 +27,9 @@ You can get a list of the verbs by typing 'get-verb' at the command line.

# TOOL-05 Use PowerShell standard parameter naming

Tools should be consistent with PowerShell native cmdlets in regards parameter naming.
Tools should be consistent with PowerShell native cmdlets in regard to parameter naming.

For example, use $ComputerName and $ServerInstance rather than something like $Param_Computer or $InstanceName
For example, use $ComputerName and $ServerInstance rather than something like $Param_Computer or $InstanceName.

# TOOL-06 Tools should output raw data

Expand All @@ -45,7 +43,6 @@ For example, a function named Get-DiskInfo would return disk sizing information

An intermediate step is useful for tools that are packaged in script modules: views. By building a manifest for the module, you can have the module also include a custom .format.ps1xml view definition file. The view can specify manipulated data values, such as the default view used by PowerShell to display the output of Get-Process. The view does not manipulate the underlying data, leaving the raw data available for any purpose.


# WAST-01 Don't re-invent the wheel

There are a number of approaches in PowerShell that will "get the job done." In some cases, other community members may have already written the code to achieve your objectives. If that code meets your needs, then you might save yourself some time by leveraging it, instead of writing it yourself.
Expand Down Expand Up @@ -77,15 +74,12 @@ It has been argued by some that, "I didn't know such-and-such existed, so I wrot

On the flip side, it's important to note that writing your own code from the ground up can be useful if you are trying to learn a particular concept, or if you have specific needs that are not offered by another existing solution.


# WAST-02 Report bugs to Microsoft

An exception: if you know that a built-in technique doesn't work properly (e.g., it is buggy or doesn't accomplish the exact task), then obviously it's fine to re-invent the wheel. However, in cases where you're doing so to avoid a bug or design flaw, then you should - as an upstanding member of the community - report the bug on [github.com/powershell](https://github.com/PowerShell/PowerShell/issues) also.


TODO: The "PURE" section is dubious at best. We need to discuss it.


# PURE-01 Use native PowerShell where possible

This means not using COM, .NET Framework classes, and so on when there is a native Windows PowerShell command or technique that gets the job done.
Expand All @@ -103,4 +97,3 @@ Document the reason for using tools other than PowerShell in your comments.
That said, you truly become a better PowerShell person if you take the time to wrap a less-preferred way in an advanced function or cmdlet. Then you get the best of both worlds: the ability to reach outside the shell itself for functionality, while keeping the advantages of native commands.

Ignorance, however, is no excuse. If you've written some big wrapper function around Ping.exe simply because you were unaware of Test-Connection, then you've wasted a lot of time, and that is not commendable. Before you move on to a less-preferred approach, make sure the shell doesn't already have a way to do what you're after.

6 changes: 3 additions & 3 deletions Best-Practices/Output-and-Formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ TODO: This whole document is STILL ROUGH DRAFT

Previous to PowerShell 5, Write-Host has no functionality at all in non-interactive scripts. It cannot be captured or redirected, and therefore should only be used in functions which are "Show"ing or "Format"ing output, or to display something as part of an interactive prompt to the user.

That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build a interactions with the user in other cases (e.g. to write extra information to the screen before prompting the user for a choice or input).
That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build interactions with the user in other cases (e.g., to write extra information to the screen before prompting the user for a choice or input).

Generally, you should consider the other Write-* commands first when trying to give information to the user.

Expand All @@ -22,7 +22,7 @@ You should use verbose output for information that contains details about the va

## Use Write-Debug to give information to someone maintaining your script

You should use the debug output stream for output that is useful for script debugging (ie: "Now entering main loop" or "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary.
You should use the debug output stream for output that is useful for script debugging (e.g., "Now entering main loop" or "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary.

> TIP: When debugging you should be aware that you can set `$DebugPreference = "Continue"` to see this information on screen without entering a breakpoint prompt.

Expand All @@ -48,7 +48,7 @@ When you combine the output of multiple types objects, they should generally be

### Two important exceptions to the single-type rule

**For internal functions** it's ok to return multiple different types because they won't be "output" to the user/host, and can offer significant savings (e.g. one database call with three table joins, instead of three database calls with two or three joins each). You can then call these functions and assign the output to multiple variables, like so:
**For internal functions** it's ok to return multiple different types because they won't be "output" to the user/host, and can offer significant savings (e.g., one database call with three table joins, instead of three database calls with two or three joins each). You can then call these functions and assign the output to multiple variables, like so:

```PowerShell
$user, $group, $org = Get-UserGroupOrg
Expand Down
9 changes: 4 additions & 5 deletions Best-Practices/Performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ If you're aware of multiple techniques to accomplish something, and you're writi
For example:

```PowerShell
foreach($result in Do-Something) { $result.PropertyOne + $result.PropertyTwo }
foreach ($result in Do-Something) { $result.PropertyOne + $result.PropertyTwo }
Do-Something | ForEach-Object { $_.PropertyOne + $_.PropertyTwo }
```

Expand Down Expand Up @@ -40,7 +40,7 @@ ForEach-Object -Process {
}
```

As described elsewhere in this guide, many folks in the community would dislike this approach for aesthetic reasons. However, this approach has the advantage of utilizing PowerShell's pipeline to "stream" the content in file.txt. Provided that the fictional "Do-Something" command isn't blocking the pipeline (a la Sort-Object), the shell can send lines of content (String objects, technically) through the pipeline in a continuous stream, rather than having to buffer them all into memory.
As described elsewhere in this guide, many folks in the community would dislike this approach for aesthetic reasons. However, this approach has the advantage of utilizing PowerShell's pipeline to "stream" the content in file.txt. Provided that the fictional "Do-Something" command isn't blocking the pipeline (à la Sort-Object), the shell can send lines of content (String objects, technically) through the pipeline in a continuous stream, rather than having to buffer them all into memory.

Some would argue that this second approach is always a poor one, and that if performance is an issue then you should devolve from a PowerShell-native approach into a lower-level .NET Framework approach:

Expand Down Expand Up @@ -73,9 +73,8 @@ The moral here is that both aesthetic and performance are important consideratio

This is just a rough guideline, but as a general rule:

1. Language features are faster than features of the .net framework
2. Compiled methods on objects and .net classes are still faster than script
1. Language features are faster than features of the .NET Framework
2. Compiled methods on objects and .NET classes are still faster than script
3. Simple PowerShell script is still faster than calling functions or cmdlets

It's counter-intuitive that script is faster than calling cmdlets that are compiled, but it's frequently true, unless there is a lot of work being done by each cmdlet. The overhead of calling cmdlets and passing data around is significant. Of course, this is just a guideline, and you should always **measure**.

13 changes: 6 additions & 7 deletions Best-Practices/Security.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ param (
)
```

If you absolutely must pass a password in a plain string to a .Net API call or a third party library it is better to decrypt the credential as it is being passed instead of saving it in a variable.
If you absolutely must pass a password in a plain string to a .NET API call or a third party library, it is better to decrypt the credential as it is being passed instead of saving it in a variable.

```PowerShell
# Get the cleartext password for a method call:
$Insecure.SetPassword( $Credentials.GetNetworkCredential().Password )
$Insecure.SetPassword($Credentials.GetNetworkCredential().Password)
```

#### Other Secure Strings
Expand All @@ -32,10 +32,10 @@ Note, if you ever need to turn a SecureString into a string, you can use this me

```PowerShell
# Decrypt a secure string.
$BSTR = [System.Runtime.InteropServices.marshal]::SecureStringToBSTR($this);
$plaintext = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($BSTR);
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR);
return $plaintext
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($this)
$plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
$plaintext
```

* For credentials that need to be saved to disk, serialize the credential object using
Expand Down Expand Up @@ -63,4 +63,3 @@ computer where it was generated.
# Read the Standard String from disk and convert to a SecureString
$Secure = Get-Content -Path "${Env:AppData}\Sec.bin" | ConvertTo-SecureString
```

Loading