Search code examples
arrayspowershellactive-directoryactive-directory-groupactivedirectorymembership

How do I send each result of Get-AdGroupMembership to my array?


I'm trying to recurse an NTFS folder structure, and output a CSV file that only displays each USER account with permissions on only the folders. Everything in the script outputs correctly EXCEPT for the portion that discovers a group and proceeds to enumerate the users in that group using Get-ADGroupMember. While debugging, I can see that each user within the group (even with nested groups) is outputted, but I guess I'm not properly "arraying" each output of the command and sending it onward to my "out" array.

I marked the section I'm having trouble with. Any help folks could provide would be very much appreciated. Thanks!

$Answer = Read-Host 'Do you wish to use an answer file? File must be named answer.csv and must reside in same directory as script. (Default is [N])'
If ($Answer -eq "y") {
  $AnsFile = Import-Csv answer.csv | Select src,outdir,domain,user,pwd
  $List_Dir = $AnsFile.src
  $OutPath = $AnsFile.outdir
  $DomainName = $AnsFile.domain
  $Admin = $AnsFile.user
  $Pwd = $AnsFile.pwd
  }
Else {
  Do {
  $List_Dir = Read-Host 'Enter the directory path to be searched/recursed'
  $TestList_Dir = Test-Path $List_Dir
    If ($TestList_Dir -eq $True) {Write-Host "List directory checks out..."}
    Else {Write-Host "Incorrect source directory.  Please try again." -foregroundcolor red -backgroundcolor yellow}
   }
  While ($TestList_Dir -eq $False)

  Do {
  $OutPath = Read-Host 'Enter the directory path where the output files will be saved.  Do not add a trailing slash.'
  $TestOutPath = Test-Path $OutPath
    If ($TestOutPath -eq $True) {Write-Host "Output path checks out..."}
    Else {Write-Host "Incorrect output path.  Please try again." -foregroundcolor red -backgroundcolor yellow}
   }
  While ($TestOutPath -eq $False)
  $DomainName = Read-Host 'Enter the non-distinguished name of the Active Directory domain'
  $Admin = Read-Host 'Type in an administrative account with rights to read AD Security Groups'
  $Pwd = Read-Host 'Enter the adminstrative account password'
}

$Folder_Array = @()

write-host "List directory = $List_Dir"
write-host "Output path = $OutPath"
write-host "Domain = $DomainName"
write-host "Admin account = $Admin"
write-host "Password = $Pwd"

Import-Module ActiveDirectory

Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$CType = [DirectoryServices.AccountManagement.ContextType]::Domain
$IDType = [DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$DomainContext = New-Object DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $CType, $DomainName, $Admin, $Pwd

#$pat = "^[a-zA-Z0-9_:.]+$"
$pat = "^[a-zA-Z0-9_:.\]+$]"

get-childitem $List_Dir -recurse | where-object {$_.psIsContainer -eq $true} | foreach-object {
   $a = ($_.FullName)
   $d = $a -match $pat
   $e = (get-acl $_.FullName).Access

    foreach ($e1 in $e) {
      $f = $e1.FileSystemRights
      $g = $e1.AccessControlType
      $SecID = $e1.IdentityReference
        foreach ($Sec in $SecID) {
          $GroupPrincipal = [DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($DomainContext, $IDType, $Sec)
          if ($GroupPrincipal -ne $null) {
            $Sec = $Sec.ToString()
            $Sec = $Sec.Split("\")[1]
            Get-AdGroupMember $Sec -Recursive | ForEach-Object {
              $User = ($_.SamAccountName)
                foreach ($u in $User) {
                $out = new-object psobject 
                $out | add-member noteproperty Path $a
                $out | add-member noteproperty Unix_Safe $d
                $out | Add-Member NoteProperty UserAccount $u
                $out | add-member noteproperty Permission $f
                $out | add-member noteproperty AccessType $g

                $Folder_Array += $out  
                }
             }
           }
           else {
          $e2 = $Sec.ToString()
          $e2 = $e2.split("\")[1]
          $out = new-object psobject 
          $out | add-member noteproperty Path $a
          $out | add-member noteproperty Unix_Safe $d
          $out | Add-Member NoteProperty UserAccount $e2
          $out | add-member noteproperty Permission $f
          $out | add-member noteproperty AccessType $g

          $Folder_Array += $out
          }
         }
        }
}

$Folder_Array | Select Path,UserAccount,Permission,AccessType,Unix_Safe | Export-Csv "$OutPath\folderonly.csv" -NoTypeInformation

Solution

  • The problem isn't so much with how you're doing it, it's more of when you're doing things. Let me explain...

           Get-AdGroupMember $Sec -Recursive | ForEach-Object {
              $User = ($_.SamAccountName)
                foreach ($u in $User) {
                  $e2 = $u
                }
             }
           }
    

    ****************************************************

          else {
          $e2 = $Sec.ToString()
          $e2 = $e2.split("\")[1]
          }
         }
        }
    

    $out = new-object psobject $out | add-member noteproperty Path $a $out | add-member noteproperty Unix_Safe $d $out | Add-Member NoteProperty UserAccount $e2 $out | add-member noteproperty Permission $f $out | add-member noteproperty AccessType $g

    $Folder_Array += $out Given that, if it is a group you are taking all users for the group and setting that array of users to $User, and then going through that array, and assigning each user, one at a time, to $e2. Once you're done with that you create your object, and add that object to the array for output.

    Let's say that group has 3 users in it, Tom, Dick, and Harvey (Harry was busy, he sent his brother instead). So now:

    $User = @("Tom","Dick","Harvey")
    

    Then you cycle through that assigning each to $e2, which basically comes out to this (some pseudocode here):

    If(is a group){
    $User = Get-ADGroup |select -expand samaccountname
    ForEach($u in $User){
    $e2 = "Tom"
    <Next item in array>
    $e2 = "Dick"
    <next item in array>
    $e2 = "Harvey"
    <No more items in array, end ForEach>
    

    So now when it moves on to create your object $e2 = "Harvey" and Tom and Dick are just out of luck. To resolve that we have options. Either:

    A) Move object creation to inside the If/Else portions of the loop, specifically to create an object every time you assign $e2, and add those objects to the output array immediately after making them.

    or:

    B) Make $e2 an array by changing all references to setting it to read either $e2 += $u or $e2 = ,$Sec.ToString().Split("\")[1]. And then when you create objects do that like:

    ForEach($User in $e2){
        $Folder_Array += [PSCustomObject][Ordered]@{
            'Path' = $a
            'Unix_Safe' = $d
            'UserAccount' = $User
            'Permission' = $f
            'AccessType' = $g
        }
    }