#Parse XML "<LinksTo>\<SOMPath>" looking for $null entries. Output list.
#Can take a few minutes to run
$unlinkedGPOs = New-Object System.Collections.Generic.List[System.Object]
$GPOs = Get-GPO -All
ForEach ($gpo in $GPOs)
{
[xml]$gpoxml = Get-GPOReport -guid $gpo.Id -ReportType xml
if(($gpoxml).GPO.LinksTo.SOMPath -eq $null)
{
$unlinkedGPOs.add($gpoxml.gpo.name)
}
}
$unlinkedGPOs | out-gridview
Tuesday, July 2, 2019
Monday, July 1, 2019
Azure PowerShell Script - Audit Recovery Plans for Missing Protected Items
Azure doesn't have a great way at the moment to report on items in a Recovery Vault that are Replicated Items but not yet assigned to a Recovery Plan. That's what this script does - list out all items in a vault and what plan (if there is one) that they're assigned to.
This is a bit complicated because the GUIDs assigned to the protected item aren't necessarily the same as what was assigned as the "group protected item" ID.
In this scenarios, we'll
Part 1
######################################################
Connect-AzAccount
$ResourceGroup = "RGNAME"
$VaultName = "VAULTNAME"
$configServer = "CONFIGSERVERNAME"
# Get the ASR vault
$vault = Get-AzRecoveryServicesVault -ResourceGroupName $ResourceGroup -Name $VaultName
# Set the context of the vault. This is required for all future commands
Set-AzRecoveryServicesAsrVaultContext -Vault $vault
# Fabric is essentially a configuration server
# FriendlyName is the name of the config server. You can just run Get-AzRecoveryServicesAsrFabric to list all the config servers to get the right name
$asrfabric = Get-AzRecoveryServicesAsrFabric -FriendlyName $configServer
# Get the fabric container. It holds the replication policies and type of replication
$asrcontainer = Get-AzRecoveryServicesAsrProtectionContainer -Fabric $asrfabric
# List all the items that are in a protected state by friendly name and resource ID
$ProtectedVMs = Get-AzRecoveryServicesAsrProtectableItem -ProtectionContainer $asrcontainer | ? { $_.ProtectionStatus -eq 'Protected' } | Select FriendlyName,ReplicationProtectedItemId | sort FriendlyName
# List all recovery plans
$RecoveryPlans = Get-AzRecoveryServicesAsrRecoveryPlan | select -expand name
# Array to house the protected items missing recovery plans
$missingRP = New-Object Collections.ArrayList
# Array to house the protected items missing recovery plans
$ObjectArray = New-Object System.Collections.Generic.List[System.Object]
foreach($recoveryplan in $recoveryplans)
{
$plandetails = Get-AzRecoveryServicesAsrRecoveryPlan -Name $recoveryplan
# This will list out the replicated items that are in the recovery plan by resource ID. This may need to be broken out into multiple foreach loops b/c I only tested with a single VM in the recovery plan.
foreach($group in $plandetails.Groups)
{
foreach ($groupprotecteditem in $group.ReplicationProtectedItems)
{
$VMmatch = Get-AzRecoveryServicesAsrProtectableItem -ProtectionContainer $asrcontainer | where { $_.ProtectionStatus -eq 'Protected' -and $_.ReplicationProtectedItemId -eq $groupprotecteditem.id} | Select -expand FriendlyName
$tempArray = New-Object System.Object
$tempArray | Add-Member -MemberType NoteProperty -Name "VMName" -Value $VMmatch
$tempArray | Add-Member -MemberType NoteProperty -Name "RecoveryPlan" -Value $recoveryplan
$tempArray | Add-Member -MemberType NoteProperty -Name "Group" -Value $group.name
$tempArray | Add-Member -MemberType NoteProperty -Name "ProtectedItem" -Value $groupprotecteditem
$tempArray | Add-Member -MemberType NoteProperty -Name "ID" -Value $groupprotecteditem.id
$ObjectArray.add($tempArray)
}
}
}
foreach($vm in $ProtectedVMs)
{
$status = $ObjectArray.VMname.contains($vm.FriendlyName)
if($status -eq $false)
{
$tempArray = New-Object System.Object
$tempArray | Add-Member -MemberType NoteProperty -Name "VMName" -Value $vm.FriendlyName
$ObjectArray.add($tempArray)
}
}
$ObjectArray | Out-GridView
######################################################
This is a bit complicated because the GUIDs assigned to the protected item aren't necessarily the same as what was assigned as the "group protected item" ID.
In this scenarios, we'll
Part 1
- Collect the vault, on-prem config server, and query the service fabric.
- Get all of the protected servers
- Get the recovery plans in the vault
- Loop through the plans, getting the groups
- Loop through the groups in the plan, dumping a complete list of protected items in the plan
- Add all protected items in the plan to an array with the recovery plan and group name
- Add protected items to the array that weren't found in a recovery plan. Leaving the plan and group blank
######################################################
Connect-AzAccount
$ResourceGroup = "RGNAME"
$VaultName = "VAULTNAME"
$configServer = "CONFIGSERVERNAME"
# Get the ASR vault
$vault = Get-AzRecoveryServicesVault -ResourceGroupName $ResourceGroup -Name $VaultName
# Set the context of the vault. This is required for all future commands
Set-AzRecoveryServicesAsrVaultContext -Vault $vault
# Fabric is essentially a configuration server
# FriendlyName is the name of the config server. You can just run Get-AzRecoveryServicesAsrFabric to list all the config servers to get the right name
$asrfabric = Get-AzRecoveryServicesAsrFabric -FriendlyName $configServer
# Get the fabric container. It holds the replication policies and type of replication
$asrcontainer = Get-AzRecoveryServicesAsrProtectionContainer -Fabric $asrfabric
# List all the items that are in a protected state by friendly name and resource ID
$ProtectedVMs = Get-AzRecoveryServicesAsrProtectableItem -ProtectionContainer $asrcontainer | ? { $_.ProtectionStatus -eq 'Protected' } | Select FriendlyName,ReplicationProtectedItemId | sort FriendlyName
# List all recovery plans
$RecoveryPlans = Get-AzRecoveryServicesAsrRecoveryPlan | select -expand name
# Array to house the protected items missing recovery plans
$missingRP = New-Object Collections.ArrayList
# Array to house the protected items missing recovery plans
$ObjectArray = New-Object System.Collections.Generic.List[System.Object]
foreach($recoveryplan in $recoveryplans)
{
$plandetails = Get-AzRecoveryServicesAsrRecoveryPlan -Name $recoveryplan
# This will list out the replicated items that are in the recovery plan by resource ID. This may need to be broken out into multiple foreach loops b/c I only tested with a single VM in the recovery plan.
foreach($group in $plandetails.Groups)
{
foreach ($groupprotecteditem in $group.ReplicationProtectedItems)
{
$VMmatch = Get-AzRecoveryServicesAsrProtectableItem -ProtectionContainer $asrcontainer | where { $_.ProtectionStatus -eq 'Protected' -and $_.ReplicationProtectedItemId -eq $groupprotecteditem.id} | Select -expand FriendlyName
$tempArray = New-Object System.Object
$tempArray | Add-Member -MemberType NoteProperty -Name "VMName" -Value $VMmatch
$tempArray | Add-Member -MemberType NoteProperty -Name "RecoveryPlan" -Value $recoveryplan
$tempArray | Add-Member -MemberType NoteProperty -Name "Group" -Value $group.name
$tempArray | Add-Member -MemberType NoteProperty -Name "ProtectedItem" -Value $groupprotecteditem
$tempArray | Add-Member -MemberType NoteProperty -Name "ID" -Value $groupprotecteditem.id
$ObjectArray.add($tempArray)
}
}
}
foreach($vm in $ProtectedVMs)
{
$status = $ObjectArray.VMname.contains($vm.FriendlyName)
if($status -eq $false)
{
$tempArray = New-Object System.Object
$tempArray | Add-Member -MemberType NoteProperty -Name "VMName" -Value $vm.FriendlyName
$ObjectArray.add($tempArray)
}
}
$ObjectArray | Out-GridView
######################################################
Monday, April 10, 2017
SCCM Compliance Item Bitlocker Status
We recently implemented Health Attestation in SCCM 1610. That took care of reporting requirements for our Windows 10 clients. However, in order to completely eliminate MBAM from our environment we still needed to report on legacy clients. So, how to create a compliance item that queries for Bitlocker status;
**Side note: Some troubleshooting done for the Windows 10 portion of with Health Attestation.
https://social.technet.microsoft.com/Forums/en-US/359c1cb5-5bb0-42a2-9151-0e0b3d769bcd/missing-health-attestation-data-in-sccm?forum=ConfigMgrCompliance
The script;
$BitlockerStatus = Get-WmiObject -Namespace “root\CIMV2\Security\MicrosoftVolumeEncryption” -Class Win32_EncryptableVolume -ErrorAction Stop| ?{$_.DriveLetter -eq "C:"} | select EncryptionMethod,ProtectionStatus
#Status (0 = disabled, 1 = enabled)
#Method {0 = none, 1 = 128diffuser, 2 = 256 diffuser, 3 = 128(default), 4 = 256(desired)}
#Verify that Bitlocker is enabled and AES 256 is used
if($BitlockerStatus.ProtectionStatus -eq 1 -and $BitlockerStatus.EncryptionMethod -eq 4)
{write-host "Compliant"}
else
{write-host "Non-Compliant"}
#############################
The compliance item;
**Side note: Some troubleshooting done for the Windows 10 portion of with Health Attestation.
https://social.technet.microsoft.com/Forums/en-US/359c1cb5-5bb0-42a2-9151-0e0b3d769bcd/missing-health-attestation-data-in-sccm?forum=ConfigMgrCompliance
The script;
- Verify that bitlocker is enabled (=1) and encryption cipher method is 256 (=4)
- Return Compliant or Non-Compliant
$BitlockerStatus = Get-WmiObject -Namespace “root\CIMV2\Security\MicrosoftVolumeEncryption” -Class Win32_EncryptableVolume -ErrorAction Stop| ?{$_.DriveLetter -eq "C:"} | select EncryptionMethod,ProtectionStatus
#Status (0 = disabled, 1 = enabled)
#Method {0 = none, 1 = 128diffuser, 2 = 256 diffuser, 3 = 128(default), 4 = 256(desired)}
#Verify that Bitlocker is enabled and AES 256 is used
if($BitlockerStatus.ProtectionStatus -eq 1 -and $BitlockerStatus.EncryptionMethod -eq 4)
{write-host "Compliant"}
else
{write-host "Non-Compliant"}
#############################
The compliance item;
View the deployment status under monitoring
Viewing the individual client report
Friday, February 17, 2017
Find Expiring Certificates in Local Computer Personal Store
I originally wrote this as a monitor for SolarWinds. I'm posting it here as it could also be used to do a foreach against an OU, csv, etc. Basically, search through the computer personal certificate store and return the certs that expire in X days.
########################
Import-Module WebAdministration
$Certificates = dir Cert:\localmachine\my
$today = get-date
$expirationcounter = 0
foreach ($cert in $Certificates)
{
$thumbprint = $Cert.Thumbprint;
$certdetails = Get-ChildItem Cert:\LocalMachine\my\$thumbprint | Select NotAfter,Subject,Issuer;
if($certdetails.notafter -lt $today.AddDays(60))
{
$expiresin = $certdetails.NotAfter - $today
Write-Host "Statistic:" $expiresin.days
Write-Host 'Message: ' $certdetails.Subject
$expirationcounter++
}
}
if($expirationcounter -eq 0)
{
Write-Host 'Statistic: ' 0
Write-Host 'Message: No Certificates Found'
}
########################
########################
Import-Module WebAdministration
$Certificates = dir Cert:\localmachine\my
$today = get-date
$expirationcounter = 0
foreach ($cert in $Certificates)
{
$thumbprint = $Cert.Thumbprint;
$certdetails = Get-ChildItem Cert:\LocalMachine\my\$thumbprint | Select NotAfter,Subject,Issuer;
if($certdetails.notafter -lt $today.AddDays(60))
{
$expiresin = $certdetails.NotAfter - $today
Write-Host "Statistic:" $expiresin.days
Write-Host 'Message: ' $certdetails.Subject
$expirationcounter++
}
}
if($expirationcounter -eq 0)
{
Write-Host 'Statistic: ' 0
Write-Host 'Message: No Certificates Found'
}
########################
Wednesday, May 11, 2016
Find Orphaned Home Drives for Deleted AD Accounts
This script will find all home drives and for each test if the AD user still exists. If it doesn't, it will gather the folder size and output that with the user ID to a text file. Good one for general housekeeping.
-----------------------------
#Compare all H drive folders to AD user accounts.
#If no match is found output a file with the user name and folder size in MB.
#Add all folders found and output total at the end
$HomeDriveFolders = Get-ChildItem -path "H:\Users" | select -expandproperty Name
$totalsize = 0
foreach($folder in $HomeDriveFolders)
{
$user = ""
$user = $(try {get-aduser $folder | select -ExpandProperty SAMACCOUNTNAME} catch {$null})
if ($user -ne $folder)
{
$foldersize = (Get-Item "H:\Users\$folder").GetFiles() | Measure-Object -Sum Length
$foldersize = [math]::Round($foldersize.sum / 1MB)
$totalsize = $totalsize + $foldersize
"$folder,$foldersize" | out-file H:\usercomparisonexport.txt -append
}
}
"---------------------((TOTAL))",$totalsize | out-file H:\usercomparisonexport.txt -append
-----------------------------
#Compare all H drive folders to AD user accounts.
#If no match is found output a file with the user name and folder size in MB.
#Add all folders found and output total at the end
$HomeDriveFolders = Get-ChildItem -path "H:\Users" | select -expandproperty Name
$totalsize = 0
foreach($folder in $HomeDriveFolders)
{
$user = ""
$user = $(try {get-aduser $folder | select -ExpandProperty SAMACCOUNTNAME} catch {$null})
if ($user -ne $folder)
{
$foldersize = (Get-Item "H:\Users\$folder").GetFiles() | Measure-Object -Sum Length
$foldersize = [math]::Round($foldersize.sum / 1MB)
$totalsize = $totalsize + $foldersize
"$folder,$foldersize" | out-file H:\usercomparisonexport.txt -append
}
}
"---------------------((TOTAL))",$totalsize | out-file H:\usercomparisonexport.txt -append
Thursday, February 4, 2016
Disk Cleanup Missing Server 2008 and Later
There is plenty of info out there about how to go about getting to Disk Cleanup (cleanmgr.exe). This is just a quick little batch file to restore it. You do have the option of installing the Desktop Experience features through Server Manager. I personally don't like that option because of the junk that comes with it. Try this out instead...
////////////////////////
Copy and Paste into notepad, save as "AddDiskCleanup.bat"
xcopy "C:\Windows\winsxs\amd64_microsoft-windows-cleanmgr_31bf3856ad364e35_6.1.7600.16385_none_c9392808773cd7da\cleanmgr.exe" "%systemroot%\System32"
xcopy "C:\Windows\winsxs\amd64_microsoft-windows-cleanmgr.resources_31bf3856ad364e35_6.1.7600.16385_en-us_b9cb6194b257cc63\cleanmgr.exe.mui" "%systemroot%\System32\en-US"
cleanmgr.exe
///////////////////////
From this point on you can simply go to start > run > cleanmgr.exe
////////////////////////
Copy and Paste into notepad, save as "AddDiskCleanup.bat"
xcopy "C:\Windows\winsxs\amd64_microsoft-windows-cleanmgr_31bf3856ad364e35_6.1.7600.16385_none_c9392808773cd7da\cleanmgr.exe" "%systemroot%\System32"
xcopy "C:\Windows\winsxs\amd64_microsoft-windows-cleanmgr.resources_31bf3856ad364e35_6.1.7600.16385_en-us_b9cb6194b257cc63\cleanmgr.exe.mui" "%systemroot%\System32\en-US"
cleanmgr.exe
///////////////////////
From this point on you can simply go to start > run > cleanmgr.exe
Wednesday, November 4, 2015
Active Directory: Find All Users with Specific UPN Suffix
$ou = "OU=Users,DC=mydomain,DC=local"
Get-ADUser -filter * -SearchBase $ou | Where-Object {$_.userprincipalname -like "*domain.com"} | Export-Csv C:\temp\UPN.csv
Get-ADUser -filter * -SearchBase $ou | Where-Object {$_.userprincipalname -like "*domain.com"} | Export-Csv C:\temp\UPN.csv
Thursday, October 22, 2015
Active Directory: Bulk Update User UPNs
#Bulk Update UPNs
Import-Module ActiveDirectory
#SPECIFY NEW SUFFIX AND OU TO CHANGE
$newSuffix = '@domain.com'
$ou = "OU=Users,DC=mydomain,DC=local"
#EXECUTE CHANGES
Get-ADUser -Filter * -SearchBase $ou | ForEach-Object {
$newUpn = $_.SamAccountName + $newSuffix
Set-ADUser -ID $_ -UserPrincipalName $newUpn
}
Import-Module ActiveDirectory
#SPECIFY NEW SUFFIX AND OU TO CHANGE
$newSuffix = '@domain.com'
$ou = "OU=Users,DC=mydomain,DC=local"
#EXECUTE CHANGES
Get-ADUser -Filter * -SearchBase $ou | ForEach-Object {
$newUpn = $_.SamAccountName + $newSuffix
Set-ADUser -ID $_ -UserPrincipalName $newUpn
}
Wednesday, October 21, 2015
Find All Windows Servers Not in a Group in Active Directory
Example:
(&(objectCategory=computer)(operatingSystem=*Windows Server*)(!memberof:1.2.840.113556.1.4.1941:=CN=ServerHardening,OU=Groups,DC=mydomain,DC=local))
& = And the following conditions together
objectCategory = is it a user, computer, etc.
operatingSystem = what is found in the computer object operating system tab "name" field
! = Condition is "Not"
memberof = Find members of the group
1.2.840.113556.1.4.1941 = Tells the lookup to recurse the member groups of the super group
You must use the full distinguished name of the group in question.
Of course you can adjust this to specific OS versions or group names or even extend it to include additional references to more groups.
(&(objectCategory=computer)(operatingSystem=*Windows Server*)(!memberof:1.2.840.113556.1.4.1941:=CN=ServerHardening,OU=Groups,DC=mydomain,DC=local))
& = And the following conditions together
objectCategory = is it a user, computer, etc.
operatingSystem = what is found in the computer object operating system tab "name" field
! = Condition is "Not"
memberof = Find members of the group
1.2.840.113556.1.4.1941 = Tells the lookup to recurse the member groups of the super group
You must use the full distinguished name of the group in question.
Of course you can adjust this to specific OS versions or group names or even extend it to include additional references to more groups.
Find Inactive Systems to Clean UP Active Directory
Shows all computers that haven't contacted the domain in 8 weeks or more. You can also use this for user objects. Run in PowerShell with the "sort-object" to sort by the DN which starts with the name of the system so it helps if you have a computer naming convention.
dsquery computer -inactive 8 | sort-object
dsquery computer -inactive 8 | sort-object
Wednesday, September 9, 2015
Get Useful Mailbox Information from Exchange Shell
Just a quick one-liner for exporting usable info about all mailboxes. Be sure to adjust your domain controller (or leave it out if in a single domain environment).
get-mailbox -ResultSize unlimited -DomainController ADC1 | Select-Object DisplayName,PrimarySmtpAddress,ExchangeUserAccountControl,RecipientTypeDetails,ServerName,Database,ProhibitSendQuota,ProhibitSendReceiveQuota,UseDatabaseQuotaDefaults,IssueWarningQuota,MaxSendSize,MaxReceiveSize,DeliverToMailboxAndForward,HiddenFromAddressListsEnabled,WhenChanged | Export-CSV C:\mailboxes.csv
Returns some good info about mailbox size quotas, send/receive limits, mailbox type, forwarding and address book status.
get-mailbox -ResultSize unlimited -DomainController ADC1 | Select-Object DisplayName,PrimarySmtpAddress,ExchangeUserAccountControl,RecipientTypeDetails,ServerName,Database,ProhibitSendQuota,ProhibitSendReceiveQuota,UseDatabaseQuotaDefaults,IssueWarningQuota,MaxSendSize,MaxReceiveSize,DeliverToMailboxAndForward,HiddenFromAddressListsEnabled,WhenChanged | Export-CSV C:\mailboxes.csv
Returns some good info about mailbox size quotas, send/receive limits, mailbox type, forwarding and address book status.
Friday, June 19, 2015
Active Directory: Bulk Update Logon Script
Modification of my bulk update home drive script.
# CHANGE LOGON SCRIPT
Import-Module ActiveDirectory
Get-ADUser -Filter * -SearchBase "OU=Users,DC=contoso,DC=com" | Foreach-Object{
$sam = $_.SamAccountName
Set-ADuser -Identity $_ -ScriptPath "LOGON-NEW.bat"
}
# CHANGE LOGON SCRIPT
Import-Module ActiveDirectory
Get-ADUser -Filter * -SearchBase "OU=Users,DC=contoso,DC=com" | Foreach-Object{
$sam = $_.SamAccountName
Set-ADuser -Identity $_ -ScriptPath "LOGON-NEW.bat"
}
Active Directory: Bulk Update Home Folder Path
Can't take any credit for this one. Just happened to stumble on it in a forum post. Just putting it here for future reference. Added a couple of checks for good measure.
# CHANGE HOME DIRECTORY
$SearchOU="OU=Users,DC=contoso,DC=com"
Import-Module ActiveDirectory
#Search for all users in OU that are not disabled or with blank homedirectory
Get-ADUser -Filter * -SearchBase $SearchOU | where-object {$_.enabled -eq $true -AND $_.homedirectory -ne ""} | Foreach-Object
{
$sam = $_.SamAccountName
Set-ADuser -Identity $_ -HomeDrive "H:" -HomeDirectory \\SERVER02\Users\$sam
}
# CHANGE HOME DIRECTORY
$SearchOU="OU=Users,DC=contoso,DC=com"
Import-Module ActiveDirectory
#Search for all users in OU that are not disabled or with blank homedirectory
Get-ADUser -Filter * -SearchBase $SearchOU | where-object {$_.enabled -eq $true -AND $_.homedirectory -ne ""} | Foreach-Object
{
$sam = $_.SamAccountName
Set-ADuser -Identity $_ -HomeDrive "H:" -HomeDirectory \\SERVER02\Users\$sam
}
Subscribe to:
Posts (Atom)



