Random Password Generator

Define the password length and you’ll get a secure, randomly generated password.

1
2
$pwdLength = 15
$rngPWD = ([char[]]([char]33..[char]95) + ([char[]]([char]97..[char]126)) + 0..9 | sort {Get-Random})[0..$pwdLength] -join ''

If you need to use this for Active Directory user creation, you can convert the string to a securestring and use it like so:

1
2
$regPWDsec =  ConvertTo-SecureString $rngPWD -AsPlainText -Force
New-ADUser -Server 'blah.blah' -Path "OU=Blah,DC=blah,DC=blah" -AccountPassword $rngPWDsec -<all other defined parameters>

Add or Remove a single forest user to a group in all child domains

1
2
3
4
5
6
7
8
9
$user = Get-ADUser 'myusername' -Server 'forestdomain.com'
#==============================================================
$domains = Get-ADForest -Server 'rootdomain.com'
ForEach ($domain in $domains.Domains) {    
    $Group = Get-ADGroup -Identity 'Account Operators' -Server $domain
    # Uncomment the command below needed to add or remove
    #Add-ADGroupMember -Identity $Group -Member $user -Server $domain
    #Remove-ADGroupMember -Identity $Group -Member $user -Server $domain
}

Get-ADDomainController returns error “Directory Object not found” when using -Filter *

We recently encountered an error when using Get-ADDomainController for a domain which had a partially decommissioned domain controller (partial demotion). When running Get-ADDomainController with the * filter, it would return “Directory Object not found” and provide no other details.
Using the script below, we were able to loop through polling Active Directory for each domain controller in that domain until it failed on the faulty DC providing our first clue to the offending server.

1
(Get-ADDomain -Identity 'childdomain.domain.com').ReplicaDirectoryServers | ForEach {Get-ADDomainController -Identity $_ -Server 'childdomain.domain.com' }

Turns out there were some replication issues, visible only on the faulty DC, resulting from the partial DC demotion, requiring a metadata cleanup.

Logon Script to automatically log off users after a specified timeframe with a warning beforehand

Using this script as a PowerShell Logon Script (via GPO / GPEDIT), you can force a logoff at after a specified timeframe and give a warning to the user X minutes beforehand. You’ll want to force hide the script window using ‘-windowstyle hidden’, otherwise they could just kill it and never get logged off.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Logon Script that will auto-logoff a user after a specified amount of time has passed,
# and will give a warning of the impending logoff at a specified amount of time beforehand.
# The Warning Message has 10 second timeout so that the user can't postpone the logoff forever.
$maxTime = 8 # hours
$warnTime = 5 # minutes
$msgTimeout = 10 # seconds
$WarningMSG = "NOTICE! You will be logged off automatically in $warnTime minutes. Please save your files."
# ========================================================================================================
# Calculate Time To Sleep
$sleepyTime = $maxTime * 60 * 60
$warningTime = $warnTime * 60
Sleep -Seconds ($sleepyTime - $warningTime)
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup($WarningMSG,$msgTimeout,"Automatic Logoff Incoming...",0x0)
Sleep -Seconds $warningTime
# ADD LOGOFF CODE BELOW!
Logoff

Receive-Output – A Write-* Wrapper and Log Writer

This is a wrapper for your Write-* cmdlets. Send the message, type and file log path, and it will write out the message with the proper write-* cmdlet and log the message to the specified file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<#
.Synopsis
   This is a wrapper for your Write-* cmdlets. Send the message, type and file log path, and it will write out the message with the proper write-* cmdlet and log the message to the specified file.
.DESCRIPTION
   A cmdlet / function to wrap all your write-* cmdlets
.EXAMPLE
   Receive-Output -Message "This is a test" -Type Default -Log "C:\temp\log.txt"
.EXAMPLE
   Receive-Output -Message "This is might be a test..." -Type Warning -Log "C:\temp\log.txt"
.EXAMPLE
   Receive-Output -Message "This is NOT a test!" -Type Error -Log "C:\temp\log.txt"
.EXAMPLE
   Receive-Output -M "This example uses alias parameters" -T Warning -L "C:\temp\log.txt"
