Category: Uncategorized

SQL Server License Retrieval

# Confirmed working for Windows versions of SQL Server 2016-2019
function GetSqlServerProductKey($InstanceName="MSSQL13.MSSQLSERVER") {
    $localmachine = [Microsoft.Win32.RegistryHive]::LocalMachine
    $defaultview = [Microsoft.Win32.RegistryView]::Default
    $reg = [Microsoft.Win32.RegistryKey]::OpenBaseKey($localmachine, $defaultview)
    $key = "SOFTWARE\Microsoft\Microsoft SQL Server\$InstanceName\Setup"
    $encodedData = $reg.OpenSubKey($key).GetValue("DigitalProductID")
    $reg.Close()
 
    try {
        $binArray = ($encodedData)[0..66]
        $productKey = $null
 
        $charsArray = "B", "C", "D", "F", "G", "H", "J", "K", "M", "P", "Q", "R", "T", "V", "W", "X", "Y", "2", "3", "4", "6", "7", "8", "9"
 
        $isNKey = ([math]::truncate($binArray[14] / 0x6) -band 0x1) -ne 0;
        if ($isNKey) {
        $binArray[14] = $binArray[14] -band 0xF7
        }
 
        $last = 0
 
        for ($i = 24; $i -ge 0; $i--) {
            $k = 0
            for ($j = 14; $j -ge 0; $j--) {
                $k = $k * 256 -bxor $binArray[$j]
                $binArray[$j] = [math]::truncate($k / 24)
                $k = $k % 24
            }
            $productKey = $charsArray[$k] + $productKey
            $last = $k
        }
 
        if ($isNKey) {
            $part1 = $productKey.Substring(1, $last)
            $part2 = $productKey.Substring(1, $productKey.Length-1)
            if ($last -eq 0) {
                $productKey = "N" + $part2
            }
            else {
                $productKey = $part2.Insert($part2.IndexOf($part1) + $part1.Length, "N")
            }
        }
 
        $productKey = $productKey.Insert(20, "-").Insert(15, "-").Insert(10, "-").Insert(5, "-")
    } 
    catch {
        $productkey = "Cannot decode product key."
    }
 
    $productKey
}

Change the instance name to what is displayed in the registry

Then run the below command in powershell, after execting the function creation script above ofcourse.

GetSqlServerProductKey($InstanceName="MSSQL13.MSSQLSERVER")

Adapt the instance name to what is required.

Exchange autodiscover URL

After certain updates, it seems autodiscover url can get removed from the existing config, the below will re-configure it. Commands are to be used in the Exchange Management Shell

Set-ClientAccessService -Identity "HOSTNAME" -AutoDiscoverServiceInternalUri https://mail.XX.com/Autodiscover/Autodiscover.xml

This will list the current autodiscover url

Get-clientAccessServer | fl Name,AutoDiscoverServiceInternalUri

This will list all exchange services url’s

$virtdirs = "ECP,OWA,OAB,WebServices,Activesync,autodiscover,mapi"
$array = $virtdirs.split(",")
foreach ($i in $array) {
$j = "Get-"+$i+"VirtualDirectory"+" | fl name,server,internalurl,externalurl"
iex $j
}

List user logon sessions on all servers in AD domain

# Define the OU path where your servers are located
$ouPath = "OU=servers,DC=domain,DC=local"  # Replace with your OU path

# Get a list of servers in the specified OU
$servers = Get-ADComputer -Filter {OperatingSystem -like "Windows Server*"} -SearchBase $ouPath | Select-Object -ExpandProperty Name

# Loop through each server and query logged-on users
foreach ($server in $servers) {
    try {
        # Use WMI to query logged-on user information
        $loggedOnUsers = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $server | Select-Object -ExpandProperty UserName

        if ($loggedOnUsers) {
            Write-Host "Logged-on users on $server"
            $loggedOnUsers | ForEach-Object {
                Write-Host "  $_"
            }
        } else {
            Write-Host "No users logged on to $server"
        }
    } catch {
        Write-Host "Failed to query $server"
    }
}

If you want to search by specific names, you can adjust the filter to this –

Get-ADComputer -Filter {Name -like "AZ-EUW-*"} -SearchBase $ouPath

Remotely change all DNS IP’s on Windows Servers in AD

# Define the new DNS server IP addresses
$dnsServers = "8.8.8.8", "1.1.1.1", "8.8.4.4"

# Define the OU where you want to change DNS servers
$OU = "OU=server,DC=domain,DC=local"  # Replace with your actual OU path

# Create an array to store results
$results = @()

