Compare Active Directory directories, delete users not in list

Viewing 3 reply threads
  • Author
    Posts
    • #1557303
      carp
      Participant
      Member Points: 206
      Rank: 2

      Greetings all,

      I have been working this issue for well over a month now. I didn’t want to post, because I didn’t want to appear too dumb on this forum. You are all so smart and experienced. I thought I could figure this out on my own. I was wrong.
      Additionally, I would like to thank you all. Your experience published here has helped me very much in furthering my PS understanding.

      My question follows from the following facts:

      <initial script received from on-high>

      # Root-Ordner in dem die Userordner liegen
      $userroot = '\\wffs01\profile_old'
      # alle User aus dem AD lesen
      $users = get-aduser -Filter * | select -Expand SamAccountName
      gci $userroot | ?{$_.PSIsContainer -and ($_.Name -replace '\.(V2|V5|V6)$','') -notin $users}

      I have two directories:

      • A) old AD Users
      • B) current AD Users
      • Each of the subdirectories in A has an extension of .V2.
      • None of the subdirectories in B has an extension of .V2.
      • I have assumed from the beginning that I need to remove the .V2 extension before I can do a compare (not sure if this is the best solution).

      This is the main issue.

      The files / profile directories as seen in Windows Explorer:

      I read that directory contents into $oldADUsers:

      PS C:\WINDOWS\system32> $oldADUsers =gci $userroot | % basename
      PS C:\WINDOWS\system32> $oldADUsers

      AfsgrA.V2
      afsgrb.V2
      afsgrC.V2
      AfsGrD.V2
      afsgre.V2
      afsgrf.V2
      afsgrg.V2
      afsgrh.V2
      afstelgte.V2

      I have a list of current AD users, read into $currentrADUsers:

      PS C:\WINDOWS\system32> $currentADUsers = get-aduser -filter * | sort-object | select samaccountname

      afsgra
      afsgrb
      afsgrc
      afsgrd
      afsgre
      afsgrf
      afsgrg
      afstelgte


      I am stuck here.
      VARIABLES:

      • $userroot:     \\fileserver\profile_test\profile_old$ — Directory which holds old profile directories.
      • $oldADUsers  —  PS Variable which holds list of names of old AD users’ profile directories.
      • $currentADUsers  —  PS Variable which holds list of names of current AD users’ profile directories.

      I am stuck here.

      Here is my logic:

      1.       Read the names of the contents of the \profile_test directory into the variable $oldADUsers.

      2.       Read the names of the current AD Users into the variable $currentADUsers.

      3.       If there exists a profile directory name in $oldADusers which matches a profile directory name in $currentADUsers, then leave it be with no changes.

      4.       If there exists NO profile directory name in $oldADUsers which matches a profile directory name in $currentADUsers, then delete the profile directory recursively from $oldADUsers.

      5.       If there exists a profile directory name in $oldADUsers for which there is NO match in $currentADUsers, then take ownership of that user’s old profile, and then delete recursively the user’s old profile directory from \profile_test.

      PS C:\WINDOWS\system32> $userroot = '\\fileserver\profile_test$'
      PS C:\WINDOWS\system32> $currentADUsers = get-aduser -filter * | select samaccountname
      PS C:\WINDOWS\system32> $oldADUsers = gci $userroot | % basename

      At this point, I have two lists of profile directory names:

      • $oldADUsers
      • $currentADUsers

      where the individual entries in the $oldADUsers list have an extension of .V2, whereas the entries in the $currentADUsers list have no extensions.
      *****

      What I believe I need to do:

      1. Remove the extensions from the directory names in $oldADUsers,
      2. compare the directory names to those in $currentADUsers,
      3. take ownership of the selected directory and
      4. perform the recursive delete.

      I believe I will need a for-loop similar to:

      Foreach-Object {$_.Name -replace "\.(V2)",""}

      …but that won’t work, because I can’t replace with a null value.

      I am stuck here.

      Following is the code which I have used to get this far…..

      PS C:\WINDOWS\system32> $userroot = '\\fileserver\profile_old$'
      PS C:\WINDOWS\system32> $currentADUsers = get-aduser -filter * | select samaccountname
      PS C:\WINDOWS\system32> $oldADUsers =gci $userroot | % basename

      I have tried separating the values in the $oldADUsers list:

      $oldadv = $oldADUsers.split(".")

      …but then I have array indices to worry about, which I think only makes things more complicated than they need to be.

      I have tried the following, but it will break on other lists:

      PS C:\> $newADName = $oldADName.split("\.")
      PS C:\> $ADNamesingle = $newADName[0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40]
      PS C:\> write $ADNamesingle

      AfsgrA
      afsgrb
      afsgrC
      AfsGrD
      afsgre
      afsgrf
      afsgrg
      afsgrh
      afstelgte

      OTHER CODE WHICH I’VE USED in this script WITHOUT SUCCESS:

      $cool = gci $userroot | ?{$_.PSIsContainer -and ($_.Name -remove '\.(V2|V5|V6)$','')} ===> problem: -remove not recognised.
      $cool = gci $userroot | ?{$_.PSIsContainer -and ($_.Name -replace "\.(V2|V5|V6)","")} ===> does not remove extension.
      $Files = $oldadv
      ForEach ($File in $Files)
      {
      $File.FullName | Copy-Item -Destination ("Drive:\Path\" + ($File.Name -replace '(.V2)','') )
      }
      Compare-Object -ReferenceObject (Get-Content -Path 'C:\jc\temp\myusers.txt') -DifferenceObject (Get-Content -Path 'C:\jc\temp\oldadname.txt')
      
      get-childitem '\\wffs02\profile_testjc' -include *.V2 | foreach ($_) {remove-item $_.name}
      
      foreach-object in c:\jc\temp\myusers.txt [io.path]::GetFileNameWithoutExtension("c:\jc\temp\myusers.txt")
      
      foreach ($file in gci -literalpath "C:\Temp") { rni $file.FullName $file.Name.Replace("A","B") }
      
      foreach ($file in gci -literalpath "\\wffs02\profile_testjc$") { rni $file.FullName $file.Name.Replace("V2","Shit") -credential $credentials }

      I need help.

      Thank you in advance to any of you out there who take a look at this.

      I wish you all a nice start into your week.

      Best regards,
      -.-johnc

    • #1557340
      David Figueroa
      Participant
      Member Points: 4,096
      Rank: 3

      I think you’re probably making this more complicated than it needs to be.

      $OldProfDir = \\server\oldprofiles$
      $NewProfDir = \\server\newprofiles$
      
      Write-Verbose -Message 'First check the new directory against the old directory'
      
      Get-ChildItem -path $OldProfDir -directory | foreach-object {
        $OriginalDir = $_
        $DirName = $OriginalDir.BaseName.split('.')[0]
        $TestDir = Join-Path -path $NewProfDir -ChildPath $DirName
        if (Test-Path -path $TestDir ){
          Write-Verbose -Message ('User ({0}) has profiles in both directories, ignoring' -f $DirName)
        }
        else {
          Write-verbose -Message ('User ({0}) has no matching profile in {1}; deleting old profile' -f $DirName, $NewProfDir)
          takeown.exe /a /f /r /skipsl "$OriginalDir"
          try {
            Remove-Item -path $TestDir -recurse -force -erroraction stop
          }
          catch {
            Write-Warning -Message ('Unable to completely remove {0}' -f $OriginalDir)
          }
        }
      Write-Verbose -Message 'Now check the old directory against the new directory'
      Get-ChildItem -path $NewProfDir -directory | foreach-object {
        $NewDir = $_
        $DirName = $_.basename 
        $OriginalDir = '{0}\{1}.v2' -f $OldProfDir, $DirName
        if (Test-Path -path $OriginalDir) {
          Write-Verbose -Message ('User ({0}) exists in both places; ignoring' -f $DirName)
        }
        else {
          # decide what you want to do here.. your logic overlaps a different logic rule; so it doesn't make sense
          # You already have logic that cleans out the "original" directory
        }
      }
      

      The logic for your rules isn’t quite right, it’s missing a piece, but that’s covered in the 2nd section.

      The logic needs to be:

      If (in old and in new) then ignore

      If (in old and NOT in new) then delete #that’s what you described

      If (NOT in old and in new) then ??

      Hope this helps 🙂

       

      David F.

      avatar
    • #1557341
      carp
      Participant
      Member Points: 206
      Rank: 2

      Good day Mr. Figueroa, and a good day to all,

      I was quite surprised to find an answer from you. You have much better things to do than to help me.

      Yes, it does help me a lot, thank you.  …and yes–making things more complicated than they need to be is one of my less-desirable traits. Don’t ask to see my grill.

      I thank you for your assistance and the time you obviously put into this.

       

      I have added “-verbose” to the end of a few lines, making it work in my environment which is here:

      PS C:\jc\temp> write $psversiontable
      
      Name Value
      ---- -----
      PSVersion 5.1.19041.1
      PSEdition Desktop
      PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
      BuildVersion 10.0.19041.1
      CLRVersion 4.0.30319.42000
      WSManStackVersion 3.0
      PSRemotingProtocolVersion 2.3
      SerializationVersion 1.1.0.1

      Changed code:

      Added curley bracket at end of script, because PS accepted it. I am not sure where it actually belongs, or if it belongs at all, and I’m just messing up. PS wants the }  before the first ELSE statement. I put it at the end of the file.

      #OldProfNewProf_test2.ps1
      
      $OldProfDir = \\server\oldprofiles$
      $NewProfDir = \\server\newprofiles$
      
      # Write-Verbose -Message 'First check the new directory against the old directory' 
      Write-Verbose -Message 'First check the new directory against the old directory' -verbose
      
      Get-ChildItem -path $OldProfDir -directory | foreach-object {
      $OriginalDir = $_
      $DirName = $OriginalDir.BaseName.split('.')[0]
      $TestDir = Join-Path -path $NewProfDir -ChildPath $DirName
      if (Test-Path -path $TestDir ){
      Write-Verbose -Message ('User ({0}) has profiles in both directories, ignoring' -f $DirName) -verbose # I believe the -f switch tells it to format.
      }
      else {
      Write-verbose -Message ('User ({0}) has no matching profile in {1}; deleting old profile' -f $DirName, $NewProfDir) -verbose
      takeown.exe /a /f /r /skipsl "$OriginalDir"
      try {
      Remove-Item -path $TestDir -recurse -force -erroraction stop
      }
      catch {
      Write-Warning -Message ('Unable to completely remove {0}' -f $OriginalDir)
      }
      }
      Write-Verbose -Message 'Now check the old directory against the new directory' -verbose
      Get-ChildItem -path $NewProfDir -directory | foreach-object {
      $NewDir = $_
      $DirName = $_.basename 
      $OriginalDir = '{0}\{1}.v2' -f $OldProfDir, $DirName
      if (Test-Path -path $OriginalDir) {
      Write-Verbose -Message ('User ({0}) exists in both places; ignoring' -f $DirName) -verbose
      }
      else {
      Write-Verbose "Well, you don't see that every day...."
      # decide what you want to do here.. your logic overlaps a different logic rule; so it doesn't make sense
      # You already have logic that cleans out the "original" directory
      }
      }}

      I have some output after making the changes:

      PS C:\jc\temp> .\OldProfNewProf_test2.ps1
      AUSFÜHRLICH: First check the new directory against the old directory
      AUSFÜHRLICH: User (carp1) has no matching profile in \\server02\profile_test$; deleting old profile
      Fehler: Ungültige Syntax. Wert für "/f" erwartet.
      Geben Sie "TAKEOWN /?" ein, um die Syntax anzuzeigen.
      WARNUNG: Unable to completely remove carp1.V2
      AUSFÜHRLICH: Now check the old directory against the new directory
      AUSFÜHRLICH: User (carp2) has no matching profile in \\server02\profile_test$; deleting old profile
      Fehler: Ungültige Syntax. Wert für "/f" erwartet.
      Geben Sie "TAKEOWN /?" ein, um die Syntax anzuzeigen.
      WARNUNG: Unable to completely remove carp2.V2
      AUSFÜHRLICH: Now check the old directory against the new directory
      AUSFÜHRLICH: User (carp3) has no matching profile in \\server02\profile_test$; deleting old profile
      Fehler: Ungültige Syntax. Wert für "/f" erwartet.
      Geben Sie "TAKEOWN /?" ein, um die Syntax anzuzeigen.
      WARNUNG: Unable to completely remove carp3.V2
      AUSFÜHRLICH: Now check the old directory against the new directory

      So, it looks like I’ll be working on that TAKEOWN statement.

      I’m not sure what’s going on with the -f switch. I don’t know why PS is throwing errors for a formatting switch (I may be wrong on that…I’m not a professional…).

       

      Again, thank you for your assistance.

       

      I’m going to go home and work on this further.

       

      A great day to all…

      Best regards,

      -.-johnc/outedtrout

      avatar
    • #1557353
      David Figueroa
      Participant
      Member Points: 4,096
      Rank: 3

      I’m glad to help. I enjoy doing this stuff, and I need help plenty of times myself 🙂

      I reverted to takeown because I wasn’t sure how to do that with Powershell (I know it *can* be done, but the how is much more complicated than I had time to research).   It can be done with subinacl also, but that syntax is definitely more complicated.

      I think this site is a fantastic site, and we all want to help each other.  🙂

       

      And the one other thing I thought about, you could just do the takeown before you run the script and then recursively insert permissions so you can delete it if needed.

      The -f switch is a fantastic feature.  One of the problems I always had in DOS and vbscripts was dealing with quote marks.  I can’t count the number of times I’ve had to go back and double & triple count my double quote marks to debug problems.

      The way -f works is it substitutes in information when it evaluates the string.  The placeholders represent the position in a list of variables after the -f.  Here’s an example:

      ‘Today is a {0} day to {1}’ -f ‘good’,’die’ evaluates to ‘Today is a good day to die’

      ‘My username is {0}, and my profile directory is {1}’ -f $env:username, $env:userprofile evaluates to ‘My username is david and my profile directory is c:\users\david’

      Good luck!

      David F.

       

Viewing 3 reply threads
  • You must be logged in to reply to this topic.
© 4sysops 2006 - 2021

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account