Network Adapter Information (GUID, MAC, IP, Interface Index, Name, Description)

Recently we needed to find a network adapter, provided with nothing but the GUID. Using the code below, we were able to pull the details for our network cards, and find the one in question. Then we went a bit further to poll more information.

1
2
3
4
5
6
$WMIinfo = Get-WmiObject win32_networkadapter -Property guid, Name, MACAddress
$NICinfo = Get-NetAdapter | Where-Object {$_.MacAddress -eq $WMIinfo.MACAddress -replace ':','-' } | Select-Object ifIndex, Status, LinkSpeed, Name
$IPinfo = Get-NetIPAddress | Where-Object {$_.InterfaceIndex -eq $NICinfo.ifIndex}
$WMIinfo | Select-Object MACAddress, GUID, Name | Out-String
$NICinfo | Out-String
$IPinfo | Select-Object IPAddress | Out-String

RDPManager – PowerShell Based

A quick quality of life script.

Add Favorites from the drop down of Domain Controllers, or manual entry from the textbox.
Double-click a Favorite or click on Launch to open MSTSC for the selected server.
Favorites are saved in a JSON file on your desktop.

Note: Change ‘forest.local’ to the proper forest name of your environment for polling domain controllers. If you’d like to pull in all servers from your forest / domain, you’ll need to change what objects are populated into the $ADDCs variable (I.E. Use the Get-ADComputer cmdlet).

RDPManager
RDPManager
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# PowerShell RDP Manager
# Version: 1.1
# Created: 2018-06-28
# Modified: 20-18-07-02
#=====================================================
$RDPFavs = "$env:USERPROFILE\Desktop\RDPFavs.json"
CLS

If ($host.Name -like "*ISE*"){    
    Write-Warning "Launching into a regular powershell console window"
    If ($MyInvocation.MyCommand.Path) {
        $scriptpath = $MyInvocation.MyCommand.Path -replace ' ', '` '
        Invoke-Expression "start powershell {$scriptpath}"        
    }
    break
}

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

Function Load-Favs {
  If (Test-Path -Path $RDPFavs -ErrorAction SilentlyContinue) {
    $Favs = Get-Content -Raw $RDPFavs | ConvertFrom-Json
    ForEach ($Fav in $Favs) {
        $list_favs.Items.Add($Fav)
    }
  } Else {
    $Favs = $null
  }
}

Function Save-Favs {
    $list_favs.Items | ConvertTo-Json | Out-File $RDPFavs
}

Function Start-RDP ($computername){    
    Start-Process "$env:windir\system32\mstsc.exe" -ArgumentList "/v:$computername"
}

Function Get-AllDCs {
    $ADDCs = (Get-ADForest 'forest.local' -ErrorAction SilentlyContinue -ErrorVariable errForest ).domains | ForEach {Get-ADDomain -Identity $_ -ErrorAction SilentlyContinue } | ForEach {Get-ADDomainController -Server $_.DNSRoot -Filter * -ErrorAction SilentlyContinue } | select HostName | Sort-Object
    If ($errForest) {
        $cmb_allDCs.items.Add("Error Loading DCs - $($errForest.message)")
    } Else {
        ForEach ($DC in $ADDCs) {
            $cmb_allDCs.items.Add( $dc.hostname )
        }
        $cmb_allDCs.Sorted = $true
    }
}

#region begin GUI{

$rdpManager                      = New-Object system.Windows.Forms.Form
$rdpManager.ClientSize           = '600,355'
$rdpManager.text                 = "RDP Manager"
$rdpManager.TopMost              = $false
#$rdpManager.icon                 = '\\path\to\icon.ico'

$Label1                          = New-Object system.Windows.Forms.Label
$Label1.text                     = "Favorites"
$Label1.AutoSize                 = $true
$Label1.width                    = 25
$Label1.height                   = 10
$Label1.location                 = New-Object System.Drawing.Point(12,15)
$Label1.Font                     = 'Microsoft Sans Serif,10'

$cmb_allDCs                      = New-Object system.Windows.Forms.ComboBox
$cmb_allDCs.text                 = "Select"
$cmb_allDCs.width                = 278
$cmb_allDCs.height               = 20
$cmb_allDCs.location             = New-Object System.Drawing.Point(302,42)
$cmb_allDCs.Font                 = 'Microsoft Sans Serif,10'
$cmb_allDCs.AutoSize             = $true

