PowerShell script to get distribution group report - powershell

I am looking for a PowerShell script to get a export Distribution Group with following Information.
Group Display Name
Group Owner
Group Member
Last Activity Date
Member Count
Received Email Count
Group ID
I am working on a script but it is returning error
$groupmembers = foreach ($i in Get-DistributionGroup)
{
Get-DistributionGroupMember $i | select Display Name, #{n='Member';e={$i.DisplayName}}
}

Get-DistributionGroupMember returns an object of type ReducedRecipient which does not contain a fields called Display or Name - but it does contain a field called DisplayName - i think this is probably your issue.
If you dont want to read the docs, an easy way of seeing the members of an object is to use ConvertTo-Json. Temporarily add the line Get-DistributionGroupMember $i | ConvertTo-Json inside your foreach loop above the command that is failing. It will output the entire ReducedRecipient Object in JSON format so you can easily read the available property names and their current values.

Related

MS Graph API - Group & membership info

I'm trying to pull out a listing of all groups in our Azure Active Directory org along with all the associated members (be them users, groups, contacts, etc).
Since I was unable to locate a method to do this through the various Microsoft portals with a simple export button I began the process of obtaining access to the Microsoft Graph API/SDK via Powershell.
I'm by no means a PowerShell expert as it's not one of my go-to scripts; however, from what I can tell the ability to pull group info in this fashion is fairly limited.
The following is what I've been able to accomplish thus far:
Pull in a list of the groups using Get-MgGroup -All
Use Get-MgGroupMembers to pull back a list of Directory Objects.
This is where I get stuck. From what I've read it looks like a Directory Object by default only returns the ID and the Deleted Date. I'd like to get a display Name for these objects; I can obviously do this by running the appropriate 'Get' cmdlet for the type of directory object (i.e. Get-MgUser); From what I can tell the type of directory object can't be gleaned via PowerShell with out 'trial-and-error'... This seems highly inefficient to simply get a displayName.
Is there a more effective way to determine either the displayName of a Directory Object via a PowerShell cmdlet or at the very least a type so I can write a case statement to run the right cmdlet on the first try?
For the record this is going to be incorporated in to a Powershell Script, the current iteration of which looks like this and sorta works okay... assuming the Id passed in $member.Id belongs to a User type directory object.
Connect-MgGraph
$groups=Get-mgGroup -All
ForEach ($group in $groups){
$members = #{}
$members = Get-MgGroupMember -GroupId $group.Id -All
ForEach ($member in $members){
$user = Get-MgUser $member.Id
Write-Output $object.ODataType
Write-output $group.DisplayName "," $member.Id "," $user.UserType"," $user.DisplayName "," $user.UserPrincipalName "," $user.Mail >> C:scripts\Azure_Groups.txt
}
}
Would appreciate any direction/assistance on this. Thanks in advance!
Not sure why its not returning all the details on the PowerShell query:
This is working fine in MS Graph Explorer with the results showing all the details of the members:
For more details:https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0&tabs=http#example-1-get-the-direct-membership-in-a-group

Copy users from a series of groups based on a filter to another series of groups based on a separate filter

