The PowerShell variable has many features that help you create reliable scripts. Understanding the most important concepts of the PowerShell variable is essential for everything you do in PowerShell. In the first post of this series, I will explain how to name variables, how to assign values, and how to deal with PowerShell data types.
Latest posts by Michael Pietroforte (see all)

The purpose of variables in any programming language is to store data items so you can reuse them later in your code. If you only know variables from batch scripts, you’ll be surprised at how many more things you can do with a PowerShell variable.

Naming

Let’s look at the basics first. An important property of a PowerShell variable is its name, which is always preceded by the dollar sign “$” and can only contain letters, numbers, and the underscore. If you feel a strong urge to use other characters, you have to enclose the name in curly braces. You should not use the name of variables that have been pre-defined (more about that later).

Here are examples of valid variable names:

$myVariable, $MyVariable_1, ${my-variable}

And these are invalid names:

myVariable, $my-variable, $my variable

Strictly speaking, the variable name is the part after the dollar sign. This is important to know because when you have to specify the variable name as a parameter in a cmdlet, you have to enter it without the dollar sign. The dollar sign tells the shell to read the variable's value.

As most keywords in PowerShell, variables are case-insensitive. Thus, PowerShell does not distinguish between $myVariable and $Myvariable.

Values

You can assign values to a PowerShell variable by combining a variable name, an assignment operator, and an expression. Here is a simple example:

$a = 1 + 1

The “=” sign is one of eight assignment operators. An expression is everything for which PowerShell can determine a value. If you enter an expression at a PowerShell prompt, PowerShell returns its value. “Hello world” and 1 are also expressions; determining their values just doesn’t need so many calculations.

If you want to populate multiple variables with the same value, you can save some typing as in the example below:

$a = $b = $c = 1

You can also define multiple variables with different values on one line:

$a, $b, $c = 1, 2, 3

This reduces the number of lines in your script but also makes it harder to read.

Other ways exist to store a value in a variable. We will see examples later.

To display the value of a variable, you don’t need a special command as in many other programming languages; entering the variable name is enough. This works in a script and on a command prompt.

$c

Thanks to the variable interpolation, you can also expand a variable in a string if the string is enclosed in double quotation marks.

"These are the values of the variables: $a, $b, $c."

Interpolation example

If you want to display the variable names as text instead of displaying their values, you have to enclose the string in single quotation marks.

'These are the names of our variables $a, $b, $c.'

No interpolation with single qutation marks

In this case, PowerShell doesn’t recognize $a, $b, and $c at all as variables—just as ordinary strings.

Data type

Thus far, we worked only with two data types: strings and integers (or—more precisely—32-bit integers). PowerShell supports many other data types, such as floating point numbers, strings, and Boolean values. You don’t have to explicitly declare the data type of a variable; PowerShell automatically chooses the data type for you when you initialize the variable—that is, when you first assign a value.

However, in some cases, PowerShell will not use the data type you intended, as the example below demonstrates:

$Number = Read-Host "Please enter a number" 
$Square=$Number*$Number 
Write-Host "The square of the number $Number is $Square."

Mulitplying a string

If you didn’t sleep in math class, the result of this calculation should be a bit of a surprise. PowerShell wrongly assumed that the data type of the variable $Number is String. Because the arithmetic operator * is overloaded (the implementation of the operator depends on the arguments), the second line in the program multiplies a string instead of a number.

The second argument of the operator * always has to be a number, so PowerShell automatically converts the data type of $Number into Int32. However, the first argument can also be a string. The result is that PowerShell determines the value of the expression “2”*2, which is 22 (the string “2” is repeated two times).

Not only does PowerShell automatically assign a data type when you initialize the variable, but the data type can also change on the fly when the original data type doesn’t fit with the operation. In the example above, PowerShell needs a number for the operator *; because the string “2” looks very much like a number, it just converts it to the integer 2.

However, if no reasonable way exists to automatically convert the data type, and the data type doesn’t fit with the operation, PowerShell will throw an error. For instance, the lines below will produce the error message “Cannot convert value ‘b’ to type ‘System.Int32.’”

