Local Site Found by a Universal device

Local Site Found by a Universal device

We have multiple locations with backup hosted sites. These sites are designed to allow the end-user to access the information they need when the network is down. They are set up with a local database and some basic asp.net coding. However, the devices that communicate with the site must be able to go between multiple locations. This means no hard-coded links as the hosting device has a different name at each location. DNS might not be available as well. The chaos this brings! There is a solution though. Here are the requirements for the solution we created.

  1. The hosting server can be a different name, but the IP address must end in the same number. In our example, it is .5
  2. The IP address must be a /24 subnet.
  3. The /site.html must be the same across the board.
    1. For example, the site can be http://10.10.1.5/thissite.html at one location and be http://10.10.13.5/thissite.html at another.

lets look at the powershell script really quickly and break it down.

The Script

$IPaddress = (Get-NetIPConfiguration -Detailed | Where-Object {$null -ne $_.IPv4DefaultGateway})[0].ipv4address.ipaddress
Start-Process -FilePath 'C:\Program Files\Internet Explorer\iexplore.exe' -ArgumentList "https://$($IPaddress.split('.')[0]).$($IPaddress.split('.')[1]).$($IPaddress.split('.')[2]).5/login.aspx"

What we are doing with this script is grabbing a detailed report on all the network IP configurations on the local computer. By default, an active network will have a default gateway assigned to it. This can be statically assigned or it can be automatically assigned. Either way, it goes, it’s assigned.

To narrow down the results we use a where-object command to compare null to the IPv4DefaultGateway. If it is null, we don’t want it. If it isn’t, then we want it. After that, we ask for the first result using the [0]. Most companies use the first network as the main network and the second network as a backup. That’s what we do at our company. Also most of the time the second network is a wifi network.

Next, we Ask for the IPv4Address and ask for just the IP address with .IPv4Address.IpAddress. Once we have all that information, we place it into a variable.

Now it’s time to start IE. You can set the browser to whatever program you like, but for now, we are still using IE for grandpa’s coded sites. We do this using the Start-Process command. We tell start-process the file path. This is where you can set google chrome, a 64 bit, edge, etc… It doesn’t matter what the browser is, as long as you point to one.

The Argument List is an important factor here. We give the site information here. We start off with our “https:// Then we do magic. We $($IP.address.split(‘.’)[0]. The act of using two $ is very important inside a double-quoted string. The reason why is we are stating everything inside $() is it’s own thing. We can run different commands inside $() and the output will output at that point in the string. Thus we are breaking the IP address string down by the . and finding each array point. Then after we create that point, we finish it off with a . and start our next $(). We do this until we reach our last octet. Which will be whatever the default IP address is. Finally finishing off with the site name. In this example, login.aspx. Below is the start process part of the command. Take a close look at the argument list. Assume the IP address is 10.10.1.15. This would start a site looking at “https://10.10.1.5/login.aspx”.

Start-Process -FilePath 'C:\Program Files\Internet Explorer\iexplore.exe' -ArgumentList "https://$($IPaddress.split('.')[0]).$($IPaddress.split('.')[1]).$($IPaddress.split('.')[2]).5/login.aspx"

The final step is to set your lnk to open the PowerShell script with a -bypass flag. This way it will execute. Some companies do not allow PowerShell to execute. In that case, this script does not help. You can however use the match version of this script found below.

@echo off
for /f "usebackq tokens=2 delims=:" %%f in (`ipconfig ^| findstr /c:"IPv4 Address"`) do set "ip=%%f"

for /f "tokens=1-4 delims=. " %%a in ("%ip%") do (
set octetA=%%a
set octetB=%%b
set octetC=%%c
set octetD=%%d
)

start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files\Internet Explorer\iexplore.exe" "https://%octetA%.%octetB%.%octetC%.5/login.aspx"
exit

Conclusion

The final idea is to have the link to point to an organic system that can read the current IP structure and point the link to the correct page. This will allow that single roaming device to find those local sites quickly assuming you have a pre-planned infrastructure in place.

Password Notifications

Password Notifications

I wanted to share a little script that has changed the world at my current company. It’s a simple password email notification script. What it does is grabs the users’ whos passwords about to expire. Then it emails that user with instructions on how to reset their password. Then it sends an email to the user’s manager with a list of when the user’s password will expire. Then finally it sends an email to another person with a complete list of all expiring users and when they will expire. After the user hits zero, the script no longer cares about that person. This is to prevent lots of headaches in the long run with on leave and whatnots. The best thing to do is to set up a scheduled task on a server with rsats. The computer will also need a relay setup on the exchange server.

