Skip to content

Commit 6dfe178

Browse files
committed
Adds 'entra group user remove' command. Closes #5472
1 parent 23612bc commit 6dfe178

File tree

8 files changed

+694
-12
lines changed

8 files changed

+694
-12
lines changed

docs/docs/cmd/entra/group/group-user-add.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Global from '/docs/cmd/_global.mdx';
22

33
# entra group user add
44

5-
Adds a user to a Microsoft Entra ID group
5+
Adds users to a Microsoft Entra ID group
66

77
## Usage
88

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import Global from '/docs/cmd/_global.mdx';
2+
3+
# entra group user remove
4+
5+
Removes users from a Microsoft Entra ID group
6+
7+
## Usage
8+
9+
```sh
10+
m365 entra group user remove [options]
11+
```
12+
13+
## Options
14+
15+
```md definition-list
16+
`-i, --groupId [groupId]`
17+
: The ID of the Entra ID group. Specify `groupId` or `groupDisplayName` but not both.
18+
19+
`-n, --groupDisplayName [groupDisplayName]`
20+
: The display name of the Entra ID group. Specify `groupId` or `groupDisplayName` but not both.
21+
22+
`--ids [ids]`
23+
: Entra ID IDs of users. You can also pass a comma-separated list of IDs. Specify either `ids` or `userNames` but not both.
24+
25+
`--userNames [userNames]`
26+
: The user principal names of users. You can also pass a comma-separated list of UPNs. Specify either `ids` or `userNames` but not both.
27+
28+
`-r, --role [role]`
29+
: The role to be removed from the users. Valid values: `Owner`, `Member`. Defaults to both.
30+
31+
`--suppressNotFound`
32+
: Suppress errors when a user was not found in a group.
33+
34+
`-f, --force`
35+
: Don't prompt for confirmation.
36+
```
37+
38+
<Global />
39+
40+
## Remarks
41+
42+
:::tip
43+
44+
When you use the `suppressNotFound` option, the command will not return an error if a user is not found as either an owner or a member of the group.
45+
This feature proves useful when you need to remove a user from a group, but you are uncertain whether the user holds the role of a member or an owner within that group.
46+
Without using this option, you would need to manually verify the user's role in the group before proceeding with removal.
47+
48+
:::
49+
50+
## Examples
51+
52+
Remove a single user specified by ID as member from a group specified by display name
53+
54+
```sh
55+
m365 entra group user remove --groupDisplayName Developers --ids 098b9f52-f48c-4401-819f-29c33794c3f5 --role Member
56+
```
57+
58+
Remove multiple users specified by ID from a group specified by ID
59+
60+
```sh
61+
m365 entra group user remove --groupId a03c0c35-ef9a-419b-8cab-f89e0a8d2d2a --ids "098b9f52-f48c-4401-819f-29c33794c3f5,f1e06e31-3abf-4746-83c2-1513d71f38b8"
62+
```
63+
64+
Remove a single user specified by UPN as an owner from a group specified by display name
65+
66+
```sh
67+
m365 entra group user remove --groupDisplayName Developers --userNames [email protected] --role Owner
68+
```
69+
70+
Remove multiple users specified by UPN from a group specified by ID
71+
72+
```sh
73+
m365 entra group user remove --groupId a03c0c35-ef9a-419b-8cab-f89e0a8d2d2a --userNames "[email protected],[email protected]"
74+
```
75+
76+
Remove a single user specified by ID as owner and member of the group and suppress errors when the user was not found as owner or member
77+
78+
```sh
79+
m365 entra group user remove --groupDisplayName Developers --ids 098b9f52-f48c-4401-819f-29c33794c3f5 --suppressNotFound
80+
```
81+
82+
## Response
83+
84+
The command won't return a response on success.

docs/src/config/sidebars.ts

+5
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ const sidebars: SidebarsConfig = {
300300
type: 'doc',
301301
label: 'group user list',
302302
id: 'cmd/entra/group/group-user-list'
303+
},
304+
{
305+
type: 'doc',
306+
label: 'group user remove',
307+
id: 'cmd/entra/group/group-user-remove'
303308
}
304309
]
305310
},