Thanks for taking a minute to look at this.
Scenario: Copy users from a series of groups based on a filter to another series of groups based on a separate filter. Essentially a one for one copy of group memberships.
I created some code that grabs all the groups based on the filter and can get all of the users but am having a hard time translating that into the individual pieces. For each group I want just that groups members and then add them to a group with the same name but of a different type (one is OKTA_GROUP and the other is APP_GROUP).
Any help on this would be appreciated. Getting the groups and members works but putting those into variables and passing them into the PUT is not working.
Here is what I have so far.
function get-oktaInvokeGroupMembers () {
$groups = Invoke-Method GET "/api/v1/groups?filter=type+eq+%22APP_GROUP%22&q=test"
Write-Output $groups
foreach ($group in $groups) {
$members = Get-OktaGroupMember $group.id
Write-Output $members
}
$oktagroups = Invoke-Method GET "/api/v1/groups?filter=type+eq+%22OKTA_GROUP%22&q=Test"
Write-Output $oktagroups
foreach ($okta in $oktagroups) {
Invoke-Method PUT "/api/v1/groups/$okta.id/users/$members.id"
}
}
You have to get your APP_GROUP.
Then for each group, you loop to get the members with Get-OktaGroupMember. Each member has normally an id.
You need to retrieve the OKTA_GROUP which has the same name than the group you are currently processing to get its group id.
Then finally for each member, you put the member.
Something like that will be a good starting point.
function get-oktaInvokeGroupMembers ($groupName) {
$sourceGroups = Invoke-Method GET "/api/v1/groups?filter=type+eq+%22APP_GROUP%22&q=$groupName"
foreach ($sourceGroup in $sourceGroups) {
$sourceMembers = Get-OktaGroupMember $sourceGroup.id
$targetGroups = Invoke-Method GET "/api/v1/groups?filter=type+eq+%22OKTA_GROUP%22&q=$groupName"
foreach($targetGroup in $targetGroups) {
foreach($sourceMember in $sourceMembers) {
Invoke-Method PUT "/api/v1/groups/$($targetGroup.id)/users/$($sourceMember.id)"
}
}
}
}
I've defined a parameter $groupName.
I don't know your API but difficulty is that for a specific groupName, you may get zero, one or many groups as result.
So you need to handle all these cases. I've handled multiple results nesting foreach loop but in such case for one APP_GROUP, the script will set members on all OKTA_GROUP that have the same name than APP_GROUP. There may be zero, one or many OKTA_GROUP groups returned by the API.
Another point, you will probably need to handle errors when adding a member. Calling the API with PUT when the member is already in the OKTA_GROUP may return an error.
And last, the script will only append members to a group, but it will never "synchronize" both groups. Meaning deleting members isn't handled. If you need to synchronize members this is bit more complexe.

fetch particular column in PowerShell

I am creating a PowerShell Script wherein I have a csv file which consist of few parameters wherein there is a parameter called as status .The ask is whenever the status is Fail ,I want to get the corresponding row. I am using Import-Csv cmdlet in order to fetch the csv file and checking the status if the status is fail or not and based on that I am fetching the corresponding details using the split function but when I am using the split method it is giving me error as
Method invocation failed because
[System.Management.Automation.PSCustomObject] does not contain a
method named 'split'.
Using the below code
$Report=(Import-Csv "C:\Users\Documents\Optim_Config_Report_20210216170900.csv")
foreach($i in $Report)
{
if($i.Status -eq "Fail")
{
$RULE_ID= $i.split(',')[0]
Write-output $RULE_ID
}
}
Can someone please help me how can I get the corresponding Row details for which the Status is fail?
Report is something like this in a csv format
Rule,Id,Category,Sub_Category,System_Value,Risk,Status
1,Operations,Access,Login,High,Pass
2,Operations,Logging,AccessControl,Medium,Pass
3,Operations,encryptions,certificate,High,Fail,
4,Security,Encryption,protcolo,High,Fail
Thanks in Advance!
When you Import-CSV, you are creating an array of [PSCustomObject]s, each of which has properties corresponding to the names of the columns in the CSV. For your example, those properties will be Rule, ID, Category, Sub_Category, System_Value, Risk, and Status.
It is not clear from your example what information you wish to report in the event of a status of Fail, but all you need to do is reference the properties that contain that information - for example, if I wanted to report the Category and Sub_Category, I might use
...
if ($i.status -eq "Fail") {
Write-Host $i.Category, $i.Sub_Category
}
...
Which would, for your sample data, output
encryptions certificate
Encryption protcolo
For a CSV file named "test.csv" with content like this:
"Rule","Id","Category","Sub_Category","System_Value","Risk","Status"
1,Operations,Access,Login,1,High,Pass
2,Operations,Logging,AccessControl,1,Medium,Pass
3,Operations,encryptions,certificate,1,High,Fail
4,Security,Encryption,protcolo,1,High,Fail
This script:
Import-Csv -Path ./test.csv | Where-Object Status -eq Fail | Format-Table
Returns:
Rule Id Category Sub_Category System_Value Risk Status
---- -- -------- ------------ ------------ ---- ------
3 Operations encryptions certificate 1 High Fail
4 Security Encryption protcolo 1 High Fail

ADSI Search for DistinguishedName of the primary group based on primarygroupid