$a = "a" 
$b = "b"
$a*$b

Automatic data type conversion does not work

However, the next example, which looks very similar, runs through:

$a = "a"
$b = "2"
$a*$b

Automatic data type conversion works

It is important to note that automatic conversion does not change the variable’s data type. Even though the value of the variable $b in the example has been used as an integer in the calculation, its data type is still String. To determine the type of a variable, you can use the GetType() method:

$b.GetType().Name

Get the type of a variable with GetType()

If it surprises you that the variable is equipped with a method, you’ll have to wait for a follow-up post where I will uncover the secret behind the command above.

We don’t have to rely on PowerShell’s ability to automatically convert data types if we tell the interpreter that we are expecting a number as input. This ensures that our script works as intended:

[Int]$Number = Read-Host "Please enter a number" 
$Square=$Number*$Number 
Write-Host "The square of the number $Number is $Square."

Mulitplying a number

In the code snippet above, we explicitly declared the number as Int32 (integer) by enclosing the type name in square brackets before the variable name. A variable is called “weakly typed” if you only declare its data type implicitly by assigning a value of a certain type. If you declare the variable’s data type explicitly in your script, the variable is strongly typed.

As the example above shows, explicitly declaring variable types can prevent unwanted results in your scripts and makes them more reliable. However, this is not the only reason it makes sense to work with strongly typed variables. The things you can do with the value of a variable often depend on its data type.

For instance, you can store a certain date in a variable of the data type String, and you can use the data type DateTime that is intended for the purpose. As long as you only want to display the date, it makes no difference which data type you use. However, if you want to use the variable in calculations, you have to declare it as DateTime.

Let’s say you imported dates from a log file and you want to know how many days passed between a certain date and today’s date:

[DateTime]$Date = "February 28, 2015" 
$Today = Get-Date 
$Days = ($Today - $Date).Days 
Write-Host "The hacker encrypted all your servers $Days day(s) ago."

If you omit the declaration of the data type in the example above, PowerShell will politely inform you in a red-colored message that something went wrong: “Multiple ambiguous overloads found for…” Yes, the - operator is also overloaded.

Below is a list of some commonly used data types:

Data Type Name Description
[Array] Array
[Bool] Value is TRUE or FALSE
[DateTime] Date and time
[Guid] Globally unique 32-byte identifier
[HashTable] Hash table, collection of key-value pairs
[Int32], [Int] 32-bit integers
[PsObject] PowerShell object
[Regex] Regular expression
[ScriptBlock] PowerShell script block
[Single], [Float] Floating point number
[String] String
[Switch] PowerShell switch parameter
[TimeSpan] Time interval
[XmlDocument] XML document

In my next post, I will cover the variable scope.

