Tuesday, July 2, 2019

GPO PowerShell Script - Search for Group Policies with No Links

#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

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
  1. Collect the vault, on-prem config server, and query the service fabric.
  2. Get all of the protected servers
  3. Get the recovery plans in the vault 
Part 2
  1. Loop through the plans, getting the groups
  2. Loop through the groups in the plan, dumping a complete list of protected items in the plan 
  3. Add all protected items in the plan to an array with the recovery plan and group name
Part 3
  1. 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;

  1. Verify that bitlocker is enabled (=1) and encryption cipher method is 256 (=4) 
  2. 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'
}
########################

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

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

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

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
}

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.

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

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.

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"
}

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
}