Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removes deprecated option from 'entra m365group user remove' #6439

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions docs/docs/cmd/entra/m365group/m365group-user-remove.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ m365 entra m365group user remove [options]
`--teamName [teamName]`
: The display name of the Microsoft Teams team. Specify only one of the following: `groupId`, `groupName`, `teamId`, or `teamName`.

`-n, --userName [userName]`
: (deprecated) User's UPN (user principal name), eg. `[email protected]`.. Specify only one of the following: `userName`, `ids` or `userNames`.

`--ids [ids]`
: Microsoft Entra IDs of users. You can also pass a comma-separated list of IDs. Specify only one of the following `userName`, `ids` or `userNames`.
: Microsoft Entra IDs of users. You can also pass a comma-separated list of IDs. Specify either `ids` or `userNames` but not both.

`--userNames [userNames]`
: The user principal names of users. You can also pass a comma-separated list of UPNs. Specify only one of the following `userName`, `ids` or `userNames`.
: The user principal names of users. You can also pass a comma-separated list of UPNs. Specify either `ids` or `userNames` but not both.

`-f, --force`
: Don't prompt for confirming removing the user from the specified Microsoft 365 Group or Microsoft Teams team.
Expand Down
71 changes: 21 additions & 50 deletions docs/docs/v10-upgrade-guidance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@

The v10 of CLI for Microsoft 365 introduces several breaking changes. To help you upgrade to the latest version of CLI for Microsoft 365, we've listed those changes along with any actions you may need to take.

## App

### Updated option names of 'app permission add' to plural

We updated the [app permission add](./cmd/app/permission/permission-add.mdx) command. Option were updated to `applicationPermissions` and `delegatedPermissions`.

#### What action do I need to take?

Please, check the documentation of the [app permission add](./cmd/app/permission/permission-add.mdx) command to see the updated options and adjust your scripts accordingly.

## CLI

