Skip to content

Commit d576b53

Browse files
Added Exchange Online properties of Microsoft 365 Groups (#3958)
* Added implementation * Added PR reference * Fixing documentation build issue --------- Co-authored-by: Gautam Sheth <gautamdsheth@outlook.com>
1 parent 7409316 commit d576b53

File tree

10 files changed

+151
-22
lines changed

10 files changed

+151
-22
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
2323
- Added `Get-PnPLibraryFileVersionBatchDeleteJobStatus` and `Get-PnPSiteFileVersionBatchDeleteJobStatus` to check on the status of applying file based version expiration based on age on a library and site level [#3828](https://github.com/pnp/powershell/pull/3828)
2424
- Added support for `Get-PnPSiteCollectionAppCatalog` and `Get-PnPTenantSite` to be used with vanity domain tenants [#3895](https://github.com/pnp/powershell/pull/3895)
2525
- Added support for using vanity domain tenants with `Grant-PnPTenantServicePrincipalPermission`, `Revoke-PnPTenantServicePrincipalPermission`, `Set-PnPWebTheme`, `Invoke-PnPListDesign`, `Set-PnPSite`, `Add-PnPSiteDesignTask`, `Get-PnPSiteDesignRun`, `Get-PnPSiteDesignTask` and `Invoke-PnPSiteDesign` cmdlets [#3898](https://github.com/pnp/powershell/pull/3898)
26+
- Added `-Detailed` to `Get-PnPMicrosoft365Group` which allows retrieval of the AllowExternalSenders, IsSubscribedByMail and AutoSubscribeNewMembers properties of the group [#3958](https://github.com/pnp/powershell/pull/3958)
27+
- Added `-RequireSenderAuthenticationEnabled` and `-AutoSubscribeNewMembers` to `Set-PnPMicrosoft365Group` which allows setting these properties on a group [#3958](https://github.com/pnp/powershell/pull/3958)
2628

2729
### Fixed
2830

documentation/Get-PnPMicrosoft365Group.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Gets one Microsoft 365 Group or a list of Microsoft 365 Groups
2020
## SYNTAX
2121

2222
```powershell
23-
Get-PnPMicrosoft365Group [-Identity <Microsoft365GroupPipeBind>] [-IncludeSiteUrl] [-IncludeOwners] [-Filter <string>]
23+
Get-PnPMicrosoft365Group [-Identity <Microsoft365GroupPipeBind>] [-IncludeSiteUrl] [-IncludeOwners] [-Detailed] [-Filter <string>]
2424
```
2525

2626
## DESCRIPTION
@@ -84,10 +84,28 @@ Retrieves all Microsoft 365 Groups in this tenant and retrieves the owners for e
8484
$groups = Get-PnPMicrosoft365Group -Filter "startswith(description, 'contoso')"
8585
```
8686

87-
Retrieves all Microsoft 365 Groups in this tenant with description starting with Contoso. This example demonstrates using Advanced Query capabilities (see: https://learn.microsoft.com/en-us/graph/aad-advanced-queries?tabs=http#group-properties).
87+
Retrieves all Microsoft 365 Groups in this tenant with description starting with Contoso. This example demonstrates using Advanced Query capabilities (see: https://learn.microsoft.com/graph/aad-advanced-queries?tabs=http#group-properties).
8888

8989
## PARAMETERS
9090

91+
### -Detailed
92+
When provided, the following properties originating from Exchange Online, will also be loaded into the returned group. Without providing this flag, they will not be populated. Providing this flag causes an extra call to be made to Microsoft Graph, so only add it when you need one of the properties below.
93+
94+
- AutoSubscribeNewMembers
95+
- RequireSenderAuthenticationEnabled
96+
- IsSubscribedByMail
97+
98+
```yaml
99+
Type: SwitchParameter
100+
Parameter Sets: (All)
101+
102+
Required: False
103+
Position: Named
104+
Default value: None
105+
Accept pipeline input: False
106+
Accept wildcard characters: False
107+
```
108+
91109
### -IncludeSiteUrl
92110
Include fetching the site URL for Microsoft 365 Groups. This slows down large listings.
93111

documentation/Set-PnPMicrosoft365Group.md

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ Sets Microsoft 365 Group properties
2020
## SYNTAX
2121

2222
```powershell
23-
Set-PnPMicrosoft365Group -Identity <Microsoft365GroupPipeBind> [-DisplayName <String>] [-Description <String>]
24-
[-Owners <String[]>] [-Members <String[]>] [-IsPrivate] [-LogoPath <String>] [-CreateTeam]
25-
[-HideFromAddressLists <Boolean>] [-HideFromOutlookClients <Boolean>] [-MailNickname <String>] [-SensitivityLabels <GUID[]>]
26-
23+
Set-PnPMicrosoft365Group -Identity <Microsoft365GroupPipeBind> [-DisplayName <String>] [-Description <String>] [-Owners <String[]>] [-Members <String[]>] [-IsPrivate] [-LogoPath <String>] [-CreateTeam] [-HideFromAddressLists <Boolean>] [-HideFromOutlookClients <Boolean>] [-RequireSenderAuthenticationEnabled <Boolean>] [-AutoSubscribeNewMembers <Boolean>] [-MailNickname <String>] [-SensitivityLabels <GUID[]>] [-Verbose]
2724
```
2825

2926
## DESCRIPTION
@@ -76,6 +73,25 @@ Sets the sensitivity label of the group
7673

7774
## PARAMETERS
7875

76+
### -AutoSubscribeNewMembers
77+
The AutoSubscribeNewMembers switch specifies whether to automatically subscribe new members that are added to the Microsoft 365 Group to conversations and calendar events. Only users that are added to the group after you enable this setting are automatically subscribed to the group.
78+
79+
To subscribe new members to conversations and calendar events, use this exact syntax: -AutoSubscribeNewMembers:$true.
80+
If you don't want to subscribe new members to conversations and calendar events, use this exact syntax: -AutoSubscribeNewMembers:$false.
81+
82+
Note: This property is evaluated only when you add internal members from your organization. Guest user accounts are always subscribed when added as a member.
83+
84+
```yaml
85+
Type: SwitchParameter
86+
Parameter Sets: (All)
87+
88+
Required: False
89+
Position: Named
90+
Default value: None
91+
Accept pipeline input: False
92+
Accept wildcard characters: False
93+
```
94+
7995
### -CreateTeam
8096
Creates a Microsoft Teams team associated with created group
8197
@@ -230,6 +246,21 @@ Accept pipeline input: False
230246
Accept wildcard characters: False
231247
```
232248
249+
### -RequireSenderAuthenticationEnabled
250+
Allows configuring if the Microsoft 365 Group should accept e-mail from senders outside of the organisation (false) or if both internal as well as external senders can send e-mail to the e-mail address of the Microsoft 365 group (true).
251+
252+
Alias: AllowExternalSenders
253+
254+
```yaml
255+
Type: Boolean
256+
Parameter Sets: (All)
257+
Required: False
258+
Position: Named
259+
Default value: None
260+
Accept pipeline input: False
261+
Accept wildcard characters: False
262+
```
263+
233264
### -SensitivityLabels
234265
The Sensitivity label to be set to the Microsoft 365 Group. To retrieve the sensitivity label Ids you can use [Get-PnPAvailableSensitivityLabel](Get-PnPAvailableSensitivityLabel.md).
235266
@@ -243,7 +274,21 @@ Accept pipeline input: False
243274
Accept wildcard characters: False
244275
```
245276
277+
### -Verbose
278+
When provided, additional debug statements will be shown while executing the cmdlet.
279+
280+
```yaml
281+
Type: SwitchParameter
282+
Parameter Sets: (All)
283+
284+
Required: False
285+
Position: Named
286+
Default value: None
287+
Accept pipeline input: False
288+
Accept wildcard characters: False
289+
```
290+
246291
## RELATED LINKS
247292
248293
[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)
249-
[Microsoft Graph documentation](https://learn.microsoft.com/graph/api/group-update)
294+
[Microsoft Graph documentation](https://learn.microsoft.com/graph/api/group-update)

src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,20 @@ public Microsoft365GroupPipeBind(Guid guid)
4444

4545
public Guid GroupId => _groupId;
4646

47-
public Microsoft365Group GetGroup(PnPConnection connection, string accessToken, bool includeSite, bool includeOwners)
47+
public Microsoft365Group GetGroup(PnPConnection connection, string accessToken, bool includeSite, bool includeOwners, bool detailed)
4848
{
4949
Microsoft365Group group = null;
5050
if (Group != null)
5151
{
52-
group = Microsoft365GroupsUtility.GetGroupAsync(connection, _group.Id.Value, accessToken, includeSite, includeOwners).GetAwaiter().GetResult();
52+
group = Microsoft365GroupsUtility.GetGroupAsync(connection, _group.Id.Value, accessToken, includeSite, includeOwners, detailed).GetAwaiter().GetResult();
5353
}
5454
else if (_groupId != Guid.Empty)
5555
{
56-
group = Microsoft365GroupsUtility.GetGroupAsync(connection, _groupId, accessToken, includeSite, includeOwners).GetAwaiter().GetResult();
56+
group = Microsoft365GroupsUtility.GetGroupAsync(connection, _groupId, accessToken, includeSite, includeOwners, detailed).GetAwaiter().GetResult();
5757
}
5858
else if (!string.IsNullOrEmpty(DisplayName))
5959
{
60-
group = Microsoft365GroupsUtility.GetGroupAsync(connection, DisplayName, accessToken, includeSite, includeOwners).GetAwaiter().GetResult();
60+
group = Microsoft365GroupsUtility.GetGroupAsync(connection, DisplayName, accessToken, includeSite, includeOwners, detailed).GetAwaiter().GetResult();
6161
}
6262
return group;
6363
}
@@ -74,7 +74,7 @@ public Guid GetGroupId(PnPConnection connection, string accessToken)
7474
}
7575
else if (!string.IsNullOrEmpty(DisplayName))
7676
{
77-
var group = Microsoft365GroupsUtility.GetGroupAsync(connection, DisplayName, accessToken, false, false).GetAwaiter().GetResult();
77+
var group = Microsoft365GroupsUtility.GetGroupAsync(connection, DisplayName, accessToken, false, false, false).GetAwaiter().GetResult();
7878
if (group != null)
7979
{
8080
return group.Id.Value;

src/Commands/Microsoft365Groups/GetMicrosoft365Group.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public class GetMicrosoft365Group : PnPGraphCmdlet
2121
[Parameter(Mandatory = false)]
2222
public SwitchParameter IncludeOwners;
2323

24+
[Parameter(Mandatory = false)]
25+
public SwitchParameter Detailed;
26+
2427
[Parameter(Mandatory = false)]
2528
public string Filter;
2629

@@ -30,7 +33,7 @@ protected override void ExecuteCmdlet()
3033

3134
if (Identity != null)
3235
{
33-
var group = Identity.GetGroup(Connection, AccessToken, includeSiteUrl, IncludeOwners);
36+
var group = Identity.GetGroup(Connection, AccessToken, includeSiteUrl, IncludeOwners, Detailed.ToBool());
3437
WriteObject(group);
3538
}
3639
else

src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ protected override void ExecuteCmdlet()
101101

102102
if (!Force)
103103
{
104-
var candidate = Microsoft365GroupsUtility.GetGroupAsync(Connection, MailNickname, AccessToken, false, false).GetAwaiter().GetResult();
104+
var candidate = Microsoft365GroupsUtility.GetGroupAsync(Connection, MailNickname, AccessToken, false, false, false).GetAwaiter().GetResult();
105105
forceCreation = candidate == null || ShouldContinue($"The Microsoft 365 Group '{MailNickname} already exists. Do you want to create a new one?", Properties.Resources.Confirm);
106106
}
107107
else
@@ -180,7 +180,7 @@ protected override void ExecuteCmdlet()
180180
Microsoft365GroupsUtility.SetVisibilityAsync(Connection, AccessToken, group.Id.Value, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult();
181181
}
182182

183-
var updatedGroup = Microsoft365GroupsUtility.GetGroupAsync(Connection, group.Id.Value, AccessToken, true, false).GetAwaiter().GetResult();
183+
var updatedGroup = Microsoft365GroupsUtility.GetGroupAsync(Connection, group.Id.Value, AccessToken, true, false, false).GetAwaiter().GetResult();
184184

185185
WriteObject(updatedGroup);
186186
}

src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupPhoto.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class RemoveMicrosoft365GroupPicture : PnPGraphCmdlet
1818

1919
protected override void ExecuteCmdlet()
2020
{
21-
var group = Identity.GetGroup(Connection, AccessToken, false, false);
21+
var group = Identity.GetGroup(Connection, AccessToken, false, false, false);
2222
if (group != null)
2323
{
2424
var response = Microsoft365GroupsUtility.DeletePhotoAsync(Connection, AccessToken, group.Id.Value).GetAwaiter().GetResult();

src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,23 @@ public class SetMicrosoft365Group : PnPGraphCmdlet
5353

5454
[Parameter(Mandatory = false)]
5555
public string MailNickname;
56-
56+
57+
[Parameter(Mandatory = false)]
58+
[Alias("AllowExternalSenders")] // This is the name used in Microsoft Graph while the name below is the one used within Exchange Online. They both are about the same feature.
59+
public bool? RequireSenderAuthenticationEnabled;
60+
61+
[Parameter(Mandatory = false)]
62+
public bool? AutoSubscribeNewMembers;
63+
5764
protected override void ExecuteCmdlet()
5865
{
59-
var group = Identity.GetGroup(Connection, AccessToken, false, false);
60-
66+
var group = Identity.GetGroup(Connection, AccessToken, false, false, false);
6167

6268
if (group != null)
6369
{
6470
bool changed = false;
71+
bool exchangeOnlinePropertiesChanged = false;
72+
6573
if (ParameterSpecified(nameof(DisplayName)))
6674
{
6775
group.DisplayName = DisplayName;
@@ -91,9 +99,28 @@ protected override void ExecuteCmdlet()
9199
}
92100
if (changed)
93101
{
102+
WriteVerbose("Updating Microsoft 365 Group properties in Microsoft Graph");
94103
group = Microsoft365GroupsUtility.UpdateAsync(Connection, AccessToken, group).GetAwaiter().GetResult();
95104
}
96105

106+
if (ParameterSpecified(nameof(RequireSenderAuthenticationEnabled)) && RequireSenderAuthenticationEnabled.HasValue)
107+
{
108+
group.AllowExternalSenders = RequireSenderAuthenticationEnabled.Value;
109+
exchangeOnlinePropertiesChanged = true;
110+
}
111+
112+
if (ParameterSpecified(nameof(AutoSubscribeNewMembers)) && AutoSubscribeNewMembers.HasValue)
113+
{
114+
group.AutoSubscribeNewMembers = AutoSubscribeNewMembers.Value;
115+
exchangeOnlinePropertiesChanged = true;
116+
}
117+
118+
if (exchangeOnlinePropertiesChanged)
119+
{
120+
WriteVerbose("Updating Microsoft 365 Group Exchange Online properties through Microsoft Graph");
121+
group = Microsoft365GroupsUtility.UpdateExchangeOnlineSettingAsync(Connection, group.Id.Value, AccessToken, group).GetAwaiter().GetResult();
122+
}
123+
97124
if (ParameterSpecified(nameof(Owners)))
98125
{
99126
Microsoft365GroupsUtility.UpdateOwnersAsync(Connection, group.Id.Value, AccessToken, Owners).GetAwaiter().GetResult();

src/Commands/Model/Microsoft365Group.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
namespace PnP.PowerShell.Commands.Model
77
{
8+
/// <summary>
9+
/// Properties of one Microsoft 365 Group
10+
/// </summary>
811
public class Microsoft365Group
912
{
1013
[JsonPropertyName("owners@odata.bind")]
@@ -53,6 +56,9 @@ public string GroupId
5356
public string SiteUrl { get; set; }
5457
public string[] GroupTypes { get; set; }
5558
public IEnumerable<Microsoft365User> Owners { get; set; }
59+
public bool? AllowExternalSenders { get; set; }
60+
public bool? IsSubscribedByMail { get; set; }
61+
public bool? AutoSubscribeNewMembers { get; set; }
5662

5763
public List<AssignedLabels> AssignedLabels { get; set; }
5864

src/Commands/Utilities/Microsoft365GroupsUtility.cs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ internal static async Task<IEnumerable<Microsoft365Group>> GetGroupsAsync(PnPCon
6767
return items;
6868
}
6969

70-
internal static async Task<Microsoft365Group> GetGroupAsync(PnPConnection connection, Guid groupId, string accessToken, bool includeSiteUrl, bool includeOwners)
70+
internal static async Task<Microsoft365Group> GetGroupAsync(PnPConnection connection, Guid groupId, string accessToken, bool includeSiteUrl, bool includeOwners, bool detailed)
7171
{
7272
var results = await GraphHelper.GetAsync<RestResultCollection<Microsoft365Group>>(connection, $"v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified') and id eq '{groupId}'", accessToken);
7373

@@ -100,7 +100,6 @@ internal static async Task<Microsoft365Group> GetGroupAsync(PnPConnection connec
100100
{
101101
if (iterations * 30 >= 300)
102102
{
103-
wait = false;
104103
throw;
105104
}
106105
else
@@ -114,12 +113,19 @@ internal static async Task<Microsoft365Group> GetGroupAsync(PnPConnection connec
114113
{
115114
group.Owners = await GetGroupMembersAsync("owners", connection, group.Id.Value, accessToken);
116115
}
116+
if (detailed)
117+
{
118+
var exchangeOnlineProperties = await GetGroupExchangeOnlineSettingsAsync(connection, group.Id.Value, accessToken);
119+
group.AllowExternalSenders = exchangeOnlineProperties.AllowExternalSenders;
120+
group.AutoSubscribeNewMembers = exchangeOnlineProperties.AutoSubscribeNewMembers;
121+
group.IsSubscribedByMail = exchangeOnlineProperties.IsSubscribedByMail;
122+
}
117123
return group;
118124
}
119125
return null;
120126
}
121127

122-
internal static async Task<Microsoft365Group> GetGroupAsync(PnPConnection connection, string displayName, string accessToken, bool includeSiteUrl, bool includeOwners)
128+
internal static async Task<Microsoft365Group> GetGroupAsync(PnPConnection connection, string displayName, string accessToken, bool includeSiteUrl, bool includeOwners, bool detailed)
123129
{
124130
var results = await GraphHelper.GetAsync<RestResultCollection<Microsoft365Group>>(connection, $"v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified') and (displayName eq '{displayName}' or mailNickName eq '{displayName}')", accessToken);
125131
if (results != null && results.Items.Any())
@@ -335,6 +341,12 @@ private static async Task<IEnumerable<Microsoft365User>> GetGroupMembersAsync(st
335341
return results;
336342
}
337343

344+
private static async Task<Microsoft365Group> GetGroupExchangeOnlineSettingsAsync(PnPConnection connection, Guid groupId, string accessToken)
345+
{
346+
var results = await GraphHelper.GetAsync<Microsoft365Group>(connection, $"v1.0/groups/{groupId}?$select=allowExternalSenders,isSubscribedByMail,autoSubscribeNewMembers", accessToken);
347+
return results;
348+
}
349+
338350
internal static async Task ClearMembersAsync(PnPConnection connection, Guid groupId, string accessToken)
339351
{
340352
var members = await GetMembersAsync(connection, groupId, accessToken);
@@ -393,6 +405,22 @@ internal static async Task UpdateMembersAsync(PnPConnection connection, Guid gro
393405
}
394406
}
395407

408+
internal static async Task<Microsoft365Group> UpdateExchangeOnlineSettingAsync(PnPConnection connection, Guid groupId, string accessToken, Microsoft365Group group)
409+
{
410+
var patchData = new
411+
{
412+
group.AllowExternalSenders,
413+
group.AutoSubscribeNewMembers
414+
};
415+
416+
var result = await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/groups/{groupId}", patchData);
417+
418+
group.AllowExternalSenders = result.AllowExternalSenders;
419+
group.AutoSubscribeNewMembers = result.AutoSubscribeNewMembers;
420+
421+
return group;
422+
}
423+
396424
internal static async Task<Dictionary<string, string>> GetSiteUrlBatchedAsync(PnPConnection connection, string accessToken, string[] groupIds)
397425
{
398426
Dictionary<string, string> returnValue = new Dictionary<string, string>();

0 commit comments

Comments
 (0)