.EXAMPLE
   Receive-Output -M "This example uses write-host with colors!" -T Host -L "C:\temp\log.txt" -C Magenta
#>

function Receive-Output
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Enter the message, use quotes as needed
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Alias("M")]
        [string]
        $Message,

        # Enter the message level
        [Parameter(Mandatory=$true,
        Position=1)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet("Default", "Warning", "Error", "Debug", "Verbose", "Host")]
        [Alias("T")]
        [String]
        $Type = "Default",

        # Enter the log file path or variable containing path
        [Parameter(Mandatory=$true,
        Position=2)]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]        
        [Alias("L")]
        [String]
        $Log,

        # Enter the color for host writes
        [Parameter(
        Position=3)]    
        [Alias("C")]
        [ConsoleColor[]]
        $Colorz = [System.ConsoleColor]::White
    )

    Switch ($Type) {    
        Default  {$out = {Write-Output $Message}}
        Debug  {$out = {Write-Debug $Message -Debug}}
        Error  {$out = {Write-Error $Message}}
        Warning  {$out = {Write-Warning $Message}}            
        Verbose  {$out = {Write-Verbose $Message -Verbose}}
        Host {$out = {Write-Host $Message -ForegroundColor $Colorz}}  
    }
    # Add timestamp and write out the message to the log
    $logMessage = "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") : $Message"
    Add-Content -Value $logMessage -Path $Log  
    # Write out the message using the proper cmdlet
    Return & $out    
}

Monitor and Change File Extension for all files in folder

Monitor and Change File Extension for all files in folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
$stop = $false
$i = 0
do{
    $files = Get-ChildItem -LiteralPath "C:\Users\Public\Documents" -Filter *.txt
    ForEach ($file in $files){
        $newfile = $file.FullName.Replace('.txt','.nfo')
        Rename-Item -LiteralPath $file.FullName -NewName $newfile
        $i += 1
        sleep -Milliseconds 250
    }
Write-Host renamed $i files. sleeping...
sleep -Seconds 1
}While($stop -eq $false)

Gmail RSS Monitor and File Downloader