17 Comments
  1. Eddie Kumar 5 years ago

    Thanks, great article, I have compile the extended list of allowed datatypes in PowerShell:
    1. [int] or [int32] 32-bit signed integer
    2. [long] 64-bit signed integer
    3. [char] Unicode 16-bit character
    4. [string] Fixed-length string of Unicode characters
    5. [single] or [float] Single-precision 32-bit floating point number
    6. [double] Double-precision 64-bit floating point number
    7. [decimal] 128-bit decimal value
    8. [bool] True/false value
    9. [byte] 8-bit unsigned integer
    10. [array] Array of values
    11. [hashtable] Hashtable object (similar to a Dictionary object)
    12. [xml] or [XmlDocument] Xmldocument object
    13. [DateTime] Date & time object.
    14. [TimeSpan] Time interval.
    15. [PsObject] PowerShell object.
    16. [Switch] PowerShell Switch parameter.
    17. [SctiptBlock] PowerShell Script object.
    18. [RegEx] Regular expression.
    19. [GUID] Globally unique 32-byte identifier.

  2. Luis 4 years ago

    Hello,

    Thanks for the article!

    Could I ask something?:

    If I declare:

    [int]$var = “2”

    Would it take it as an integer or as a string? . Because with “int” you are forcing it to be a number, but with the double quotes , would it take it as a string?

    I am unsure, thanks

     

    • @Luis

      It doesn’t matter if you use single or double quotes or noting. The type is enforced by the content between the brackets.

      PS C:\> [int]$var = "2"
      PS C:\> $var.GetType()
      IsPublic IsSerial Name                                     BaseType
      -------- -------- ----                                     --------
      True     True     Int32                                    System.ValueType                                                                            
      
      PS C:\> [int]$var = '2'
      PS C:\> $var.GetType()
      IsPublic IsSerial Name                                     BaseType
      -------- -------- ----                                     --------
      True     True     Int32                                    System.ValueType                                                                            
      
      PS C:\> [int]$var = 2
      PS C:\> $var.GetType()
      IsPublic IsSerial Name                                     BaseType
      -------- -------- ----                                     --------
      True     True     Int32                                    System.ValueType                                                                            
      
      PS C:\>
      avatar
  3. Luis 4 years ago

    Many thanks Luc.

    avatar
  4. Kenneth 4 years ago

    Michael,

    Are there any down-sides to always using strongly typed variables in PowerShell (when initializing the variable, force the data type with the [<data type>] data type)?

    From a security standpoint, are there any possible reasons we would always use strongly typed variables?   I worry about a scenario like the following example code:

    function main {
        # Initialize our weakly typed variable.
        $IntVarUserInput = 0
    
        # Request the users age from STDIN.
        $IntVarUserInput = Read-Host "Hello User!  What is your age? "
    
        # Display what the user inputed.
        Write-Output "You are $IntVarUserInput old!"
    }
    
    # Entry-point for our example script.
    # Call our main function.
    main
    
    
    ## ** Simulated Execution of PowerShell Script ** ##
    ./MyTest.ps1
    
    Hello User!  What is your age? (<bad-command>)
    
    or
    
    Hello User!  What is your age? $(<bad-command>)

    Obviously, this example is not a PoC, albeit it should (hopefully) convey my question in a more understandable way, if the worded question wasn't 100% clear.

    In my example code, the PowerShell interpreter parses the script.   It reads the main function, it makes its way down to our entry-point, it comes across the call the main function, it executes the main function, it asks the user for their age, however, the user types a crafted string that is a command that gets executed when the Write-Output function is printed the users age.

    Does that make sense?  If so, is such a scenario possible with weakly typed variables vs strongly typed variables?   Finally, is there ever a time we would want to use weakly typed variables?   My understanding (please correct me if I'm wrong), we can type cast a variable if we need to.   So for the most part, strongly typed variables, if I am understanding everything correctly, should always be used, right?

    Thanks!

    • Leos Marek (Rank 4) 4 years ago

      Whatever the user enter will never be executed with Write-Output command, it will be simply displayed.

  5. Kenneth 4 years ago

    @Leos Marek,

    Just so I am understanding you correctly, are you saying Write-Output is not exploitable in any way?   I remember with an old device called an Archos, there was a field where you could type some sort of text which would set a setting for the Archos.   Someone found a way to exploit that by tricking the software on the Archos to think that the actual user data entered was over with and then a php command was supposed to be run.

    I just wanted to make certain we didn't have to parse the user data to make sure there is no types of injection or anything like that.

    Thank you!!!!!

    • Leos Marek (Rank 4) 4 years ago

      Hi Kenneth,

      its not about Write-Output or any other command. It is about what Powershell allows you to execute. You can only execute things in certain way. Example below, I need to create a variable which is a Scriptblock type.

      PS C:\Users\Balrog> $test = {get-date}
      PS C:\Users\Balrog> & $test
      
      středa 9. října 2019 15:45:54
      
      
      PS C:\Users\Balrog> $test | gm
      
      
         TypeName: System.Management.Automation.ScriptBlock

      While with Read-host, the variable will always be only a string.

      PS C:\Users\Balrog> $test2 = Read-Host
      {get-date}
      PS C:\Users\Balrog> $test2 | gm
      
      
         TypeName: System.String

      So its only text, nothing else. You cant execute text.

      So, this is safe 🙂

      avatar
      • Author

        So its only text, nothing else. You cant execute text.

        Actually, you can execute strings in PowerShell. Try this:

        "Please execute me"

        And Kenneth, everything that is stored in a computer's memory is exploitable. This also applies to Write-Output. The only question how easy it is to exploit certain commands.

        • Leos Marek (Rank 4) 4 years ago

          Michael,

          sorry I dont get the point. This will only write the text, as expected.

          • Author

            Exactly. If you execute a string on a PowerShell console, PowerShell takes the string and prints it on the screen. Try this on a Command Prompt and you will get an error message because this shell doesn't know how to execute strings.

            Or try this to better understand what is actually happening:

            PS: >$b = 2*3                                                                                                
            PS: >$a = "Compute me $b"                                                                                    
            PS: >$a                                                                                                      
            Compute me 6
            PS: >$a.GetType()                                                                                            
            
            IsPublic IsSerial Name                                     BaseType                                         
            -------- -------- ----                                     --------                                         
            True     True     String                                   System.Object 

            When you are dealing with strings, you have to be particularly careful because hackers love to execute code in strings. This is why we need to sanitize strings in many cases.

            • Leos Marek (Rank 4) 4 years ago

              Of course, this is clear as day. That is different thing – enumerating variables in string.
              But you will not achieve this in the case Kenneth asked:

              PS C:UsersBalrog> $b = Read-Host
              2*3
              PS C:UsersBalrog> $a = "Compute me $b"
              PS C:UsersBalrog> $a
              Compute me 2*3

              Same as if I type

              PS C:UsersBalrog> "get-date"
              get-date

              I will receive the string, not the actual output of Get-date. So from this perspective, I don't see a way how a user executing script provided by his IT department could inject his code by Read-Host.

              • Author

                I wasn't replying to Kenneth, but to your claim that text can't be executed.

                And Read-Host doesn't always safe you. Just because you enter a string, does not guarantee that your input cannot be executed:

                PS: >$a = read-host                                                                                          
                get-date
                PS: >invoke-expression $a                                                                                    
                
                Thursday, October 10, 2019 4:47:36 PM

                And I think Kenneth's original question was if strongly typed variables improve security. In general, I would say yes, but in the case of PowerShell strings, it doesn't really matter because PowerShell executes strongly typed string variables anyway.

                • Leos Marek (Rank 4) 4 years ago

                  Okay, seems like a wording issue here.

                  On one side you are right. What you showed can be done, but that has to be already badly coded in the script.

                  But, the query was about exploting this by user

                   # Request the users age from STDIN.
                      $IntVarUserInput = Read-Host "Hello User!  What is your age? "
                   
                      # Display what the user inputed.
                      Write-Output "You are $IntVarUserInput old!"

                  Which I dont think its possible, the input will be always a string, no matter what, and cannot be executed this way.

              • Author

                I think this is not a real world script. It is just an example how a string can get executed in PowerShell. I guess Kenneth asked a general question and that is if strong typing improves security in the case of user input and I would say no because Read-Host sets the type to string anyway.

                If you allow users to input text, you always have to be extra careful with what you do with those strings. The type of the input variable doesn't really matter that much. There are a myriad of ways how attackers can execute input strings. 

  6. For what it is worth, you can take the input from read-host and as long as you type your receiving variable, and the input can be coerced to the correct type, that's what you will receive.

    PS C:\WINDOWS\system32> [int]$result = Read-Host 'What is your age'                                                     What is your age: 50
    PS C:\WINDOWS\system32> $result.GetType()                                                                               
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     Int32                                    System.ValueType 
    
    
    PS C:\WINDOWS\system32> [datetime]$WhatDay = Read-Host 'what day is it?'                                                what day is it?: April 1, 1990
    PS C:\WINDOWS\system32> $WhatDay                                                                                        
    Sunday, April 1, 1990 12:00:00 AM
    
    
    PS C:\WINDOWS\system32> $WhatDay.GetType()                                                                              
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     DateTime                                 System.ValueType
    avatar

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account