Hosting a Static Site from pCloud Public Files (and Why I Moved Off It)

Hosting a Static Site from pCloud Public Files (and Why I Moved Off It) Before this blog lived on Cloudflare Pages, it ran out of a pCloud public folder with Cloudflare in front for the domain and TLS. It works, it’s free if you already pay for pCloud, and it’s a fun little hack. Here’s how to do it — plus the honest reasons I eventually bailed. The idea Every pCloud account has a public folder, and whatever’s in it is reachable over HTTPS at a filedn.com link tied to your account: ...

October 30, 2026

Host a Static Site for Free with Cloudflare Pages and GitHub

Host a Static Site for Free with Cloudflare Pages and GitHub This blog runs on exactly what I’m about to describe. If you’ve got a static site — Hugo, plain HTML, whatever — Cloudflare Pages will build it, serve it fast, and redeploy it every time you push to GitHub. Free, no server to babysit, free TLS, and preview URLs for branches. Here’s the whole thing. Why bother Free tier is plenty: unlimited sites and bandwidth, 500 builds a month. Push to deploy. Every git push rebuilds and ships it. You stop touching the dashboard after setup. Every branch gets its own preview URL, so you can look before you merge. Every deploy is kept, so rollback is one click. What you need Your site in a GitHub repo — private is fine. If your theme is a git submodule (normal with Hugo), just make sure it’s committed. Cloudflare clones submodules for you, so a theme on a public repo needs nothing extra. ...

October 30, 2026

Active Directory Group Monitoring

Active Directory Group Monitoring This script can be used to monitor certain AD groups for changes. It uses the AD RSAT tools to gather information and can email you a summary of what has changed. This is very useful when monitoring important or restricted groups for changes. The script is however limited to finding only changes at the time it checks (every 5 minutes). If a user were to be put in and taken out of a group within the 5 minutes between checks then it would not be logged. ...

June 22, 2025

Active Directory Reporting