Loops at a specified interval and monitors emails for titles which RegEx match [identifier] [url] (e.g. mycoolID http://mirror.math.princeton.edu//ubuntu-17.10.1-desktop-amd64.iso) and downloads the files using BITS to a specified storage location. Uses background jobs so there’s no blocking.

Usage: Configure the settings in the script and run it. Then send an email to the email account configure for RSS monitoring. The email subject / tile should contain your unique identifier (whatever random string you choose (configured in the RegEx variable)), a space, and the URL you wish to have downloaded by the script.

If Windows Defender is enabled on your computer, the script will initiate a scan of the files upon download completion.

It is recommended to setup a separate account to do this with since you have to allow “less secure apps” access in your Gmail Security settings.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    # Monitor Gmail and Download Files Based On Title Contents using RegEx
    # Uses the BITS client to download
    # If Enabled, Files are scanned with Windows Defender upon download completion
    # OPTIONS
    # Folder where files will be dropped, no trailing slash \
    $FileDrop = "Z:\FileDrop"
    # RegEx for Title Matching. Should be [Unique Identifier] [URL] E.G.: 'MYuniqID https://download.com/this/file/here.exe'
    # The filename to save in the FileDrop is pulled from the last / of the URL, would be "here.exe" from the example above.
    $titleRegEx = '^myCoolID\s(http[s]{0,1}://.*)'
    # Frequency to check email in minutes
    $freq = 1
    # Gmail Account Info (must allow insecure apps in your security settings)
    $user = "username@gmail.com"
    $pwd = "password1234!"
    # Scan downloaded files with Windows Defender upon completion?
    $scanFiles = $true
    #==============================================================================

    cls
    $makeItStop = $false
    Import-Module BitsTransfer
    $ToGrabFiles = New-Object System.Collections.ArrayList
    $GrabbedFiles = New-Object System.Collections.ArrayList
    $currentJobs = New-Object System.Collections.ArrayList
    $doneJobs = New-Object System.Collections.ArrayList
    $scannedFiles = New-Object System.Collections.ArrayList
    $webclient = new-object System.Net.WebClient
    # access the rss-feed - uses your Gmail email address and password
    $webclient.Credentials = new-object System.Net.NetworkCredential ($user, $pwd)

    Do {

    # download the rss as xml from google
    [xml]$xml = $webclient.DownloadString("https://mail.google.com/mail/feed/atom")
    # check each email
    ForEach ($email in $xml.feed.entry){    
        If ($email.title -match $titleRegEx){ # does the email title match our regex for ID and URL?
            $grab = [pscustomobject] @{URL = $matches[1]}
            If ($GrabbedFiles -notcontains $grab.URL -and $ToGrabFiles -notcontains $grab.URL){
                $null = $ToGrabFiles.Add($grab.URL)
                Write-Host "Added $($grab.URL) to the download list" -ForegroundColor Green            
            }
        }
    }
    # check each URL found in the email titles
    ForEach ($URL in $ToGrabFiles){
        If ($GrabbedFiles -notcontains $URL){        
            $fileName = $URL.Substring($url.LastIndexOf("/") + 1)
            $saveFileName = "$FileDrop\$filename"        
            If (!(Test-Path $saveFileName) ){            
                $newJob = Start-Job -ScriptBlock {
                    param ([string]$URL, [string]$saveFileName)                
                    Start-BitsTransfer -Source $URL -Destination $saveFileName -ErrorVariable jobErr -ErrorAction SilentlyContinue;                
                    Write-Output $URL;                            
                } -ArgumentList $URL, $saveFileName                        
                $null = $currentJobs.Add($newJob.Id)
                $null = $GrabbedFiles.Add($URL)                          
            }Else{
                Write-Output "$saveFileName already exists - skipping!"            
                $null = $GrabbedFiles.Add($URL)
            }
        }Else{
            #"File already grabbed!"
        }
    }    

       ForEach ($job in $currentJobs){
            If ($doneJobs -notcontains $job){
                $status = Get-Job -Id $job
                If ($status.State -eq 'Completed' -and $status.HasMoreData -eq 'True'){                
                    Do{
                        $result = Receive-Job -id $job                    
                        Write-Host "$result :: $($status.state)" -ForegroundColor Green                                                                              
                    }Until($result -eq $null)
                    $null = Stop-Job -Id $job
                    $null = Remove-Job -Id $job                
                    $null = $doneJobs.Add($job)                                    
                }ElseIf ($status.State -eq 'Running'){
                    Do{
                        $result = Receive-Job -id $job                    
                        Write-Host "$result :: $($status.state)" -ForegroundColor Cyan                                                          
                    }Until($result -eq $null)
                }            
                           
                sleep -Seconds 1
            }
        }

         ForEach ($done in $doneJobs){
            $currentJobs.Remove($done)
        }

        If ($scanFiles -eq $true){
            ForEach ($file in Get-ChildItem -Exclude "*.tmp" -LiteralPath $FileDrop | where {$scannedFiles -notcontains $_.Name}){            
                If (Get-Service windefend -ErrorAction SilentlyContinue | where {$_.status -eq "Running"}) {    
                    $scan = Start-MpScan -ScanPath $file.FullName -ScanType QuickScan    
                    $null = $scannedFiles.Add($File.Name)
                }Else{
                    Write-Output "Windows Defender is not running. Unable to scan file: $($file.FullName)"
                    $svc = Get-Service windefend -ErrorAction SilentlyContinue | select status
                    Write-Output "Windows Defender Service is: $($svc.Status)"                
                }
            }
        }
    Write-Output "Sleeping for $freq minutes... Press CTRL+C to Quit"
    sleep -Seconds ($freq * 60)

    } Until ($makeItStop -eq $true)

Generate MOD ID list from Steam Mod Collection URL

This script enables you to generate a simple list of all MOD IDs contained in a Mod Collection for use in Server Managers for Steam Games.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#=========================================================================
# Generate MOD ID List from a Steam Mod Collection URL
# Created by Archi
# Last modified 2018-04-04
# Version 1.1
#=========================================================================
cls
#Set URL of your Mod Collection for your server
#SE archicraft v2
$WorkshopCollectionURL = 'http://steamcommunity.com/sharedfiles/filedetails/?id=1157100543'
#Set Output Path and Filename of file which will contain MOD ID list
#Note: List will also display on screen
$modList = 'c:\Users\Public\mods.txt'
#==============================================================================================
#End configurable options
#==============================================================================================
$getPage = Invoke-WebRequest –Uri $WorkshopCollectionURL
$modIDCollection = @()
$links = $getPage.Links
foreach ($link in $links){
    if ($link.innerHTML -like "*workshopItemTitle*" ){
        $modID = $link.href.Replace('http://steamcommunity.com/sharedfiles/filedetails/?id=','')
        if($modIDCollection -notcontains $modID){
            $desc = $link.innerText
            Write-Host "Found Mod: $desc"
            $modIDCollection += $modID
        }
    }
}
if (Test-Path $modList) {del $modList}
Set-Content -Path $modList $modIDCollection
Write-Host "Your mod list is at: $modList"
$modIDCollection
start $modList

Namecheap Dynamic DNS (DDNS) Looping Updater PowerShell Script

This script will loop endlessly and update your hosts with NameCheap’s Dynamic DNS (DDNS) service at the interval you specify.

It also verifies and outputs the status response from NameCheap so you’ll know if the update was successful, or if not, why.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# Namecheap DDNS Updater Script
# A one file looping script that updates your hosts every X minutes.
# Created by: https://FrontRunnerTek.com
# Version 1.0
# Some bits sourced from: https://gist.github.com/dredix/3508143
# :: Settings
# ::::: General
# ::::::: Update Frequency In Minutes
$UpdateFreq = 15
# ::::::: Force Update even if IP hasn't changed
$ForceUpdate = $false
# ::::: Proxy
$ProxyEnabled = $false # Use Proxy? $true or $false
$ProxyAddress  = "" # Proxy IP Address
$ProxyPort     = "" # Proxy Port
$ProxyDomain   = "" # Proxy Domain Name
$ProxyUser     = "" # Proxy Username
$ProxyPassword = "" # Proxy Password
# ::::: Domain Configs
# DDNSSubdomain = Host. The subdomain name or if root domain, use '@'.
# DDNSDomain = Domain. The root domain name you are updating.
# DDNSPassword = Dynamic DNS Password. Unique to each Domain Name - NOT your account password!
# Duplicate DomainX entries for as many as you need. Add each DomainX variable to the ArrayList below
$Domain1 = [pscustomobject] @{
    DDNSSubdomain = "sub"
    DDNSDomain = "domain.com"
    DDNSPassword = "040vacdsbcf7dsfae85850a07d2435296f7"
}
$Domain2 = [pscustomobject] @{
    DDNSSubdomain = "sub2"
    DDNSDomain = "domain.com"
    DDNSPassword = "040vacdsbcf7dsfae85850a07d2435296f7"
}
# :::: Add each DomainX variable to this list
[System.Collections.ArrayList]$Domains = @($Domain1,$Domain2)
#================= END CONFIG - TOUCH NOTHING BELOW ====================================
CLS
# Ok, if you want to touch something, you can adjust this file path
$logFile = "C:\Users\Public\DDNSupdate_$(Get-Date -Format yyyy-MM-dd_HH_mm_ss).txt"
# But that's as far as you go. <img draggable="false" class="emoji" alt="" src="https://s.w.org/images/core/emoji/2.4/svg/1f609.svg">
$breakOut = $false
# Write a message to log.
function Log-Message ($MSG) {
    $script:Logger += "$(get-date -format u) $MSG`r`n"
    Write-Output $MSG
}
# Write an error to log.
function Log-Error ($MSG) {
    $script:Logger += "$(get-date -format u) ERROR`: $MSG`r`n"
    Write-Output "ERROR`: $MSG"
}
$Logger = ""    
Log-Message "Namecheap Dynamic DNS Update Client Log"
 
Do {
 
$client = New-Object System.Net.WebClient
if ($ProxyEnabled) {
    $proxy         = New-Object System.Net.WebProxy
    $proxy.Address = $ProxyAddress
    if ($ProxyPort -and $ProxyPort -ne 80) {
        $proxy.Address = "$ProxyAddress`:$ProxyPort"
    } else {
        $proxy.Address = $ProxyAddress
    }
    $account = New-Object System.Net.NetworkCredential($ProxyUser, $ProxyPassword, $ProxyDomain)
    $proxy.Credentials = $account
    $client.Proxy = $proxy      
}
$client
 
# Get current public IP address
Log-Message "Retrieving the current public IP address"
$Pattern   = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
$CurrentIp = $client.DownloadString('http://myip.dnsomatic.com/')
Log-Message "Retrieving stored IP address"
$StoredIp  = [Environment]::GetEnvironmentVariable("PUBLIC_IP","User")
if (!($CurrentIp -match $Pattern)) {
    Log-Error "A valid public IP address could not be retrieved"    
}
Log-Message "Stored IP: [$StoredIp] Retrieved IP: [$CurrentIp]"
# Compare current IP address with environment variable.    
if ($StoredIp -eq $CurrentIp -and $ForceUpdate -eq $false) {
    Log-Message "No update required. IP hasn't changed. Not forcing Update."    
}Else{
    ForEach ($domain in $Domains){  
    Log-Message "Updating IP address on domain registrar for $($domain.DDNSSubdomain) @ $($domain.DDNSDomain)"
    #https://dynamicdns.park-your-domain.com/update?host=SUBDOMAIN&amp;domain=YOURDOMAIN&amp;password=0123456789ABCDEF&amp;ip=$CurrentIp
    $DDNSSubdomain = $domain.DDNSSubdomain
    $DDNSDomain    = $domain.DDNSDomain
    $DDNSPassword  = $domain.DDNSPassword
    $UpdateUrl     = "https://dynamicdns.park-your-domain.com/update?host=$DDNSSubdomain&domain=$DDNSDomain&password=$DDNSPassword&ip=$CurrentIp"
    $UpdateDDNS    = $client.DownloadString($UpdateUrl)
    # Actually verify we have a good update or not
    $response = $UpdateDDNS
    If ($response.'interface-response'.ErrCount -eq 0){
        Log-Message "Update of $($domain.DDNSSubdomain) @ $($domain.DDNSDomain) was successful at Namecheap.com"        
    }Else{
        Log-Message "Update of $($domain.DDNSSubdomain) @ $($domain.DDNSDomain) failed"
        Log-Error $response.'interface-response'.errors.Err1
    }
    Log-Message "$UpdateDDNS"    
    [Environment]::SetEnvironmentVariable("PUBLIC_IP", $CurrentIp, "User")
    Log-Message "Environment variable set: $CurrentIp"
    }
}
# Sleep until next interval
Log-Message "Sleeping for $UpdateFreq minutes. Press CTRL+C to Quit."
sleep -Seconds ($UpdateFreq * 60)
 
} Until ($breakOut -eq $true)

Active Directory Auto-Generate Username

If you need to generate and test for an available username in Active Directory, this script will allow you to do so up to a specified maximum attempts before requiring manual input. Each loop adds a letter from the first name to the last name for the username. JWayne, JoWayne, JohWayne, JohnWayne.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
CLS
$firstName = &quot;John&quot;
$lastName = &quot;Wayne&quot;
$Global:attempts = 0
$Global:available = $false
$Global:finalName = $null

Function Test-SAM {
    $samAccountName = $($firstName.Substring(0, (1 + $Global:attempts)) + $(($lastName.Split() | select -Last 1) -join ' '))
    Write-Host &quot;Testing $samAccountName&quot;
    Try {
        $userTest = Get-ADUser $samAccountName -ErrorAction SilentlyContinue
    }Catch{
        # Ignore Error - Available Username
        $Global:available = $true
        $Global:finalName = $samAccountName
    }
}

Do {
    Test-SAM
    $Global:attempts +=1
} Until ($Global:available -eq $true -and $Global:attempts -le $firstName.Length -and $Global:attempts -le 2) #specify max attempts here
   
If (!$Global:finalName){
    Write-Host &quot;Could not automagically generate a username, please manually input one below...&quot;
    # do stuff here

    #
}Else{
    Write-Host &quot;Automagically generated username: $Global:finalName&quot;
    # do stuff here

    #
}