src/m365/entra/commands.ts

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default {
2626
GROUP_REMOVE: `${prefix} group remove`,
2727
GROUP_USER_ADD: `${prefix} group user add`,
2828
GROUP_USER_LIST: `${prefix} group user list`,
29+
GROUP_USER_REMOVE: `${prefix} group user remove`,
2930
GROUPSETTING_ADD: `${prefix} groupsetting add`,
3031
GROUPSETTING_GET: `${prefix} groupsetting get`,
3132
GROUPSETTING_LIST: `${prefix} groupsetting list`,

src/m365/entra/commands/group/group-user-add.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import GlobalOptions from '../../../../GlobalOptions.js';
33
import request, { CliRequestOptions } from '../../../../request.js';
44
import { entraGroup } from '../../../../utils/entraGroup.js';
55
import { entraUser } from '../../../../utils/entraUser.js';
6+
import { GraphBatchRequest, GraphBatchRequestItem } from '../../../../utils/types.js';
67
import { validation } from '../../../../utils/validation.js';
78
import GraphCommand from '../../../base/GraphCommand.js';
89
import commands from '../../commands.js';
@@ -27,7 +28,7 @@ class EntraGroupUserAddCommand extends GraphCommand {
2728
}
2829

2930
public get description(): string {
30-
return 'Adds a user to a Microsoft Entra ID group';
31+
return 'Adds users to a Microsoft Entra ID group';
3132
}
3233

3334
constructor() {
@@ -75,19 +76,19 @@ class EntraGroupUserAddCommand extends GraphCommand {
7576
#initValidators(): void {
7677
this.validators.push(
7778
async (args: CommandArgs) => {
78-
if (args.options.groupId && !validation.isValidGuid(args.options.groupId)) {
79-
return `${args.options.groupId} is not a valid GUID for option groupId.`;
79+
if (args.options.groupId !== undefined && !validation.isValidGuid(args.options.groupId)) {
80+
return `'${args.options.groupId}' is not a valid GUID for option 'groupId'.`;
8081
}
8182

82-
if (args.options.ids) {
83+
if (args.options.ids !== undefined) {
8384
const ids = args.options.ids.split(',').map(i => i.trim());
8485
if (!validation.isValidGuidArray(ids)) {
8586
const invalidGuid = ids.find(id => !validation.isValidGuid(id));
8687
return `'${invalidGuid}' is not a valid GUID for option 'ids'.`;
8788
}
8889
}
8990

90-
if (args.options.userNames) {
91+
if (args.options.userNames !== undefined) {
9192
const isValidUserPrincipalNameArray = validation.isValidUserPrincipalNameArray(args.options.userNames.split(',').map(u => u.trim()));
9293
if (isValidUserPrincipalNameArray !== true) {
9394
return `User principal name '${isValidUserPrincipalNameArray}' is invalid for option 'userNames'.`;
@@ -133,7 +134,7 @@ class EntraGroupUserAddCommand extends GraphCommand {
133134
responseType: 'json',
134135
data: {
135136
requests: []
136-
}
137+
} as GraphBatchRequest
137138
};
138139

139140
for (let j = 0; j < userIdsBatch.length; j += 20) {
@@ -148,7 +149,7 @@ class EntraGroupUserAddCommand extends GraphCommand {
148149
body: {
149150
[`${args.options.role === 'Member' ? 'members' : 'owners'}@odata.bind`]: userIdsChunk.map(u => `${this.resource}/v1.0/directoryObjects/${u}`)
150151
}
151-
});
152+
} as GraphBatchRequestItem);
152153
}
153154

154155
const res = await request.post<{ responses: { status: number; body: any }[] }>(requestOptions);
@@ -170,7 +171,7 @@ class EntraGroupUserAddCommand extends GraphCommand {
170171
}
171172

172173
if (this.verbose) {
173-
await logger.logToStderr(`Retrieving ID of group ${options.groupDisplayName}...`);
174+
await logger.logToStderr(`Retrieving ID of group '${options.groupDisplayName}'...`);
174175
}
175176

176177
return entraGroup.getGroupIdByDisplayName(options.groupDisplayName!);

0 commit comments

Comments
 (0)