07 March, 2022

The Book of Job

Don't take the story of Job so literally - It's not like there was a journalist there writing everything down...

Stripped down to its core... The story is a lesson - It tells the student to not have a shitty attitude - because doing so just generates misery.

Otherwise stated... What consumes your thoughts, controls your life.

It is probably one of my favorite stories ever.

The story / lesson it is passing along is some very powerful advice that anyone can benefit from. Hence its tenacity.

Just because religiofucktards call it theirs is no reason to disavow its intent.

It is at least 8000 years old (probably out-dating King Hammurabis' code) and was (as most stories were) passed by word of mouth up until about 1700 years ago. The original story was not likely to have been centered around supernatural mono-theistic entities... Whereas, mono-theisim is about 5000-3000 years old.

The Jewish faith managed to hijack much of Sumerian mythology along with that cultures oral histories and parables... Including the story of 'Job'... And still Hebrews insist 'collectively' to call it all 'Jewish' / ''Torah' - Talk about plagiarism!!!

Removing all of the Judaeo-Christian bullshit and referring to the symbolisms as they were called out in antiquity - This is the lesson:

Don't cop an attitude! Be able and willing to let go of life's adversities and stresses... Because clinging to them, and / or worrying to much is just not healthy.

AKA:
Curse god, curse yourself = A bad attitude only hurts yourself.

WHEN YOU SEE IT, YOU WILL SHIT BRICKS

I composed a similar concept regarding Noah and the Ark here:
http://rich-laduca.blogspot.com/2013/01/while-global-flood-noah-and-ark-are.html

02 March, 2022

BeeLink Tools

A do all Powershell version is at the bottom here - Or...
Grab the tool here:
https://drive.google.com/file/d/1wmXwT6erye3U-l6YxwK5XUMa06CDregy/view?usp=sharing

Unzip it... (not shady looking at all, is it - Who you gonna trust? This post is mostly for me anyway)

I got this from BeeLink computers support thru Amazon...
I had to contact them, because the last several computers I've gotten had a generic "123456789" as the serial number in the BIOS.
But, for auditing purposes (on domain computers), I need to be able to programmatically see the serial number

If you run 'WriteOENSN.cmd', you can correct / update the serial number in BIOS.

I manually typed in the serial number from the sticker on the bottom of the computer, BUT -
The tool / process actually prompts to 'scan' it in (from the barcode on that same sticker).
So - If you have a USB barcode scanner you can plug into the computer... 

Scan it in... Way faster than typing!

These commands will show the serial number:

WMIC BIOS GET SERIALNUMBER

get-ciminstance win32_bios | select SerialNumber


Or - Grab this PoSh and it will do most of the work for you:

$Share_URL = "https://drive.google.com/file/d/1wmXwT6erye3U-l6YxwK5XUMa06CDregy/view?usp=sharing"

$Share_URL = "https://drive.google.com/uc?export=download&id=$($Share_URL.Split('/')[-2])"

$ZipDownload = "C:\Users\Public\Downloads\BeelinkTools.zip"
$ToolFolder = "$(Split-Path -Path $ZipDownload)\Beelink Tools"

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

(New-Object System.Net.WebClient).DownloadFile($Share_URL, $ZipDownload)

Expand-Archive -LiteralPath $ZipDownload -DestinationPath $ToolFolder

cd $ToolFolder
Start-Process -FilePath "C:\Windows\System32\cmd.exe" -Verb runas -ArgumentList {/k WriteOENSN.cmd}

21 February, 2022

Domain Controller Sync partner, force sync and show results

I ran into some annoyances related to it taking more time than my patience was allowing, for all of our domain controllers to be in sync... 

Because, while I am connected to a DC in the mid-West, making changes, it can take up to 15 minutes for those changes to be reflected with the DC's on the East coast... 






Adding in an Azure DC, and o365 to that, just shortens my patience even more, when I need to move things along...

Plus, if I suspect there may be sync issues, I needed a quick way to know / rule that out.

This script does a line by line replication with each respective DC sync partner - And reports that result.

Then it confirms the partner sync status.

I set the confirmation bit as a function, so that it can also easily be run by itself.

$SyncMeta = @()
Write-Host "Gathering DC sync partner list..." -Fore 14
$All_DCs_Sync = (Get-ADDomainController -Filter * | sort).Hostname | % {($_).Split('.')[0]} | % {
$Server_ = $_
$SyncMeta += Get-ADReplicationPartnerMetadata -Target $Server_ | select @{n='Server';e={$Server_}}, @{n='Partner';e={($_.Partner).Split(',')[1].Split('=')[1]}}
}
$DomPart = (Get-ADDomainController).DefaultPartition
$SyncMeta | % {
$SyncResult = Repadmin /replicate $_.Partner $_.Server $DomPart
Write-Host "  $(($SyncResult | Out-String).Trim())" -Fore 11
}

##################################

Function SyncReport {
''
Write-Host "Start sync query : " -Fore 14 -No
Get-Date -Format "MM/d/yyyy hh:mm:ss tt"

$All_DCs_Sync = (Get-ADDomainController -Filter * | sort).Hostname | % {($_).Split('.')[0]} | % {
$Server_ = $_
Get-ADReplicationPartnerMetadata -Target $Server_ | select @{n='Server';e={$Server_}}, @{n='Partner';e={($_.Partner).Split(',')[1].Split('=')[1]}}, LastReplicationAttempt, LastReplicationSuccess
}
Write-Host "Finish sync query: " -Fore 14 -No
Get-Date -Format "MM/d/yyyy hh:mm:ss tt"
''
Write-Host (($All_DCs_Sync | sort -Descending  LastReplicationSuccess | Out-String)).Trim()
''
$Newest_Sync = ($All_DCs_Sync.LastReplicationSuccess | sort -Descending)[0]
$Oldest_Sync = ($All_DCs_Sync.LastReplicationSuccess | sort -Descending)[-1]
Write-Host "Time between MOST recent, and LEAST recent:" -Fore 11
(($Newest_Sync - $Oldest_Sync) | Select Days, Hours, Minutes, Seconds | fl | Out-String).Trim()
}

SyncReport

22 December, 2021

APC by Schneider - GIU locked up

Issue / Symptom:

The APC web interface / GUI shows this, after attempting to log in:

"The maximum number of web connections has been reached."

Resolution:

The only way to resolve this is via telnet…

Note: This command will enable telnet - (needed if you get an error about 'telnet' not being a recognized command):

pkgmgr /iu:"TelnetClient"

How to get connected - Open a CMD prompt and:

telnet <UPS IP address> 23

User Name: apc

Password: apc (this is the default password - you should change this, AND disable FTP*)

This command will list all of the open sessions:

session

This is the format for deleting sessions:

session -d <all session ID's other than the telnet session>

Finally:

session -d <the telnet session>

Examples:

session -d 17

session -d 18

session -d 20

session -d 21

session -d 22

session -d 23

session -d 36

session -d 39

session -d 63


* Disable FTP...
Configuration > Network > FTP Server: 'Enable' (uncheck)


25 October, 2021

A quick way to make a PoSh Array with a header

I am not really satisfied with how one is 'expected to create arrays in PoSh'...
The conventional methods look pretty, but I find them inflexible...
And I just don't like the way the result behaves... 

PSCustomObjects are a pain in the ass IMO... But I digress.

Here are some methods to look at, and an expanded example of how I use this approach to easily create multidimensional arrays:


$ThisX = @('one','two','nine') | Select @{n='Numbers';e={$_}}
$ThisX
$ThisX.Gettype() | Select IsPublic, IsSerializable, Name,BaseType | fl 

# ~ Result ~

# Numbers
# -------
# one    
# two    
# nine

# IsPublic       : True
# IsSerializable : True
# Name           : Object[]
# BaseType       : System.Array

#####################################

$ThisY = @('one') | Select @{n='Numbers';e={$_}}
$ThisY
$ThisY.Gettype() | Select IsPublic, IsSerializable, Name,BaseType | fl 

# ~ Result ~

# Numbers
# -------
# one

# IsPublic       : True
# IsSerializable : False
# Name           : PSCustomObject
# BaseType       : System.Object

####################################

$ThisZ = @('one')
$ThisZ
$ThisZ.Gettype() | Select IsPublic, IsSerializable, Name,BaseType | fl 

# ~ Result ~

# one

# IsPublic       : True
# IsSerializable : True
# Name           : Object[]
# BaseType       : System.Array

####################################

$This0 = "
zero,0
one,1
two,2
three,3
four,4
five,5
six,6
"
$This0 = ($This0).Split("`n|`r",[System.StringSplitOptions]::RemoveEmptyEntries)

$This0 = $This0 | Select @{n='Number';e={"$(($_).Split(',')[0])"}}, @{n='digit';e={[int]($_).Split(',')[1]}}, @{n='binary';e={[Convert]::ToString(([int]($_).Split(',')[1]),2)}}, Random

$This0.Gettype() | Select IsPublic, IsSerializable, Name,BaseType | fl 

$This0 

# ~ Result ~

# IsPublic       : True
# IsSerializable : True
# Name           : Object[]
# BaseType       : System.Array


# Number digit binary Random
# ------ ----- ------ ------
# zero       0 0            
# one        1 1            
# two        2 10           
# three      3 11           
# four       4 100          
# five       5 101          
# six        6 110  

################################

$This0 | % { $_.Random = 65..90|%{[char]$_}|Get-Random }


# Number digit binary Random
# ------ ----- ------ ------
# zero       0 0           E
# one        1 1           N
# two        2 10          I
# three      3 11          L
# four       4 100         Y
# five       5 101         T
# six        6 110         Z

20 July, 2021

Delinquent KnowBe4 training list - API seriously / sadly lacking

If any of you folks use KnowBe4...
The below script may be helpful...
I tapped into their API - Hoping to pull in an automated list of users who have not completed mandatory training -

This list of users was being manually harvested, via the web interface (not an easy, intuitive, or remotely contiguous process either).

Only to discover that such base level, rudimentary details are not actually available from a simple API call...
I even asked their support team... Nope!


To say the least, I was dismayed, and annoyed in learning this.


(I did promise KnowBe4 support that I would call them out on Reddit, hoping that some amount of public embarrassment, might get their dev team to fix " this shortsighted absurdity"... " And act like a company that offers IT related services" - 
https://www.reddit.com/r/PowerShell/comments/oo9ir1/delinquent_knowbe4_training_list_api_seriously/

https://www.reddit.com/r/sysadmin/comments/oo9n51/delinquent_knowbe4_training_list_api_seriously/

I sent a link to those Reddit posts, in my reply to the KnowBe4 manager that replied to me, when I explained my feelings about this, in the support email)

~~~~~~~~~~~~~~~~~~~~~~~~~~
It is necessary to call three (3) different API directories, in order to get something useful.
Querying first ‘Campaigns’, and then using THAT result, and querying ‘Enrollments’, and THEN using THAT result, querying ‘users’.

The below code does a far bit of data massaging, and may be interspersed with some filtering that not everyone might need in place...
No doubt there are ways to streamline it - TBH - I don't really care.

Much of what I did, was done on-the-fly... And like I said - I was annoyed... I probably still am.


$EAP = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue" # Because possibly dealing with converting a null value to 'DateTime', below.

Function Token {
$AccessToken = "'Reporting' API key goes in here" # Get this from within the KnowBe4 portal
$authorizationHeader = "Authorization: Bearer $AccessToken"   
}

$URL = 'https://us.api.knowbe4.com/v1/training/campaigns/'
Token
$campaigns = $null; $campaigns = (curl.exe --url $URL --header $authorizationHeader --silent | ConvertFrom-Json)
# $campaigns | select name, campaign_id, status, completion_percentage, start_date, end_date, relative_duration, duration_type | sort completion_percentage | ft

$campaigns | % {
If ($_.start_date -ne $null) { $_.start_date = [DateTime]"$($_.start_date)" }
If ($_.start_date -eq $null) { $_.start_date = [Nullable[DateTime]]$null }
If ($_.end_date -ne $null) { $_.end_date = [DateTime]"$($_.end_date)" }
If ($_.end_date -eq $null) { $_.end_date = [Nullable[DateTime]]$null }
}

$PastDue = @()
$campaigns | ? { $_.Status -ne "closed" -and ($_.end_date -eq $null -or (Get-Date) -lt $_.end_date) } | sort name  | % {

    Write-Host $_.campaign_id -Fore 14 -No
    Write-Host " - "$_.name -Fore 11 -No
    If ($_.relative_duration) { 
        $_relative_duration = $_.relative_duration
        Write-Host " - Rel. Dur.:"$_.relative_duration -No 
    }
    If (!$_.relative_duration) { 
    
    }
    If ($_.end_date -ne $null) { 
        Write-Host " (End date: $($_.end_date))" -Fore 10 
    }
    If ($_.end_date -eq $null) { 
        Write-Host " (No End date)" 
    }
#>
$URL = "https://us.api.knowbe4.com/v1/training/enrollments?campaign_id=$($_.campaign_id)?per_page=500"
Token
$enrollments = $null; $enrollments = (curl.exe --url $URL --header $authorizationHeader --silent | ConvertFrom-Json)
$enrollments = $enrollments | select campaign_name, completion_date, content_type, enrollment_date, enrollment_id, module_name, policy_acknowledged, start_date, status, user_status, user, id, first_name, last_name, email
$enrollments | % {
$_.id = $_.user.id
$_.first_name = $_.user.first_name
$_.last_name = $_.user.last_name
$_.email = $_.user.email
}

$PastDue += $enrollments | ? { $_.status -eq "past due" } | select campaign_name, enrollment_date, @{ Name='duration';Expression ={ $_relative_duration } }, days_late, module_name, status, user_status, id, first_name, last_name, email } 
$ErrorActionPreference = $EAP

$PastDue | sort campaign_name, email  | % {
$duration_period = (($_.duration).Trim()).Split(' ')
If ($duration_period[1] -match "weeks")  { $_days = ((Get-Date).AddDays(-([int]$duration_period[0] *7)) - [Datetime]"$($_.enrollment_date)").Days } 
If ($duration_period[1] -match "months") { $_days = ((Get-Date).AddMonths(-([int]$duration_period[0])) - [Datetime]"$($_.enrollment_date)").Days }
$_.days_late = "  $_days "
$_.enrollment_date = [DateTime]"$($_.enrollment_date)"
}
# Use the user 'Active', or 'Archived'?
$PastDue | % { $StatusURL = "https://us.api.knowbe4.com/v1/users/$($_.id)"; Token; $_.user_status = (curl.exe --url $StatusURL --header $authorizationHeader --silent | ConvertFrom-Json).Status }

''; $PastDue | ? {$_.user_status -eq "Active" -and $_.campaign_name -NOTmatch "The.Inside.Man"} | sort campaign_name, email | select campaign_name,duration,days_late,status,user_status,id,first_name,last_name,email | ft

01 July, 2021

User info Function

I call this function into both Console, and ISE profiles.
It will pull in the contents of the clipboard, and pull in pertinent user info.
For me - The users location is really helpful, but It will also show if a users account is locked, the password, or the account is expired, etc...

If the clipboard contents don't work, it will rerun the function and prompt...



Function Office {

$EAP = $ErrorActionPreference 
$ErrorActionPreference = "SilentlyContinue"
$Who = Get-Clipboard

if ($Who -eq "x") {Write-Host " - Exiting..."; break}
If ($Who -is [array]) {
$Who =  [string]::Join(" ",$Who)
}
If (!$Who -or $Who.Length -gt 30) {
Write-Host "Full name, username, or email address? " -NoNewline -ForegroundColor Yellow -BackgroundColor DarkMagenta
Write-Host "('X' to exit)" -NoNewline -ForegroundColor Cyan -BackgroundColor DarkMagenta
Write-Host ":" -NoNewline -ForegroundColor Yellow -BackgroundColor DarkMagenta; Write-Host " " -NoNewline
$Who = (Read-Host).Trim()
}
If ($Who -eq "x") {break}
If ($Who) {$Who = "$(($Who).Trim())"}

If (!$Who) {Write-host "Nothing to look for / Nothing entered." -ForegroundColor Yellow -BackgroundColor Black; break}
$Quality = 0
If ($Who -NOTmatch " " -and $Who -NOTmatch "@") { $Quality = 1; Write-Host "Looking for this username: $Who" -ForegroundColor Cyan }
If ($Who -match "@") { $Quality = 2; Write-Host "Looking for this email address: $Who" -ForegroundColor Cyan }
If ($Who -match " ") { $Quality = 3; Write-Host "Looking for this name: $Who" -ForegroundColor Cyan }

Function ADU1 {
$ErrorActionPreference = "SilentlyContinue"
Get-ADUser -filter * -Properties City, State, 
telephoneNumber, mail, Title, Manager, Description, 
PasswordLastSet, LastLogonDate, PasswordExpired, PasswordNeverExpires, 
lockoutTime, LockedOut, AccountExpirationDate
$ErrorActionPreference = "Continue"
} 
$Output = $null
If ($Quality -eq 1) { $Output = ADU1 | ? {$_.SamAccountName -match $Who} }
If ($Quality -eq 2) { $Output = ADU1 | ? {$_.mail -match $Who} }
If ($Quality -eq 3) { $Output = ADU1 | ? {$_.Name -match $Who} }

If (!$Output) {Write-host "`'$Who`' is not valid." -ForegroundColor Yellow -BackgroundColor Black
Set-Clipboard $null
office
}

$Pwd_Age = "$( ( (Get-Date)-($Output.PasswordLastSet) ).Days ) day(s), $(((Get-Date)-($Output.PasswordLastSet) ).Hours) hour(s), $(((Get-Date)-($Output.PasswordLastSet) ).Minutes) minute(s) ago"

$lockoutTime = $Output.lockoutTime
If ($lockoutTime -gt 0) {$lockoutTime = [datetime]::FromFileTime($($lockoutTime))}

$Output = $Output | Select Name, `
@{Name='Username';Expression ={($_.SamAccountName).ToUpper()}}, `
@{Name='Desc';Expression ={(($_.Description).Replace('|','-'))}},`
@{Name='Location';expression={"$($Output.City), $($Output.State)"}},` `
@{Name='Phone';Expression={$("{0:# ###-###-####}" -f [int64](($_.telephoneNumber).Trim('+')))}}, `
Mail, Title, `
@{Name='Manager';Expression ={"$((Get-ADUser ((get-aduser ($_.SamAccountName) -Properties Manager).Manager) | Select `
    @{name="Manager";expression={"$($_.Name) ($(($_.SamAccountName).ToUpper())) - $($_.UserPrincipalName)"}}).Manager)"}}, `
