PowerShell Beginner’s Guide – Using IF Else Statements

In this post we will be going through using if else statements.

If else statement in PowerShell evaluates a condition and executes code if the condition is true. If the condition is false, alternative code is executed.

If else can also be nested to create more complex scripts that handle multiple conditions and execute specific code for each condition.

If else follows the below format

if (condition) {
    # code to execute if condition is true
} 
else {
    # code to execute if condition is false
}

If Statement

We will first go through using a single if statement, when using a if the part between the two () is the conditions which needs to be checked.

When using an if without an operator (eq, like, gt…..), the if will check that the variable is not empty.

The below shows the difference between the if statement when there is a value and no value.

When using an operator we can specify if the value matches then run the code.

$con = "True"
if($con -eq "True"){
    Write-Host "Con is true" -ForegroundColor Green
}

If Else

Next we will go through using if and else. Adding else is a way to executed code if none of the conditions are meet.

$con = "True"
if($con -eq "True"){
    Write-Host "Con is true" -ForegroundColor Green
}
else
{
    Write-Host "Con is false" -ForegroundColor Red
}

We can add an elseif to add another condition, there is no limit to how many can be added but it can be a bit messy if there are two many if elseif’s. Also the first condition that is meet is will stop the rest from being tested.

$con = "True"
if($con -eq "True"){
    Write-Host "Con is true" -ForegroundColor Green
}
elseif ($con -eq "False") {
    Write-Host "Con is false" -ForegroundColor Red
}
else
{
    Write-Host "Con does not meet condition" -ForegroundColor Yellow
}

If with additional operator

We can test multiple conditions by using different operator in the if statement. In the below we are using the -and operator to check that condition one is true and condition two is greater than 3

We can also use -or operator which allows for us to specify two expressions and returns true if either one of them is true.

$con1 = "True"
$con2 = "5"
if ($con1 -eq "True" -or $con2 -gt "6"){Write-Host "Condition are meet" -ForegroundColor Green}

If Null

We can also check for Null values in If statements, to check if a variable or result is null we use the $null variable.

The $null variable must be on the left side of the statement as if it not PowerShell may not

Using if statements in your PowerShell scripts creates a lot of possibilities for creating dynamic and responsive automation. They can be a bit confusing at the start but with a bit of practice and experimentation, if statements become powerful tools in your scripting.

PowerShell Beginner’s Guide – Looping through objects

In this post we will be going through the different types of loops in PowerShell.

There are a few different types of loops and we will go through each and showing examples on how to use them. Loops are very useful and will be required in most scripts.

The first we will go through is a ForEach-Object loop, this is one of the main loops I use and can be used a few different ways.

Foreach Objects

First way is to get a list of objects and then pipe to our foreach-object to go through each.

Below example will get all services, loop through the results and return each name.

Get-Service | ForEach-Object { $_.Name}
Foreach-Object

Another method for using foreach-object is to use a variable with a set of object or a command that will retrieve objects and loop through each object in the variable.

There are two short hand commands for foreach-object, % and foreach.

In the below I am getting all folders under the directory and adding them to a $folders variable and looping through each.

$folders = Get-ChildItem C:\temp\ -Directory
foreach ($folder in $folders){$folder | Select-Object Name,CreationTime}

We can also use the foreach method on the variable to loop through the objects.

I mostly use foreach in my scripts as I find it the easiest to read when I have to go back over my own scripts.

While Loops

The other type of loops is a while loop.

Do while basically mean that while a condition is true the script will then keep looping.

This can be useful if we want to run a script for a certain period of time or while a process is running.

Below will loop through till the number in the variable $i is 10 then it will run the second write host.

$i = 1;
Do {
    Write-host "is $i less then 10" -ForegroundColor Red
    $i++;
}
While ($i -lt 10)
Write-Host "is 10 or greater" -ForegroundColor Green

Next we will go through do until, the different between do while and do until is that while uses true and until uses false condition.

This could be used if we wanted to check if a certain number of files or in an install script we could wait for certain process to finish before completing the next part.

Below example I am using a do until on a folder to check how many logs are there and using a do while to create the logs.

Check logs count.

Do {

    $logs = Get-ChildItem -Path C:\temp\Logs
    Start-Sleep 5
    Write-Host "Less than 10 logs found" -ForegroundColor Green
}
Until ($logs.count -gt "10" )
Write-Host "More than 10 logs found" -ForegroundColor Red

Create logs files.

$i = 1;
Do {
    New-Item "C:\temp\Logs\Log$i.log"
    $i++;
}
While ($i -lt 10)

