Archive for Security

Copying AD User Group Permissions with PowerShell

June 6th, 2017 by and tagged , , ,

One of the tasks that I’m often asked to perform as an Active Directory domain administrator is to assign a user the same set of permissions as an existing user. This is something you can do fairly easily in the GUI (Active Directory Users and Computers, dsa.msc) when you’re first creating the user, but which is a pain if the target user already exists. Turns out PowerShell can help with this, of course.

First, you need to get the list of groups that the template or source user ($TemplateUser) is a member of. That’s fairly simple:

$UserGroups =@()
$UserGroups = (Get-ADUser -Identity $TemplateUser -Properties MemberOf).MemberOf

A couple of important points in the above:

  • First, you should create the empty array first. That tells PowerShell that you’re going to be creating a list of groups, not a single one. You can often get away without doing this at the command line because of PowerShell’s command line magic, but in a script, you need to be explicit.
  • Second, you need to include the MemberOf property in the Get-ADUser query. By default, that isn’t returned and you’ll end up with an empty $UserGroups variable.

So, you’ve got a list of groups. If you’re just doing an “additive” group membership change, all you need to do is add the target user ($TargetUser) to the all the groups. However, if you want to exactly match the group memberships, you need to first remove the target user from any groups s/he is part of before adding groups back. To do that, we need to first find out what groups the target user is currently in with much the same command as above:

$CurrentGroups = @()
$CurrentGroups = (Get-ADUser -Identity $TargetUser -Properties MemberOf).MemberOf

Now, we can remove the user from all current groups with:

foreach ($Group in $CurrentGroups) {
    Remove-ADGroupMember -Identity $Group -Members $TargetUser

Notice in the above that -Identity is the identity of the group, not the user. This is because we’re acting on the groups, not acting on the user(s).

Finally, we can now add $TargetUser back in to the groups that $TemplateUser had with:

foreach ($Group in $UserGroups) {
    Add-ADGroupMember -Identity $Group -Members $TargetUser

All of this, of course, happens quietly with no confirmation. So, just to verify that everything went as expected, use:

(Get-ADUser -Identity $TargetUser -Properties MemberOf).MemberOf

And you should get back a list of user groups the target user is now a member of.

Note: If you’re including this code in a new user script, you won’t need to remove the user from current groups, merely add them to the same groups as the template user.

Posted in Active Directory, IT Admin, PowerShell, Security | Comments Off on Copying AD User Group Permissions with PowerShell

PowerShell: Sending password expiration notices via GMail – Part 3

December 20th, 2016 by and tagged , , , , , ,

In Part 1 of this series, I showed you how to identify users whose password was about to expire. Then in Part 2 of the series, I took that list of users and sent email to them using gmail-hosted company email. This third part of the series pulls all that together into a single script, complete with comment-based help. As always, this and all my scripts are copyrighted, but you’re welcome to use them as the basis for your own scripts. However, I do appreciate attribution. Thanks, and enjoy.

Sends a "Password Expiring" warning email through TreyResearch's gmail. 
Send-TreyPasswordExpiryNotice first creates a list of accounts whose password will expire in the 
near future (default is 1 week). It then emails the users to warn them that their password will expire soon. 

This initial version runs interactively only. 
Sends a warning notice to all TreyResearch users whose password will expire in the next 7 days or less.
Send-TreyPasswordExpiryNotice -Logging
Sends a warning notice to all TreyResearch users whose password will expire in the next 7 days or less, and 
creates a log file that is echoed to the console at the end. 
Send-TreyPasswordExpiryNotice -DaysWarning 14
Sends a warning notice to all TreyResearch users whose password will expire in the next 14 days or less.
Send-TreyPasswordExpiryNotice -DaysWarning 5 -Logging -Testing -Verbose
Does NOT send a warning notice to TreyResearch users, but rather processes the first user and sends a notice
to the admin user(s) and writes to the log file. The -Verbose switch will make it additionally chatty.
.Parameter DaysWarning
The number of days advanced warning to give users whose passwords are close to expiration. 
The default is 7 days or less. 
.Parameter Logging
Switch to enable logging. Logs are written to C:\Temp\emaillogs.csv. When this switch is true, 
Send-TreyPasswordExpiryNotice outputs a table with a list of accounts due to expire as well as 
writing to a log file. 
.Parameter Testing
Switch to enable testing. When enabled, email is sent to a list of Admin users and only a single account is processed. 
    Author: Charlie Russel
  ThanksTo: Robert Pearman (WSSMB MVP),Jeffrey Hicks (PS MVP)
 Copyright: 2016 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 06 Sept, 2016 (cpr)
          : 09 Dec,  2016 (cpr) -(Ver 1.5) -- Reworked: Only process users who need reminding. Formatting changes
     $DaysWarning = 7, 

#Set parameters for gmail.
$smtpServer  =""
$SMTPPort    = 587
$from        = "IT Notification <>"
$AdminUser1  = ""
$AdminUser2  = ""
$AdminUser3  = ""
$externalUser= ""

# Cast this to a list of strings to allow for multiple test recipients
[string[]]$testRecipient = $AdminUser1,$AdminUser2,$AdminUser3

 This uses a stored password sitting on a local hard drive. This is a reasonably
 secure way to work with passwords in a file, and is ONLY accessible by the user that created 
 it. Create the password with: 
   PSH> Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File $home\Documents\TreyPW.txt

 See blog post at: for
 full details. 

 Alternately, simply prompt for the credentials here with Get-Credential.


$TreyUsr = ""
$TreyPW = Get-Content $Home\Documents\TreyPW.txt | ConvertTo-SecureString
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $TreyUsr, $TreyPW

# Check Logging Settings 
if ($Logging) { 
   $logFile = "C:\Temp\emaillogs.csv"
   if (! (Test-Path "C:\Temp") ) {
      Write-Verbose "No C:\Temp directory, so creating one..."
      New-Item -Path "C:\" -Name Temp -ItemType Directory

    # Remove Logfile if it already exists
    If ( (Test-Path $logFile)) { 
      Remove-Item $logFile 
    # Create CSV File and Headers 
    New-Item -Path $logfile -ItemType File 
    Add-Content $logfile "Date,Name,EmailAddress,DaysLeft,ExpiresOn,Notified" 

# System Settings 
$textEncoding = [System.Text.Encoding]::UTF8 
$date = Get-Date -format "MM/dd/yyyy"

# Explicitly import the Active Directory module, but get rid of the noise if it's already loaded. 
Import-Module ActiveDirectory 4>$NULL

# Use the following to query the domain for who the PDC Emulator role holder is. 
$TreyDC = (Get-ADDomain -Identity "" -Credential $Cred).PDCEmulator

# Send a cc: to myself or a list of users
$AdminUser = ""
$cclist = @($AdminUser)

# Do calculations outside the ForEach loop whenever possible
$maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy -Server $TreyDC -Credential $Cred).MaxPasswordAge
$today = (get-date) 

# Notice this doesn't get Expired or NeverExpires users. Don't want to send them emails.  
$TreyUsers = Get-ADUser -filter * `
                    -properties Name,PasswordNeverExpires,PasswordExpired,PasswordLastSet,EmailAddress `
                    -Server $TreyDC `
                    -Credential $Cred `
         | where { $_.Enabled -eq $True `
             -AND  $_.PasswordNeverExpires -eq $False `
             -AND  $_.passwordexpired -eq $False `
             -AND  $_.EMailAddress `
             -AND  (($today - $_.PasswordLastSet).Days -ge ($MaxPasswordAge.Days - $DaysWarning))
<# Get notification credentials. Prompt with Get-Credential if not using stored creds. 
$gCred = Get-Credential -UserName "" `
                        -Message  "Enter Password for IT-Notification account"
$gUsr = ""
$gPW = Get-Content "$Home\Documents\itnotificationsPW.txt" | ConvertTo-SecureString
$gCred = New-Object System.Management.Automation.PSCredential -ArgumentList $gUsr, $gPW

# Now, we start to do the work. 
foreach ($user in $TreyUsers) { 
    Write-Verbose "Processing user $user"
    $Name = $user.Name 
    $Email = $user.emailaddress 
    $SAM = $user.SAMAccountName
    $sent = " " 
    $passwordSetDate = $user.PasswordLastSet 
    Write-Verbose "$SAM last set their password on $PasswordSetDate"

    $expiresOn = $passwordSetDate + $maxPasswordAge 
    $DaysLeft = (New-TimeSpan -Start $today -End $Expireson).Days 
    if (($DaysLeft) -gt "1") { 
        $MessageDays = "in " + "$DaysLeft" + " days." 
    } else { 
        $MessageDays = "today!" 
    # Email Subject Set Here 
    $subject="Your password will expire $messageDays" 
    Write-Verbose "$Name`'s password will expire $messageDays"
    # Email Body Set Here, Note You can use HTML, including Images. 
    # This uses PowerShell's here-string. 
$body =@" 
Dear $name, 
<p>Your Active Directory Domain credentials <b>will expire $messagedays</b> 
Please update your credentials as soon as possible! <br> </p>
<p>If you are using a Windows domain joined system and are connected to the intranet, 
press ctrl-alt-delete and select change password. Alternatively, if you are outside of the 
network, connect to the corporate VPN and reset your password with the same process.<br> </p>
<p>If you are not using a Windows based system, ensure you are on the intranet or connected to 
the corporate VPN.  Proceed to <> 
and reset your password.<br> </p>
<p>This process will also sync your newly created AD password to your Gmail password. Please 
allow up to 5 minutes for replication of the passwords to occur.<br><br> </p>
<p><br><b>Problems</b>? <br>Please open a Service Desk request by clicking on the 
Help Agent icon on your system. If you are NOT running a Help Agent, please contact a member
of the IT Team for instructions on how to install the agent. It is a strict TreyResearch 
company policy that all company-owned systems run the Help Agent. <br></p>
<p>Thanks, <br>  
IT Team

    # If Testing Is Enabled - Email Administrator 
    if ($testing) { 
        $email = $testRecipient 
        $Subject = "PasswordExpiration Test Message"
        $cclist = $AdminUser2,$externalUser

   # Send Email Message 
    Write-Verbose "$SAM's password is due to expire in $DaysLeft which is less than the "
    Write-Verbose "DaysWarning Parameter setting of $DaysWarning days."

    # I've left this as a straight output to the host. If you want it quieter, make it a Write-Verbose
    "Sending Email Message to $email using $gUsr account"
    Send-Mailmessage -smtpServer $smtpServer `
                     -from $from `
                     -to $email `
                     -cc $cclist `
                     -subject $subject `
                     -body $body `
                     -bodyasHTML `
                     -priority High `
                     -Encoding $textEncoding `
                     -UseSSL `
                     -port $SMTPPort `
                     -Credential $gCred 
    $sent = "Yes"  # Used for logging
    if ($Logging) {
        Add-Content $logfile "$date,$Name,$email,$DaysLeft,$expiresOn,$Sent"  
   if ($Testing) {
       "Sleeping 5, then breaking so we only process a single record"
       Sleep 5

If ($Logging) { 
   # Use the logging file to display a table of the accounts about to expire. 
   $expiringAccts = Import-Csv -Path $logfile
   $expiringAccts | Sort-Object -Property ExpiresOn `
                  | Format-Table -AutoSize `
                           width=7}, `
                    @{Expression={(Get-Date -Date $_.ExpiresOn -Format 'MMMM dd')};`
                           Label="Expires On:"}

ETA: Minor bug fix (-Identity instead of -Identify. Sheesh!)

Posted in Active Directory, Network Administration, PowerShell, PSCredential, Security | Comments Off on PowerShell: Sending password expiration notices via GMail – Part 3

Remote Desktop Protocol Vulnerability

March 15th, 2012 by and tagged , , ,

I don’t usually bother to write about Microsoft patches. After all, they happen every month, and we all are in the habit of patching (updating) moderately regularly. However, the update in MS12-020, described in this post, is both serious, and very likely to be a target in the very near future, though there aren’t any known exploits. Yet.

First, download and install the patch. That’s the obvious fix. However, if there is some particular reason why you can’t install the patch immediately, then you should configure all clients and servers that have Remote Desktop enabled to require Network Level Authentication (NLA). Microsoft has even made it easy for you. At the bottom of the TechNet post are links to apply a registry change to enforce Network Level Authentication. If you’ve got Windows XP or Windows Server 2003 clients, there’s even a Fix-It to turn on CredSSP so that they can connect using NLA.

Finally, you can configure group policy to require NLA on your network (and really, isn’t it about time you did?) Instructions are here:

I know I was lazy about it for a long time, even after pretty much all my downlevel clients were long gone. But now that GPO is set and enabled. And my SBS WSUS server is sending that patch to every computer in my network. No playing around with this one, folks.

Posted in Patching, RD Session Host, Security | 1 Comment »