@{name="Last Logon";expression={$_.LastLogonDate}},`
@{name="Lockout time";expression={$lockoutTime}},` 
@{name="Locked out";expression={$_.LockedOut}},` 
@{name="Pwd No Exp";expression={$_.PasswordNeverExpires}},`
@{name="Expiration";expression={$_.AccountExpirationDate}},` 
@{name="Pwd Last Set";expression={$_.PasswordLastSet}},` 
@{name="Pwd Age";expression={$Pwd_Age}},` 
@{name="Pwd Expired";expression={$_.PasswordExpired}}

Write-Host " "

($Output | Out-String).Split("`n|`r",[System.StringSplitOptions]::RemoveEmptyEntries)  | % {
If ($_ -like "Name *") {Write-Host "   $_ ($($Output.Username))"}
If ($_ -like "Location *" ) { Write-Host "     $(($_).Replace('  :',':'))" -ForegroundColor Green }
If ($_ -like "Desc *" -and $Output.Desc -ne $null) {Write-Host "   $_"}
If ($_ -like "Phone *" -and $Output.Phone -ne $null) {Write-Host "   $_"}
If ($_ -like "Mail *") {Write-Host "   $_"}
If ($_ -like "Title *") {Write-Host "   $_"}
If ($_ -like "Manager *") {Write-Host "   $_"}
If ($_ -like "Last Logon *") {Write-Host "   $_"}
If ($_ -like "Expiration *" -and $Output.Expiration -ne $null) { Write-Host "   " -No; Write-Host "$_"  -Fore Red -Back Yellow }
If ($_ -like "Locked out *" -and $Output."Lockout time" -gt 0) {Write-Host "   $_"}
If ($_ -like "Last Logon *" -and $Output."Lockout time" -gt 0) {Write-Host "   $_"}
If ($_ -like "Pwd Last Set *" -and $Output."Pwd No Exp" -ne $True) {Write-Host "   $_" -No}
If ($_ -like "Pwd Expired *" -and $Output."Pwd Expired" -eq $True) {Write-Host " <EXPIRED> " -Fore Red -Back Yellow -No}
If ($_ -like "Pwd Age *" -and $Output."Pwd No Exp" -ne $True) {Write-Host " [$($Output.'Pwd Age')]" -Fore Yellow}
}

If ($host.name -eq "ConsoleHost") {''}
Set-Clipboard $null
$ErrorActionPreference = $EAP 

} # END Function Office {

set-alias -name ofc -value Office