Active Directory Reporting Here is a handy script that will help you collect information from Active Directory. It will dump this data to file and output to screen. I like to run this every month or so to gauge AD growth. #AD Reporting #This script requires: AD PowerShell tools from RSAT, read-only access to AD import-module ActiveDirectory $OutputPath = "$($env:userprofile)\Desktop\" $cdt = get-date #Get inital sets of data from AD $AD_All_Objects = Get-ADObject -Filter {name -like '*'} -SearchBase 'CN=Schema,CN=Configuration,DC=[mydomain],DC=[com/local]' -ResultSetSize $null $AD_Users = Get-ADUser -Filter * -Properties "msDS-UserPasswordExpiryTimeComputed", useraccountcontrol, PasswordNeverExpires, accountExpires $AD_Groups = Get-ADGroup -Filter * $AD_Computers = Get-ADComputer -Filter * -Properties OperatingSystem, PasswordLastSet, PasswordNeverExpires, accountExpires $AD_Contacts = Get-ADObject -LDAPFilter "objectClass=Contact" $AD_ManagedServiceAccounts = Get-ADServiceAccount -Filter * #handy for seeing all values by count and type # $AD_Groups | Group-Object GroupCategory # $AD_Groups | Group-Object GroupScope # $AD_Computers | Group-Object OperatingSystem #users $AD_Users_Enabled = $AD_Users | where {$_.Enabled -eq $true} $AD_Users_Disabled = $AD_Users | where {$_.Enabled -eq $false} $AD_Users_ExpiredPasswords = $AD_Users | where { if ($_."msDS-UserPasswordExpiryTimeComputed" -ne "9223372036854775807") { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed") -lt ($cdt)}} $AD_Users_NonExpiredPasswords = $AD_Users | where { if ($_."msDS-UserPasswordExpiryTimeComputed" -ne "9223372036854775807") { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed") -gt ($cdt)}} $AD_Users_NeverExpiredPasswords = $AD_Users | where { $_."msDS-UserPasswordExpiryTimeComputed" -eq "9223372036854775807" } #$AD_Users_NeverExpiredPasswords2 = $AD_Users | where { $_.PasswordNeverExpires -eq $true } #shows same data as above, just a different way of getting it $AD_Users_AccountNeverExpires = $AD_Computers | where { ($_.accountExpires -eq "9223372036854775807") -or ($_.accountExpires -eq "0") } $AD_Users_AccountExpired = $AD_Computers | where { if( ($_.accountExpires -ne "9223372036854775807") -and ($_.accountExpires -ne "0")) { [datetime]::FromFileTime($_.accountExpires) -lt ($cdt) } } #computers $AD_Computers_Servers = $AD_Computers | where {$_.OperatingSystem -Like "Windows Server*"} $AD_Computers_Workstation = $AD_Computers | where { ($_.OperatingSystem -NotLike "Windows Server*") -and ($_.OperatingSystem -Like "Windows*") } $AD_Computers_Other = $AD_Computers | where {$_.OperatingSystem -NotLike "Windows*"} $AD_Computers_Enabled = $AD_Computers | where {$_.Enabled -eq $true} $AD_Computers_Disabled = $AD_Computers | where {$_.Enabled -eq $false} $AD_Computers_ExpiredPasswords = $AD_Computers | where {$_.PasswordLastSet -le ($cdt).adddays(-90)} #$AD_Computers_ExpiredPasswords2 = $AD_Computers | where { $_.PasswordNeverExpires -eq $true } #shows same data as above, just a different way of getting it $AD_Computers_OKPasswords = $AD_Computers | where {$_.PasswordLastSet -ge ($cdt).adddays(-90)} $AD_Computers_NeverExpire = $AD_Computers | where { $_.accountExpires -eq "9223372036854775807" } $AD_Computers_Expire = $AD_Computers | where { $_.accountExpires -ne "9223372036854775807" } #groups $AD_Groups_Security = $AD_Groups | where {$_.GroupCategory -eq "Security"} $AD_Groups_Distribution = $AD_Groups | where {$_.GroupCategory -eq "Distribution"} $AD_Groups_Universal = $AD_Groups | where {$_.GroupScope -eq "Universal"} $AD_Groups_DomainLocal = $AD_Groups | where {$_.GroupScope -eq "DomainLocal"} $AD_Groups_Global = $AD_Groups | where {$_.GroupScope -eq "Global"} #security stuff $AD_Sec_UAC65536 = Get-ADUser -Filter 'useraccountcontrol -band 65536' -Properties useraccountcontrol $AD_Sec_UAC32 = Get-ADUser -Filter 'useraccountcontrol -band 32' -Properties useraccountcontrol $AD_Sec_UAC128 = Get-ADUser -Filter 'useraccountcontrol -band 128' -Properties useraccountcontrol $AD_Sec_UAC524288 = Get-ADUser -Filter 'useraccountcontrol -band 524288' -Properties useraccountcontrol $AD_Sec_UAC419304 = Get-ADUser -Filter 'useraccountcontrol -band 4194304' -Properties useraccountcontrol $AD_Sec_UAC2097152 = Get-ADUser -Filter 'useraccountcontrol -band 2097152' -Properties useraccountcontrol #OU listing $info = ([adsisearcher]"objectclass=organizationalunit") $info.PropertiesToLoad.AddRange("CanonicalName") $OUs = $info.findall().properties.canonicalname | sort $SB_Report = { "AD Users, Groups, and Computers Total Counts" " Reporting Date: $($cdt)" " " "Overall Counts" " Objects: $($AD_All_Objects.count)" " Users: $($AD_Users.count)" " Groups: $($AD_Groups.count)" " Computers: $($AD_Computers.count)" " Contacts: $($AD_Contacts.count)" " Managed Service Accounts: $($AD_ManagedServiceAccounts.count)" " Organizational Units: $($OUs.count)" " " "Users" " Enabled: $($AD_Users_Enabled.count)" " Disabled: $($AD_Users_Disabled.count)" " Password Not Expired: $($AD_Users_NonExpiredPasswords.count)" " Password Expired: $($AD_Users_ExpiredPasswords.count)" " Password Never Expire: $($AD_Users_NeverExpiredPasswords.count)" #" Password Never Expire2: $($AD_Users_NeverExpiredPasswords2.count)" " Expired: $($AD_Users_AccountExpired.count)" " Never Expires: $($AD_Computers_NeverExpire.count)" " " "Computers" " Server OS: $($AD_Computers_Servers.count)" " Workstation OS: $($AD_Computers_Workstation.count)" " Other OS: $($AD_Computers_Other.count)" " Enabled: $($AD_Computers_Enabled.count)" " Disabled: $($AD_Computers_Disabled.count)" " Password Expired: $($AD_Computers_ExpiredPasswords.count)" " Password Expired2: $($AD_Computers_ExpiredPasswords.count)" #" Password OK: $($AD_Computers_ExpiredPasswords2.count)" " Expired: $($AD_Computers_Expire.count)" " Never Expires: $($AD_Computers_NeverExpire.count)" " " "Groups by Category" " Security: $($AD_Groups_Security.count)" " Distribution: $($AD_Groups_Distribution.count)" "Groups by Scope" " Universal: $($AD_Groups_Universal.count)" " DomainLocal: $($AD_Groups_DomainLocal.count)" " Global: $($AD_Groups_Global.count)" " " "AD Security" " Check for accounts that don't have password expiry set (UAC65536): $($AD_Sec_UAC65536.Count)" " Check for accounts that have no password requirement (UAC32): $($AD_Sec_UAC32.Count)" " Accounts that have the password stored in a reversibly encrypted format (UAC128): $($AD_Sec_UAC128.Count)" " List users that are trusted for Kerberos delegation (UAC524288): $($AD_Sec_UAC524288.Count)" " List accounts that don't require pre-authentication (UAC419304): $($AD_Sec_UAC419304.Count)" " List accounts that have credentials encrypted with DES (UAC2097152): $($AD_Sec_UAC2097152.Count)" } $SB_OutputData = { $FileExtension = ".CSV" $FileOutputFolder = $OutputPath + "ADDS_Stats_" + (get-date -UFormat "%Y-%m-%d_%H-%M-%S") + "\" if (-not (test-path $FileOutputFolder)) { New-Item -Path $FileOutputFolder -ItemType directory | out-null } Invoke-Command $SB_Report | out-file -filepath ($FileOutputFolder + "Summary" + ".txt") $AD_Users_Enabled | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Users_Enabled" + $FileExtension) $AD_Users_Disabled | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Users_Disabled" + $FileExtension) $AD_Users_ExpiredPasswords | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Users_ExpiredPasswords" + $FileExtension) $AD_Users_NeverExpiredPasswords | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Users_NeverExpiredPasswords" + $FileExtension) $AD_Users_AccountNeverExpires | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Users_AccountNeverExpires" + $FileExtension) $AD_Users_AccountExpired | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Users_AccountExpired" + $FileExtension) $AD_Computers_Servers | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_Servers" + $FileExtension) $AD_Computers_Workstation | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_Workstation" + $FileExtension) $AD_Computers_Other | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_Other" + $FileExtension) $AD_Computers_Enabled | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_Enabled" + $FileExtension) $AD_Computers_Disabled | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_Disabled" + $FileExtension) $AD_Computers_ExpiredPasswords | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_ExpiredPasswords" + $FileExtension) $AD_Computers_OKPasswords | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_OKPasswords" + $FileExtension) $AD_Computers_NeverExpire | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_NeverExpire" + $FileExtension) $AD_Computers_Expire | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Computers_Expire" + $FileExtension) $AD_Groups_Security | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Groups_Security" + $FileExtension) $AD_Groups_Distribution | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Groups_Distribution" + $FileExtension) $AD_Groups_Universal | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Groups_Universal" + $FileExtension) $AD_Groups_DomainLocal | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Groups_DomainLocal" + $FileExtension) $AD_Groups_Global | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Groups_Global" + $FileExtension) $AD_Sec_UAC65536 | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Sec_UAC65536" + $FileExtension) $AD_Sec_UAC32 | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Sec_UAC32" + $FileExtension) $AD_Sec_UAC128 | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Sec_UAC128" + $FileExtension) $AD_Sec_UAC524288 | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Sec_UAC524288" + $FileExtension) $AD_Sec_UAC419304 | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Sec_UAC419304" + $FileExtension) $AD_Sec_UAC2097152 | Export-CSV -notypeinformation -Path ($FileOutputFolder + "AD_Sec_UAC2097152" + $FileExtension) $OUs | Out-File -FilePath ($FileOutputFolder + "AD_OUS" + ".txt") } Invoke-Command $SB_Report Invoke-Command $SB_OutputData

June 22, 2025

Bulk Update AD UPNs

Bulk Update AD UPNs You may have a need to update UPNs (user principal names) for your AD users so they can be more easily used with Microsoft Office 365. The below script can be used to generate a list of accounts that need to be update and the commands to do them. The script also generates an ‘undo’ command so you can easily go back. This is handy for testing on a few users at a time versus doing all at once. ...

June 22, 2025