The Script

param (
    [parameter(Helpmessage = "Return Email", Mandatory = $true)][string]$ReturnEmail,
    [parameter(Helpmessage = "Email Sent to IT", Mandatory = $true)][string]$ITEmail,
    [parameter(Helpmessage = "Mail Server", Mandatory = $true)][string]$MailServer,
    [parameter(Helpmessage = "Employee Organizational Unit", Mandatory = $true)][string]$EmployeeOU,
    [parameter(Helpmessage = "Training Link", Mandatory = $true)][string]$TrainingLink,
    [parameter(Helpmessage = "Training Link", Mandatory = $true)][string]$DaysWithinExpiration
)
$MaxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
$expiredDate = (Get-Date).addDays(-$MaxPwdAge)
$emailDate = (Get-Date).addDays( - ($MaxPwdAge - $DaysWithinExpiration))
$ExpiredUsers = Get-ADUser -Filter { (PasswordLastSet -lt $emailDate) -and (PasswordLastSet -gt $expiredDate) -and (PasswordNeverExpires -eq $false) -and (Enabled -eq $true) } -Properties DisplayName, PasswordNeverExpires, Manager, PasswordLastSet, Mail, "msDS-UserPasswordExpiryTimeComputed" -SearchBase "$EmployeeOU" | Select-Object DisplayName, samaccountname, manager, PasswordLastSet, @{name = "DaysUntilExpired"; Expression = { $_.PasswordLastSet - $ExpiredDate | Select-Object -ExpandProperty Days } }, @{name = "EmailAddress"; Expression = { $_.mail } }, @{Name = "ExpiryDate"; Expression = { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed") } } | Sort-Object PasswordLastSet
$ExpiredUsers
foreach ($ExpiredUser in $ExpiredUsers) {
    $ReturnHTML = @" 
<html> 
<head>
<style>
body {
    Color: #252525;
    font-family: Verdana,Arial;
    font-size:11pt;
}
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
</style>
</head>
<body>
Dear $($ExpiredUser.DisplayName),<br><br>
Your password was last set on <i>$($ExpiredUser.PasswordLastSet)</i>. This means your password will expire in <b>$($ExpiredUser.DaysUntilExpired) Days</b>. Please reset your password before <b>$($ExpiredUser.ExpiryDate)</b><br>
<ol>
<li>Find a computer connected to the network</li>
<li>if the computer is logged in, on your keyboard press, <b>ctrl+alt+del</b> at the same time. </li>
<li>Select <b>Change Password</b></li>
<li>replace the username section with <b>$($ExpiredUser.samaccountname)</b>.</li>
<li>Enter your old password in the old password location.</li>
<li>Enter a new password in the new password location.</li>
<li>Confirm your new password in the confirm password location</li>
<li>Click Enter</li>
</ol>
<br>
<hr>
<br>
For more Information on how to change your password Please watch these videos.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
    Send-MailMessage -To $($ExpiredUser.EmailAddress) -From "$ReturnEmail" -Subject "$($ExpiredUser.DisplayName) Password Notification" -BodyAsHtml $ReturnHTML -SmtpServer $MailServer
}
$Managers = $ExpiredUsers | Select-Object -ExpandProperty manager -Unique
foreach ($Manager in $Managers) {
    $ReportingtoHTML = $ExpiredUsers | Where-Object { $_.manager -eq $Manager } | Select-Object -Property DisplayName, @{name = "EmailAddress"; Expression = { $_.mail } }, @{label = "DaysUntilExpired"; expression = { "$($_.DaysUntilExpired) Days" } }, ExpiryDate | ConvertTo-Html -Fragment -As Table
    $Manager = Get-ADUser $Manager -Properties DisplayName, PasswordNeverExpires, Manager, PasswordLastSet, "msDS-UserPasswordExpiryTimeComputed" | Select-Object DisplayName, samaccountname, mail, manager, PasswordLastSet, @{name = "DaysUntilExpired"; Expression = { $_.PasswordLastSet - $ExpiredDate | Select-Object -ExpandProperty Days } }, @{name = "EmailAddress"; Expression = { $_.mail } }, @{Name = "ExpiryDate"; Expression = { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed") } } | Sort-Object PasswordLastSet
    $ManagerReturnHTML = @" 
<html> 
<head>
<style>
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
body {
width:75%;
background-color:#ffffff;}
table {border-collapse: collapse; border: 1px solid rgb(45,41,38); text-align: Center;}
th {background-color: #f7a1af; text-align: Center;}
tr:nth-child(even) {background-color: #f2f2f2;}
tr {text-align: Center;}
td {border:1px solid rgb(45,41,38);text-align: Center;}
</style>
</head>
<body style="font-family:verdana;font-size:13"> 
Dear $($Manager.DisplayName),<br><br>
Here is a list of all employees that are reporting to you whose passwords are to expire within the next 14 days. Please have these employees reset their passwords <b>before</b> their expirydate. Please note that we have also sent an email to the employee with the instructions on how to reset their password.<br>
<br>
$ReportingtoHTML
<br>
The below videos have been sent to these employees. If the employee needs help, refer them to these videos or instruct them how to reset their passwords.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>
<br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
    Send-MailMessage -To $($Manager.EmailAddress) -From "$ReturnEmail" -Subject "User Password Notification" -BodyAsHtml $ManagerReturnHTML -SmtpServer $MailServer
}
$ITReturnHTML = $ExpiredUsers | Select-Object @{label = "Name"; expression = { $_.Displayname } }, @{Label = "UserName"; expression = { $_.samaccountname } }, EmailAddress, @{label = "Manager"; expression = { $(($_.Manager.Split(',')).split('=')[1]) } }, ExpiryDate, @{Label = "Days"; expression = { $_.DaysUntilExpired } } | ConvertTo-Html -Fragment -As Table
$ItReportReturnHTML = @" 
<html> 
<head>
<style>
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
body {
background-color:#ffffff;}
table {border-collapse: collapse; border: 1px solid rgb(45,41,38); text-align: Center;}
th {background-color: #54c6ff; text-align: Center;}
tr:nth-child(even) {background-color: #f2f2f2;}
tr {text-align: Center;width:20%;}
td {border:1px solid rgb(45,41,38);text-align: Center;}
</style>
</head>
<body style="font-family:verdana;font-size:13"> 
Dear IT,<br><br>
Please see the expiring/expired password list below.
<br><br>
$ITReturnHTML
<br>
The below videos have been sent to these employees. If the employee needs help, refer them to these videos or instruct them how to reset their passwords.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>
<br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
Send-MailMessage -To $ITEmail -From "$ReturnEmail" -Subject "Password Expiry Notification" -BodyAsHtml $ItReportReturnHTML -SmtpServer $MailServer
$Admins = Get-ADUser -Filter { (samaccountname -like "*_adm") -and (PasswordLastSet -lt $emailDate) -and (PasswordLastSet -gt $expiredDate) -and (PasswordNeverExpires -eq $false) -and (enabled -eq $true) } -Properties *
Foreach ($Admin in $Admins) {
    $Username = $Admin.samaccountname -replace "(_.*)", ""
    $User = Get-ADUser -Identity $Username -Properties *
    $ReturnHTML = @" 
<html> 
<head>
<style>
body {
    Color: #252525;
    font-family: Verdana,Arial;
    font-size:11pt;
}
h1 {
    text-align: center;
    color:#C8102E;
    Font-size: 34pt;
    font-family: Verdana, Arial;
}
h2 {
    text-align: center;
    color:#9EA2A2;
    Font-size: 20pt;
}
h3 {
    text-align: center;
    color:#211b1c;
    Font-size: 15pt;
}
h4 {
    text-align: center;
    color:#242526;
    Font-size: 15pt;
}
a:link {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
a:visited {
    color:#C8102E;
    text-decoration: underline;
    cursor: auto;
    font-weight: 700;
}
</style>
</head>
<body>
Dear $($User.DisplayName),<br><br>
Your Admin password was last set on <i>$($Admin.PasswordLastSet)</i>. This means your password will expire in <b>$($admin.DaysUntilExpired) Days</b>. Please reset your password before <b>$($Admin.ExpiryDate)</b><br>
<ol>
<li>Find a computer connected to the network</li>
<li>if the computer is logged in, on your keyboard press, <b>ctrl+alt+del</b> at the same time. </li>
<li>Select <b>Change Password</b></li>
<li>replace the username section with <b>$($Admin.samaccountname)</b>.</li>
<li>Enter your old password in the old password location.</li>
<li>Enter a new password in the new password location.</li>
<li>Confirm your new password in the confirm password location</li>
<li>Click Enter</li>
</ol>
<br>
<hr>
<br>
For more Information on how to change your password Please watch these videos.<br>
<a href='$TrainingLink'>How to change your Password.</a><br><br>

Thank you<br>
IT Department
</body> 
</html> 
"@  
    Send-MailMessage -To $($User.EmailAddress) -From "$ReturnEmail" -Subject "$($User.DisplayName) Password Notification" -BodyAsHtml $ReturnHTML -SmtpServer $MailServer
}
PSBoundParameters – Credentials

PSBoundParameters – Credentials

The PSBoundParameter is an amazing tool that will clear your mind of worries about non-mandatory parameter sets. In my Super Help Desk module I use the credential flag. Inside this flag, I tell the system to use the credentials provided or the currently running credentials.

I do this by testing the $PSBoundParameter.ContainsKey(‘Credential’). What this basically does is test the bounded parameters coming in to see if it contains a keyword. In this case I’m looking for Credential.

if ($PSBoundParameters.ContainsKey('Credential')) {
    Do-Something -Credential $Credential
} else {
    Do-Something
}

By having the flag set inside the parameter area not set to mandatory, you can test to see if they want to use a special set of credentials or not.

[Parameter(HelpMessage = "Allows for custom Credential.")][System.Management.Automation.PSCredential]$Credential

You can use other parameters as well but the credentials are one of the best usages for $PSboundparameter.containskey(). I do hope you enjoy using this little trick in your future scripts! Let me know what you create.

SHD – Disable Inactive Users

SHD – Disable Inactive Users

Now we have a way to find the disable OU, and to disable a user, now it’s time to disable old accounts. We do this by targeting the Last Logon dates.

Word of warning before we continue. When you do this, target a single OU instead of all of the company. Target only enabled users as well. Targeting all of AD will cause you to disable service accounts and system accounts. This can cause your system to crash. Also, target enabled users as many companies us templates that are disabled.

Now we got the warning out of the way, lets get started. The code is very simple. The first step is to get the days back you want to go and find that date/time. We are going to us Get-Date and your input which we are calling $DaysBack.

$Time = (get-date).AddDays(-($DaysBack))

We get the Current date. Then we want to add days to that date, but backwards. So the (Get-Date) gives us the current date. Then .AddDays() will add the days. To make $DaysBack Negative, we simple multiple it by -1. if you completed algebra 1 in high school, you already see it. -($DaysBack) is the multiplication $DaysBack * -1.

Now we have the date we want to search the OU in question and get the users that haven’t log on since $DaysBack. We will do this with the simple Get-ADuser and the Where-Object commands.

$users = Get-aduser -Filter { Enabled -eq $true } -SearchBase $SearchOU -Properties LastLogondate | Where-Object {($_.LastLogonDate -le $Time) -and ($null -ne $_.LastLogonDate)}

We use the get-aduser filter to filter out everything that is enabled. Then we use the search base to search the OU we want to pull the information from. This saves us heartache of “oh crap I disabled my bosses test user.” Next, we pass it through the Where-object. We filter out everyone who last logon date that is less than or equal to the time we set earlier. We also search if nothing is not equal to the last logon date. We do this part because if an account hasn’t logged into the system yet, it will appear. Doing this avoids removing new hires on accident. Another note on the $null -ne $_.lastlogondate, it’s faster to search like this instead of $_.lastlogondate -ne $null. I don’t know why, but it is much faster.

Now we have a list of users who we need to disable, we just start up a for loop and disable them using the Disable-SHDUser.

foreach ($User in $users) {
     Disable-SHDUser -Username $user.samaccountname -OU $DisabledOU 
}

After the loop is done, it’s done. They are all disabled. But David, I want to see who was disabled! I agree. So I added a simple ShowResults switch for this loop. This is for those who need to see the results.

foreach ($User in $users) {
     Disable-SHDUser -Username $user.samaccountname -OU $DisabledOU
     if ($ShowResults) {
          Get-aduser -Identity $user.samaccountname -Properties LastLogonDate,Memberof | Select-Object DistinguishedName,Samaccountname,Name,Lastlogondate,Enabled,@{l="GroupCount";e={$_.memberof.count}}
     }
}

Combining functions in a module is a great way to get things done. These last two blogs should have shown this to you. Now, lets look at the script itself.

The Script

function invoke-SHDDisableInactiveUsers {
    [cmdletbinding()]
    param (
        [parameter(HelpMessage = "Days after", Mandatory = $True)][int]$DaysBack,
        [parameter(HelpMessage = "Moves to this OU if provided, If not, finds disable ou and moves it.")][string]$DisabledOU,
        [parameter(HelpMessage = "Moves to this OU if provided, If not, finds disable ou and moves it.", Mandatory = $true)][string]$SearchOU,
        [Parameter(HelpMessage = "Allows for custom Credential.")][System.Management.Automation.PSCredential]$Credential,
        [parameter(HelpMessage = "Show results")][switch]$ShowResults
    )
    $Time = (get-date).AddDays(-($DaysBack))
    if ($PSBoundParameters.ContainsKey('Credential')) {
        $users = Get-aduser -Filter { Enabled -eq $true } -Credential $Credential -SearchBase $SearchOU -Properties LastLogondate | Where-Object {($_.LastLogonDate -le $Time) -and ($null -ne $_.LastLogonDate)} | Sort-Object Samaccountname
        if ($PSBoundParameters.ContainsKey('DisabledOU')) {
            $DisabledOU = $DisabledOU
        }
        else {
            $DisabledOU = Find-SHDDisabledUsersOU -Credential $Credential
        }
        foreach ($User in $users) {
            Disable-SHDUser -Username $user.samaccountname -OU $DisabledOU -Credential $Credential 
            if ($ShowResults) {
                Get-aduser -Identity $user.samaccountname -Properties LastLogonDate,Memberof -Credential $Credential | Select-Object DistinguishedName,Samaccountname,Name,Lastlogondate,Enabled,@{l="GroupCount";e={$_.memberof.count}}
            }
        }
    }
    else {
        $users = Get-aduser -Filter { Enabled -eq $true } -SearchBase $SearchOU -Properties LastLogondate | Where-Object {($_.LastLogonDate -le $Time) -and ($null -ne $_.LastLogonDate)} | sort-object Samaccountname
        if ($PSBoundParameters.ContainsKey('OU')) {
            $DisabledOU = $DisabledOU
        }
        else {
            $DisabledOU = Find-SHDDisabledUsersOU
        }
        foreach ($User in $users) {
            Disable-SHDUser -Username $user.samaccountname -OU $DisabledOU
            if ($ShowResults) {
                Get-aduser -Identity $user.samaccountname -Properties LastLogonDate,Memberof | Select-Object DistinguishedName,Samaccountname,Name,Lastlogondate,Enabled,@{l="GroupCount";e={$_.memberof.count}}
            }
        }
    }
}

Next time I will explain the PSBoundParameters that you see in almost all scripts I run. It gives me the ability to give you the ability to have credentials but not require it.

If you have any questions, feel free to email me. These functions can be found on my git hub.

SHD – Disable User

SHD – Disable User

My last blog was about how to find the disabled user OU. Now we will go over how I disable users and move them around to the disabled OU. The next blog will combine all this together and remove inactive accounts that are within a set OU. Let’s rock this!

First first step is to get the disabled OU. In this script, you can either add it yourself or allow it to find the OU for you. It uses the Find-SHDDisabledUsersOU to find it for you.

The next step is to get the user info. We do this with the simple command, Get-ADuser.

$TargetUser = Get-ADUser -Identity $user -Properties * 

Now, I believe in removing a user from all Groups that it can be removed from. By default, the default group can’t be removed only reassigned. So, I will not go through the process of reassigning the default group. If that is part of your disabling process, you may modify this code to add it. So, lets remove all the groups.

$Targetuser.memberof | foreach-object { Remove-ADGroupMember -Identity $_.DistinguishedName -Members $targetuser.samaccountname -Confirm:$false }

We list all the memberof of the target user. Then we start a foreach-object. Inside the foreach-object we have a remove-adgroupmember. Notice the Identity tag is using the $_.DistinguishedName. This prevents errors and makes things generally faster. Only by a few milliseconds. Those milliseconds do add up over time. Then we tell it to remove the Samaccountname of the target user. The final step is what devides automation from “Damn confirmation boxes” -Confirm:$False. This flag surpresses the need to confirm everything. Thus clearing out all the groups. On average this takes about 1 second for me whie on network.

The next step is to clear all the information from the user account. 90% of the time the users that do come back, come back as something else. Most of the time, they don’t come back. We do this with a Set-ADuser command.

Set-ADUser -Identity $TargetUser.samaccountname -Department '' -Title '' -City '' -Company '' -Country '' -Description '' -Division '' -EmailAddress '' -EmployeeID '' -EmployeeNumber '' -Fax '' -Enabled $false -HomeDirectory '' -HomeDrive '' -HomePage '' -HomePhone '' -OtherName '' -Manager '' -LogonWorkstations '' -MobilePhone '' -Office '' -OfficePhone '' -Organization '' -POBox '' -PostalCode '' -ProfilePath '' -ScriptPath '' -State '' -StreetAddress '' 

The set-aduser command is straight forward. It uses the Target user samaccountname and clears anything I can. Now we will reset the password.

$Password = -join ((32..95) + (97..126) | Get-Random -Count 90 | ForEach-Object { [char]$_ })

Set-ADAccountPassword -Identity $TargetUser.Samaccountname -Reset -NewPassword (ConvertTo-SecureString -AsPlainText -String $Password -Force) 

We want the length of the password to be 90 characters long. We then start a loop and join the characters we want (Password-safe characters) and get the random of these characters. Then We loop through that to get our password. After that, we push this password into the password reset command. Set-ADAccountPassword.

Notice in the password reset command, we use the flag Reset. Then NewPassword. Notice with the newpassword tag we have a converto-securestring.

ConvertTo-SecureString -AsPlainText -String $Password -Force

We run the text as plain text and force the string. This gives us a secure password to use. As the admin, we don’t need to know the password as we can reset it later.

Now we disable the account with disable-adaccount.

Disable-ADAccount -Identity $TargetUser.samaccountname

Disable-ADAccount is very straight forward. You give it the identity of the user with the targetuser.samaccountname and your done.

The final step is to move the user to the Disabled OU. We do this with Move-ADObject.

Move-ADObject -Identity $TargetUser.samaccountname -TargetPath $DisabledOU

We are telling the system to move the AD user to the disabled ou path. Now we are done. From here you can have an email sent or any other notification information if you want to see the output. Ok, it’s time. lets put it all together.

The Script

Function Disable-SHDUser {
    <#
    .SYNOPSIS
    .DESCRIPTION
    .PARAMETER
    .EXAMPLE
    .INPUTS
    .OUTPUTS
    .NOTES
    .LINK
    #>
    [cmdletbinding()]
    param (
        [Parameter(
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            HelpMessage = "Provide the target hostname",
            Mandatory = $true)][Alias('Hostname', 'cn')][String[]]$Username,
        [parameter(HelpMessage = "Moves to this OU if provided, If not, finds disable ou and moves it.")][string]$OU,
        [Parameter(HelpMessage = "Allows for custom Credential.")][System.Management.Automation.PSCredential]$Credential
    )
    
    if ($PSBoundParameters.ContainsKey('Credential')) {
        if ($PSBoundParameters.ContainsKey('OU')) {
            $DisabledOU = $OU
        } else {
            $DisabledOU = Find-SHDDisabledUsersOU -Credential $Credential
        }
        
        foreach ($user in $Username) {
            $TargetUser = Get-ADUser -Identity $user -Properties * -Credential $Credential 
            $Targetuser.memberof | foreach-object { Remove-ADGroupMember -Credential $Credential -Identity $_.DistinguishedName -Members $targetuser.samaccountname -Confirm:$false }
            Set-ADUser -Identity $TargetUser.samaccountname -Department '' -Title '' -City '' -Company '' -Country '' -Description '' -Division '' -EmailAddress '' -EmployeeID '' -EmployeeNumber '' -Fax '' -Enabled $false -HomeDirectory '' -HomeDrive '' -HomePage '' -HomePhone '' -OtherName '' -Manager '' -LogonWorkstations '' -MobilePhone '' -Office '' -OfficePhone '' -Organization '' -POBox '' -PostalCode '' -ProfilePath '' -ScriptPath '' -State '' -StreetAddress '' -Credential $Credential
            $Password = -join ((32..95) + (97..126) | Get-Random -Count 90 | ForEach-Object { [char]$_ })
            Set-ADAccountPassword -Identity $TargetUser.Samaccountname -Reset -NewPassword (ConvertTo-SecureString -AsPlainText -String $Password -Force) -Credential $Credential
            Disable-ADAccount -Identity $TargetUser.samaccountname -Credential $Credential
            Move-ADObject -Identity $TargetUser.samaccountname -TargetPath $DisabledOU -Credential $Credential
        }
    }
    else {
        if ($PSBoundParameters.ContainsKey('OU')) {
            $DisabledOU = $OU
        } else {
            $DisabledOU = Find-SHDDisabledUsersOU
        }
        foreach ($user in $Username) {
            $TargetUser = Get-ADUser -Identity $user -Properties * 
            $Targetuser.memberof | foreach-object { Remove-ADGroupMember -Identity $_.DistinguishedName -Members $targetuser.samaccountname -Confirm:$false }
            Set-ADUser -Identity $TargetUser.samaccountname -Department '' -Title '' -City '' -Company '' -Country '' -Description '' -Division '' -EmailAddress '' -EmployeeID '' -EmployeeNumber '' -Fax '' -Enabled $false -HomeDirectory '' -HomeDrive '' -HomePage '' -HomePhone '' -OtherName '' -Manager '' -LogonWorkstations '' -MobilePhone '' -Office '' -OfficePhone '' -Organization '' -POBox '' -PostalCode '' -ProfilePath '' -ScriptPath '' -State '' -StreetAddress '' -Credential $Credential
            $Password = -join ((32..95) + (97..126) | Get-Random -Count 90 | ForEach-Object { [char]$_ })
            Set-ADAccountPassword -Identity $TargetUser.Samaccountname -Reset -NewPassword (ConvertTo-SecureString -AsPlainText -String $Password -Force)
            Disable-ADAccount -Identity $TargetUser.samaccountname 
            Move-ADObject -Identity $TargetUser.samaccountname -TargetPath $DisabledOU 
        }
    }
} 
SHD – Find Disabled OU

SHD – Find Disabled OU

Have you ever started in a company and there was no documentation? The disabled OU isn’t named “Disabled Users” and things are just what the heck? This powershell script will help find that disabled user OU. Believe it or not, it’s a one liner.

((Get-ADUser -filter { enabled -eq $false }).Distinguishedname -replace '^CN=.*?,', '' | Group-Object | Sort-Object -Property Count -Descending | Select-Object -First 1).name

Lets tare this bad boy apart. First we have a Get-Aduser -filter { Enabled -eq $False}. So we want all the users in the company who are disabled. From there we are selecting only the DistinguishedName. We want to remove the first part of the DistinguishedName with a replace command. The Regex is ^CN=.*?,’,” Lets break this down.

.Distinguishedname -replace '^CN=.*?,', ''

^CN= tells us we are looking for the first “CN=” inside this string. Then we ask for the wild cards up to the first , with .*?. We tell it to replace it with nothing, aka double single quotes, .

| Group-Object

Now this gives us all the OUs that everyone who is disabled lives in. Next we group them together with Group-Object. Group-object is going to give us a clean count of each OU and how many unique items there are for each OU.

| Sort-Object -Property Count -Descending 

Next we want to organize everything with a Sort-Object. We select the count property and put it in descending order. This way we can select the first one in the final piece of the puzzle.

| Select-Object -First 1).name

Now we use the Select-object -First 1 command to get the first object from the descending list. This will give you the highest disabled users counted OU.

The Script

function Find-SHDDisabledUsersOU {
    [cmdletbinding()]
    param (
        [Parameter(HelpMessage = "Allows for custom Credential.")][System.Management.Automation.PSCredential]$Credential
    )
    if ($PSBoundParameters.ContainsKey('Credential')) {
        ((Get-ADUser -filter { enabled -eq $false } -Credential $Credential).Distinguishedname -replace '^CN=.*?,', '' | Group-Object | Sort-Object -Property Count -Descending | Select-Object -First 1).name
    }
    else {
        ((Get-ADUser -filter { enabled -eq $false }).Distinguishedname -replace '^CN=.*?,', '' | Group-Object | Sort-Object -Property Count -Descending | Select-Object -First 1).name
    }
}