@pnp/cli-microsoft365 10.5.0-beta.b872a3f → 10.5.0-beta.d99742c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Auth.js CHANGED
@@ -597,7 +597,9 @@ export class Auth {
597
597
  await connectionStorage.set(JSON.stringify(this.connection));
598
598
  let allConnections = await this.getAllConnections();
599
599
  if (this.connection.active) {
600
- allConnections = allConnections.filter(c => c.identityId !== this.connection.identityId);
600
+ allConnections = allConnections.filter(c => c.identityId !== this.connection.identityId ||
601
+ c.appId !== this.connection.appId ||
602
+ c.tenant !== this.connection.tenant);
601
603
  allConnections.forEach(c => c.active = false);
602
604
  allConnections = [{ ...this.connection }, ...allConnections];
603
605
  }
@@ -5,7 +5,7 @@ process.env.APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL = 'none';
5
5
  process.env.APPLICATION_INSIGHTS_NO_STATSBEAT = 'true';
6
6
  // suppress all logging
7
7
  process.env.APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL = 'NONE';
8
- import * as appInsights from 'applicationinsights';
8
+ import appInsights from 'applicationinsights';
9
9
  import crypto from 'crypto';
10
10
  import fs from 'fs';
11
11
  import os from 'os';
@@ -15,21 +15,27 @@ import { app } from './utils/app.js';
15
15
  const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
16
16
  appInsights
17
17
  .setup('InstrumentationKey=6b908c80-d09f-4cf6-8274-e54349a0149a;IngestionEndpoint=https://westeurope-3.in.applicationinsights.azure.com/;LiveEndpoint=https://westeurope.livediagnostics.monitor.azure.com/;ApplicationId=d42020c6-da32-44f7-bbb7-666f0b3a664d')
18
+ .setAutoCollectRequests(false)
19
+ .setAutoCollectPerformance(false, false)
20
+ .setAutoCollectExceptions(false)
21
+ .setAutoCollectDependencies(false)
22
+ .setAutoCollectConsole(false, false)
23
+ .setAutoCollectPreAggregatedMetrics(false)
24
+ .setSendLiveMetrics(false)
25
+ .enableWebInstrumentation(false)
18
26
  .setInternalLogging(false, false);
19
27
  // append -dev to the version number when ran locally
20
28
  // to distinguish production and dev version of the CLI
21
29
  // in the telemetry
22
30
  const version = `${app.packageJson().version}${fs.existsSync(path.join(__dirname, `..${path.sep}src`)) ? '-dev' : ''}`;
23
31
  const env = process.env.CLIMICROSOFT365_ENV !== undefined ? process.env.CLIMICROSOFT365_ENV : '';
24
- const appInsightsClient = appInsights.default.defaultClient;
32
+ const appInsightsClient = appInsights.defaultClient;
25
33
  appInsightsClient.commonProperties = {
26
34
  version: version,
27
35
  node: process.version,
28
36
  env: env,
29
37
  ci: Boolean(process.env.CI).toString()
30
38
  };
31
- appInsightsClient.config.proxyHttpUrl = process.env.HTTP_PROXY ?? '';
32
- appInsightsClient.config.proxyHttpsUrl = process.env.HTTPS_PROXY ?? '';
33
39
  appInsightsClient.context.tags[appInsightsClient.context.keys.cloudRoleInstance] = crypto.createHash('sha256').update(os.hostname()).digest('hex');
34
40
  delete appInsightsClient.context.tags[appInsightsClient.context.keys.cloudRole];
35
41
  delete appInsightsClient.context.tags['ai.cloud.roleName'];
@@ -9,6 +9,7 @@ import { formatting } from '../../../../utils/formatting.js';
9
9
  import { validation } from '../../../../utils/validation.js';
10
10
  import SpoCommand from '../../../base/SpoCommand.js';
11
11
  import commands from '../../commands.js';
12
+ const BannerWebPartId = 'cbe7b0a9-3504-44dd-a3a3-0e5cacd07788';
12
13
  class SpoPageHeaderSetCommand extends SpoCommand {
13
14
  get name() {
14
15
  return commands.PAGE_HEADER_SET;
@@ -28,8 +29,8 @@ class SpoPageHeaderSetCommand extends SpoCommand {
28
29
  }
29
30
  async commandAction(logger, args) {
30
31
  const noPageHeader = {
31
- "id": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
32
- "instanceId": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
32
+ "id": BannerWebPartId,
33
+ "instanceId": BannerWebPartId,
33
34
  "title": "Title Region",
34
35
  "description": "Title Region Description",
35
36
  "serverProcessedContent": {
@@ -50,8 +51,8 @@ class SpoPageHeaderSetCommand extends SpoCommand {
50
51
  }
51
52
  };
52
53
  const defaultPageHeader = {
53
- "id": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
54
- "instanceId": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
54
+ "id": BannerWebPartId,
55
+ "instanceId": BannerWebPartId,
55
56
  "title": "Title Region",
56
57
  "description": "Title Region Description",
57
58
  "serverProcessedContent": {
@@ -72,8 +73,8 @@ class SpoPageHeaderSetCommand extends SpoCommand {
72
73
  }
73
74
  };
74
75
  const customPageHeader = {
75
- "id": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
76
- "instanceId": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
76
+ "id": BannerWebPartId,
77
+ "instanceId": BannerWebPartId,
77
78
  "title": "Title Region",
78
79
  "description": "Title Region Description",
79
80
  "serverProcessedContent": {
@@ -177,6 +178,14 @@ class SpoPageHeaderSetCommand extends SpoCommand {
177
178
  title = pageData.Title;
178
179
  topicHeader = topicHeader || pageData.TopicHeader || "";
179
180
  }
181
+ const pageControls = JSON.parse(pageData.CanvasContent1);
182
+ //In the new design page header is is a configurable Banner webpart in the first full-width section
183
+ const headerControl = pageControls.find(control => control?.position?.zoneIndex === 1 && control?.position?.sectionFactor === 0 && control?.webPartId === BannerWebPartId);
184
+ const isStandardPageHeader = pageData.LayoutWebpartsContent !== '[]';
185
+ //LayoutWebpartsContent represents standard page header
186
+ if (!isStandardPageHeader) {
187
+ header = headerControl?.webPartData || header;
188
+ }
180
189
  header.properties.title = title;
181
190
  header.properties.textAlignment = args.options.textAlignment || 'Left';
182
191
  header.properties.showTopicHeader = args.options.showTopicHeader || false;
@@ -232,8 +241,39 @@ class SpoPageHeaderSetCommand extends SpoCommand {
232
241
  }
233
242
  }
234
243
  const requestBody = {
235
- LayoutWebpartsContent: JSON.stringify([header])
244
+ LayoutWebpartsContent: JSON.stringify([header]),
245
+ CanvasContent1: canvasContent
236
246
  };
247
+ if (!isStandardPageHeader) {
248
+ requestBody.LayoutWebpartsContent = '[]';
249
+ header.properties.title = topicHeader;
250
+ if (headerControl) {
251
+ headerControl.webPartData = header;
252
+ }
253
+ else {
254
+ for (const pageControl of pageControls) {
255
+ if (pageControl?.position?.sectionIndex) {
256
+ pageControl.position.sectionIndex += pageControl.position.sectionIndex;
257
+ }
258
+ }
259
+ pageControls.push({
260
+ id: BannerWebPartId,
261
+ controlType: 3,
262
+ displayMode: 2,
263
+ emphasis: {},
264
+ position: {
265
+ zoneIndex: 1,
266
+ sectionFactor: 0,
267
+ layoutIndex: 1,
268
+ controlIndex: 1,
269
+ sectionIndex: 1
270
+ },
271
+ webPartId: BannerWebPartId,
272
+ webPartData: header
273
+ });
274
+ }
275
+ requestBody.CanvasContent1 = JSON.stringify(pageControls);
276
+ }
237
277
  if (title) {
238
278
  requestBody.Title = title;
239
279
  }
@@ -249,9 +289,6 @@ class SpoPageHeaderSetCommand extends SpoCommand {
249
289
  if (bannerImageUrl) {
250
290
  requestBody.BannerImageUrl = bannerImageUrl;
251
291
  }
252
- if (canvasContent) {
253
- requestBody.CanvasContent1 = canvasContent;
254
- }
255
292
  requestOptions = {
256
293
  url: `${args.options.webUrl}/_api/sitepages/pages/GetByUrl('sitepages/${formatting.encodeQueryParameter(pageFullName)}')/SavePageAsDraft`,
257
294
  headers: {
@@ -0,0 +1,113 @@
1
+ import { z } from 'zod';
2
+ import { globalOptionsZod } from '../../../../Command.js';
3
+ import { zod } from '../../../../utils/zod.js';
4
+ import GraphCommand from '../../../base/GraphCommand.js';
5
+ import commands from '../../commands.js';
6
+ import { validation } from '../../../../utils/validation.js';
7
+ import { vivaEngage } from '../../../../utils/vivaEngage.js';
8
+ import request from '../../../../request.js';
9
+ import { entraUser } from '../../../../utils/entraUser.js';
10
+ import { formatting } from '../../../../utils/formatting.js';
11
+ const options = globalOptionsZod
12
+ .extend({
13
+ communityId: z.string().optional(),
14
+ communityDisplayName: zod.alias('n', z.string().optional()),
15
+ entraGroupId: z.string()
16
+ .refine(id => validation.isValidGuid(id), id => ({
17
+ message: `'${id}' is not a valid GUID.`
18
+ })).optional(),
19
+ ids: z.string()
20
+ .refine(ids => validation.isValidGuidArray(ids) === true, invalidIds => ({
21
+ message: `The following GUIDs are invalid: ${invalidIds}.`
22
+ })).optional(),
23
+ userNames: z.string()
24
+ .refine(userNames => validation.isValidUserPrincipalNameArray(userNames) === true, invalidUserNames => ({
25
+ message: `The following user principal names are invalid: ${invalidUserNames}.`
26
+ })).optional(),
27
+ role: zod.alias('r', z.enum(['Admin', 'Member']))
28
+ })
29
+ .strict();
30
+ class VivaEngageCommunityUserAddCommand extends GraphCommand {
31
+ get name() {
32
+ return commands.ENGAGE_COMMUNITY_USER_ADD;
33
+ }
34
+ get description() {
35
+ return 'Adds a user to a specific Microsoft 365 Viva Engage community';
36
+ }
37
+ get schema() {
38
+ return options;
39
+ }
40
+ getRefinedSchema(schema) {
41
+ return schema
42
+ .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, {
43
+ message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.'
44
+ })
45
+ .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, {
46
+ message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.'
47
+ })
48
+ .refine(options => options.ids || options.userNames, {
49
+ message: 'Specify either of ids or userNames.'
50
+ })
51
+ .refine(options => typeof options.userNames !== undefined && typeof options.ids !== undefined, {
52
+ message: 'Specify either ids or userNames, but not both.'
53
+ });
54
+ }
55
+ async commandAction(logger, args) {
56
+ try {
57
+ if (this.verbose) {
58
+ await logger.logToStderr('Adding users to community...');
59
+ }
60
+ let entraGroupId = args.options.entraGroupId;
61
+ if (args.options.communityDisplayName) {
62
+ const community = await vivaEngage.getCommunityByDisplayName(args.options.communityDisplayName, ['groupId']);
63
+ entraGroupId = community.groupId;
64
+ }
65
+ if (args.options.communityId) {
66
+ const community = await vivaEngage.getCommunityById(args.options.communityId, ['groupId']);
67
+ entraGroupId = community.groupId;
68
+ }
69
+ const userIds = args.options.ids ? formatting.splitAndTrim(args.options.ids) : await entraUser.getUserIdsByUpns(formatting.splitAndTrim(args.options.userNames));
70
+ const role = args.options.role === 'Member' ? 'members' : 'owners';
71
+ for (let i = 0; i < userIds.length; i += 400) {
72
+ const userIdsBatch = userIds.slice(i, i + 400);
73
+ const requestOptions = {
74
+ url: `${this.resource}/v1.0/$batch`,
75
+ headers: {
76
+ 'content-type': 'application/json;odata.metadata=none'
77
+ },
78
+ responseType: 'json',
79
+ data: {
80
+ requests: []
81
+ }
82
+ };
83
+ // only 20 requests per one batch are allowed
84
+ for (let j = 0; j < userIdsBatch.length; j += 20) {
85
+ // only 20 users can be added in one request
86
+ const userIdsChunk = userIdsBatch.slice(j, j + 20);
87
+ requestOptions.data.requests.push({
88
+ id: j + 1,
89
+ method: 'PATCH',
90
+ url: `/groups/${entraGroupId}`,
91
+ headers: {
92
+ 'content-type': 'application/json;odata.metadata=none'
93
+ },
94
+ body: {
95
+ [`${role}@odata.bind`]: userIdsChunk.map((u) => `${this.resource}/v1.0/directoryObjects/${u}`)
96
+ }
97
+ });
98
+ }
99
+ const res = await request.post(requestOptions);
100
+ for (const response of res.responses) {
101
+ if (response.status !== 204) {
102
+ throw response.body;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ catch (err) {
108
+ this.handleRejectedODataJsonPromise(err);
109
+ }
110
+ }
111
+ }
112
+ export default new VivaEngageCommunityUserAddCommand();
113
+ //# sourceMappingURL=engage-community-user-add.js.map
@@ -0,0 +1,107 @@
1
+ import { z } from 'zod';
2
+ import { globalOptionsZod } from '../../../../Command.js';
3
+ import { zod } from '../../../../utils/zod.js';
4
+ import GraphCommand from '../../../base/GraphCommand.js';
5
+ import commands from '../../commands.js';
6
+ import { validation } from '../../../../utils/validation.js';
7
+ import { vivaEngage } from '../../../../utils/vivaEngage.js';
8
+ import request from '../../../../request.js';
9
+ import { entraUser } from '../../../../utils/entraUser.js';
10
+ import { cli } from '../../../../cli/cli.js';
11
+ const options = globalOptionsZod
12
+ .extend({
13
+ communityId: z.string().optional(),
14
+ communityDisplayName: zod.alias('n', z.string().optional()),
15
+ entraGroupId: z.string()
16
+ .refine(id => validation.isValidGuid(id), id => ({
17
+ message: `'${id}' is not a valid GUID.`
18
+ })).optional(),
19
+ id: z.string()
20
+ .refine(id => validation.isValidGuid(id), id => ({
21
+ message: `'${id}' is not a valid GUID.`
22
+ })).optional(),
23
+ userName: z.string()
24
+ .refine(userName => validation.isValidUserPrincipalName(userName), userName => ({
25
+ message: `'${userName}' is not a valid user principal name.`
26
+ })).optional(),
27
+ force: z.boolean().optional()
28
+ })
29
+ .strict();
30
+ class VivaEngageCommunityUserRemoveCommand extends GraphCommand {
31
+ get name() {
32
+ return commands.ENGAGE_COMMUNITY_USER_REMOVE;
33
+ }
34
+ get description() {
35
+ return 'Removes a specified user from a Microsoft 365 Viva Engage community';
36
+ }
37
+ get schema() {
38
+ return options;
39
+ }
40
+ getRefinedSchema(schema) {
41
+ return schema
42
+ .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, {
43
+ message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.'
44
+ })
45
+ .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, {
46
+ message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.'
47
+ })
48
+ .refine(options => options.id || options.userName, {
49
+ message: 'Specify either of id or userName.'
50
+ })
51
+ .refine(options => typeof options.userName !== undefined && typeof options.id !== undefined, {
52
+ message: 'Specify either id or userName, but not both.'
53
+ });
54
+ }
55
+ async commandAction(logger, args) {
56
+ try {
57
+ if (args.options.force) {
58
+ await this.deleteUserFromCommunity(args.options, logger);
59
+ }
60
+ else {
61
+ const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove the user ${args.options.id || args.options.userName} from the community ${args.options.communityDisplayName || args.options.communityId || args.options.entraGroupId}?` });
62
+ if (result) {
63
+ await this.deleteUserFromCommunity(args.options, logger);
64
+ }
65
+ }
66
+ }
67
+ catch (err) {
68
+ this.handleRejectedODataJsonPromise(err);
69
+ }
70
+ }
71
+ async deleteUserFromCommunity(options, logger) {
72
+ if (this.verbose) {
73
+ await logger.logToStderr('Removing user from community...');
74
+ }
75
+ let entraGroupId = options.entraGroupId;
76
+ if (options.communityDisplayName) {
77
+ const community = await vivaEngage.getCommunityByDisplayName(options.communityDisplayName, ['groupId']);
78
+ entraGroupId = community.groupId;
79
+ }
80
+ else if (options.communityId) {
81
+ const community = await vivaEngage.getCommunityById(options.communityId, ['groupId']);
82
+ entraGroupId = community.groupId;
83
+ }
84
+ const userId = options.id || await entraUser.getUserIdByUpn(options.userName);
85
+ await this.deleteUser(entraGroupId, userId, 'owners');
86
+ await this.deleteUser(entraGroupId, userId, 'members');
87
+ }
88
+ async deleteUser(entraGroupId, userId, role) {
89
+ try {
90
+ const requestOptions = {
91
+ url: `${this.resource}/v1.0/groups/${entraGroupId}/${role}/${userId}/$ref`,
92
+ headers: {
93
+ accept: 'application/json;odata.metadata=none'
94
+ },
95
+ responseType: 'json'
96
+ };
97
+ await request.delete(requestOptions);
98
+ }
99
+ catch (err) {
100
+ if (err.response.status !== 404) {
101
+ throw err.response.data;
102
+ }
103
+ }
104
+ }
105
+ }
106
+ export default new VivaEngageCommunityUserRemoveCommand();
107
+ //# sourceMappingURL=engage-community-user-remove.js.map
@@ -5,7 +5,9 @@ export default {
5
5
  ENGAGE_COMMUNITY_GET: `${prefix} engage community get`,
6
6
  ENGAGE_COMMUNITY_LIST: `${prefix} engage community list`,
7
7
  ENGAGE_COMMUNITY_SET: `${prefix} engage community set`,
8
+ ENGAGE_COMMUNITY_USER_ADD: `${prefix} engage community user add`,
8
9
  ENGAGE_COMMUNITY_USER_LIST: `${prefix} engage community user list`,
10
+ ENGAGE_COMMUNITY_USER_REMOVE: `${prefix} engage community user remove`,
9
11
  ENGAGE_GROUP_LIST: `${prefix} engage group list`,
10
12
  ENGAGE_GROUP_USER_ADD: `${prefix} engage group user add`,
11
13
  ENGAGE_GROUP_USER_REMOVE: `${prefix} engage group user remove`,
@@ -0,0 +1,67 @@
1
+ import Global from '/docs/cmd/_global.mdx';
2
+ import Tabs from '@theme/Tabs';
3
+ import TabItem from '@theme/TabItem';
4
+
5
+ # viva engage community user add
6
+
7
+ Adds a user to a specific Microsoft 365 Viva Engage community
8
+
9
+ ## Usage
10
+
11
+ ```sh
12
+ m365 viva engage community user add [options]
13
+ ```
14
+
15
+ ## Options
16
+
17
+ ```md definition-list
18
+ `-i, --communityId [communityId]`
19
+ : The ID of the Viva Engage community. Specify `communityId`, `communityDisplayName` or `entraGroupId`.
20
+
21
+ `-n, --communityDisplayName [communityDisplayName]`
22
+ : The display name of the Viva Engage community. Specify `communityId`, `communityDisplayName` or `entraGroupId`.
23
+
24
+ `--entraGroupId [entraGroupId]`
25
+ : The ID of the Microsoft 365 group. Specify `communityId`, `communityDisplayName` or `entraGroupId`.
26
+
27
+ `--ids [ids]`
28
+ : Microsoft Entra IDs of users. You can pass a comma-separated list of multiple IDs. Specify either `ids` or `userNames` but not both.
29
+
30
+ `--userNames [userNames]`
31
+ : The user principal names of users. You can pass a comma-separated list of multiple UPNs. Specify either `ids` or `userNames` but not both.
32
+
33
+ `-r, --role <role>`
34
+ : The role to be assigned to the new users. Valid values: `Admin`, `Member`.
35
+ ```
36
+
37
+ <Global />
38
+
39
+ ## Examples
40
+
41
+ Add a single user specified by ID as a member to a community specified by display name.
42
+
43
+ ```sh
44
+ m365 viva engage community user add --communityDisplayName "All company" --ids 098b9f52-f48c-4401-819f-29c33794c3f5 --role Member
45
+ ```
46
+
47
+ Add multiple users specified by ID as members to a community specified by ID.
48
+
49
+ ```sh
50
+ m365 viva engage community user add --communityId eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIzNjAyMDAxMTAwOSJ9 --ids "098b9f52-f48c-4401-819f-29c33794c3f5,f1e06e31-3abf-4746-83c2-1513d71f38b8" --role Member
51
+ ```
52
+
53
+ Add a single user specified by UPN as an admin to a community specified by display name.
54
+
55
+ ```sh
56
+ m365 viva engage community user add --communityDisplayName "All company" --userNames john.doe@contoso.com --role Admin
57
+ ```
58
+
59
+ Adds multiple users specified by UPN as admins to a community specified by its group ID.
60
+
61
+ ```sh
62
+ m365 viva engage community user add --entraGroupId a03c0c35-ef9a-419b-8cab-f89e0a8d2d2a --userNames "john.doe@contoso.com,adele.vance@contoso.com" --role Admin
63
+ ```
64
+
65
+ ## Response
66
+
67
+ The command won't return a response on success.
@@ -0,0 +1,55 @@
1
+ import Global from '/docs/cmd/_global.mdx';
2
+ import Tabs from '@theme/Tabs';
3
+ import TabItem from '@theme/TabItem';
4
+
5
+ # viva engage community user remove
6
+
7
+ Removes a specified user from a Microsoft 365 Viva Engage community
8
+
9
+ ## Usage
10
+
11
+ ```sh
12
+ m365 viva engage community user remove [options]
13
+ ```
14
+
15
+ ## Options
16
+
17
+ ```md definition-list
18
+ `-i, --communityId [communityId]`
19
+ : The ID of the Viva Engage community. Specify `communityId`, `communityDisplayName` or `entraGroupId`.
20
+
21
+ `-n, --communityDisplayName [communityDisplayName]`
22
+ : The display name of the Viva Engage community. Specify `communityId`, `communityDisplayName` or `entraGroupId`.
23
+
24
+ `--entraGroupId [entraGroupId]`
25
+ : The ID of the Microsoft 365 group. Specify `communityId`, `communityDisplayName` or `entraGroupId`.
26
+
27
+ `--id [id]`
28
+ : Microsoft Entra ID of the user. Specify either `id` or `userName` but not both.
29
+
30
+ `--userName [userName]`
31
+ : The user principal name of the user. Specify either `id` or `userName` but not both.
32
+
33
+ `-f, --force`
34
+ : Don't prompt for confirming removing the user from the specified Viva Engage community.
35
+ ```
36
+
37
+ <Global />
38
+
39
+ ## Examples
40
+
41
+ Remove a user specified by ID as a member from a community specified by display name.
42
+
43
+ ```sh
44
+ m365 viva engage community user remove --communityDisplayName "All company" --id 098b9f52-f48c-4401-819f-29c33794c3f5
45
+ ```
46
+
47
+ Remove a user specified by UPN from a community specified by its group ID without confirmation.
48
+
49
+ ```sh
50
+ m365 viva engage community user remove --entraGroupId a03c0c35-ef9a-419b-8cab-f89e0a8d2d2a --userName john.doe@contoso.com --force
51
+ ```
52
+
53
+ ## Response
54
+
55
+ The command won't return a response on success.