Because we don't have the active directory module available on all our systems we're using ADSI instead. The following code retrieves a user object from AD by using the AdsiSearcher:
$ADUser = ([AdsiSearcher]"(samaccountname=$SamAccountName)").FindOne()
This results in finding the property primarygroupid which represents the domain primary group for user, usually number 513. When we have this number we would like to find the distinguishedName of the group. However, the code below does that just fine I was wondering if there is a better filter that can be used instead of filtering after the FindAll() method?
$searcher = [adsisearcher]'objectclass=group'
$searcher.PropertiesToLoad.Add('primarygrouptoken')
$searcher.PropertiesToLoad.Add('distinguishedName')
$searcher.FindAll() |
Where-Object { $_.Properties.primarygrouptoken -eq 513}
Something like this would be great but it's not possible:
([adsisearcher]”(&(objectCategory=group)(primaryGroupid=513))”).FindOne()
The primaryGroupToken is a constructed attribute, meaning that it's not actually materialized in the database, and can't be filtered using LDAP.
In order to build an equivalent filter we'll need to look at how it is constructed - and the primary group token in Active Directory is always the same as the group's RID part (the relative identifier) of the objectSid attribute.
So, if we want to search by it, we can simply filter by objectSid instead:
# Obtain domain SID
$dncDN = ([adsi]"LDAP://RootDSE").defaultNamingContext
$dnc = [adsi]"LDAP://$dncDN"
$domainSID = [System.Security.Principal.SecurityIdentifier]::new($dnc.objectSid.Value, 0)
# Set the group ID we're looking for
$RID = 513
# Search for group by objectSid value:
([adsisearcher]"(&(objectCategory=group)(objectSid=${domainSID}-${RID}))").FindOne()

Powershell: Checking for duplicate email in AD

Background:
I'm trying to make a script that will see if a new users email ($email) is the same as one already existing (which would cause an error). I have a very remedial understanding of objects so this is what I have so far (yes it is ugly):
$email = "smithj#company.com"
$mailcheck = Get-ADUser -filter * -Properties * | ForEach-Object {$_.mail}
$mailcheck | ForEach-Object {if ($email -eq $_.mail){"$email = $($_.mail) - Matching email"}else{"$email = $($_.mail) - No duplicate email"}}
Problem 1:
The script doesn't match emails. When I have a matching email in AD it doesn't recognize it.
Problem 2: When executing just the 2nd line, indexing doesn't work properly. While it looks like a consecutive list of emails, if a user doesn't have an email at all (blank) really it could be something like this:
smithj#company.com
johnsonj#company.com
robertsr#company.com
doej#company.com
So $mailcheck[0] returns smithj#company.com while $mailcheck[1] returns blank despite the list actually looking like this:
smithj#company.com
johnsonj#company.com
robertsr#company.com
doej#company.com
Conclusion: I really just need problem 1 solved but problem 2 peaked my curiosity. Thanks.
The way you are doing it above is really inefficient. -Properties * will return every property on the user, some properties are expensive in terms of processing power to return. Only use the properties you need. The properties returned by default without specifying that parameters do not need to be specified with -Properties, only additional nondefault properties. -Filter * will also match on literally any value for any field, effectively returning every ADUser, further increasing the resources required for your script to execute as you will now have to process every user to find any accounts matching that email.
Now that that's out of the way, here is a more efficient method to implement what you're asking:
# Set the email address to search for
$emailAddress = 'box#domain.tld'
# Get all users where the email address matches what is set above
# Force it as an array so you can treat it like one even if only
# one or zero users are returned
$adUsers = #( Get-ADUser -Filter "EmailAddress -eq '${emailAddress}'" )
# Make sure no accounts were returned
# If there are, throw an error with the number of users and who they are
if( $adUsers ) {
throw "Found $($adUsers.Count) users matching EmailAddress ${emailAddress}: $($adUsers.SamAccountName -join ', ')"
}
By using the filter to only match the specific email address, Powershell does not need to collect every single AD user in the system, or iterate over all of them to find a specific email address. This will take a long time to check, especially in larger environments, whereas filtering the returned objects based on email address (or on any other property) results in a faster operation and less data to sift through.
You can then check whether $adUsers contains anything (an array count of anything but 0 evaluates to $True, you could also use if( $adUsers.Count -gt 0 ) as the condition), and if so, throw an error with more information as I do above.
Update for comment question:
To answer your other question in the comment, "I didn't know what object to compare $email to", EmailAddress and Mail both look to be valid properties, but I don't know the difference between them. In my environment, both Mail and EmailAddress are populated with my email address, but have always used EmailAddress and haven't run into issues using that. Maybe one is deprecated and the other is new or something, but I'm not really sure.
There is also yet another property called proxyAddresses as well, which preliminary research shows that both EmailAddress and Mail are related to it, but I don't know much about it. It's not populated on my ADUser objects, so I can't poke around with it.