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

    #
}

Install all drivers in folder – automagically

Specify the folder which contains all the drivers you wish to have installed. Adjust the -recurse option as needed.

1
2
3
4
5
6
$driversFolder = &quot;C:\xtraDrivers&quot;
$driversCollection = (Get-ChildItem -Path $driversFolder -Filter &quot;*.inf&quot; -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Fullname)
ForEach($driver In $driversCollection){
    # Add and install driver package
    pnputil.exe -i -a $driver
}

Remotely search AD FS event logs of all ADFS Servers for specific UPN

This script enables you to remotely search the event logs of all ADFS servers for a particular UPN (email address) and log those events, and optionally related events based on the InstanceIDs.

Configure $servers to reflect the ADFS servers you wish to query.

Configure $queryXTRA per your requirement for the extra logs based on the InstanceID

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
# ADFS Event Log Query - Search by UPN
cls
$start = (Get-Date).AddHours(-96)
#$start = [datetime]&quot;3/28/2018 2:00:00 PM&quot; # &quot;3/27/2018 11:00:00 AM&quot;
$end = Get-Date
#$end = [datetime]&quot;3/28/2018 4:00:00 PM&quot; # &quot;3/27/2018 11:30:00 AM&quot;

$logfile = &quot;C:\users\public\ADFSlogging_$(Get-Date -f yyyy-MM-dd_HH_mm_ss).txt&quot;
$xmlfile = &quot;C:\users\public\ADFSlogging_$(Get-Date -f yyyy-MM-dd_HH_mm_ss).xml&quot;
$servers = &quot;server1.domain.com&quot;, &quot;server2.domain.com&quot;
$queryXTRA  = $true # $true / $false ; Queries and logs the additional associated events based on the activityID / transactionID
#==================================================================================================================================
$xtraInstances = New-Object System.Collections.ArrayList
Add-Content -Path $xmlfile -Value &quot;&lt;xml&gt;&quot; -Encoding Ascii
    # START UPN Search
    $searchInput = Read-Host -Prompt &quot;Enter UPN to query (username@domain.com)&quot;    
    Add-Content -Path $logfile -Value &quot;Searching for UPN: $searchInput&quot;
    ForEach ($server in $servers){
        Write-Host &quot;:::: Processing $server...&quot; -ForegroundColor Yellow
        Add-Content -Path $logfile -Value  &quot;:::: Processing $server...&quot;
        $events = Get-WinEvent -FilterHashtable @{Logname = 'security'; Data = $searchInput; ProviderName = 'AD FS Auditing'; StartTime = $start; EndTime = $end}  -ComputerName $server -ErrorVariable EvtERR -ErrorAction SilentlyContinue
        If (!$EvtERR){            
            ForEach ($entry in $events){
                $event = [xml]$entry[0].ToXml()
                $InstanceID = $event.Event.EventData.Data[0]                
                $upn = $entry.Message -match [regex]::Escape($searchInput) # &quot;\w+(-+.']\w+)*@domain\.com&quot; # optional regex for matching domain email / upn
                $timestamp = $entry.TimeCreated
                $eventID = $entry.Id
                If ($upn){                    
                    Add-Content -Path $logfile -Value &quot;MSG: $($entry.Message)&quot;
                    Write-Host &quot;UPN: $($matches[0])`tEventID: $eventID`tTimestamp: $timestamp`tInstanceID: $InstanceID&quot; -ForegroundColor Cyan  
                    Add-Content -Path $logfile -Value &quot;UPN: $($matches[0])`tEventID: $eventID`tTimeStamp: $timestamp`tInstanceID: $InstanceID&quot;
                    Add-Content -Path $logfile -Value &quot;:::: =================================================================== ::::&quot;
                    If ($queryXTRA -eq $true){                                                
                        If ($xtraInstances -notcontains $InstanceID){
                            $xtraInstances.Add($InstanceID)
                        }
                    }
                }
            }      
        }Else{
           #Add-Content -Path $logfile -Value $EvtERR  
        }        
        # poll extra events
        If ($queryXTRA -eq $true){
            ForEach ($Instance in $xtraInstances){
                # Query logs for InstanceID
                $xEvents = Get-WinEvent -FilterHashtable @{Logname = 'security'; Data = $Instance; ProviderName = 'AD FS Auditing'; StartTime = $start; EndTime = $end}  -ComputerName $server -ErrorVariable xEvtERR -ErrorAction SilentlyContinue
                If (!$xEvtERR){
                    Write-Host &quot;Found extra events for $Instance&quot; -ForegroundColor Yellow
                    $xEvents.count
                    ForEach ($entry in $events){
                        $event = [xml]$entry[0].ToXml()                                
                        Add-Content -Path $xmlfile -Value $event.InnerXML.Replace('&lt;Data&gt;-&lt;/Data&gt;','') -Encoding Ascii
                    }
                }
            }
        }
    }
    # END UPN Search
Add-Content -Path $xmlfile -Value &quot;&lt;/xml&gt;&quot; -Encoding Ascii
start $logfile
start $xmlfile

PowerShell PIA VPN Port Forwarding Assignment Reservation Script

This script will loop continuously and keep your port forwarding assignment updated and assigned to your IP while you are connected to the Private Internet Access VPN (PIA VPN).

Configure the $user, $pass, and $clientID variables per your PIA account information. The result from the webrequest which contains the assigned port will be written to the PowerShell console. You must be connected to the PIA VPN before running this script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# PowerShell Private Internet Access (PIA) VPN Port Forwarding Assignment Reservation Script
# POST to:  https://www.privateinternetaccess.com/vpninfo/port_forward_assignment
# Vars:     user=username
#           pass=password
#           client_id=a random string that no one should be able to guess, use the same string every time
#           local_ip=the 10.x.x.x IP you get assigned after connecting to the VPN

