Powershell code doing unexpected things for this newby

  • I am a real newby on powershell, but trying to learn. I copied some code I found on the Internet and am trying to enhance it a bit to make it display better info. The goal is to pull AD group membership information from the domain. The code I copied has provisions for using a variable in the code or an input file. That has no bearing on this, but is background.

    When I run this:

    $File = "F:\Temp\GroupsInput.txt" #File must contain one group per line with no qualifiers, like quotes.
    $group = ""
    $groups = ""
    If (Test-Path -Path $File -PathType Leaf)
    {$groups = Get-Content $File}
    Else {"No group list file was found."}
    If ($groups -ne "") {
    $results = foreach ($group in $groups) {
    Get-ADGroupMember $group | select samaccountname, name, @{n='GroupName';e={$group}}, @{n='Description';e={(Get-ADGroup $group -Properties description).description}}
    }
    write-output $results

    $results | Export-csv F:\Temp\GroupMemberShip.txt -NoTypeInformation
    }

    It runs well and outputs the $Results to a file as quoted, comma-delimited text, including columns names. The problem with this is that when it cannot find a group it throws an error, and that failure information only goes to the console. The info does not get output to the file. So, I tried to enhance the script to catch the error and output that information in an elegant manner, e.g. "Group: " + $group + " not found in " + $Domain + "."

    The Try-Catch structure is causing problems and the domain info will not output that way I want it to. Enhanced code:

    $File = "F:\Temp\GroupsInput.txt" #File must contain one group per line with no qualifiers, like quotes.
    $group = ""
    $groups = ""
    $Extra = ""
    If (Test-Path -Path $File -PathType Leaf)
    {$groups = Get-Content $File}
    Else {"No group list file was found."}
    If ($groups -ne "") {
    $results = foreach ($group in $groups) {
    Try{
    Get-ADGroupMember $group | select samaccountname, name, @{n='GroupName';e={$group}}, @{n='Description';e={(Get-ADGroup $group -Properties description).description}}
    }
    Catch {$Extra = $Domain = Get-ADDomain -Current LocalComputer| Select-object Name
    "Group: " + $group + " not found in " + $Domain + "."}
    }
    write-output $results

    $results | Export-csv F:\Temp\GroupMemberShip.txt -NoTypeInformation
    }

    After adding the Try Catch structure I found that the file output looks like this:

    "Length"

    "72"

    It is no longer a list of accounts and group membership. I have no idea why Try Catch is doing this, since it still outputs to the console in a nice table format, with the Catch info looking like this (of course it's stuffed into the middle of the table formatting)

    Group: Biztalk_PRD_Affiliate not found in @{Name=mydomain}.

    Group: Biztalk_PRD_EDI_Sub not found in @{Name=mydomain}.

    My goal here is to catch the errors and store them into another variable, separate from $Results, and then add them at the end of the $Results variable for output to console and file.

    Next is the output of $Domain = Get-ADDomain -Current LocalComputer | Select-object Name.

    It stores the column name and the value in the variable, e.g. @{Name=mydomain}. I would like it to only store the value in the variable, e.g. mydomain.

    Some help on these two issues, and explanation, or links to explanations, would be appreciated. While I want this fixed, I want to understand it and why this is happening so I learn something.

    Thanks,

    Chris

    Learning something new on every visit to SSC. Hoping to pass it on to someone else.

  • your try/catch would need to be done differently as it needs to be outside the variable assignment

    Constant variables should also be set outside of loops - that is the case of your $domain variable for example.

    as your code would be a bit hard to change as well as the fact that you could, potentially, bump into the limit of get-AdGroupMember (max members if 5k - if you have a group with more members it fails) I'm giving below the code I use (with minor changes) to get AD groups

    the block "(($member).Contains('ForeignSecurityPrincipal'))" relates to cross domain/forest members - if you don't have those you can remove that block.

    $domain = Get-ADDomain -Current LocalComputer


    $Record = @{
    "SourceDomain" = ""
    "SamAccountName" = ""
    "Name" = ""
    "GroupDomain" = ""
    "GroupName" = ""
    "GroupDistinguishedName" = ""
    "Description" = ""
    "ObjectClass" = ""
    }

    $ErrorRecord = @{
    "Message" = ""
    }


    $Table = $null
    $Table = [System.Collections.ArrayList]@()
    $Errors = $null
    $Errors = [System.Collections.ArrayList]@()

    foreach ($grp in $adgroups)
    {
    try
    {
    $groupmembers = Get-AdGroup $grp -properties SamAccountName,members,DistinguishedName,Description|Select-Object SamAccountName,members,DistinguishedName,Description
    foreach ($groupmember in $groupmembers)
    {
    $Record.Description = $groupmember.Description
    $Record.GroupDistinguishedName = $groupmember.DistinguishedName
    $Record.GroupDomain = $domain.Name
    $Record.GroupName = $grp

    foreach ($member in $groupmember.members)
    {
    if (($member).Contains('ForeignSecurityPrincipal'))
    {
    try
    {
    $securityidentifier = new-object security.principal.securityidentifier( $member.Substring(3,$member.IndexOf(",") - 3))
    $foreignmemberdetail = ( $securityidentifier.translate( [security.principal.ntaccount] ) ) -split "\\"
    $Record.SourceDomain = $foreignmemberdetail[0]
    $Record.SamAccountName = $foreignmemberdetail[1]
    }

    catch

    {
    $Record.SourceDomain = "**ERROR**"
    $Record.SamAccountName = $member.Substring(3,$member.IndexOf(",") - 3)
    $Record.Name = ""
    $Record.ObjectClass = ""
    }

    $objRecord = New-Object PSObject -property $Record
    [void]$Table.Add($objRecord)
    }
    else
    {
    $adobject = Get-ADObject -Identity $member
    $Record.SourceDomain = (($member -split ","| ? {$_ -like "DC=*"}) -join ".") -replace ("DC=", "")
    $Record.SamAccountName = $adobject.SamAccountName
    $Record.Name = $adobject.Name
    $Record.ObjectClass = $adobject.ObjectClass
    }
    }
    }
    }

    catch

    {
    $ErrorRecord.Message = "Group $($grp) does not exist in domain $($domain)"
    $objRecord = New-Object PSObject -property $ErrorRecord
    [void]$Errors.Add($objRecord)

    }
    }


    if ($Table)
    {
    $Table|select SourceDomain,SamAccountName,Name,GroupDomain,GroupName,GroupDistinguishedName,Description,ObjectClass|Export-Csv c:\temp\groups.csv -Encoding UTF8 -NotypeInformation
    }

    if ($Errors)
    {
    $Errors|select Message|Export-Csv c:\temp\groups_errors.csv -Encoding UTF8 -NotypeInformation
    }
  • OK, I made some mods to the code and now it's doing almost everything I want.

    I originally put the $Domain code in the Catch section because it did something unexpected when I put it at the beginning of the code. Don't remember what that was at this time. I have since moved it back, and I suppose with other code changes, it is acting properly.

    The only problem now is that when I add $NotFound, the error info gathered in the Catch, it displays nicely in the console but does not make it into the output file, showing as only what appears to be an empty record, ",,,,'.

    $File = "F:\Temp\GroupsInput.txt" #File must contain one group per line with no qualifiers, like quotes.
    $group = ""
    $groups = ""
    $NotFound = ""
    $Domain = Get-ADDomain -Current LocalComputer| Select-object Name
    If (Test-Path -Path $File -PathType Leaf)
    {$groups = Get-Content $File}
    Else {"No group list file was found."}
    #To put a list of groups into args, enclose in double quotes " and separate with commas.
    #$groups = "Group1", "Group2"
    If ($groups -ne "") {
    $results = foreach ($group in $groups) {
    Try{
    Get-ADGroupMember $group | select samaccountname, name, @{n='GroupName';e={$group}}, @{n='Description';e={(Get-ADGroup $group -Properties description).description}}
    }
    Catch {$NotFound = $NotFound +
    "Group: " + $group + " not found in domain " + $Domain.name + ".`n"}
    }
    $Results = $Results + $NotFound
    write-output $results

    $results | Export-csv F:\Temp\GroupMemberShip.txt -NoTypeInformation
    }

    Console:

    BIZ_PRD_SUPPORT_L2 BIZ_PRD_SUPPORT_L2 BizTalk_PRD_Isolated_Host_Users Created on 03/20/2019

    BIZ_PRD_ADMIN BIZ_PRD_ADMIN BizTalk_PRD_Isolated_Host_Users Created on 03/20/2019

    Group: BizTalk_STG_Application_Users not found in domain mydomain.

    Group: Biztalk_STG_SSO Adminstrator not found in domain mydomain.

    File Contents:

    "BIZ_PRD_SUPPORT_L2","BIZ_PRD_SUPPORT_L2","BizTalk_PRD_Isolated_Host_Users","Created on 03/20/2019"

    "BIZ_PRD_ADMIN","BIZ_PRD_ADMIN","BizTalk_PRD_Isolated_Host_Users","Created on 03/20/2019"

    ,,,

    Any ideas on how to get the $NotFound string value into the output file?

    Thanks,

    Chris

    Learning something new on every visit to SSC. Hoping to pass it on to someone else.

  • the code I gave above show you how to do it - just adapt to your needs.

  • While I appreciate the effort, the code does not work well for me. I added my checking and loading of the input file at the top of the code. The code does generate an error file, but does not generate the groups file and just displays the code in the console window. These variances from my goal could be me not understanding the code, but I can't say for sure. I will study it and see if I can make out what you are doing with it. Heavy commenting would be helpful in this effort.

    Thanks,

    Chris

    Learning something new on every visit to SSC. Hoping to pass it on to someone else.

  • I dug around some more on the internet and finally got the code to do exactly what I want. This code will display the list of group members in columns and display the groups it could not find elegantly at the bottom, and does the same thing in the comma-delimited file it outputs.

    #Requires -Module ActiveDirectory
    $File = "F:\Temp\GroupsInput.txt" #File must contain one group per line with no qualifiers, like quotes.
    Clear-variable -name group #Make sure variables are empty.
    Clear-variable -name groups
    Clear-variable -name NotFound
    $Domain = Get-ADDomain -Current LocalComputer| Select-object Name #Get the domain name.
    If (Test-Path -Path $File -PathType Leaf) #Check if input file exists.
    {$groups = Get-Content $File} #If it does, read the contents into the variable.
    Else {"No group list file was found."}
    #To put a list of groups into args, enclose in double quotes " and separate with commas.
    #$groups = "Group1", "Group2"
    If ($groups -ne "") #If $groups is not blank...
    {
    $results = foreach ($group in $groups) {
    Try{
    Get-ADGroupMember $group | select samaccountname, name, @{n='GroupName';e={$group}}, @{n='Description';e={(Get-ADGroup $group -Properties description).description}}
    }
    Catch {$NotFound = $NotFound +
    "Group: " + $group + " not found in domain " + $Domain.name + ". rn"} #If we couldn't find the group in AD for this domain.
    }
    $Results = $Results
    write-output $Results
    write-output $NotFound
    $Results | Export-csv F:\Temp\GroupMemberShip.txt -NoTypeInformation #Output the $Results variable to file.
    Add-Content F:\Temp\GroupMemberShip.txt "`n" #Add the $NotFound Variable to the file.
    Add-Content F:\Temp\GroupMemberShip.txt $NotFound #Add the $NotFound Variable to the file.
    }

    I will be looking to refine it a bit more, once I figure out how I want to run it normally.

    Thanks,

    Chris

    Learning something new on every visit to SSC. Hoping to pass it on to someone else.

  • on my script, after the first line

    $domain = Get-ADDomain -Current LocalComputer

    add your code

    File = "F:\Temp\GroupsInput.txt" #File must contain one group per line with no qualifiers, like quotes.

    $group = ""

    $groups = ""

    $NotFound = ""

    $Domain = Get-ADDomain -Current LocalComputer| Select-object Name

    If (Test-Path -Path $File -PathType Leaf)

    {$groups = Get-Content $File}

    Else {"No group list file was found."}

    #To put a list of groups into args, enclose in double quotes " and separate with commas.

    #$groups = "Group1", "Group2"

    my code used "$adgroups" to hold the list of groups to process - you can either change it to be your own name ($groups) or add the following line before the remaining of my code

    $adgroups = $groups

    if the above changes do not give you output on the 2 files generated then post here your FULL modified script to see what may be wrong.

    if you are trying to combine valid groups and an error message for groups not found on same output file then you need to define WHAT column is going to contain the message and define a record type to hold both the error message and the remaining columns

    for valid rows the message will be empty and the remaining columns populated.

    for invalid rows the message will be populated and the remaining columns empty

    otherwise your generated file will NOT be a valid CSV file.

    this method is what I said was on the example on the try/catch block for Foreign Security.

    although the "*error" is going to a common column as defined on the @record, you could as easy have a "message" column and populate it on the catch block.

Viewing 7 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic. Login to reply