This has been overview of some of the different types of loops and how to use them. There are pros and cons to using each and the right one to use will really depend on what the script is being used for.

Using Group Managed Services Account with Scheduled Tasks

In this post we will be going through the steps required to create and use group managed services account (gMSA) with a scheduled task.

gMSA are a managed domain account that provides automatic password management. These accounts provide a single identity to use on multiple servers.

By using a gMSA account, we can configure services / scheduled tasks with the gMSA principal and Active Directory handles the password management.

gMSAs where introduced since Windows Server 2012.

There are pre-requests to use gMSA that most domain should already meet, this is AD Schema of 52 (2012) and at least one 2012 DC.

Once we meet the minimum scheme the second part is that we have to create the Key Distribution Services Root Key.

This involves running a command in PowerShell, we can the below command to confirm that a kds root key doesn’t exist already.

Get-KdsRootKey
Get KDS Root Key

To create the KDS run

Add-KdsRootKey –EffectiveImmediately
Adding root key

Now when we check KDS again we can see the root key.

Get KDS Root Key

Now that we have the KDS root key we can create the gMSA

We can add the host either individually or using a security group, we will be using a group in this post as it will be easier to mange and just need to add any additional servers to the group to allow access.

I have create a group called tskgmsa_access to use and added some server computer accounts.

The below command is used to create the gMSA account (The DNS is required by the command but not needed for running scheduled task so you can use whatever name as it doesn’t need to be resolvable)

New-ADServiceAccount -name gMSAName -DNSHostName DNSName -PrincipalsAllowedToRetrieveManagedPassword "Group or Hosts"

If you receive the below error New-ADServiceAccount : Key does not exist
this is probable due to the KDS key not be being actives yet and you will have to wait around 10 hours before trying to create the gMSA.

There is a way to force this using the below command but I didn’t want to force it and just left the server over night.

Add-KdsRootKey –EffectiveTime ((get-date).addhours(-10))

Once we wait the 10 hours the command should now run without the error.

Create gMSA Account

The accounts are create under the Managed Services Accounts OU.

Managed Accounts OU

Note: To add additional accounts or groups to gMSA account you need to append the existing group or the command will remove the existing account

If running the below the new object will be added and remove the existing objects.

Set-ADServiceAccount -Identity gMSA_Account -PrincipalsAllowedToRetrieveManagedPassword "NewAccount$"

Instead we need to run this command to add the new object and kept existing object.

Set-ADServiceAccount -Identity gMSA_Account -PrincipalsAllowedToRetrieveManagedPassword "ExistingAccount$", "NewAccount$"

If we need to get a list of the existing object we can use.

Set-ADServiceAccount -Identity gMSA_Account -Property PrincipalsAllowedToRetrieveManagedPassword

We can add the account to a security group to give more rights, this could be used if the account needs admin right to do a specific tasks.

The Microsoft documentation says that you need to install RSAT tools and run Install-ADServiceAccount but I didn’t have to do this for scheduled task to run.

To allow the account to run a script we need to add the account to the logon as a batch job under user rights assignment. This can either be done using group policy or using secpol.msc.

I used secpol as i only have two servers to configure.

To test we will create a scheduled task that will out put text to a log file.

Below is the script file that will be run.

Test Script

To use the gMSA account we will create the task using either PowerShell (Run as administrator as this required elevated privilege’s) or the task scheduler GUI. In this example we will use PowerShell.

Below is the command I used.

$arg = "-ExecutionPolicy Bypass -NoProfile -File C:\scriptpath\script.ps1"

$ta = New-ScheduledTaskAction -Execute C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe  -Argument $arg

$tt = New-ScheduledTaskTrigger -At "Time to Run"

$ap = New-ScheduledTaskPrincipal -UserID Domain\gMSA_AccountName -LogonType Password

Register-ScheduledTask gMSA_Test_Task –Action $ta –Trigger $tt –Principal $ap

If you get incorrect users or password and only recently added the computer account to the security group, the server will need a reboot to pickup the membership.

Scheduled Task Error
Create Scheduled Task
Scheduled Task

If creating in the GUI you will probable get an error while searching for the gMSA account.

gMSA search error

To get the search to work correctly we need to remove user and built-in-security principals from the search object types and leaving just service accounts.

Search Object types

Next we can search for the gMSA account.

gMSA Results after object change

Next we need to run the task and confirm data is written to the log. From the task events we can see the account used is gmsa_tsksch$.

Scheduled Task Event

We can also see the authentication on the domain controller.

Logon Event

When we check the logs folders we can see the text files are created.

Test Log

This has been a overview of creating and using a Group Managed Service Account for running scheduled tasks.