# Get all computer objects in the specified OU
$computers = Get-ADComputer -Filter {Name -like "AZ-EIW-DC01*"} -SearchBase $OU

# Loop through each computer object
foreach ($computer in $computers) {
    $computerName = $computer.Name
    $computerDN = $computer.DistinguishedName

    try {
        # Set the DNS server IP addresses on the remote computer
        Invoke-Command -ComputerName $computerName -ScriptBlock {
            $networkAdapter = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' }
            Set-DnsClientServerAddress -InterfaceAlias $networkAdapter.Name -ServerAddresses $using:dnsServers -ErrorAction Stop
        }

        # Record the success in the results array
        $results += [PSCustomObject]@{
            ComputerName = $computerName
            Status = "Success"
        }
    }
    catch {
        # If an error occurs, record the failure in the results array
        $results += [PSCustomObject]@{
            ComputerName = $computerName
            Status = "Error: $_"
        }
    }
}

# Export the results to a CSV file
$results | Export-Csv -Path "C:\Temp\DNSChangeResults.csv" -NoTypeInformation

# Display the results on the console
$results

Find memory in use by sql databases

Run this query in SSMS to show how much memory the DB’s are currently using.

DECLARE @total_buffer INT;

SELECT @total_buffer = cntr_value
FROM sys.dm_os_performance_counters 
WHERE RTRIM([object_name]) LIKE '%Buffer Manager'
AND counter_name = 'Database Pages';

;WITH src AS
(
SELECT 
database_id, db_buffer_pages = COUNT_BIG(*)
FROM sys.dm_os_buffer_descriptors
--WHERE database_id BETWEEN 5 AND 32766
GROUP BY database_id
)
SELECT
[db_name] = CASE [database_id] WHEN 32767 
THEN 'Resource DB' 
ELSE DB_NAME([database_id]) END,
db_buffer_pages,
db_buffer_MB = db_buffer_pages / 128,
db_buffer_percent = CONVERT(DECIMAL(6,3), 
db_buffer_pages * 100.0 / @total_buffer)
FROM src
ORDER BY db_buffer_MB DESC;

NPS Smartcard/Certificate Authentication

HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel\

value: CertificateMappingMethods
Data Type: DWORD
Data: 0x1F

If you are facing issues on a newly deployed NPS or even an existing one, whilst using a certificate method to authenticate your users/devices, you might find that the above registry key will resolve the issue.

There has also been reports about the below updates causing issues with NPS on Server 2019 –

KB5013641

KB5013941

KB5014754

Azure Resource Inventory Export

If you do not have any CMDB actively scanning you Azure environment, things could get messy if you’ve had several rushed deployments into your Azure infrastructure. I recently got to know about a script by a colleague, that can export every single resource in your subscription and display it in a very organized excel sheet.

Example

I’ve uploaded the file here in TXT format, download it and rename to .ps1 ; You might need to have the below modules installed on your PC and one extension –

Azure AD
Azure CLI

Once those are installed, add the resource graph extension with this command -|

az extension add --name resource-graph

Now run the script, it will take you to the login page of Microsoft, use an admin account with permissions to read the tenant.

Some users reported having to run the script with the trigger -Online as the excel was not being generated. I have also verified this bug occurs and using the -Online resolves it. A second bug is that it will show 0 resources in the excel. This is something on your PC and you must re-install the powershell modules and extension.

Credits for this script go to https://github.com/microsoft/ARI

Check password expiry Office365

$cred = Get-Credential “[email protected]

Connect-MsolService -Credential $cred

$domain = Get-MsolDomain | where {$_.IsDefault -eq $true}

$PasswordPolicy = Get-MsolPasswordPolicy -DomainName $domain.Name

$Account = “[email protected]” # Change HERE !!

$UserPrincipal = Get-MsolUser -UserPrincipalName $Account

$UserPrincipal | fl PasswordNeverExpires

$PasswordExpirationDate = $UserPrincipal.LastPasswordChangeTimestamp.AddDays($PasswordPolicy.ValidityPeriod)

Write-host “Password will Expire on : $PasswordExpirationDate”

$StartDate = (GET-DATE)
$DaysLeft = NEW-TIMESPAN -Start $StartDate -End $PasswordExpirationDate
$DaysLeft = [math]::Floor($DaysLeft.TotalDays)

Write-host “Password will Expire in # Days : $DaysLeft”

$UserPrincipal | select DisplayName, LastPasswordChangeTimeStamp,@{Name=”PasswordAge”;Expression={((Get-Date).ToUniversalTime())-$_.LastPasswordChangeTimeStamp}} | sort-object PasswordAge -desc