### Removed command `cli reconsent`
Expand Down Expand Up @@ -70,50 +60,10 @@ The deprecated Guest value was removed from the `role` option in the [aad m365gr

Please, check the documentation of the [aad m365group user list](./cmd/entra/m365group/m365group-user-list.mdx) command to see the updated ``role` option and adjust your scripts accordingly.

#### Aligned options with naming convention

We updated option naming from `groupDisplayName` to `groupName` and `userPrincipalName` to `userName`.

**Affected commands:**

- [entra group member list](./cmd/entra/group/group-member-list.mdx)
- [entra m365group conversation post list](./cmd/entra/m365group/m365group-conversation-post-list.mdx)
- [entra m365group recyclebinitem list](./cmd/entra/m365group/m365group-recyclebinitem-list.mdx)
- [outlook message get](./cmd/outlook/message/message-get.mdx)

#### What action do I need to take?

Please, check the documentation of the affected commands to see the updated option naming and adjust your scripts accordingly.

### Aligns options of `entra enterpriseapp` commands

For this new major version, we've aligned the options of the `entra enterpriseapp` commands as follows:

[entra enterpriseapp add](https://pnp.github.io/cli-microsoft365/cmd/entra/enterpriseapp/enterpriseapp-add)

| Old option | New option |
|---------------|---------------|
| --appId [appId] | -i, --id [id] |
| --appName [appName] | -n, --displayName [displayName] |

[entra enterpriseapp get](https://pnp.github.io/cli-microsoft365/cmd/entra/enterpriseapp/enterpriseapp-get)

| Old option | New option |
|---------------|---------------|
| -i, --appId [appId] | -i, --id [id] |
| -n, --appDisplayName [appDisplayName] | -n, --displayName [displayName] |
| --appObjectId [appObjectId] | --objectId [objectId] |

[entra enterpriseapp list](https://pnp.github.io/cli-microsoft365/cmd/entra/enterpriseapp/enterpriseapp-list)

| Old option | New option |
|---------------|---------------|
| --displayName [displayName] | -n, --displayName [displayName] |

#### What action do I need to take?

Please, check the documentation of the [entra enterpriseapp add](./cmd/entra/enterpriseapp/enterpriseapp-add), [entra enterpriseapp get](./cmd/entra/enterpriseapp/enterpriseapp-get), and [entra enterpriseapp list](./cmd/entra/enterpriseapp/enterpriseapp-list) commands to see the updated options and adjust your scripts accordingly.

### Removed deprecated option `userName` from the `entra m365group user add` command.

The deprecated option `userName` was removed from the command [entra m365group user add](./cmd/entra/m365group/m365group-user-add.mdx).
Expand Down Expand Up @@ -365,6 +315,27 @@ Replace any of the aliases mentioned above with the corresponding command name.

## General

### Aligned options with naming convention

In version 10 of the CLI for Microsoft 365, we have made updates to the options for specific commands, aligning with our naming convention. This includes renaming options to ensure consistency and improve the CLI experience. Some other options have been renamed to align with the response output of the commands. These changes aim to make it easier for you to use the CLI.

We've updated the following commands and options:

Command|Old option|New option
-------|----------|----------
[app permission add](./cmd/app/permission/permission-add.mdx) | `applicationPermission` <br /> `delegatedPermission` | `applicationPermissions` <br /> `delegatedPermissions`
[entra enterpriseapp add](./cmd/entra/enterpriseapp/enterpriseapp-add) | `appId` <br /> `appName` | `id` <br /> `displayName`
[entra enterpriseapp get](./cmd/entra/enterpriseapp/enterpriseapp-get) | `appId` <br /> `appDisplayName` <br /> `appObjectId` | `id` <br /> `displayName` <br /> `objectId`
[entra group member list](./cmd/entra/group/group-member-list.mdx) | `groupDisplayName` | `groupName`
[entra m365group conversation post list](./cmd/entra/m365group/m365group-conversation-post-list.mdx) | `groupDisplayName` | `groupName`
[entra m365group recyclebinitem list](./cmd/entra/m365group/m365group-recyclebinitem-list.mdx) | `groupDisplayName` | `groupName`
[entra m365group user remove](./cmd/entra/m365group/m365group-user-remove.mdx) | `userName` | `userNames`
[outlook message get](./cmd/outlook/message/message-get.mdx) | `userPrincipalName` | `userName`

#### What action do I need to take?

If you use any of the commands listed above, ensure that you use the new option names.

### Ensured output for `list` commands

We noticed that some of the `list` commands were not returning any output when no items were found. We've updated all `list` commands to return an empty array when no items are found.
Expand Down
61 changes: 18 additions & 43 deletions src/m365/entra/commands/m365group/m365group-user-remove.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,11 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
assert.notStrictEqual(command.description, null);
});

it('fails validation if userName is not a valid upn', async () => {
const actual = await command.validate({
options: {
groupId: groupOrTeamId,
userName: 'invalid'
}
}, commandInfo);
assert.notStrictEqual(actual, true);
});

it('fails validation if the groupId is not a valid guid', async () => {
const actual = await command.validate({
options: {
groupId: 'invalid',
userName: userName
userNames: userName
}
}, commandInfo);
assert.notStrictEqual(actual, true);
Expand All @@ -106,7 +96,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
const actual = await command.validate({
options: {
teamId: 'invalid',
userName: userName
userNames: userName
}
}, commandInfo);
assert.notStrictEqual(actual, true);
Expand All @@ -132,16 +122,6 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
assert.notStrictEqual(actual, true);
});

it('passes validation when a valid teamId and userName are specified', async () => {
const actual = await command.validate({
options: {
teamId: groupOrTeamId,
userName: userName
}
}, commandInfo);
assert.strictEqual(actual, true);
});

it('passes validation when a valid teamId and userNames are specified', async () => {
const actual = await command.validate({
options: {
Expand All @@ -164,13 +144,13 @@ describe(commands.M365GROUP_USER_REMOVE, () => {


it('prompts before removing the specified user from the specified Microsoft 365 Group when force option not passed', async () => {
await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } });
await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } });

assert(promptIssued);
});

it('prompts before removing the specified user from the specified Team when force option not passed (debug)', async () => {
await command.action(logger, { options: { debug: true, teamId: "00000000-0000-0000-0000-000000000000", userName: userName } });
await command.action(logger, { options: { debug: true, teamId: "00000000-0000-0000-0000-000000000000", userNames: userName } });

assert(promptIssued);
});
Expand All @@ -180,7 +160,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
sinonUtil.restore(cli.promptForConfirmation);
sinon.stub(cli, 'promptForConfirmation').resolves(false);

await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } });
await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } });
assert(postSpy.notCalled);
});

Expand All @@ -189,7 +169,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
sinonUtil.restore(cli.promptForConfirmation);
sinon.stub(cli, 'promptForConfirmation').resolves(false);

await command.action(logger, { options: { debug: true, groupId: groupOrTeamId, userName: userName } });
await command.action(logger, { options: { debug: true, groupId: groupOrTeamId, userNames: userName } });
assert(postSpy.notCalled);
});

Expand All @@ -213,7 +193,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
sinonUtil.restore(cli.promptForConfirmation);
sinon.stub(cli, 'promptForConfirmation').resolves(true);

await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } });
await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } });
assert(memberDeleteCallIssued);
});

Expand All @@ -235,7 +215,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {

});

await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } });
await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } });
assert(memberDeleteCallIssued);
});

Expand All @@ -261,7 +241,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {

});

await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } });
await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } });
assert(memberDeleteCallIssued);
});

Expand All @@ -287,7 +267,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
sinonUtil.restore(cli.promptForConfirmation);
sinon.stub(cli, 'promptForConfirmation').resolves(true);

await command.action(logger, { options: { teamName: groupOrTeamName, userName: userName, verbose: true } });
await command.action(logger, { options: { teamName: groupOrTeamName, userNames: userName, verbose: true } });
assert(deleteStub.calledTwice);
});

Expand Down Expand Up @@ -318,7 +298,6 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
assert(deleteStub.calledTwice);
});


it('removes the specified members of the specified Microsoft 365 Group specified by groupName', async () => {
sinon.stub(entraGroup, 'getGroupIdByDisplayName').withArgs(groupOrTeamName).resolves(groupOrTeamId);

Expand All @@ -343,11 +322,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
});

it('does not fail if the user is not owner or member of the specified Microsoft 365 Group when prompt confirmed', async () => {
let memberDeleteCallIssued = false;

sinon.stub(request, 'delete').callsFake(async (opts) => {
memberDeleteCallIssued = true;

const deleteStub = sinon.stub(request, 'delete').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${groupOrTeamId}/owners/${userId}/$ref`) {
return {
"response": {
Expand All @@ -369,8 +344,8 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
});


await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } });
assert(memberDeleteCallIssued);
await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } });
assert(deleteStub.calledTwice);
});