PowerShell Beginner’s Guide – Filtering Objects

In this post we will be going over filtering in PowerShell and different methods available.

Filtering should be done as close to the source command as possible, this will speed up the time it takes for the command to complete and return the data.

Depending on the command used there can be a filter parameters available, if there is no filtering we would use where-object.

Where-Object can be used to filter objects based on any property that is returned from the command.

If we need help with example or the right syntax to run the command we can use

Get-Help Where-Object

Get-Help Where-Object -Examples
Where-object help

Where-object can be used with lots of filtering parameters to return objects like Contains, eg (equal to), gt (greater than), lt (less than), like…….

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/where-object?view=powershell-7.4#parameters

Once we have the syntax worked out we can start to filter the data to returned. In the below command we will return any file that has .pdf extension and just select the name to make the results easier to read.

Get-ChildItem -Path 'C:\Program Files\' -Recurse -File | Where-Object {$_.Name -like "*.pdf"} | Select-Object Name
where-object results

Get-ChildItem has a filter parameter, this allow us to test and show the speed difference when we can.

Below is the time when running the command using where-object.

Where-object command time

When we use the -filter with Get-ChildItem we can see that the time to run went from 6 second to 2 seconds.

Get-ChildItem Filter command time

This was only a small subset of data but if we where running against thousand or hundred of thousand of files this can add up and save minutes to hours of time for data to returned.

Windows Server 2022 RDS HA Broker: Privilege not held

I have been building and setting up some new Windows Server 2022 RDS farms recently and ran in to an issue when adding a second RDS connect broker after configuring High Availability.

The server would install the broker role and then fail to configure. This would return the below error.

The list of joined nodes could not be retrieved on servername. Privilege not held.

RDS Broker Error

I was also getting EventID 32814 and 4119 in the Microsoft-Windows-Rdms-UI/Admin event logs.

I enabled the debug logging also but this only returned the same error as where showing in the event logs.

https://learn.microsoft.com/en-us/troubleshoot/windows-server/remote/log-files-to-troubleshoot-rds-issues

To fix the issue I had to logon to the server I was trying to add as a secondary broker and remove the below registry value.

Make sure to backup the registry key before deleting any values.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tssdis\Parameters\DBConnString

Tssdis Service Registry Key

After deleting, I had to remove the RDS broker roles from the server that failed to install and reboot.

Then I tried the install again and this time it completed without issue.

This seems to be a bug in Windows Server 2022.

PowerShell Beginner’s Guide – Variables, Arrays and Hash Tables

In this post we will be going through the use of variables, arrays and hash table.

Variables are object that are created temporarily in the PowerShell console or created as part of script that are removed once the console is closed. There are system variables that are part of PowerShell but we will just be covering user variables in this post.

Variables are $ sign with a name.

We can then call the variable by using the name

Variable

There are two type of quotation marks single will only display the text between the quotes. We can use these for variable that have text data.

Single Quotes Variable

With double quotation marks we can passthrough text but also passthrough a variable. If we try to add a variable without double quotes it will just write the variable name and not the text in the variable.

Double Quotes Variable

There are some instance where we need to create a subexpression using $() around the variable to pass through specific data.

In the below I want to return just the first folder. The first command return all the folder names.

The second command is using a subexpression, we are then able to return the first folder name.

Subexpression results

An array is pretty similar to a variable the only really difference is that it has multiple objects broken up by using comma’s.

Text Array

We can also use @() to create any array if there is text other than number we have to use quotes.

Number Array

We can also create a blank array, I use these when doing reports and want to output data from a script.

Blank Array

We will go more in depth on using the blank array in a future post but I mostly use them with hash tables.

Next we will go through using a hash table, these are like array but there is key / value pair.

Below we have a table where we created a name, surname and role heading and set a value for each.

Hash table

We can also use array or variables in hash tables

Hash table with array

This is where hash tables can come in hand for doing reports as we can output the results of multiple commands using a blank array and a hash table, then export the results to a CSV, text or html file.

We will cover both these in later post.

In this post we covered create variables, types of quotes and there uses, array’s and hash tables with some examples of each.

PowerShell Beginner’s Guide – Sorting and Selecting Data

In this post we are going to go through sorting, selecting and formatting object data.

To be able to get the right information back in both the PowerShell console and to output to files like txt, csv, html…, it’s import to be able to format the data correctly.

We can sort by using sort-object command, in the below example we can see that the process are sorted by process name by A to Z.

Now we can use sort-object to sort the processes by ID instead of name.

We can added -descending to sort from other way either.

There can be hidden properties that can be useful, to view these we can use the Get-Member and use member type property.

