PowerShellTips.json

[
  {
    "CreatedDate": "2023-07-16T00:00:00",
    "Title": "PowerShell is open source",
    "TipText": "Did you know that PowerShell is open source? You can contribute to the project on GitHub.",
    "Example": "",
    "Urls": [
      "https://github.com/PowerShell/PowerShell"
    ],
    "Category": 0,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-07-17T00:00:00",
    "Title": "Set Strict Mode on your scripts",
    "TipText": "Enforce coding rules and raise errors for common coding mistakes by declaring strict mode at the top of your scripts.",
    "Example": "Set-StrictMode -Version Latest",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-08-28T00:00:00",
    "Title": "View your command line history",
    "TipText": "PowerShell lets you view your session history with `Get-History` and it's alias `h`.\r\n`Get-PSReadLineOption` used with `Get-Content` takes history reading further by allowing you to read your current users lifetime history.",
    "Example": "Get-Content (Get-PSReadLineOption).HistorySavePath",
    "Urls": [
      "https://learn.microsoft.com/powershell/module/psreadline/about/about_psreadline",
      "https://learn.microsoft.com/powershell/module/microsoft.powershell.core/get-history"
    ],
    "Category": 7,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Emil Larsson (ehmiiz)"
  },
  {
    "CreatedDate": "2023-09-05T00:00:00",
    "Title": "When checking for $null, put $null on the left",
    "TipText": "When checking if a variable or expression is null, put the $null on the left side of the comparison.\r\n\r\nIf the variable you are checking is an array that contains a null value, the comparison may not return the expected result if you put the $null on the right side of the comparison.\r\n\r\nDo this: if ($null -eq $variable)\r\nNot this: if ($variable -eq $null)",
    "Example": "if ($null -eq $variable) { \"The variable really is null.\" }",
    "Urls": [
      "https://powershellexplained.com/2018-12-23-Powershell-null-everything-you-wanted-to-know/",
      "https://stackoverflow.com/a/60996703/602585"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-06T00:00:00",
    "Title": "Use Ctrl+R to search your terminal history",
    "TipText": "When in your command prompt, press Ctrl+R to reverse search your terminal history. As you type, it will show the most recent matching command. This is a great way to find a command you ran previously, but can't remember the exact command or parameters.\r\n\r\nIf you press Ctrl+R again, it will show the next most recent matching command. You can keep pressing Ctrl+R to cycle through all matching commands. If you go past the command you wanted, press Ctrl+S and it will cycle through the matching commands in the opposite direction.\r\n\r\nNote: Requires the `PSReadLine` module, which is included in PowerShell 5.1 and newer.",
    "Example": "",
    "Urls": [
      "https://woshub.com/powershell-commands-history/"
    ],
    "Category": 7,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-06T00:00:00",
    "Title": "Use Ctrl+Space to list all parameters, properties, and possibilities",
    "TipText": "In the terminal, when you are typing a command, parameter, or property, press Ctrl+Space to see a list of all the possible options. If there is only a single option, it will auto-complete the field for you. If there are many options, it will list them all for you to choose from and you can use the arrow keys to navigate the list and select one.\r\n\r\nCtrl+Space is similar to tab completion, except it will show you all of the options instead of just one option at a time and having to tab cycle through them.\r\n\r\nNote: Requires the `PSReadLine` module, which is included in PowerShell 5.1 and newer.",
    "Example": "- Type `Get-Process -` and press Ctrl+Space to list all the possible parameters you can use with the `Get-Process` command.\r\n- Type `$variableName.` and press Ctrl+Space to list all the properties and methods for the variable.\r\n- Type `Get-Pro` and press Ctrl+Space to list all the commands that start with `Get-Pro`, including their parameter sets.\r\n- Type part of a variable name (e.g. $var) and press Ctrl+Space to list all the variables that start with `$var`, including their type if available (e.g. [string], [int]).",
    "Urls": [
      "https://blog.danskingdom.com/PowerShell-intellisense-on-the-command-line/"
    ],
    "Category": 7,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-11T00:00:00",
    "Title": "Ensure prerequisites are met by using #Requires",
    "TipText": "Use the #Requires statement to ensure that the environment meets the prerequisites for your script to run. You can use #Requires to check that:\r\n- A minimum PowerShell version is being used\r\n- PowerShell Desktop or PowerShell Core is being used\r\n- Specific modules or snap-ins are installed\r\n- The script is running as Administrator\r\n\r\nIf one of the required prerequisites are not met, PowerShell will throw an error and the script will not run.",
    "Example": "#Requires -Version 7.2\r\n#Requires -PSEdition Core\r\n#Requires -Modules @{ ModuleName=\"Az.KeyVault\"; ModuleVersion=\"4.0.0\" }\r\n#Requires -RunAsAdministrator",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-11T00:00:00",
    "Title": "Join the monthly PowerShell community call!",
    "TipText": "The PowerShell Community Call is held on the 3rd Thursday of every month at 9:30 AM US Pacific Time. Topics include PowerShell 7, Windows OpenSSH, PSEditorServices/VSCode-PowerShell, PSScriptAnalyzer, PowerShell Gallery, and any other projects owned by the PowerShell Team.\r\n\r\nYou can join the Teams live event at https://aka.ms/JoinPSCall. The call is recorded and posted on YouTube.",
    "Example": "",
    "Urls": [
      "https://github.com/PowerShell/PowerShell-RFC/blob/master/CommunityCall/README.md",
      "https://powershell.org/series/powershell-community-call/",
      "https://www.youtube.com/@powershellanddscteamchanne5739"
    ],
    "Category": 0,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-11T00:00:00",
    "Title": "Read and write Excel spreadsheets with ImportExcel",
    "TipText": "Need to share large data sets with colleagues or your boss? Excel is still a popular way to do that. ImportExcel is a PowerShell module that makes it easy to read and write Excel spreadsheets. Use it to export your data to Excel, and even add charts and pivot tables. It's fast and doesn't require Excel to be installed on your computer. It's also cross-platform, so you can use it on Windows, macOS, and Linux.",
    "Example": "[PSCustomObject[]] $data = @(\r\n\t[PSCustomObject] @{\r\n\t\tFirstName = 'John'\r\n\t\tLastName = 'Doe'\r\n\t\tAge = 42\r\n\t}\r\n\t[PSCustomObject] @{\r\n\t\tFirstName = 'Jane'\r\n\t\tLastName = 'Doe'\r\n\t\tAge = 39\r\n\t}\r\n)\r\n$data | Export-Excel -Path 'C:\\Temp\\Results.xlsx' -WorksheetName 'People' -FreezeTopRow -AutoFilter -AutoSize",
    "Urls": [
      "https://www.powershellgallery.com/packages/ImportExcel",
      "https://github.com/dfinke/ImportExcel"
    ],
    "Category": 2,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-12T00:00:00",
    "Title": "Get text file updates in realtime with Get-Content",
    "TipText": "Get-Content provides a `-Wait` switch that can be used to get updates to a text file in realtime. This is useful for monitoring log files from the terminal, or any other text file that is updated over time.\r\n\r\nIn addition, if you want to get the last N lines of a file, you can use the `-Tail` parameter. This is useful for getting only the last few lines of a very large text file, especially if it's too large for a text editor to load.\r\n\r\nOnce you are done monitoring the file, you can press `Ctrl+C` to stop the command.",
    "Example": "# Show the last 10 lines of the file and display new content as it is written to the file.\r\nGet-Content -Path $filePath -Wait -Tail 10\r\n\r\n# Show the last 1000 lines of a very large file that is too big to open in a text editor.\r\nGet-Content -Path $veryLargeLogFile -Tail 1000",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-content",
      "https://4sysops.com/archives/parse-log-files-with-powershell/"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-16T00:00:00",
    "Title": "Use Out-GridView to view and select tabular data",
    "TipText": "Instead of piping data to Out-Table, use Out-GridView. This opens a window with the data in a grid view, allowing you to interactively sort and filter the data. If you provide the -PassThru parameter, you can select one or more rows and the selected data will be returned to the pipeline, allowing you to save it to a variable or pipe it to other commands.\r\n\r\nNOTE: Out-GridView is only available on Windows.",
    "Example": "Get-Process | Out-GridView -PassThru -Title \"Select processes to return\" | Select-Object -Property ProcessName,Id",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/out-gridview",
      "https://woshub.com/using-out-gridview-table-powershell/"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-17T00:00:00",
    "Title": "Use F2 to toggle PSReadLine predictions to list view",
    "TipText": "PSReadLine v2.1.0 introduced history-based predictions. As you type, PSReadLine will show you a command that you previously typed, thinking that you may want to run the same command again. You can use the right-arrow to accept the suggestion.\r\n\r\nIf you want to see more predictions, you can press the F2 key to swap from Inline View (which only shows a single suggestion), to List View, which shows many. You can then use the up and down arrow keys to select a command from the list.\r\n\r\nUse 'Update-Module -Name PSReadLine' to update to the latest version of PSReadLine and use these features.",
    "Example": "Type \"Get-\" and then press F2 to see a list of commands that you have previously typed that start with \"Get-\"",
    "Urls": [
      "https://devblogs.microsoft.com/powershell/announcing-psreadline-2-1-with-predictive-intellisense/",
      "https://learn.microsoft.com/en-us/powershell/module/psreadline/"
    ],
    "Category": 7,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-18T00:00:00",
    "Title": "Split long lines of code into multiple lines",
    "TipText": "Long lines of code can be difficult to read, especially when doing code reviews in a web browser and for users not using word-wrap in their editors. It is good practice to try and avoid long lines of code.\r\n\r\nPowerShell lines can naturally be split into multiple lines at many operators, such as =, +, -, |, {, -eq, etc.\r\n\r\nIf you need to split a line at an unnatural location, you can use the backtick character (`) at the end of each line. This is generally discouraged though as the backtick can be hard to see, easy to forget, and a space after the backtick breaks the line continuation. However, there are times when it may still be useful.",
    "Example": "# Split into multiple lines using natural operators.\r\n[bool] $arrayIsNullOrEmpty =\r\n $null -eq $myIntegerArray -or\r\n $myIntegerArray.Length -eq 0\r\n\r\n[int[]] $valuesGreaterThan10AndSorted =\r\n $myIntegerArray |\r\n Where-Object {\r\n $_ -gt 10\r\n } |\r\n Sort-Object\r\n\r\n[string] $myString =\r\n 'This is a very long string that is split into multiple lines ' +\r\n 'using the + operator.'\r\n\r\n# Split into multiple lines at an unnatural locations using the backtick.\r\nGet-ChildItem `\r\n -Path 'C:\\' `\r\n -Recurse `\r\n -File `\r\n -Filter '*.txt'",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing?view=powershell-7.3#line-continuation"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-19T00:00:00",
    "Title": "Use Splatting for nicer code and dynamic parameters",
    "TipText": "Splatting allows you to pass parameters to a function as a hashtable. This can make your code more readable when you have a lot of parameters to pass to a function. It also allows you to build the parameters dynamically.\r\n\r\nTo use splatting, create a hashtable with the parameter names as the keys and the parameter values as the values. Then, pass the hashtable to the function using the `@` operator.\r\n\r\nYou can also use splatting with arrays, where the array values are passed to the function as positional parameters.\r\n\r\nIn a function, you can use the $PSBoundParameters automatic variable to access the parameters that were passed to the function. This allows you to modify and extend the given parameters before passing them to another function.",
    "Example": "$hashtableParameters = @{\r\n Path = \"test.txt\"\r\n Destination = \"test2.txt\"\r\n WhatIf = $true\r\n}\r\nCopy-Item @hashtableParameters\r\n\r\n$arrayParameters = \"test.txt\", \"test2.txt\"\r\nCopy-Item @arrayParameters -WhatIf",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting",
      "https://adamtheautomator.com/powershell-splatting/",
      "https://4sysops.com/archives/use-splatting-and-psboundparameters-to-pass-parameters-in-powershell/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-21T00:00:00",
    "Title": "Use Here-strings for hardcoded multiline strings",
    "TipText": "When you are defining multiline strings in code, you could create a string and use `r`n or [Environment]::NewLine to create new lines in it. If it is a long string, you may even choose to break it into multiple lines in code and concatenate them using the + operator. However, this can be cumbersome to both write and read. Instead, you can use a here-string to define a multiline string.\r\n\r\nA here-string is a string that is defined between two `@ symbols. The here-string can be defined with single or double quotes. If you use single quotes, the string will be a literal string and no variable expansion will occur. If you use double quotes, variable expansion will occur.\r\n\r\nHere-strings allow you to define and see a multiline string in your code exactly as it will appear when it is output.",
    "Example": "[string] $multilineString = @'\r\nThis is the first line of the string.\r\nThis is the second line of the string.\r\n\r\n There is a blank line above this line, and 2 spaces at the start of this line.\r\nThis $variable will not be expanded because we used single quotes.\r\n`'@\r\n\r\n[int] $johnDoeAge = 42\r\n[string] $jsonString = @\"\r\n{\r\n \"Name\": \"John Doe\",\r\n \"Age\": $johnDoeAge\r\n}",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules#here-strings",
      "https://devblogs.microsoft.com/scripting/maximizing-the-power-of-here-string-in-powershell-for-configuration-data"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-21T00:00:00",
    "Title": "Use PSBoundParameters to check if a parameter was provided",
    "TipText": "When writing a function you may want to check if a parameter was provided by the caller. With reference types (e.g. [string]) you can simply check if the parameter is null, but what is $null is a valid value for the parameter and you need to take different action if the parameter was not provided? Similarly, what if the parameter is a value type (e.g. [int]) and you need to take action if the caller provided the default value (e.g. zero), but not if they didn't provide the parameter?\r\n\r\nYou can check if a parameter was passed to a script/function by using the $PSBoundParameters automatic variable. This variable contains a hashtable of all parameters that were passed to the function, and you can use the ContainsKey function to check if a specific parameter was passed.",
    "Example": "function Test-ParameterWasPassed([string] $OptionalParameter = $null)\r\n{\r\n\tif ($PSBoundParameters.ContainsKey('OptionalParameter'))\r\n\t{\r\n\t\tWrite-Output 'OptionalParameter was passed.'\r\n\t}\r\n\telse\r\n\t{\r\n\t\tWrite-Output 'OptionalParameter was not passed.'\r\n\t}\r\n}",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables#psboundparameters",
      "https://devblogs.microsoft.com/powershell/checking-for-bound-parameters/",
      "https://www.reza-aghaei.com/how-to-determine-if-a-parameter-is-passed-to-a-powershell-cmdlet/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-22T00:00:00",
    "Title": "Avoid Array addition",
    "TipText": "Array addition is an expensive and inefficient operation and can usually be replaced by PowerShell explicit loop assignment.\r\n\r\nUse a `List<T>` instead in those cases when adding to a collection while looping is required.",
    "Example": "# Array addition:\r\n$items = @()\r\nforeach ($i in 0..10) {\r\n $items += $i\r\n}\r\n\r\n# Can be easily replaced with explicit assignment:\r\n$items = foreach ($i in 0..10) {\r\n $i\r\n}\r\n\r\n# And, when not possible, a List<T> is recommended:\r\n$items = [System.Collections.Generic.List[int]]::new()\r\nforeach ($i in 0..10) {\r\n $items.Add($i)\r\n}",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations?view=powershell-7.3#array-addition"
    ],
    "Category": 4,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Santiago Squarzon (santisq)"
  },
  {
    "CreatedDate": "2023-09-23T00:00:00",
    "Title": "Define hardcoded array items on new lines without commas",
    "TipText": "When defining hardcoded arrays with many values in PowerShell, you can define each item on a new line without commas; the commas are optional. This makes it easier to read and maintain the array items, and you don't have to worry about commas when adding or rearranging items.\r\n\r\nIf you choose to define multiple array values on a single line, then you will need a comma between each item on the line.",
    "Example": "[string[]] $stringArray = @(\r\n 'Notice that each item is on a new line without commas.'\r\n 'This is much easier to read and maintain.'\r\n 'And you can add comments to explain each item if needed.' # Like this.\r\n)\r\n\r\n[int[]] $intArray = @(1, 2, 3)",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-arrays?view=powershell-7.3#create-an-array"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-23T00:00:00",
    "Title": "Use $PSVersionTable to get PowerShell session info",
    "TipText": "The $PSVersionTable automatic variable contains information about the current PowerShell session, including the PowerShell version, operating system, and more.",
    "Example": "Write-Output \"The PowerShell version being used is $($PSVersionTable.PSVersion)\"\r\n\r\nWrite-Output \"Below is the full contents of the `$PSVersionTable variable:\"\r\n$PSVersionTable",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables#psversiontable"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-23T00:00:00",
    "Title": "Use Flags to allow multiple enum values",
    "TipText": "You can use the [Flags()] attribute to allow multiple enum values to be used at once. In addition to the attribute, you need to set the enum values to powers of 2 (1, 2, 4, 8, 16, etc.). This allows you to use the bitwise operators to combine enum values.\r\n\r\nWhen using a flags enum you must be careful to not use the -eq operator to check if a variable contains a specific enum value. This will only return true if the variable contains only that enum value. Instead, use the -band operator or the .HasFlag() method to check if the variable contains a specific enum value.",
    "Example": "[Flags()]\r\nenum FileAttributes {\r\n Archive = 1\r\n Compressed = 2\r\n Device = 4\r\n Directory = 8\r\n Encrypted = 16\r\n Hidden = 32\r\n}\r\n\r\n# Set a variable to multiple enum values.\r\n$attributes = [FileAttributes]::Archive + [FileAttributes]::Hidden\r\n$attributes += [FileAttributes]::Compressed\r\n\r\n# Check if the variable contains a specific enum value.\r\n$attributes -eq [FileAttributes]::Archive # False\r\n$attributes -band [FileAttributes]::Archive # True\r\n$attributes.HasFlag([FileAttributes]::Archive) # True",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_enum#enumerations-as-flags",
      "https://arcanecode.com/2021/12/06/fun-with-powershell-enum-flags/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-09-29T00:00:00",
    "Title": "Use dynamic values in ValidateSet while maintaining tab completion",
    "TipText": "The ValidateSet attribute is a great way to limit the values that can be passed to a parameter, and it provides tab autocompletion for the parameter values. Typically if you wanted to use dynamic values you would use the ValidateScript attribute instead, but then you lose the tab autocompletion.\r\n\r\nA way to get the best of both worlds is to create a class that implements the System.Management.Automation.IValidateSetValuesGenerator interface. This interface has a single method, GetValidValues(), that returns an array of strings. You can put the logic to dynamically retrieve your allowed parameter values in the GetValidValues() method. You can then use the ValidateSet attribute with the type of your class, and the tab autocompletion will work as expected.",
    "Example": "class AllowedParameterValues : System.Management.Automation.IValidateSetValuesGenerator {\r\n [string[]] GetValidValues() {\r\n # Populate these values dynamically by querying a database, reading a file, calling an API, etc.\r\n $values = @('Value1', 'Value2', 'Value3')\r\n return $values\r\n }\r\n}\r\n\r\nfunction Test-ValidateSet {\r\n param (\r\n [ValidateSet([AllowedParameterValues])]\r\n [string] $Value\r\n )\r\n return $Value\r\n}\r\n\r\nTest-ValidateSet -Value # Tab complete here to see the dynamic values.",
    "Urls": [
      "https://www.linkedin.com/feed/update/urn:li:activity:7113300637735407618/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-02T00:00:00",
    "Title": "Quickly create temporary file",
    "TipText": "Quickly create a temporary file by using the New-TemporaryFile command, or the GetTempFileName() .NET function.\r\n\r\nThis is handy when you want save data to a temporary log file and not worry about path/permissions/filename. The temporary file will be created in \"C:\\Users\\USER\\AppData\\Local\\Temp\\1\\some-tmp.tmp\".",
    "Example": "# Create temp file and get the path to it using the .NET class.\r\n[string] $tmpFile = [System.IO.Path]::GetTempFileName()\r\n\r\n# Or use the native PowerShell cmdlet instead, which returns a full FileInfo object.\r\n[System.IO.FileInfo] $tmpFile = New-TemporaryFile\r\n\r\n# Easily write/read/remove the temp file.\r\n\"Some data to be saved\" | Out-File $tmpFile\r\n\r\n# Cleanup when you no longer need it.\r\nRemove-Item $tmpFile",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/new-temporaryfile",
      "https://learn.microsoft.com/en-us/dotnet/api/system.io.path.gettempfilename#system-io-path-gettempfilename"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Manjunath Beli (belibug)"
  },
  {
    "CreatedDate": "2023-10-02T00:00:00",
    "Title": "Use PowerShell classes for strongly-typed objects",
    "TipText": "PowerShell 5.0 introduced classes. Classes allow you to create strongly-typed objects, encapsulating properties and methods. This allows you to keep related data together and to define functions for manipulating the data, allowing for validation if necessary. Classes also allow for inheritance.\r\n\r\nThere are some caveats to working with classes in PowerShell though:\r\n- If you make changes to the class definition you must reload the PowerShell session for the changes to take effect.\r\n- PowerShell classes defined in modules are not exported when 'Import-Module' is used; Instead the 'using module' syntax must be used.",
    "Example": "class Employee\r\n{\r\n\t[string] $FirstName\r\n\t[string] $LastName\r\n\t[DateTime] $DateOfBirth\r\n\t[int] $Salary\r\n\r\n\t[string] GetFullName()\r\n\t{\r\n\t\treturn \"$this.FirstName $this.LastName\"\r\n\t}\r\n\r\n\t[void] IncreaseSalary([int] $amount)\r\n\t{\r\n\t\t$this.Salary += $amount\r\n\t}\r\n}\r\n\r\n$employee = [Employee]::new()\r\n$employee.FirstName = 'John'\r\n$employee.LastName = 'Doe'\r\n$employee.DateOfBirth = [DateTime]::Parse('1983-01-15')\r\n$employee.Salary = 50000\r\n$employee.GetFullName()\r\n$employee.IncreaseSalary(10000)",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes",
      "https://xainey.github.io/2016/powershell-classes-and-concepts/",
      "https://blog.danskingdom.com/PowerShell-class-definition-pros-cons-and-performance-comparison/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-03T00:00:00",
    "Title": "Process file lines with the switch statement",
    "TipText": "While the switch statement is typically used to evaluate a single value, it can also be used to evaluate arrays. By using the -File parameter with the switch statement, it will treat each line of the file as an array item, allowing you to process a text file line-by-line and take actions when a specific value is found. You can use the typical switch parameters to match on exact text, wildcards, or regular expressions.",
    "Example": "# Output the file contents to the console, converting error lines to errors, and warning lines to warnings.\r\nswitch -Wildcard -File $path\r\n{\r\n 'Error*'\r\n {\r\n Write-Error -Message $PSItem\r\n }\r\n 'Warning*'\r\n {\r\n Write-Warning -Message $PSItem\r\n }\r\n default\r\n {\r\n Write-Output $PSItem\r\n }\r\n}",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-switch#-file",
      "https://twitter.com/dfinke/status/1698733677285388581"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-07T00:00:00",
    "Title": "Store and retrieve secrets securely with SecretManagement",
    "TipText": "The Microsoft.PowerShell.SecretManagement module allows you to store secrets securely in a vault and retrieve them interactively, or in automated processes. This provides a secure alternative to storing secrets in plain text files, or continually being prompted for them.\r\n\r\nThe Microsoft.PowerShell.SecretStore module is a vault for storing secrets locally on your machine. There are other modules that provide integration with other vaults, such as Azure Key Vault, HashiCorp Vault, and LastPass. You can view them at https://aka.ms/SecretManagementVaults.",
    "Example": "# Install the SecretManagement and SecretStore modules.\r\nInstall-Module Microsoft.PowerShell.SecretManagement -Repository PSGallery\r\nInstall-Module Microsoft.PowerShell.SecretStore -Repository PSGallery\r\n\r\n# Register the SecretStore vault with the SecretManagement module.\r\nRegister-SecretVault -Name LocalFileSecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault\r\n\r\n# Store a secret in the vault (will be prompted for a vault password the 1st time).\r\nSet-Secret -Name MySecret -Secret 'MySecretValue'\r\n\r\n# Retrieve the secret from the vault.\r\nGet-Secret -Name MySecret",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.secretmanagement/?view=ps-modules",
      "https://devblogs.microsoft.com/powershell/secretmanagement-and-secretstore-are-generally-available/",
      "https://www.pdq.com/blog/how-to-manage-powershell-secrets-with-secretsmanagement/"
    ],
    "Category": 5,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-08T00:00:00",
    "Title": "Prompt users for credentials and secrets securely",
    "TipText": "Sometimes we write scripts that prompt the user for their credentials, or for a secret like a password. We should avoid retrieving these values in plain text.\r\n\r\nTo securely prompt a user for their credentials use the Get-Credential cmdlet. This will prompt the user for their username and password, and return a PSCredential object with the password stored as a SecureString.\r\n\r\nThe securely prompt a user for a secret, use the Read-Host cmdlet with the -AsSecureString parameter. This will prompt the user for a secret and return a SecureString.\r\n\r\nMost cmdlets that require sensitive info will accept PSCredential or SecureString objects. In the rare case that you need the secret as plain text, you can convert the SecureString to plain text. This should be avoided if possible, as it exposes the secret in memory.",
    "Example": "# Prompt user for their username and password. The password will be stored as a SecureString.\r\n[PSCredential] $userCredentials = Get-Credential -Message 'Enter your credentials'\r\n\r\n# Some webhooks contain secrets, so treat them as sensitive.\r\n[SecureString] $secureWebhook = Read-Host -Prompt 'Enter the webhook URL' -AsSecureString\r\n\r\n# Convert a SecureString to plain text. AVOID THIS WHEN POSSIBLE.\r\n[string] $plainTextWebhook = [System.Net.NetworkCredential]::new('', $secureWebhook).Password",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-credential",
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/read-host",
      "https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-1/"
    ],
    "Category": 5,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-09T00:00:00",
    "Title": "PowerShell Conference Europe 2024",
    "TipText": "PSConfEU 2024 will be held June 24 - 27, 2024. Save the date!\r\n\r\nMore details will be released at PSConfEU MiniCon on October 24, 2023.",
    "Example": "",
    "Urls": [
      "https://psconf.eu/"
    ],
    "Category": 0,
    "ExpiryDate": "2024-06-27T00:00:00",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-11T00:00:00",
    "Title": "Create test files of arbitrary size",
    "TipText": "When doing performance testing it can be useful to have a file of a specific size to test with. Creating files of a specific size is fairly straightforward with the System.IO.FileStream class.",
    "Example": "[string] $fileName = [System.IO.Path]::GetRandomFileName()\r\n[string] $filePath = Join-Path -Path (Get-Location) -ChildPath $fileName\r\n[int64] $fileSize = 10MB\r\n\r\ntry {\r\n $tempFile = [System.IO.FileStream]::new($filePath, Create, ReadWrite)\r\n $tempFile.SetLength($fileSize)\r\n $tempFile.Close()\r\n\r\n Write-Output 'Created the following temp file:'\r\n Get-ChildItem $filePath | Select-Object FullName,DirectoryName,Name,Length,CreationTime\r\n} catch {\r\n Write-Error $_\r\n}",
    "Urls": [
      "https://learn.microsoft.com/en-us/dotnet/api/system.io.filestream",
      "https://claytonerrington.com/blog/creating-a-test-file"
    ],
    "Category": 8,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-11T00:00:00",
    "Title": "Get file name from URL",
    "TipText": "If you have a URL that contains a path to a file, you can use the [URI] class to extract the file path and name.",
    "Example": "PS> [URI] $uri = 'https://some-server.com/Path/To/File.txt?someQueryParameter=123#Heading1'\r\n\r\nPS> $uri\r\nAbsolutePath : /Path/To/File.txt\r\nAbsoluteUri : https://some-server.com/Path/To/File.txt?someQueryParameter=123#Heading1\r\nPathAndQuery : /Path/To/File.txt?someQueryParameter=123\r\nSegments : {/, Path/, To/, File.txt}\r\nHost : some-server.com\r\nPort : 443\r\nQuery : ?someQueryParameter=123\r\nFragment : #Heading1\r\nScheme : https\r\n\r\nPS> Split-Path -Path $uri.AbsolutePath -Leaf\r\nFile.txt\r\n\r\nPS> Split-Path -Path $uri.AbsolutePath -LeafBase\r\nFile",
    "Urls": [
      "https://learn.microsoft.com/en-us/dotnet/api/system.uri",
      "https://twitter.com/devopsjeremy/status/1709773064563744888?s=46&t=-Ox1iPV67-4Vqb8wIZ275A"
    ],
    "Category": 8,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-14T00:00:00",
    "Title": "Create new objects with dynamic properties using calculated properties",
    "TipText": "PowerShell is a dynamic language, which means that you can add properties to objects on the fly. By using calculated properties, you can add properties to output objects that are calculated from other properties on the input object, or data from external sources.\r\n\r\nThe easiest way to add calculated properties is by using the `Select-Object` cmdlet to return a new object back with the additional properties. You provide a hashtable to the `-Property` parameter, with a `Name` key for the name of the new property and an `Expression` key for the value. You can add as many calculated properties as you want.\r\n\r\nThe main difference between a calculated property and adding a property to an object with Add-Member is that calculated properties are added to a new output object, while Add-Member modifies the original object.",
    "Example": "# Get the first process and add a single calculated property to show how long it has been running.\r\n> Get-Process | Select-Object -First 1 -Property ProcessName,StartTime,@{ Name = \"RunningTime\"; Expression = { (Get-Date) - $_.StartTime } }\r\n\r\nProcessName StartTime RunningTime\r\n----------- --------- -----------\r\n1Password 10/14/2023 12:59:24 PM 00:32:59.1893342\r\n\r\n\r\n# Add multiple calculated properties (split into multiple lines for readability).\r\n> Get-Process | Select-Object -First 1 -Property ProcessName,`\r\n StartTime,@{ Name = \"RunningTime\"; Expression = { (Get-Date) - $_.StartTime } },`\r\n WorkingSet,@{ Name = \"WorkingSetMb\"; Expression = { $_.WorkingSet / 1MB }}\r\n\r\nProcessName : 1Password\r\nStartTime : 10/14/2023 12:59:24 PM\r\nRunningTime : 00:33:03.0717040\r\nWorkingSet : 73527296\r\nWorkingSetMb : 70.12109375\r\n\r\n# Get all processes with all original properties, plus the new calculated property.\r\n> Get-Process | Select-Object -Property *,@{ Name = \"RunningTime\"; Expression = { (Get-Date) - $_.StartTime } }",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_calculated_properties",
      "https://4sysops.com/archives/add-a-calculated-property-with-select-object-in-powershell/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-20T00:00:00",
    "Title": "Use Out-ConsoleGridView for a cross-platform grid view",
    "TipText": "The Out-GridView cmdlet is very useful, but it only works on Windows. The Microsoft.PowerShell.ConsoleGuiTools module provides a cross-platform alternative called Out-ConsoleGridView. Rather than opening a new window, it displays the grid view in the current terminal window.\r\n\r\nYou can use Out-ConsoleGridView to display a table of data that the user is able to interactively navigate. The user can select rows from the list that can be saved to a variable or piped to another command, allowing you to perform actions on the selected rows.",
    "Example": "Install-Module Microsoft.PowerShell.ConsoleGuiTools\r\n\r\nGet-Process | Out-ConsoleGridView -Title \"Select processes to return\" | Select-Object -Property ProcessName,Id",
    "Urls": [
      "https://github.com/PowerShell/GraphicalTools/"
    ],
    "Category": 2,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-21T00:00:00",
    "Title": "Get the .NET version your code is running on",
    "TipText": ".NET has a FrameworkDescription property that returns the version of .NET your code is running on. This is useful if you want to write code that behaves differently depending on the version of .NET it's running on, or when troubleshooting issues that users may be experiencing.",
    "Example": "# Running the command on PowerShell 7.\r\n> [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription\r\n.NET 7.0.11\r\n\r\n# Running the command on PowerShell 5.1.\r\n> [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription\r\n.NET Framework 4.8.4644.0",
    "Urls": [
      "https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.runtimeinformation.frameworkdescription",
      "https://twitter.com/JeffHicks/status/1705007619499237525"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-21T00:00:00",
    "Title": "Learn about a random PowerShell cmdlet or topic",
    "TipText": "The tiPS module (which is bringing you these tips) is a great way to learn PowerShell every day. There are other ways to learn PowerShell every day too. Use the example code to learn about a random PowerShell cmdlet or topic. Try adding it to your daily routine, or even to your PowerShell profile.",
    "Example": "# Show the Help documentation for a random native cmdlet.\r\nGet-Command -Module Microsoft*,Cim*,PS* | Get-Random | Get-Help -ShowWindow\r\n\r\n# Show the Help documentation for a random PowerShell About topic.\r\nGet-Random -input (Get-Help about*) | Get-Help -ShowWindow\r\n\r\n# Open your PowerShell profile in VS Code so you can add the below code to it\r\n# to automatically see Help documentation each time you start a new PowerShell\r\n# session on Mondays before 10am.\r\ncode $Profile\r\n\r\n[DateTime] $now = Get-Date\r\nif ($now.DayOfWeek -eq 'Monday' -and $now.Hour -lt 10)\r\n{\r\n Get-Command -Module Microsoft*,Cim*,PS* | Get-Random | Get-Help -ShowWindow\r\n Get-Random -input (Get-Help about*) | Get-Help -ShowWindow\r\n}",
    "Urls": [
      "https://jdhitsolutions.com/blog/essential-powershell-resources/"
    ],
    "Category": 8,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-24T00:00:00",
    "Title": "Use trap to catch unhandled exceptions",
    "TipText": "Similarly to how you can catch terminating errors (i.e. exceptions) with a try-catch block, you can use the `trap` keyword to catch unhandled terminating errors in a script or function. The trap keyword is a global exception handler, allowing you to catch and handle terminating errors in the current scope and all child scopes.\r\n\r\nWhen an exception is caught by a trap statement, you can use the `break` statement to abort execution of the script or function, or use the `continue` statement to continue execution where the script left off. If you do not use either of these statements, execution will continue after the trap statement.\r\n\r\nYou can defined multiple traps at different scopes. Also, like catch blocks, you can restrict trap blocks to only catch exceptions of a certain type.",
    "Example": "trap {\r\n [bool] $imFeelingLucky = (Get-Random -Maximum 10) -eq 5\r\n if ($imFeelingLucky)\r\n {\r\n Write-Warning \"I'm feeling lucky, so I'll continue execution.\"\r\n continue\r\n }\r\n Write-Error \"The following unhandled error occurred, so exiting script: $_\"\r\n break\r\n}\r\n\r\nInvokingAFunctionThatDoesNotExist # Will throw an exception, which will be caught by the trap.\r\n\r\nWrite-Output \"I see you're feeling lucky today.\"",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_trap",
      "https://www.sconstantinou.com/powershell-trap/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-28T00:00:00",
    "Title": "Write events to the Windows Event Viewer with Write-EventLog",
    "TipText": "When logging from scripts or modules, a common practice is to log to the console or a log file. Another option is to log to the Windows Event Viewer with the `Write-EventLog` cmdlet. This allows you to log to the Application, System, or Security logs, or create a new log specifically for your script or module.\r\n\r\nThis can be especially useful when using a centralized monitoring solution like System Center Orchestrator or Azure Monitor, as they can monitor the Windows Event Viewer from multiple servers for specific events. You can also use the `Get-EventLog` cmdlet to manually retrieve events from the Windows Event Viewer on the local or remote computers.\r\n\r\nThe `Write-EventLog` cmdlet is available on Windows PowerShell and PowerShell, but can only be used on Windows.\r\nThe `New-EventLog` and `Write-EventLog` cmdlets may require elevated permissions (i.e. run as admin).",
    "Example": "# You must first create the \"Source\" before you can write to it.\r\nNew-EventLog -LogName Application -Source \"My Script\"\r\nWrite-EventLog -LogName Application -Source \"My Script\" -EntryType Information -EventID 1 -Message \"This is a test message.\"\r\n\r\n# You can also create a new log just for your script.\r\nNew-EventLog -LogName \"My Script Log\" -Source \"My Script\"\r\nWrite-EventLog -LogName \"My Script Log\" -Source \"My Script\" -EntryType Error -EventID 99 -Message \"This is a test error message.\"",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-eventlog",
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/write-eventlog",
      "https://devblogs.microsoft.com/scripting/how-to-use-powershell-to-write-to-event-logs/"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-10-31T00:00:00",
    "Title": "Use Select-Object * to see all object properties",
    "TipText": "When an object is written to the console, PowerShell by default hides many of its properties. This is to make the output easier to read, but it can hide many useful properties that you may not know exist. This can be detrimental when interactively exploring objects in the console.\r\n\r\nTo see all properties of an object, and their values, use the `Select-Object` cmdlet with the `-Property *` parameter.",
    "Example": "# See the properties of an object shown by default (for comparison).\r\nGet-Process | Select-Object -First 1\r\n\r\n# See all properties of an object.\r\nGet-Process | Select-Object -First 1 -Property *\r\n\r\n# Shorthand alternatives for Select-Object -Property *.\r\nGet-Process | Select-Object -First 1 *\r\nGet-Process | Select-Object *\r\nGet-Process | Select *",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/select-object"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-11-01T00:00:00",
    "Title": "Use Get-Member to see an object's type, methods, and properties",
    "TipText": "When interactively exploring on the command line, use the `Get-Member` cmdlet to see an object's type, methods, properties, and events. This is especially useful when you're not sure what type of object you're working with, such as those returned by other cmdlets.\r\n\r\nGet-Member will also show the type of each property and method, such as if they are a native property, an alias, or a property that was dynamically added using `Add-Member`. It also shows the parameters that each method accepts, and its return type.",
    "Example": "# See all of the properties and methods of the objects returned by Get-Process.\r\nGet-Process | Get-Member\r\n\r\n# Shorthand for Get-Member is gm.\r\nGet-Process | gm\r\n\r\nYou can also use the -InputObject property instead of piping to Get-Member.\r\n$process = Get-Process | Select-Object -First 1\r\nGet-Member -InputObject $process",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-member",
      "https://linuxhint.com/use-get-member-powershell/"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-11-05T00:00:00",
    "Title": "Get and Set the clipboard contents",
    "TipText": "You can capture the contents of the clipboard to a variable with the Get-Clipboard command. Similarly, you can set the contents of the clipboard with the Set-Clipboard command. This is useful for automating interactive tasks that involve the user copying and pasting text or files.",
    "Example": "# Capture the contents of the clipboard to a variable.\r\nRead-Host -Prompt \"Copy all of the files you want to rename to the clipboard, then press Enter.\"\r\n$filesToRename = Get-Clipboard\r\n\r\n$renamedFiles = SomeFunctionToRenameFiles $filesToRename\r\n\r\n# Set the contents of the clipboard to the renamed files.\r\n$renamedFiles | Set-Clipboard\r\nWrite-Host \"The renamed files are now on the clipboard, ready for you to paste somewhere else.\"",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-clipboard",
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-clipboard",
      "https://adamtheautomator.com/powershell-copy-to-clipboard/"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-11-10T00:00:00",
    "Title": "Use Add-Member to add custom properties and methods to an object",
    "TipText": "The Add-Member cmdlet can be used to add custom properties and methods to an object, whether it is a .NET object or PowerShell object. This can be useful when you want to add additional information to an object, or when you want to add a method to an object to perform some action.\r\n\r\nThe `Add-Member` cmdlet has a `-MemberType` parameter that can be used to specify the type of member to add. The most common types are `NoteProperty` and `ScriptMethod`. The `NoteProperty` type is used to add a new property to an object, while the `ScriptMethod` type is used to add a method to an object.\r\n\r\nThe main difference between a calculated property and adding a property to an object with Add-Member is that calculated properties are added to a new output object, while Add-Member modifies the original object.",
    "Example": "# Add a new 'Status' property, 'Size' alias, and 'SizeInMB' method to the FileInfo object.\r\n> $A = Get-ChildItem c:\\ps-test\\test.txt\r\n> $A | Add-Member -NotePropertyName Status -NotePropertyValue InProgress\r\n> $A.Status\r\nInProgress\r\n\r\n> $A | Add-Member -MemberType AliasProperty -Name Size -Value Length\r\n> $A.Size\r\n93681943\r\n\r\n> $method = {[math]::Round(($this.Length / 1MB), 2)}\r\n> $A | Add-Member -MemberType ScriptMethod -Name \"SizeInMB\" -Value $method\r\n> $A.SizeInMB()\r\n89.34\r\n\r\n> $A.Status = 'Complete'\r\n> $A.Status\r\nComplete",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-member",
      "https://ilovepowershell.com/powershell-modern/ultimate-guide-to-using-powershell-add-member-cmdlet/"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2023-11-18T00:00:00",
    "Title": "The ternary operator can replace if-else statements",
    "TipText": "PowerShell 7.0 introduced the ternary operator. This can useful for replacing very short if-else statements.\r\n\r\nThe ternary operator syntax is as follows: <condition> ? <if-true> : <if-false>\r\n\r\nWhile the ternary operator can reduce the number of lines required, not everyone will find it more readable than an if-else statement. Use your best judgement when deciding whether to use the ternary operator or an if-else statement, and remember that it is only available in PowerShell 7.0 and later.",
    "Example": "# Example of using an if-else statement to set a variable.\r\nif (Test-Path $path) {\r\n $message = \"Path exists\"\r\n}\r\nelse {\r\n $message = \"Path not found\"\r\n}\r\n\r\n# The equivalent statement using the ternary operator.\r\n$message = (Test-Path $path) ? \"Path exists\" : \"Path not found\"\r\n\r\n# Another example of using the ternary operator to toggle a service between started and stopped.\r\n$service = Get-Service ServiceNameToToggleOnOrOff\r\n$service.Status -eq 'Running' ? (Stop-Service $service) : (Start-Service $service)",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators#ternary-operator--if-true--if-false",
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_if#using-the-ternary-operator-syntax"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-01-07T00:00:00",
    "Title": "Use null-coalescing to avoid nulls and null checks",
    "TipText": "PowerShell 7.0 introduced the null-coalescing operator (??). This can help reduce the number of explicit null checks you need to write.\r\n\r\nThe null-coalescing operator syntax is as follows: <value> ?? <value-if-null>\r\n\r\nIf the value on the left-hand side of the operator is not null, the statement on the right-hand side of the operator will not be evaluated.\r\n\r\nNull-coalescing can be used in expressions, conditional (if) statements, and variable assignments.",
    "Example": "# $x is null so the value on the right-hand side of the operator is returned.\r\n> $x = $null\r\n> $x ?? 'Default value for x'\r\nDefault value for x\r\n\r\n# $y is not null so the value on the left-hand side of the operator is returned.\r\n> $y = 'Explicit value for y'\r\n> $y ?? 'Default value for y'\r\nExplicit value for y\r\n\r\n# The null-coalescing operator can be used in variable assignments.\r\n# Since $x is null, $z is assigned the value of $y.\r\n> $z = $x ?? $y\r\n> $z\r\nExplicit value for y\r\n\r\n# Use the above expression ($z = $x ?? $y) instead of:\r\nif ($null -eq $x) {\r\n $z = $y\r\n}",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-7.4#null-coalescing-operator-",
      "https://toastit.dev/2020/03/10/ps7now-null-conditional/"
    ],
    "Category": 6,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-01-17T00:00:00",
    "Title": "PowerShell has a Discord Community",
    "TipText": "Join the PowerShell Virtual User Group on Discord to connect with some of the brightest minds to get help, discuss scripts, challenge others and socialize.",
    "Example": "",
    "Urls": [
      "https://discord.gg/powershell"
    ],
    "Category": 0,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Dwayne Gibbs"
  },
  {
    "CreatedDate": "2024-02-11T00:00:00",
    "Title": "Get web request from browser dev tools",
    "TipText": "Open up your web browser's developer tools (usually by pressing the F12 key) and navigate to the Network tab to see the network requests that are being made when you load a webpage. You can right-click on a request and copy it as PowerShell. This will copy the request and all headers as a PowerShell command that you can paste in your scripts.\r\n\r\nThis is an easy way to explore APIs and help automate flows that you typically do in the browser. You may even find APIs that are not listed in the web application's documentation, but beware that endpoints not listed in documentation may change without notice.",
    "Example": "",
    "Urls": [
      "https://x.com/_mbanana/status/1714337624674939081?s=20"
    ],
    "Category": 8,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-02-11T00:00:00",
    "Title": "Play PowerShelldle to test and expand your PowerShell knowledge",
    "TipText": "Head on over to https://powershelldle.com. PowerShelldle is a Wordle-like game where you guess a new PowerShell cmdlet each day.\r\n\r\nPlay with your friends and colleagues. It's a fun way to test and expand your PowerShell knowledge.",
    "Example": "",
    "Urls": [
      "https://powershelldle.com"
    ],
    "Category": 0,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-02-18T00:00:00",
    "Title": "See variable values inline while debugging",
    "TipText": "The Visual Studio Code extension \"Inline Values support for PowerShell\" by Tyler Leonhardt is great. While you are debugging and stepping through your PowerShell code, it shows the values of variables inline in the editor.\r\n\r\nWithout this extension, to see a variable's value you would need to hover your mouse over the variable, type it into your debug prompt,look for it in the Locals debug pane, or add it to the Watch list debug pane. Being able to see the variable values without having to take any additional steps is a huge time saver and makes debugging much easier.\r\n\r\nIf you try the extension and love it, show your appreciation by giving it a rating and review on the Visual Studio Code Marketplace.",
    "Example": "",
    "Urls": [
      "https://marketplace.visualstudio.com/items?itemName=TylerLeonhardt.vscode-inline-values-powershell"
    ],
    "Category": 1,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-02-18T00:00:00",
    "Title": "Use Get-Help -Online to view the help in your web browser",
    "TipText": "You can use Get-Help to view the documentation for a cmdlet. This will use the help files on your local machine, which may be out of date. You can update your local files with Update-Help, but it uses more disk space and may take a while to run.\r\n\r\nTo easily ensure you are viewing the most up-to-date help, use the -Online parameter. This will open the help docs in your default web browser. Some people prefer to view the help docs in their web browser because it is easily searchable, and you can have multiple tabs open at once for different cmdlets.\r\n\r\nCAUTION: The web browser will default to showing the help for the latest version of PowerShell. If you are using an older version of PowerShell, be sure to change the version picker to the version of PowerShell you are using.",
    "Example": "# Use your local help, which may not be up-to-date.\r\nGet-Help Get-Item\r\n\r\n# Open the help in your default web browser.\r\nGet-Help Get-Item -Online",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-help?view=powershell-7.4"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-02-26T00:00:00",
    "Title": "VS Code keybinding to always run PowerShell",
    "TipText": "VS Code is the preferred IDE to write and run PowerShell scripts. If the workspace has a .vscode\\launch.json file configured though, pressing F5 may run the application instead of running the PowerShell script that you have in focus. To force VS Code to always run the PowerShell script in focus, you can add a keyboard shortcut to the keybindings.json file.\r\n\r\nTo add the keybinding, press Ctrl+Shift+P to open the Command Palette, open \"Preferences: Open Keyboard Shortcuts (JSON)\", and then add the following JSON to the keybindings.json file:\r\n\r\n{\r\n \"key\": \"f5\",\r\n \"command\": \"PowerShell.Debug.Start\",\r\n \"when\": \"editorTextFocus && debugState == 'inactive' && editorLangId == 'powershell'\"\r\n}",
    "Example": "",
    "Urls": [
      "https://coryknox.dev/posts/2024/run-powershell-no-mater-what/",
      "https://twitter.com/CoryKnox/status/1762253942753493451"
    ],
    "Category": 1,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-03-04T00:00:00",
    "Title": "Get filesystem friendly DateTime with Get-Date",
    "TipText": "It's common to use a date timestamp in a filename for things like log files. The default output of Get-Date includes characters that are not valid for the filesystem, such as colons. You can use the -Format parameter to specify a custom format, or leverage the built-in FileDate and FileDateTime formats that are filesystem friendly.",
    "Example": "PS> Get-Date\r\nMonday, March 4, 2024 9:43:18 AM\r\n\r\nPS> Get-Date -Format 'yyyy-MM-dd_HH-mm-ss'\r\n2024-03-04_09-43-37\r\n\r\nPS> Get-Date -Format FileDate\r\n20240304\r\n\r\nPS> Get-Date -Format FileDateTime\r\n20240304T0943556270\r\n\r\nPS> Get-Date -Format FileDateTimeUniversal\r\n20240304T1544081746Z",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-date?view=powershell-7.4#-format"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  },
  {
    "CreatedDate": "2024-04-28T00:00:00",
    "Title": "Use Select-String to find text in files and objects",
    "TipText": "The Select-String cmdlet works similar to grep and searches for the specified text in files or objects. By default it will use a regular expression pattern to search, but you can provide the -SimpleMatch parameter to do a basic text compare instead. The -Path parameter accepts wildcards and can be used to search multiple files at once. You can also pipe objects to Select-String to search them instead of files.\r\n\r\nWhen searching files, Select-String will return the filename and line number where the match was found. When searching objects, it will return the object itself. You can also use the -Context parameter to return surrounding lines of text for each match.",
    "Example": "# Search log files for errors and warnings.\r\nSelect-String -Pattern 'error','warning' -Path 'C:\\temp\\log\\*.log'\r\n\r\n# Use -NotMatch to find lines that don't match the pattern, showing 2 lines before and 3 lines after the match.\r\nSelect-String -Pattern 'success' -Path 'C:\\temp\\log\\*.log' -NotMatch -Context 2,3\r\n\r\n# Search objects and arrays for text. This uses a case-sensitive basic text compare.\r\n'Hello.', 'HELLO.', 'HELLO!' | Select-String -Pattern 'HELLO.' -CaseSensitive -SimpleMatch\r\n\r\n# Search using regular expressions.\r\n'bat', 'batman', 'sabbatical', 'take a bath' | Select-String -Pattern 'bat.+'\r\n\r\n# Search through Windows Event Logs for events containing the word 'Failed'.\r\n$events = Get-WinEvent -LogName Application -MaxEvents 50\r\n$events | Select-String -InputObject { $_.message } -Pattern 'Failed'\r\n\r\n# If you need to search files in subdirectories too, use Get-ChildItem and the -Recurse parameter.\r\nGet-ChildItem -Path \"C:\\temp\" -Recurse -Include '*.log' | Select-String -Pattern \"error\"",
    "Urls": [
      "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/select-string",
      "https://lazyadmin.nl/powershell/powershell-grep-select-string/"
    ],
    "Category": 3,
    "ExpiryDate": "9999-12-31T23:59:59.9999999",
    "Author": "Daniel Schroeder (deadlydog)"
  }
]