Save Passwords Securely in Windows Using PowerShell

Passwords are the keys to our digital lives, and it is essential to keep them secure. With so many passwords to remember, it’s easy to fall into the trap of using the same one for multiple accounts or writing them down on sticky notes. However, these practices are not secure and can put your personal data at risk.

In this blog post, we will explore how to save passwords securely in Windows using PowerShell. PowerShell is a powerful tool for managing Windows, and it can be used for password management as well. We will use the ConvertTo-SecureString cmdlet to encrypt the password and then save it as a string in a text file, which can be used by a scheduled task running as the user who needs it.

The first step is to open PowerShell and run the following script:

$password = ConvertTo-SecureString "password here" -AsPlainText -Force

# Get content of the string
[string]$stringObject = $password |  ConvertFrom-SecureString

@"
 [string]`$encPassword = "$($stringObject)"
 [SecureString]`$securePwd = `$encPassword  | ConvertTo-SecureString
 `$password = [System.Net.NetworkCredential]::new("", `$securePwd).Password
"@ | Set-Content -Path "cred.txt"

This script will create a text file named “cred.txt” containing the script to decrypt the password and the encrypted password. The contents will look similar to the following:

[string]$encPassword = "encrypted password"
[SecureString]$securePwd = $encPassword  | ConvertTo-SecureString
$password = [System.Net.NetworkCredential]::new("", $securePwd).Password

Place that code at the top of any PowerShell script you have scheduled, and it will be able to decrypt that password. If your scheduled tasks are running as a user that you can’t start, then use a scheduled task as that user to run the first script and get the code you need.

In conclusion, saving passwords securely is crucial to protecting your personal data. PowerShell provides a simple and effective way to do so in Windows. By following the steps outlined in this blog post, you can securely store your passwords and use them when needed.

Efficiently Retrieve Active Directory Group Members with PowerShell and Caching

Retrieving Active Directory group members can be a time-consuming process, especially for large groups. This can cause slowdowns in your PowerShell scripts and lead to poor performance. Fortunately, there’s a solution: PowerShell caching. In this post, I’ll show you how to optimize the process of retrieving AD group members with caching, resulting in faster script execution and improved performance.

The “Get-CachedADGroupMembers” Function

The “Get-CachedADGroupMembers” function takes the name of the AD group to retrieve members from.

$cache = @{}
function Get-CachedADGroupMembers {
    param($group_name)
    if ($cache -eq $null) {
        $cache = @{}
    }
    if ($cache.ContainsKey($group_name)) {
        return $cache[$group_name]
    } else {
        $data = ((Get-ADGroupMember -Identity $group_name -Recursive | %{
            if ($_.objectClass -eq "computer") {
                $_.SamAccountName
            } else {
                $_.SamAccountName | Get-ADUser -Properties samAccountName, employeeType 
            }
        } | select samAccountName,employeeType,enabled) | Where {$_.samAccountName -notin $excluded_users})
        $cache[$group_name] = $data
        return $data
    }
}

The function first checks if the $cache variable has been initialized. If it hasn’t, it initializes it as an empty hashtable. It then checks if the group_name exists in the hashtable. If it does, the function returns the cached data. If it doesn’t, the function retrieves the group members using the “Get-ADGroupMember” cmdlet and stores the results in the $data variable. The function then adds the $data variable to the $cache hashtable and returns the $data variable.

The function also uses a pipeline to filter out any members that are computers and to retrieve additional user properties using the “Get-ADUser” cmdlet. Finally, it uses the “Where” cmdlet to exclude any users in a specified list of excluded users.

Conclusion

By using PowerShell caching, you can significantly speed up the process of retrieving Active Directory group members, resulting in faster script execution and improved performance. The “Get-CachedADGroupMembers” function is a simple and effective way to implement caching in your PowerShell scripts. Give it a try and see how much time you can save!

Automating DHCP Scope Option Removal with PowerShell

If you’re managing a large network with multiple DHCP servers, you know how tedious it can be to manually remove DHCP scope options. Fortunately, PowerShell can help automate the process and make network management much more efficient. In this post, we’ll walk you through a PowerShell function that can remove DHCP scope options with just a few lines of code.

The “Remove-DhcpScopeOptions” Function

The “Remove-DhcpScopeOptions” function takes two parameters: the name of the DHCP server and a switch for the “-WhatIf” parameter. The “-WhatIf” parameter allows you to see what the function would do without actually making any changes.

function Remove-DhcpScopeOptions {
  [CmdletBinding(SupportsShouldProcess=$true)]
  param (
    [Parameter(Mandatory=$true)]
    [string]$ComputerName,
    [switch]$WhatIf
  )

  # Get all DHCP scopes on the server
  $scopes = Get-DhcpServerv4Scope -ComputerName $ComputerName

  # Get all options for the DHCP server
  $serverOptions = Get-DhcpServerv4OptionValue -ComputerName $ComputerName

  # For each scope, get the options and compare them to the server-level options
  foreach ($scope in $scopes) {
    $scopeOptions = Get-DhcpServerv4OptionValue -ScopeId $scope.ScopeId -ComputerName $ComputerName

    # For each option in the scope, check if it exists at the server level with the same value
    foreach ($option in $scopeOptions) {
      $serverOption = $serverOptions | Where-Object { $_.OptionId -eq $option.OptionId }
      if ($serverOption -ne $null -and $serverOption.Value -eq $option.Value) {
        # If the option exists at the server level with the same value, remove it from the scope
        if ($WhatIf) {
          Write-Host "What if: Removing option $($option.OptionId) from scope $($scope.ScopeId) on DHCP server $ComputerName."
        } else {
          Remove-DhcpServerv4OptionValue -ScopeId $scope.ScopeId -OptionId $option.OptionId -ComputerName $ComputerName -Confirm:$false
        }
      }
    }
  }

  # Replicate the scopes to all DHCP servers in the enterprise
  if (!$WhatIf) {
    Invoke-DhcpServerv4ReplicateScopes -ComputerName $ComputerName
  }
}

How it Works

The “Remove-DhcpScopeOptions” function works by first getting all DHCP scopes on the server using the “Get-DhcpServerv4Scope” cmdlet. It stores the results in the $scopes variable. It then gets all options for the DHCP server using the “Get-DhcpServerv4OptionValue” cmdlet and stores the results in the $serverOptions variable.

For each DHCP scope, the function gets the options using the “Get-DhcpServerv4OptionValue” cmdlet and stores the results in the $scopeOptions variable. It then compares the options in each scope to the server-level options and removes any options that exist at the server level with the same value. Finally, the function replicates the scopes to all DHCP servers in the enterprise.

Example

Remove-DhcpScopeOptions -ComputerName "dhcpserver01" -WhatIf

How to Convert DHCP Leases to Reservations Using PowerShell

As a system administrator, you might have to manage a Windows DHCP server and convert DHCP leases to reservations at some point. However, this can be a tedious task if you have many leases to convert. Fortunately, PowerShell can automate this process, making it much easier and faster. This post provides a step-by-step guide on how to convert DHCP leases to reservations using PowerShell.

Step 1: Open PowerShell

The first step is to open PowerShell with administrative privileges on the Windows DHCP server. You can do this by right-clicking on the PowerShell icon and selecting “Run as administrator.”

Step 2: Define the Functions

Copy and paste the following functions into PowerShell:

function Convert-DhcpLeasesToReservations
{
    param (
        [Parameter(Mandatory=$true)]
        [string]$ScopeId,

        [Parameter()]
        [switch]$WhatIf
    )

    $leases = Get-DhcpServerv4Lease -ComputerName localhost -ScopeId $ScopeId

    foreach ($lease in $leases)
    {
        $reservation = New-DhcpServerv4Reservation -IPAddress $lease.IPAddress -ClientId $lease.ClientId -ScopeId $lease.ScopeId -Description "Converted from DHCP lease"
        if (!$WhatIf) {
            Add-DhcpServerv4Reservation -ComputerName localhost -Reservation $reservation
        }
    }

    Write-Host "All DHCP leases within scope $ScopeId have been converted to reservations"
}

function Convert-DhcpLeasesToReservationsByFilter
{
    param (
        [Parameter(Mandatory=$true)]
        [string]$Filter,

        [Parameter()]
        [switch]$WhatIf
    )

    $scopes = Get-DhcpServerv4Scope -ComputerName localhost | Where-Object { $_.Name -like $Filter }

    foreach ($scope in $scopes)
    {
        Convert-DhcpLeasesToReservations -ScopeId $scope.ScopeId -WhatIf:$WhatIf
    }
}

Step 3: Run the Functions

To use these functions, you need to run Convert-DhcpLeasesToReservationsByFilter and specify the filter to select the DHCP scopes you want to convert DHCP leases to reservations. For instance, you can run:

Convert-DhcpLeasesToReservationsByFilter -Filter "192.168.*"

This command will convert all DHCP leases within the scopes that match the filter “192.168.*” to reservations.

You can also use the -WhatIf parameter to simulate the execution of the function without making any changes. This helps you to see what the function will do before actually running it. For instance, you can run:

Convert-DhcpLeasesToReservationsByFilter -Filter "192.168.*" -WhatIf

This command will display the details of the actions the function will perform, but it will not execute them.

Conclusion:

Using PowerShell to convert DHCP leases to reservations can save you time and effort. The functions provided in this blog post simplify the process, allowing you to automate the conversion of DHCP leases to reservations in a few simple steps. By following the steps outlined above, you can easily and quickly convert DHCP leases to reservations on your Windows DHCP server.

PowerShell Script to Audit User Permissions and Identify Users with High Permission

Managing permissions and ensuring proper access control is critical to maintain the security of your organization’s resources. In this blog post, we introduce a PowerShell script that can audit user permissions on file shares and directories, calculate a permission score for each user, and help you identify users with the most permissive rights.

Script

The PowerShell script consists of the following main parts:

  1. A hashtable that defines important rights and their corresponding scores.
  2. A list of excluded users and shares that should not be considered in the audit process.
  3. A function called Get-ImportantDirectoryACLs that retrieves the Access Control List (ACL) for a given directory, filters out excluded users, and calculates a permission score for each user.
  4. A main script block that connects to a target server, retrieves the file shares and directories, and invokes the Get-ImportantDirectoryACLs function.

The script calculates the permission scores for each user based on the importance of their rights. In this example, we assign a score of 100 to the ‘TakeOwnership’ right, 90 to the ‘FullControl’ right, and lower scores to other important rights. The resulting CSV file will display the users with the highest scores at the top, making it easy to identify users with the most permissive rights.

$ImportantRights = @{
    'FullControl' = 100
    'Modify' = 80
    'ReadAndExecute' = 60
    'Write' = 40
    'CreateFiles' = 20
    'CreateDirectories' = 20
    'Delete' = 10
    'TakeOwnership' = 100
}
$ExcludedUsers = @('NT AUTHORITY\SYSTEM', 'BUILTIN\Administrators', 'NT SERVICE\TrustedInstaller', 'BUILTIN\Users', 'APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES')
$ExcludedShares = @("IPC$", "ADMIN$")

function Get-ImportantDirectoryACLs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string]$DirectoryPath,
        [Parameter(Mandatory)]
        [string[]]$ExcludedUsers
    )

    process {
        $ACL = Get-Acl $DirectoryPath
        Foreach ($AccessRule in $ACL.Access) {
            If (!$AccessRule.IsInherited) {
                $User = $($AccessRule.IdentityReference)
                if ($User -notin $ExcludedUsers) {
                    $TotalScore = 0
                    $Rights = @($AccessRule.FileSystemRights -split ",") | Where-Object { $ImportantRights.ContainsKey($_) }
                    Foreach ($Right in $Rights) {
                        $TotalScore += $ImportantRights[$Right]
                    }
                    if ($TotalScore -gt 0) {
                        [pscustomobject]@{
                            DirectoryPath = $DirectoryPath
                            IdentityReference = $AccessRule.IdentityReference
                            AccessControlType = $AccessRule.AccessControlType
                            FileSystemRights = $AccessRule.FileSystemRights
                            Score = $TotalScore
                        }
                    }
                }
            }
        }
    }
}

$Server = "ServerName"

$Cim = New-CimSession -ComputerName $Server
if ($Cim) {
    $Shares = Get-SmbShare -CimSession $Cim | Where-Object { $ExcludedShares -notcontains $_.Name } | ForEach-Object {
        "\\$($_.PSComputerName)\$($_.Name)"
    }
    $Directories = $Shares | ForEach-Object { Get-ChildItem $_ -Directory } | Select-Object -ExpandProperty FullName

    $ShareRights = $Shares | Get-ImportantDirectoryACLs -ExcludedUsers $ExcludedUsers
    $DirectoryRights = $Directories | Get-ImportantDirectoryACLs -ExcludedUsers $ExcludedUsers

    $Results = @($ShareRights) + @($DirectoryRights)

    # Sort results by Score in descending order
    $SortedResults = $Results | Sort-Object -Property Score -Descending

    # Export the sorted results to a CSV file
    $SortedResults | Export-Csv -Path "$Server.csv" -NoTypeInformation
}

How to Use It

  1. Copy the script to your PowerShell environment and save it as a .ps1 file.
  2. Replace “ServerName” in the $Server variable with the name of the server you want to audit.
  3. Run the script. It will generate a CSV file named ServerName.csv that contains the audit results.
  4. Review the CSV file to identify users with the highest permission scores.

Conclusion

Using PowerShell to audit user permissions and identify users with the most permissive rights is an efficient and effective way to manage access control in your organization. By assigning permission scores to important rights, you can quickly identify the users that may require further investigation. Remember to adapt the script to your environment and adjust the scores as needed to suit your organization’s security requirements.

Getting ElasticSearch Node Versions

Kibana doesn’t tell you what version your nodes are currently running which can be frustrating if you get distracted during an upgrade process. Here is a simple PowerShell script that gets all the nodes and their current version.

$cred = Get-Credential
$nodes_raw = Invoke-RestMethod -Method Get -Uri "http://elasticsearch:9200/_nodes" -Credential $cred

#get the names of each node
$node_names = $nodes_raw.nodes | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name

#convert from object properties to an array of objects
$nodes = foreach ($node_name in $node_names) {
    $nodes_raw.nodes."$node_name"   
}

# Select desired info about the node
$nodes | select name, version

Grafana Elasticsearch Moving Averages

If you’re working with Elasticsearch data in Grafana, you may want to add moving averages to your graphs to help visualize trends over time. However, the process for doing so is not always well-documented. Fortunately, it’s actually quite simple.

First, add a new metric to your graph. From there, select “MovingFunction” and choose the metric you want to average. Then, expand the options and set the window to the number of samples you want your moving function to be based on.

Next, you’ll need to provide a script for the moving function to use. Depending on your needs, you can choose from several different options, including:

  • MovingFunctions.max(values)
  • MovingFunctions.min(values)
  • MovingFunctions.sum(values)
  • MovingFunctions.unweightedAvg(values)
  • MovingFunctions.linearWeightedAvg(values)

Once you’ve selected the appropriate script, you should be able to see your moving average data displayed on your graph.

While adding moving averages to Elasticsearch data in Grafana can be a helpful way to visualize trends, it can also be frustrating if you’re not sure how to do it. By following these simple steps, you’ll be able to quickly and easily add moving averages to your graphs and gain deeper insights into your data.

References:
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-pipeline-movfn-aggregation.html#:~:text=The%20Moving%20Function%20aggregation%20allows,script%20in%20the%20values%20variable.

Use PowerShell to export AD User accounts to a CSV with their properties flattened

This queries for specific users (modify this to match your need)

Taking a list of users Flatten-Object will go through their properties and join multivalued attributes with a semi-colon so that you can export it in a useful way to CSV.

# Change this to and LDAP filter that fits your needs
$admins = Get-ADUser -filter "employeeType -eq 'Administrator Acct'" -Properties *

# take multivalued properties and join them with ;
function Flatten-Object {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        $object
    )
    process {
        $flattened_object = [pscustomobject]@{}

        foreach ($field in $object | get-member) {
            if ($field.MemberType -eq “Property” -and $field.Name -notlike “__*”) {
                $flattened_object | Add-Member -NotePropertyName $field.Name -NotePropertyValue $($object.$($field.Name) -join ";")
            }
        }
        $flattened_object
    }
}

$admins | Flatten-Object | Export-Csv -Path "user.csv" -NoTypeInformation

Prettify/Format LDAP Queries using Powershell

When working with LDAP queries in PowerShell, it can be challenging to keep track of all the parentheses and other syntax that’s required. This is especially true for complex queries that include multiple filters and logical operators. Fortunately, there’s a simple PowerShell script that can help make these queries more readable and easier to work with.

Here’s the script:

$ldap = "(&(&(objectCategory=person)(objectClass=user))(lastLogonTimestamp<=128752108510000000)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"

$ldap = $ldap -replace '\(([\&\|\!]+)', "(`$1`n"
$ldap = $ldap.replace(')', ")`n")

$lines = $ldap.split("`n")
$indent = 0
$new_lines = ForEach ($line in $lines) {
    if  ($line -eq ")") {
        $indent--
    }

    ("`t" * $indent) + $line

    if ($line -eq "(&" -or $line -eq "(|" -or $line -eq "(!") {
        $indent++
    }
}

$new_lines

What this script does is take an LDAP query string, such as the one shown above, and format it so that each filter and operator is on its own line with appropriate indentation. This makes it much easier to read and understand the query, especially if it’s a complex one.

To use the script, simply replace the query string in the first line with your own LDAP query. Then, run the script and the formatted query will be output to the console.

By using this script to format your LDAP queries, you’ll save time and reduce errors when working with complex filters and logical operators. Plus, it’s a great way to make your PowerShell scripts more readable and maintainable.

Results in a nicely formatted query:

(&
	(&
		(objectCategory=person)
		(objectClass=user)
	)
	(lastLogonTimestamp<=128752108510000000)
	(!
		(userAccountControl:1.2.840.113556.1.4.803:=2)
	)
)

Dynamically Getting AD User Properties

Sometimes you want to return properties that match a specific pattern. The following will do that for you:

Get-ADUser Administrator -Properties * | %{Get-Aduser $_.sAMAccountName -Properties @($_ | Get-Member -MemberType Property | Select -ExpandProperty Name | Where {$_ -like "ms*"})}

This gets a user with all properties, then filters those properties by the final like statement and re-gets the user with just those properties. You can also do it with a single get-aduser:

Get-ADUser Administrator -Properties * | %{$_ | Select -Property @($_ | Get-Member -MemberType Property | Select -ExpandProperty Name | Where {$_ -like "ms*"})}

The only downside to this is it doesn’t include the default attributes, so it can be a bit less usefull. If you want to include some specific attributes + all matching a like statement then you can do the following:

Get-ADUser Administrator -Properties * | %{$_ | Select -Property @(@("Name","samACcountName", "Department", "Description", "DisplayName", "DistinguishedName", "employeeType") + ($_ | Get-Member -MemberType Property | Select -ExpandProperty Name | Where {$_ -like "ms*"}))}

And finaly as a code block instead of a one liner:

$user = Get-ADUser Administrator -Properties * 
$msProperties = $user | Get-Member -MemberType Property | Select -ExpandProperty Name | Where {$_ -like "ms*"}
$user | Select -Property @(@("Department", "Description", "DisplayName", "DistinguishedName", "employeeType") + $msProperties)