Get-Process | Get-Member -MemberType Property

We can now use the property names with select-object to return the additional properties.

Select-Object has a first and last parameters that let you skip everything but the specified number of objects.

Using the below will only return the first 20 processes.

Get-Process | Select-Object -First 20

Using calculated property in Select-Object is a good way to rename a properties or use math’s to work out a value like the size of a folder or memory used by a process.

To create a calculated property we use @{Name=”Name of property”;Expression={$_.propertyname}}.

Get-Process | Select-Object Name, CPU, @{Name="Memory";Expression={$_.WorkingSet/1KB}}

In the next post we will be going through Variables, Arrays and Hash Tables

PowerShell Beginner’s Guide – Getting Started with PowerShell

In this next series of post we will go through getting start with basic commands, more advances commands and then going through how to use these to creating more complex scripts.

First cmdlets in PowerShell are named according to a verb-noun naming (mostly, there are times when people create there own command that don’t use the verb-noun naming). This pattern is to help give an understanding of what the command does and how to search for them.

Sometimes finding the specific command you want is time consuming in PowerShell. This is where the Get-Command command can come in handy

If we want to find a command by name we can use the full name

If we don’t know what the command is we can use wildcard (*) to return all command that have a conmen name.

Below example returns all command that have network in the name.

Once we have a command we can either look up the documentation or use Get-Help to view how to use the command and examples.

To view the full help with examples on how to use.

Get-Help Get-Service -Full

To view just the examples

Get-Help Get-Service -Examples

To open the webpage for the command use -Online

Get-Help Get-Service -Online

Below is a quick overview of what each verb function should be.

VerbFunction
GetReturn values (object, properties….)
NewCreate new objects (File, object….)
SetSet object (value, properties….)
OutOutput to (Console, file…..)
StartStart service, process’s ….
StopStop service, process’s…..

To return a list service’s we can run Get-Service.

If we want to filter to only return service with specific status like running we can use where-object and the status property.

If we wanted to only return the display name and status we would pipe the result using | and then use Select-Object.

If we know there is a certain restart order for an application or a restart of a service will fix a specific issue on server or endpoint device, we could then use the name and status to start or restart the service.

To run remotely (if the command supports remote calls) we an use the ComputerName (sometimes its Server, depends on the command).

In the next post we will look at sorting, selecting and additional properties of Objects in PowerShell.

Azure FileShare: CMDKEY: Credentials cannot be saved

I was setting up an Azure FileShare recently and wanted to connect with the storage account key to configure the initial permission and folder structure.

When trying to added the connect account details I was getting the below response.

CMDKEY: Credentials cannot be saved

The issue was due to one group policy that was blocking saved passwords. The specific policy setting was Network access: Do not allow storage of passwords and credentials for network authentication

To fix the issue I had to create a new GPO that would change the policy setting for the device I was connecting from. Setting to Disabled.

Once this was updated the command then completed successfully.

After getting the credential to successfully add I still couldn’t map the shared.

I was getting New-PSDrive : The specified network password is not correct

After a bit of troubleshooting the issue was down to having NTLM v2 disabled on the security setting on the Azure FileShare.

The settings is under storage account > select the storage > Data storage > File shares > Security.

Under security check that NTLM v2 is enabled under Authentication mechanisms.

After enabling NTLM v2, I was able to connect to the Azure FileShare using the storage account key.

Microsoft Entra ID App Registration Certs / Client Secret HTML Report

In this post we will be going through creating a HTML report for Microsoft Entra ID App registration to export a list of expired and expiring client secrets and certificates.

I wanted to create a HTML report that would highlight cert / secrets that where expired or due to expire and make it quicker to check what is due to expire.

This report will use the existing PowerShell script I created last year to export Azure App registration details to a CSV file, just updated to use an embedded CSS style sheet to format the html output.

First I needed to create a HTML style sheet, below is the HTML code I use for the HMTL formatting.

The last step was update the rows to set each row to green, orange or red depending on the status of the cert / secret.

To run the script I will be using a certificate and app registration as this allow for better automation and certificate are recommend over client secrets for security. If you need to know how to set this up I have done a previous post on this.

.\Get-AppRegistrationdetailsHTMLv2.ps1 -CertificateThumbprint thumbprint -ClientId ClientID -TenantId TenantID -ReportExport C:\temp\Graph\ -ExpiryDate 200

Below is an example of the full export.

The table and colors can be modified just have to update the style sheet.

To download the full script use the below GitHub link.

https://github.com/TheSleepyAdmin/Scripts/blob/master/MSGraph/AppRegistration/Get-AppRegistrationdetailsHTMLv2.ps1