it('stops removal if an unknown error message is thrown when deleting the owner', async () => {
Expand Down Expand Up @@ -400,7 +375,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {

});

await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } });
await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } });
assert(memberDeleteCallIssued);
});

Expand All @@ -425,7 +400,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
);
sinon.stub(cli, 'promptForConfirmation').resolves(true);

await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } } as any),
await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } }),
new CommandError(errorMessage));
});

Expand All @@ -448,7 +423,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
sinonUtil.restore(cli.promptForConfirmation);
sinon.stub(cli, 'promptForConfirmation').resolves(true);

await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } } as any),
await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } } as any),
new CommandError('Invalid object identifier'));
});

Expand Down Expand Up @@ -481,15 +456,15 @@ describe(commands.M365GROUP_USER_REMOVE, () => {
sinonUtil.restore(cli.promptForConfirmation);
sinon.stub(cli, 'promptForConfirmation').resolves(true);

await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } } as any),
await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } }),
new CommandError('Invalid object identifier'));
});

it('throws error when the group is not a unified group', async () => {
sinonUtil.restore(entraGroup.isUnifiedGroup);
sinon.stub(entraGroup, 'isUnifiedGroup').resolves(false);

await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: '[email protected]', force: true } } as any),
await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: '[email protected]', force: true } }),
new CommandError(`Specified group with id '${groupOrTeamId}' is not a Microsoft 365 group.`));
});
});
21 changes: 4 additions & 17 deletions src/m365/entra/commands/m365group/m365group-user-remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ interface Options extends GlobalOptions {
teamName?: string;
groupId?: string;
groupName?: string;
userName?: string;
ids?: string;
userNames?: string;
force?: boolean;
Expand Down Expand Up @@ -51,7 +50,6 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand {
groupId: typeof args.options.groupId !== 'undefined',
teamName: typeof args.options.teamName !== 'undefined',
groupName: typeof args.options.groupName !== 'undefined',
userName: typeof args.options.userName !== 'undefined',
ids: typeof args.options.ids !== 'undefined',
userNames: typeof args.options.userNames !== 'undefined'
});
Expand All @@ -72,9 +70,6 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand {
{
option: '--teamName [teamName]'
},
{
option: '-n, --userName [userName]'
},
{
option: '--ids [ids]'
},
Expand Down Expand Up @@ -112,10 +107,6 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand {
}
}

if (args.options.userName && !validation.isValidUserPrincipalName(args.options.userName)) {
return `The specified userName '${args.options.userName}' is not a valid user principal name.`;
}

return true;
}
);
Expand All @@ -127,21 +118,17 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand {
options: ['groupId', 'teamId', 'groupName', 'teamName']
},
{
options: ['userName', 'ids', 'userNames']
options: ['ids', 'userNames']
}
);
}

#initTypes(): void {
this.types.string.push('groupId', 'groupName', 'teamId', 'teamName', 'userName', 'ids', 'userNames');
this.types.string.push('groupId', 'groupName', 'teamId', 'teamName', 'ids', 'userNames');
this.types.boolean.push('force');
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
if (args.options.userName) {
await this.warn(logger, `Option 'userName' is deprecated. Please use 'ids' or 'userNames' instead.`);
}

const removeUser = async (): Promise<void> => {
try {
const groupId: string = await this.getGroupId(logger, args.options.groupId, args.options.teamId, args.options.groupName, args.options.teamName);
Expand All @@ -151,7 +138,7 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand {
throw Error(`Specified group with id '${groupId}' is not a Microsoft 365 group.`);
}

const userNames = args.options.userNames || args.options.userName;
const userNames = args.options.userNames;
const userIds: string[] = await this.getUserIds(logger, args.options.ids, userNames);

await this.removeUsersFromGroup(groupId, userIds, 'owners');
Expand All @@ -166,7 +153,7 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand {
await removeUser();
}
else {
const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove ${args.options.userName || args.options.userNames || args.options.ids} from ${args.options.groupId || args.options.groupName || args.options.teamId || args.options.teamName}?` });
const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove ${args.options.userNames || args.options.ids} from ${args.options.groupId || args.options.groupName || args.options.teamId || args.options.teamName}?` });

if (result) {
await removeUser();
Expand Down