$Label2                          = New-Object system.Windows.Forms.Label
$Label2.text                     = "All Domain Controllers"
$Label2.AutoSize                 = $true
$Label2.width                    = 25
$Label2.height                   = 10
$Label2.location                 = New-Object System.Drawing.Point(302,18)
$Label2.Font                     = 'Microsoft Sans Serif,10'

$btn_AddToFavs                   = New-Object system.Windows.Forms.Button
$btn_AddToFavs.text              = "Add To Favs"
$btn_AddToFavs.width             = 184
$btn_AddToFavs.height            = 27
$btn_AddToFavs.location          = New-Object System.Drawing.Point(354,133)
$btn_AddToFavs.Font              = 'Microsoft Sans Serif,10'

$btn_removeFav                   = New-Object system.Windows.Forms.Button
$btn_removeFav.text              = "Remove Selected From Favs"
$btn_removeFav.width             = 195
$btn_removeFav.height            = 30
$btn_removeFav.location          = New-Object System.Drawing.Point(44,310)
$btn_removeFav.Font              = 'Microsoft Sans Serif,10'

$txt_addFav                      = New-Object system.Windows.Forms.TextBox
$txt_addFav.multiline            = $false
$txt_addFav.width                = 279
$txt_addFav.height               = 20
$txt_addFav.location             = New-Object System.Drawing.Point(302,74)
$txt_addFav.Font                 = 'Microsoft Sans Serif,10'

$Label3                          = New-Object system.Windows.Forms.Label
$Label3.text                     = "Select from above, or enter hostname manually"
$Label3.AutoSize                 = $true
$Label3.width                    = 25
$Label3.height                   = 10
$Label3.location                 = New-Object System.Drawing.Point(302,109)
$Label3.Font                     = 'Microsoft Sans Serif,10'

$btn_launch                      = New-Object system.Windows.Forms.Button
$btn_launch.text                 = "Launch RDP for Selected Fav"
$btn_launch.width                = 199
$btn_launch.height               = 30
$btn_launch.location             = New-Object System.Drawing.Point(337,310)
$btn_launch.Font                 = 'Microsoft Sans Serif,10'

$list_favs                        = New-Object system.Windows.Forms.ListBox
$list_favs.text                   = "Select"
$list_favs.width                  = 275
$list_favs.height                 = 249
$list_favs.location               = New-Object System.Drawing.Point(12,42)

$rdpManager.controls.AddRange(@($Label1,$cmb_allDCs,$Label2,$btn_AddToFavs,$btn_removeFav,$txt_addFav,$Label3,$btn_launch,$list_favs))

#region gui events {
$btn_launch.Add_Click({
    Start-RDP($list_favs.SelectedItem)
    Write-Host "Launching RDP for $($list_favs.SelectedItem)"
})
$btn_removeFav.Add_Click({    
    $list_favs.Items.RemoveAt($list_favs.SelectedIndex)
    Save-Favs
})
$btn_AddToFavs.Add_Click({    
    If ($txt_addFav.Text.Length -gt 0){
        $list_favs.Items.Add($txt_addFav.Text)
        Save-Favs
        $txt_addFav.Text = ""
    } Else {
        $list_favs.Items.Add($cmb_allDCs.SelectedItem)
        Save-Favs
    }
})

$txt_addFav.Add_KeyUp({
    if ($_.KeyCode -eq "Enter") {
        If ($txt_addFav.Text.Length -gt 0){
            $list_favs.Items.Add($txt_addFav.Text)
            Save-Favs
            $txt_addFav.Text = ""
        }
    }
})

$rdpManager.Add_Load({ Load-Favs; Get-AllDCs })
$list_favs.Add_DoubleClick({
    Start-RDP($list_favs.SelectedItem)
    Write-Host "Launching RDP for $($list_favs.SelectedItem)"
})
#endregion events }

#endregion GUI }

[void]$rdpManager.ShowDialog()

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.

RegEx validate NetBIOS computer name for Domain Controllers

1
2
3
4
5
6
7
8
9
# DomainController NetBIOS naming convention validation - using RegEx.
# This RegEx requires 1-11 characters in length, allows A-Z and 0-9 PLUS Requiring an additional 4 characters, to be 'DC' followed by 2 digits.
$regString = '^[A-Z0-9-]{1,11}DC[0-9]{2}$'
$netBIOSname = 'ADFREWGEHDEDC01'
If ($netBIOSname -cmatch $regString){
    Write-Host "Name passes"
}else{
    Write-Host "Name fails"
}

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)