Target all users in two OU's and remove Distri

2019-09-14 01:55发布

问题:

@BenH and @TheMadTechnician were extremely helpful in assisting me with a script, to remove Distro Lists (only) from users in specific AD OU's. I forgot to add a needed criteria, so decided to post this as a separate question (original thread here)

@BenH's approach was like this:

$OUs = 'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net','OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'
$Users = ForEach ($OU in $OUs) {
    Get-ADUser -Filter * -SearchBase $OU 
}

ForEach ($User in $Users) {
    Get-ADPrincipalGroupMembership -Identity $user |
    Where-Object {$_.GroupCategory -eq 0} |
    ForEach-Object {
        Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $_
    }
} 

My question - can I force the script to only take action on accounts that have expired more than 30days ago, by adding a variable and "Where-Object" logic to the first loop like this?:

    $OUs = 'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net','OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'
    $30DaysOld = (Get-Date).AddDays(-30)

    $Users = ForEach ($OU in $OUs) {
        Get-ADUser -Filter * -SearchBase $OU |
        Where-Object {$_.AccountExpirationDate -gt $30DaysOld}}

    ForEach ($User in $Users) {
    Get-ADPrincipalGroupMembership -Identity $user |
    Where-Object {$_.GroupCategory -eq 0} |
    ForEach-Object {
        Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $_
    }
} 

Possible? Or would I need to change the -gt to a -lt in order to get the correct date range?

Thanks for looking!

回答1:

Making an answer as requested. The problem is with the Where statement:

Where-Object {$_.AccountExpirationDate -gt $30DaysOld}

While logically is sounds like it's right 'where the account expired more than 30 days ago' it actually comes out to 'where the Date that the account expired is greater than what the Date was 30 days ago'. When you consider that some systems measure dates as seconds passed since the Unix Epoch (Jan 1, 1970 at 12:00:00 AM UTC), and dates are converted to integers, and it makes more sense that the -gt operator selects whichever date happens later chronologically as more seconds have passed since the epoch, and the integer is a larger number.

If you change the -gt to -lt it accomplishes what you're looking for. Also, adding -and $_.AccountExpirationDate to it makes sure that the AccountExpirationDate is not null. So we end up with:

Where-Object {$_.AccountExpirationDate -lt $30DaysOld -and $_.AccountExpirationDate}


回答2:

@TheMadTechnician nailed it - I needed to change the nested "where" statement to:

 Where-Object {$_.AccountExpirationDate -lt $30DaysOld -and $_.AccountExpirationDate}

So the functioning code is:

$30DaysOld = (Get-Date).AddDays(-30)
$OUs = 'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net','OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'

$Users = ForEach ($OU in $OUs) {
    Get-ADUser -Filter * -Properties AccountExpirationDate -SearchBase $OU  |
    Where-Object {$_.AccountExpirationDate -lt $30DaysOld -and $_.AccountExpirationDate}
    }

ForEach ($User in $Users) {
    Get-ADPrincipalGroupMembership -Identity $user |
    Where-Object {$_.GroupCategory -eq 0} |
    ForEach-Object {
        Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $_ -Confirm:$false
    }
} 

Hopefully TheMadTechnician will submit their comment as an answer so I can update and give karma where karma is due! If this solves a problem you're searching for, please upvote TheMadTechnician's comment!



回答3:

OP here - I've been continuing to work on this (with assistance), and added some additional embellishments that I thought someone else might find useful, so I wanted to share it back.

Please don't upvote this answer, TheMadTechnician and BenH deserve all the credit for breaking the back of this. Go upvote that answer, if you find this useful.

This will now write the names of distros removed to the AD account, use a semicolon separator (to cut/paste the names if you need to re-add the distros), and won't add clutter to the AD account if it's run against the account more than once.

Enjoy!

#  Variables

$30DaysOld = (Get-Date).AddDays(-30)
$OUs = (
  'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net',
  'OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'
)

#  Collect the needed users

$Users = ForEach ($OU in $OUs) {
  Get-ADUser -Filter * -Properties AccountExpirationDate,info -SearchBase $OU |
  Where-Object {$_.AccountExpirationDate -lt $30DaysOld -and $_.AccountExpirationDate}
}

#  Collect each user's Distro Lists & REMOVE

ForEach ($User in $Users) {
  $distrosremoved=@()

  Get-ADPrincipalGroupMembership -Identity $user |
  Where-Object {$_.GroupCategory -eq "distribution"} |
  ForEach-Object {
    $distrosremoved+=$_.name
    Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $_ -Confirm:$false
  }

  #  Collect info from the Telephone > Notes field, and ADD the list of Distros into the existing info

  if($distrosremoved){

      $distro_str="Removed Distro Lists: `r`n"+($distrosremoved -join "; ")

      if ($user.info){
        $newinfo=$user.info+"`r`n"+$distro_str
        Set-ADUser $user -Replace @{info=$newinfo}
      }else{
        $newinfo=$distro_str
        Set-ADUser $user -Add @{info=$distro_str}
      }
  }
}