I am doing this PowerShell command Get-DistributionGroupMember
but it is not returning the user's middle name. Is there something I am missing? Any other way to do this?
public List<EmailGroup> GetDistributionGroupMember(string distributionAddress, string displayName)
{
ClearPowerShell();
List<EmailGroup> EmailGroups = new List<EmailGroup>();
powerShell.AddCommand("get-distributiongroupmember")
.AddParameter("Identity", distributionAddress)
.AddParameters(new Dictionary<string, object>()
{
{ "ResultSize", "Unlimited" }
})
.AddCommand("Select-Object")
.AddArgument(new string[] { "PrimarySMTPAddress", "DisplayName", "FirstName", "Initials", "LastName" });
Collection<PSObject> results = powerShell.Invoke();
foreach (var result in results)
{
var propertyName = result.Properties.Where(w => w.Name == "DisplayName").Select(s => s.Value).FirstOrDefault();
var propertyEmail = result.Properties.Where(w => w.Name == "PrimarySmtpAddress").Select(s => s.Value).FirstOrDefault();
var FirstName = result.Properties.Where(w => w.Name == "FirstName").Select(s => s.Value).FirstOrDefault();
var Initials = result.Properties.Where(w => w.Name == "Initials").Select(s => s.Value).FirstOrDefault();
var LastName = result.Properties.Where(w => w.Name == "LastName").Select(s => s.Value).FirstOrDefault();
EmailGroup emailGroup = new EmailGroup();
emailGroup.Email = (string)propertyEmail;
string fullName = string.Format("{0},{1},{2}", LastName, FirstName, Initials);
emailGroup.FullName = fullName;
emailGroup.GroupName = displayName;
EmailGroups.Add(emailGroup);
}
if (powerShell.HadErrors)
{
var exception = powerShell.Streams.Error.ElementAt(0).Exception;
}
return EmailGroups;
}
I had to add a Get-contact ps call in order to get the Initials its very slow.
public List<EmailGroup> GetDistributionGroupMember(string distributionAddress, string displayName)
{
ClearPowerShell();
List<EmailGroup> EmailGroups = new List<EmailGroup>();
Console.WriteLine(distributionAddress);
powerShell.AddCommand("get-distributiongroupmember")
.AddParameter("Identity", distributionAddress)
.AddParameters(new Dictionary<string, object>()
{
{ "ResultSize", "Unlimited" }
});
Collection<PSObject> results = powerShell.Invoke();
foreach (PSObject result in results)
{
ClearPowerShell();
string propertyEmail = result.Properties.Where(w => w.Name == "PrimarySmtpAddress").Select(s => s.Value).FirstOrDefault().ToString();
Console.WriteLine(propertyEmail);
powerShell.AddCommand("Get-contact")
.AddParameter("Identity", propertyEmail)
.AddCommand("Select-Object")
.AddArgument(new string[] { "FirstName", "Initials", "LastName" });
Collection<PSObject> contact = powerShell.Invoke();
if (contact.Count != 0)
{
var FirstName = contact[0].Properties.Where(w => w.Name == "FirstName").Select(s => s.Value).FirstOrDefault();
var Initials = contact[0].Properties["Initials"].Value;
var LastName = contact[0].Properties.Where(w => w.Name == "LastName").Select(s => s.Value).FirstOrDefault();
EmailGroup emailGroup = new EmailGroup();
emailGroup.Email = propertyEmail;
string fullName = string.Format("{0},{1},{2}", LastName, FirstName, Initials);
emailGroup.FullName = fullName;
emailGroup.GroupName = displayName;
EmailGroups.Add(emailGroup);
}
}
if (powerShell.HadErrors)
{
var exception = powerShell.Streams.Error.ElementAt(0).Exception;
}
return EmailGroups;
}
The issue seem to be a typo, you're projecting Initials
with Select-Object
yet are filtering for Initial
on your Where
. However, according to the docs, Get-DistributionGroupMember
outputs a ReducedRecipient
object, and said object doesn't seem to have such property.
In this case, you could also benefit from Invoke<T>()
as long as you reference include the assembly in your project, in which case you could do:
Collection<ReducedRecipient> results;
using (PowerShell powerShell = PowerShell.Create())
{
results = powerShell
.AddCommand("Get-DistributionGroupMember")
.AddParameters(new Dictionary<string, object>()
{
{ "Identity", distributionAddress },
{ "ResultSize", "Unlimited" }
})
.Invoke<ReducedRecipient>();
}
foreach (ReducedRecipient result in results)
{
// do work
}
To help you find the assembly to add it to your project, if it's a Binary
module, you can do:
(Get-Command Get-DistributionGroupMember).Module.ImplementingAssembly.Location
And, according to the .NET doc of ReducedRecipient
, the assembly should be called: Microsoft.Exchange.Data.Directory.dll
.
If you can't include the assembly and have to stick to Collection<PSObject>
, you should note that .Properties
implements it's own indexer so this is perfectly valid:
foreach (PSObject result in results)
{
object? displayName = result.Properties["DisplayName"].Value;
// and so on
}
Regarding the new update, having to use Get-Contact
and the code being too slow, you might be able to improve performance by letting PowerShell do all the work instead of having to do multiple PowerShell invocations calling Get-Contact
for each group member.
This is how it'd look:
public EmailGroup[] GetDistributionGroupMember(
string distributionAddress,
string displayName)
{
ClearPowerShell();
Console.WriteLine(distributionAddress);
Collection<Hashtable> results = powerShell.AddScript(@"
param($identity)
$members = Get-DistributionGroupmember -Identity $identity -ResultSize Unlimited
foreach ($member in $members) {
if (-not $member.PrimarySmtpAddress) {
continue
}
$contact = Get-contact $member.PrimarySmtpAddress -ErrorAction SilentlyContinue
if (-not $contact) {
continue
}
@{
PrimarySmtpAddress = $member.PrimarySmtpAddress
FirstName = $contact.FirstName
Initials = $contact.Initials
LastName = $contact.LastName
}
}")
.AddParameter("identity", distributionAddress)
.Invoke<Hashtable>();
List<EmailGroup> EmailGroups = new List<EmailGroup>(results.Count);
foreach (Hashtable result in results)
{
EmailGroup emailGroup = new EmailGroup()
{
GroupName = displayName,
Email = result["PrimarySmtpAddress"].ToString(),
FullName = $"{result["LastName"]},{result["FirstName"]},{result["Initials"]}"
};
EmailGroups.Add(emailGroup);
}
return EmailGroups.ToArray();
}
Assuming you are referencing the PowerShell SDK 7.0
or above in your project, you could change the foreach
loop for a ForEach-Object -Parallel
loop, allowing you to process the $members
array in parallel, however by doing so, the complexity of the PowerShell script will increase, for example, you may need to handle throttling responses from the Exchange API.