$user = 'user'
$pass = 'pwd'
$clientID = '2vn03242435fvdfasc349c234'

$i=&quot;go&quot;
Do {
    cls
    $timestamp = (Get-Date –f o)
    $ip2=get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1 -and $_.Description -like &quot;*TAP-Windows Adapter V9*&quot; }
    $ip = $ip2.ipaddress[0]
    $postParams = @{user=$user;pass=$pass;client_id=$clientID;local_ip=$ip}
    $info = Invoke-WebRequest -Uri https://www.privateinternetaccess.com/vpninfo/port_forward_assignment -Method POST -Body $postParams
    Write-Host $info.Content
    Write-Host &quot;$timestamp | sleeping...&quot;
    sleep -Seconds 3200
    $i = &quot;go&quot;
}while ($i = &quot;go&quot;)

Multi-Threaded Remote Net Shares Report For Domain Contollers In Forest

This script generates a report of shares on domain controllers which are not in the allowed list, allowing you to find shares which rogue admins have created.
Change domain.com to your forest name.

Adjust the shares allowed if required.

Adjust the maxThread variable as required.

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
# Shares Report Script
# Configure max threads and invalid share / error logging file path
# The query filters on the remote side so less data transfer across the WAN is required
# Once the jobs are done, a report will auto-open in Notepad to display the invalid shares found

#==============================================================================
#= OPTIONS ====================================================================
$maxThread = 30
$invalidSharesLog = &quot;C:\Users\Public\InvalidShares_$(Get-Date -f yyyy-MM-dd_HH_mm_ss).txt&quot;
#==============================================================================
CLS
Stop-Job *; Remove-Job *
$allDCs = New-Object System.Collections.ArrayList; $allJobs = New-Object System.Collections.ArrayList
$doneJobs = New-Object System.Collections.ArrayList; $DCsToProcess = New-Object System.Collections.ArrayList
$start = Get-Date
If (Test-Path $invalidSharesLog){del $invalidSharesLog}
(Get-ADForest -Server 'domain.com').Domains | % {
    $DCs = Get-ADDomainController -Server $_ -Filter *
    ForEach ($DC in $DCs) {
        Write-Host &quot;Found $($DC.Name).$_ to process...&quot; -ForegroundColor Cyan
        $null = $allDCs.Add(&quot;$($DC.Name).$_&quot;)
    }
}
Write-Host &quot;Found $($allDCs.Count) Domain Controllers to process...&quot; -ForegroundColor Yellow
$DCcount = $allDCs.Count
$DCsToProcess = $allDCs
Do {
    Write-Host &quot;Current Jobs: $($allJobs.count)&quot;    
    If ($allJobs.Count -le $maxThread){        
        $addMore = ($maxThread - $allJobs.Count)
        ForEach ($DC in ($DCsToProcess | Select-Object -First $addMore)){            
            $newJob = Start-Job -ScriptBlock {        
                param ([string]$DC)
                $shares = Get-WmiObject -ComputerName $DC -ClassName Win32_Share -Filter `
                &quot;`
                Not Name like 'ADMIN$' and `
                Not Name like 'C$' and `
                Not Name like 'D$' and `
                Not Name like 'IPC$' and `
                Not Name like 'NETLOGON' and `
                Not Name like 'SYSVOL' `
                &quot; `
                -ErrorVariable shareERR -ErrorAction SilentlyContinue;
                If ($shares -eq $null -and -not $shareERR){
                    Write-Output &quot;STATUS: $($DC): No Invalid Shares Found&quot;
                }ElseIf ($shareERR){
                    Write-Output &quot;ERROR: $($DC): $($shareERR | Out-String)&quot;
                }Else{
                    Write-Output &quot;ERROR: $($DC): Invalid Shares Found`n$($shares | Out-String)&quot;
                }
            } -ArgumentList $DC
            Write-Host &quot;Started Job for $DC...&quot; -ForegroundColor Cyan
            $null = $allJobs.Add($newJob.Id)
            $null = $DCsToProcess.Remove($DC)              
        }
    }
        ForEach ($job in $allJobs){
            If ($doneJobs -notcontains $job){
                $status = Get-Job -id $job            
                If ($status.State -eq 'Completed' -and $status.HasMoreData -eq 'True'){
                    $end = Get-Date
                    $dateDiff = New-TimeSpan $start $end
                    $minutes = $dateDiff.Minutes
                    $seconds = $dateDiff.Seconds
                    Write-Host &quot;Time Elapsed: $($minutes)m $($seconds)s&quot; -ForegroundColor Cyan
                    Do{
                        $result = Receive-Job -id $job
                        If ($result -like &quot;ERROR:*&quot;){
                            Write-Host $result -ForegroundColor Red
                            $result | Out-File -Append -FilePath $invalidSharesLog
                        }Else{
                            Write-Host $result -ForegroundColor Green
                        }
                    }Until($result -eq $null)
                    $null = stop-job -Id $job;$null = Remove-Job -Id $job;
                    $null = $doneJobs.Add($job)                    
                }                
                sleep -s 1
            }
        }
        ForEach ($done in $doneJobs){
            $allJobs.Remove($done)
        }
        Write-Host &quot;Completed Jobs: $($doneJobs.count)&quot; -ForegroundColor Cyan
} Until ($doneJobs.count -eq $DCcount)
Write-Host &quot;Processing is complete&quot; -ForegroundColor Yellow
Stop-Job *; Remove-Job *
start notepad.exe $invalidSharesLog