@pnp/cli-microsoft365 10.5.0-beta.31bfdff → 10.5.0-beta.63404d1
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/allCommands.json +1 -1
- package/allCommandsFull.json +1 -1
- package/dist/Auth.js +61 -3
- package/dist/appInsights.js +10 -4
- package/dist/m365/commands/login.js +3 -0
- package/dist/m365/spo/commands/homesite/homesite-add.js +66 -0
- package/dist/m365/spo/commands.js +1 -0
- package/dist/m365/viva/commands/engage/engage-community-user-add.js +113 -0
- package/dist/m365/viva/commands/engage/engage-community-user-remove.js +107 -0
- package/dist/m365/viva/commands.js +2 -0
- package/docs/docs/cmd/login.mdx +9 -1
- package/docs/docs/cmd/spo/homesite/homesite-add.mdx +124 -0
- package/docs/docs/cmd/viva/engage/engage-community-user-add.mdx +67 -0
- package/docs/docs/cmd/viva/engage/engage-community-user-remove.mdx +55 -0
- package/npm-shrinkwrap.json +97 -84
- package/package.json +14 -14
package/dist/Auth.js
CHANGED
|
@@ -53,6 +53,7 @@ export var AuthType;
|
|
|
53
53
|
AuthType["Password"] = "password";
|
|
54
54
|
AuthType["Certificate"] = "certificate";
|
|
55
55
|
AuthType["Identity"] = "identity";
|
|
56
|
+
AuthType["FederatedIdentity"] = "federatedIdentity";
|
|
56
57
|
AuthType["Browser"] = "browser";
|
|
57
58
|
AuthType["Secret"] = "secret";
|
|
58
59
|
})(AuthType || (AuthType = {}));
|
|
@@ -153,7 +154,8 @@ export class Auth {
|
|
|
153
154
|
// wasn't specified
|
|
154
155
|
if (this.connection.authType !== AuthType.Certificate &&
|
|
155
156
|
this.connection.authType !== AuthType.Secret &&
|
|
156
|
-
this.connection.authType !== AuthType.Identity
|
|
157
|
+
this.connection.authType !== AuthType.Identity &&
|
|
158
|
+
this.connection.authType !== AuthType.FederatedIdentity) {
|
|
157
159
|
this.clientApplication = await this.getPublicClient(logger, debug);
|
|
158
160
|
if (this.clientApplication) {
|
|
159
161
|
const accounts = await this.clientApplication.getTokenCache().getAllAccounts();
|
|
@@ -177,6 +179,9 @@ export class Auth {
|
|
|
177
179
|
case AuthType.Identity:
|
|
178
180
|
getTokenPromise = this.ensureAccessTokenWithIdentity.bind(this);
|
|
179
181
|
break;
|
|
182
|
+
case AuthType.FederatedIdentity:
|
|
183
|
+
getTokenPromise = this.ensureAccessTokenWithFederatedIdentity.bind(this);
|
|
184
|
+
break;
|
|
180
185
|
case AuthType.Browser:
|
|
181
186
|
getTokenPromise = this.ensureAccessTokenWithBrowser.bind(this);
|
|
182
187
|
break;
|
|
@@ -552,6 +557,56 @@ export class Auth {
|
|
|
552
557
|
}
|
|
553
558
|
}
|
|
554
559
|
}
|
|
560
|
+
async ensureAccessTokenWithFederatedIdentity(resource, logger, debug) {
|
|
561
|
+
if (debug) {
|
|
562
|
+
await logger.logToStderr('Trying to retrieve access token using federated identity...');
|
|
563
|
+
}
|
|
564
|
+
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL || !process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN) {
|
|
565
|
+
throw new CommandError('Federated identity is currently only supported in GitHub Actions.');
|
|
566
|
+
}
|
|
567
|
+
if (debug) {
|
|
568
|
+
await logger.logToStderr('ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN env variables found. The context is GitHub Actions...');
|
|
569
|
+
}
|
|
570
|
+
const federationToken = await this.getFederationTokenFromGithub(logger, debug);
|
|
571
|
+
const queryParams = [
|
|
572
|
+
'grant_type=client_credentials',
|
|
573
|
+
`scope=${encodeURIComponent(`${resource}/.default`)}`,
|
|
574
|
+
`client_id=${this.connection.appId}`,
|
|
575
|
+
`client_assertion_type=${encodeURIComponent('urn:ietf:params:oauth:client-assertion-type:jwt-bearer')}`,
|
|
576
|
+
`client_assertion=${federationToken}`
|
|
577
|
+
];
|
|
578
|
+
const requestOptions = {
|
|
579
|
+
url: `https://login.microsoftonline.com/${this.connection.tenant}/oauth2/v2.0/token`,
|
|
580
|
+
headers: {
|
|
581
|
+
accept: 'application/json',
|
|
582
|
+
'x-anonymous': true,
|
|
583
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
584
|
+
},
|
|
585
|
+
data: queryParams.join('&'),
|
|
586
|
+
responseType: 'json'
|
|
587
|
+
};
|
|
588
|
+
const accessTokenResponse = await request.post(requestOptions);
|
|
589
|
+
return {
|
|
590
|
+
accessToken: accessTokenResponse.access_token,
|
|
591
|
+
expiresOn: new Date(parseInt(accessTokenResponse.expires_on) * 1000)
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
async getFederationTokenFromGithub(logger, debug) {
|
|
595
|
+
if (debug) {
|
|
596
|
+
await logger.logToStderr('Retrieving GitHub federation token...');
|
|
597
|
+
}
|
|
598
|
+
const requestOptions = {
|
|
599
|
+
url: `${process.env.ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${encodeURIComponent('api://AzureADTokenExchange')}`,
|
|
600
|
+
headers: {
|
|
601
|
+
Authorization: `Bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`,
|
|
602
|
+
accept: 'application/json',
|
|
603
|
+
'x-anonymous': true
|
|
604
|
+
},
|
|
605
|
+
responseType: 'json'
|
|
606
|
+
};
|
|
607
|
+
const accessTokenResponse = await request.get(requestOptions);
|
|
608
|
+
return accessTokenResponse.value;
|
|
609
|
+
}
|
|
555
610
|
async ensureAccessTokenWithSecret(resource, logger, debug, fetchNew) {
|
|
556
611
|
this.clientApplication = await this.getConfidentialClient(logger, debug, undefined, undefined, this.connection.secret);
|
|
557
612
|
return this.clientApplication.acquireTokenByClientCredential({
|
|
@@ -597,7 +652,9 @@ export class Auth {
|
|
|
597
652
|
await connectionStorage.set(JSON.stringify(this.connection));
|
|
598
653
|
let allConnections = await this.getAllConnections();
|
|
599
654
|
if (this.connection.active) {
|
|
600
|
-
allConnections = allConnections.filter(c => c.identityId !== this.connection.identityId
|
|
655
|
+
allConnections = allConnections.filter(c => c.identityId !== this.connection.identityId ||
|
|
656
|
+
c.appId !== this.connection.appId ||
|
|
657
|
+
c.tenant !== this.connection.tenant);
|
|
601
658
|
allConnections.forEach(c => c.active = false);
|
|
602
659
|
allConnections = [{ ...this.connection }, ...allConnections];
|
|
603
660
|
}
|
|
@@ -624,7 +681,8 @@ export class Auth {
|
|
|
624
681
|
// When using an application identity, there is no account in the MSAL TokenCache
|
|
625
682
|
if (this.connection.authType !== AuthType.Certificate &&
|
|
626
683
|
this.connection.authType !== AuthType.Secret &&
|
|
627
|
-
this.connection.authType !== AuthType.Identity
|
|
684
|
+
this.connection.authType !== AuthType.Identity &&
|
|
685
|
+
this.connection.authType !== AuthType.FederatedIdentity) {
|
|
628
686
|
this.clientApplication = await this.getPublicClient(logger, debug);
|
|
629
687
|
if (this.clientApplication) {
|
|
630
688
|
const tokenCache = this.clientApplication.getTokenCache();
|
package/dist/appInsights.js
CHANGED
|
@@ -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
|
|
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.
|
|
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'];
|
|
@@ -184,6 +184,9 @@ class LoginCommand extends Command {
|
|
|
184
184
|
auth.connection.authType = AuthType.Identity;
|
|
185
185
|
auth.connection.userName = args.options.userName;
|
|
186
186
|
break;
|
|
187
|
+
case 'federatedIdentity':
|
|
188
|
+
auth.connection.authType = AuthType.FederatedIdentity;
|
|
189
|
+
break;
|
|
187
190
|
case 'browser':
|
|
188
191
|
auth.connection.authType = AuthType.Browser;
|
|
189
192
|
break;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { zod } from '../../../../utils/zod.js';
|
|
3
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
4
|
+
import { spo } from '../../../../utils/spo.js';
|
|
5
|
+
import { validation } from '../../../../utils/validation.js';
|
|
6
|
+
import SpoCommand from '../../../base/SpoCommand.js';
|
|
7
|
+
import commands from '../../commands.js';
|
|
8
|
+
import request from '../../../../request.js';
|
|
9
|
+
const options = globalOptionsZod
|
|
10
|
+
.extend({
|
|
11
|
+
url: zod.alias('u', z.string()
|
|
12
|
+
.refine((url) => validation.isValidSharePointUrl(url) === true, url => ({
|
|
13
|
+
message: `'${url}' is not a valid SharePoint Online site URL.`
|
|
14
|
+
}))),
|
|
15
|
+
audiences: z.string()
|
|
16
|
+
.refine(audiences => validation.isValidGuidArray(audiences) === true, audiences => ({
|
|
17
|
+
message: `The following GUIDs are invalid: ${validation.isValidGuidArray(audiences)}.`
|
|
18
|
+
})).optional(),
|
|
19
|
+
vivaConnectionsDefaultStart: z.boolean().optional(),
|
|
20
|
+
isInDraftMode: z.boolean().optional(),
|
|
21
|
+
order: z.number()
|
|
22
|
+
.refine(order => validation.isValidPositiveInteger(order) === true, order => ({
|
|
23
|
+
message: `'${order}' is not a positive integer.`
|
|
24
|
+
})).optional()
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
class SpoHomeSiteAddCommand extends SpoCommand {
|
|
28
|
+
get name() {
|
|
29
|
+
return commands.HOMESITE_ADD;
|
|
30
|
+
}
|
|
31
|
+
get description() {
|
|
32
|
+
return 'Adds a home site';
|
|
33
|
+
}
|
|
34
|
+
get schema() {
|
|
35
|
+
return options;
|
|
36
|
+
}
|
|
37
|
+
async commandAction(logger, args) {
|
|
38
|
+
try {
|
|
39
|
+
const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.verbose);
|
|
40
|
+
const requestOptions = {
|
|
41
|
+
url: `${spoAdminUrl}/_api/SPHSite/AddHomeSite`,
|
|
42
|
+
headers: {
|
|
43
|
+
accept: 'application/json;odata=nometadata'
|
|
44
|
+
},
|
|
45
|
+
responseType: 'json',
|
|
46
|
+
data: {
|
|
47
|
+
siteUrl: args.options.url,
|
|
48
|
+
audiences: args.options.audiences?.split(','),
|
|
49
|
+
vivaConnectionsDefaultStart: args.options.vivaConnectionsDefaultStart ?? true,
|
|
50
|
+
isInDraftMode: args.options.isInDraftMode ?? false,
|
|
51
|
+
order: args.options.order
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
if (this.verbose) {
|
|
55
|
+
await logger.logToStderr(`Adding home site with URL: ${args.options.url}...`);
|
|
56
|
+
}
|
|
57
|
+
const res = await request.post(requestOptions);
|
|
58
|
+
await logger.log(res);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
this.handleRejectedODataJsonPromise(err);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export default new SpoHomeSiteAddCommand();
|
|
66
|
+
//# sourceMappingURL=homesite-add.js.map
|
|
@@ -116,6 +116,7 @@ export default {
|
|
|
116
116
|
GROUP_MEMBER_REMOVE: `${prefix} group member remove`,
|
|
117
117
|
HIDEDEFAULTTHEMES_GET: `${prefix} hidedefaultthemes get`,
|
|
118
118
|
HIDEDEFAULTTHEMES_SET: `${prefix} hidedefaultthemes set`,
|
|
119
|
+
HOMESITE_ADD: `${prefix} homesite add`,
|
|
119
120
|
HOMESITE_GET: `${prefix} homesite get`,
|
|
120
121
|
HOMESITE_LIST: `${prefix} homesite list`,
|
|
121
122
|
HOMESITE_REMOVE: `${prefix} homesite remove`,
|
|
@@ -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`,
|
package/docs/docs/cmd/login.mdx
CHANGED
|
@@ -22,7 +22,7 @@ m365 login [options]
|
|
|
22
22
|
: ID of the tenant from which accounts should authenticate. Use `common` or `organization` for multitenant apps. Defaults to `common` if not specified and if the config value `tenantId` and the environment variable `CLIMICROSOFT365_TENANT` are also not set.
|
|
23
23
|
|
|
24
24
|
`-t, --authType [authType]`
|
|
25
|
-
: The type of authentication to use. Allowed values `certificate`, `deviceCode`, `password`, `identity`, `browser`, `secret`. Default `deviceCode`
|
|
25
|
+
: The type of authentication to use. Allowed values `certificate`, `deviceCode`, `password`, `identity`, `federatedIdentity`, `browser`, `secret`. Default `deviceCode`
|
|
26
26
|
|
|
27
27
|
`-u, --userName [userName]`
|
|
28
28
|
: Name of the user to authenticate. Required when `authType` is set to `password`
|
|
@@ -78,6 +78,8 @@ When logging in to Microsoft 365 using the user name and password, next to the a
|
|
|
78
78
|
|
|
79
79
|
When logging in to Microsoft 365 using a certificate, the CLI for Microsoft 365 will store the contents of the certificate so that it can automatically re-authenticate if necessary. The contents of the certificate are removed by re-authenticating using the device code or by calling the [logout](logout.mdx) command.
|
|
80
80
|
|
|
81
|
+
Federated Identity is currently supported in GitHub Actions. To use this you need to set `authType` to `federatedIdentity` and specify `appId` and `tenant` to the values of the app registration you've configured a federated credential on.
|
|
82
|
+
|
|
81
83
|
To log in to Microsoft 365 using a certificate or secret, you will typically [create a custom Microsoft Entra application](../user-guide/using-own-identity.mdx). To use this application with the CLI for Microsoft 365, you will set the `CLIMICROSOFT365_ENTRAAPPID` environment variable to the application's ID and the `CLIMICROSOFT365_TENANT` environment variable to the ID of the Microsoft Entra tenant, where you created the Microsoft Entra application. Also, please make sure to read about [the caveats when using the certificate login option](../user-guide/cli-certificate-caveats.mdx).
|
|
82
84
|
|
|
83
85
|
Managed identity in Azure Cloud Shell is the identity of the user. It is neither system- nor user-assigned and it can't be configured. To log in to Microsoft 365 using managed identity in Azure Cloud Shell, set `authType` to `identity` and don't specify the `userName` option.
|
|
@@ -182,6 +184,12 @@ Log in to Microsoft 365 using a user-assigned managed identity with `clientId` s
|
|
|
182
184
|
m365 login --authType identity --userName ac9fbed5-804c-4362-a369-21a4ec51109e
|
|
183
185
|
```
|
|
184
186
|
|
|
187
|
+
Log in to Microsoft 365 using a federated identity. Can currently only be used in GitHub Actions.
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
m365 login --authType federatedIdentity --appId eb96cfd0-abfa-4477-8d1d-c6dfde3efb19 --tenant 9a1a1bbd-e158-4cf5-967d-a53b8e85c628
|
|
191
|
+
```
|
|
192
|
+
|
|
185
193
|
Log in to Microsoft 365 using the interactive browser authentication with `clientId` set in the configuration. Uses the identity of the current user.
|
|
186
194
|
|
|
187
195
|
```sh
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import Global from '/docs/cmd/_global.mdx';
|
|
2
|
+
import Tabs from '@theme/Tabs';
|
|
3
|
+
import TabItem from '@theme/TabItem';
|
|
4
|
+
|
|
5
|
+
# spo homesite add
|
|
6
|
+
|
|
7
|
+
Adds a home site
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
m365 spo homesite add [options]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Options
|
|
16
|
+
|
|
17
|
+
```md definition-list
|
|
18
|
+
`-u, --url <url>`
|
|
19
|
+
: URL of the site to use as a home site.
|
|
20
|
+
|
|
21
|
+
`--isInDraftMode [isInDraftMode]`
|
|
22
|
+
: Specifies whether the home site is in draft mode. Accepts `true` or `false`. Default is `false`.
|
|
23
|
+
|
|
24
|
+
`--vivaConnectionsDefaultStart [vivaConnectionsDefaultStart]`
|
|
25
|
+
: Specifies whether the home site is the default start for Viva Connections. Accepts `true` or `false`. Default is `true`.
|
|
26
|
+
|
|
27
|
+
`--audiences [audiences]`
|
|
28
|
+
: Comma-separated list of Microsoft Entra group IDs that will be used as audience.
|
|
29
|
+
|
|
30
|
+
`--order [order]`
|
|
31
|
+
: Order of the home site. Must be a positive integer.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
<Global />
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
Add a home site
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
m365 spo homesite add --url "https://contoso.sharepoint.com/sites/testcomms"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Add a home site with additional options
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
m365 spo homesite add --url "https://contoso.sharepoint.com/sites/testcomms" --isInDraftMode true --vivaConnectionsDefaultStart false --audiences "af8c0bc8-7b1b-44b4-b087-ffcc8df70d16,754ff15c-76b1-44cb-88c7-0065a4d3cfb7" --order 2
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Response
|
|
51
|
+
|
|
52
|
+
<Tabs>
|
|
53
|
+
<TabItem value="JSON">
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"Audiences": [
|
|
58
|
+
{
|
|
59
|
+
"Email": "active@contoso.onmicrosoft.com",
|
|
60
|
+
"Id": "7a1eea7f-9ab0-40ff-8f2e-0083d9d63451",
|
|
61
|
+
"Title": "active Members"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"IsInDraftMode": true,
|
|
65
|
+
"IsVivaBackendSite": false,
|
|
66
|
+
"SiteId": "ca49054c-85f3-41eb-a290-46ffda8f219c",
|
|
67
|
+
"TargetedLicenseType": 0,
|
|
68
|
+
"Title": "testcommsite",
|
|
69
|
+
"Url": "https://contoso.sharepoint.com/sites/testcomms",
|
|
70
|
+
"VivaConnectionsDefaultStart": false,
|
|
71
|
+
"WebId": "256c4f0f-e372-47b4-a891-b4888e829e20"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
</TabItem>
|
|
76
|
+
<TabItem value="Text">
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
Audiences : [{"Email":"active@contoso.onmicrosoft.com","Id":"7a1eea7f-9ab0-40ff-8f2e-0083d9d63451","Title":"active Members"}]
|
|
80
|
+
IsInDraftMode : true
|
|
81
|
+
IsVivaBackendSite : false
|
|
82
|
+
SiteId : ca49054c-85f3-41eb-a290-46ffda8f219c
|
|
83
|
+
TargetedLicenseType : 0
|
|
84
|
+
Title : testcommsite
|
|
85
|
+
Url : https://contoso.sharepoint.com/sites/testcomms
|
|
86
|
+
VivaConnectionsDefaultStart: false
|
|
87
|
+
WebId : 256c4f0f-e372-47b4-a891-b4888e829e20
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
</TabItem>
|
|
91
|
+
<TabItem value="CSV">
|
|
92
|
+
|
|
93
|
+
```csv
|
|
94
|
+
IsInDraftMode,IsVivaBackendSite,SiteId,TargetedLicenseType,Title,Url,VivaConnectionsDefaultStart,WebId
|
|
95
|
+
1,0,ca49054c-85f3-41eb-a290-46ffda8f219c,0,testcommsite,https://contoso.sharepoint.com/sites/testcomms,0,256c4f0f-e372-47b4-a891-b4888e829e20
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</TabItem>
|
|
99
|
+
<TabItem value="Markdown">
|
|
100
|
+
|
|
101
|
+
```md
|
|
102
|
+
# spo homesite add --url "https://contoso.sharepoint.com/sites/testcomms"
|
|
103
|
+
|
|
104
|
+
Date: 12/23/2024
|
|
105
|
+
|
|
106
|
+
## testcommsite (https://contoso.sharepoint.com/sites/testcomms)
|
|
107
|
+
|
|
108
|
+
Property | Value
|
|
109
|
+
---------|-------
|
|
110
|
+
IsInDraftMode | true
|
|
111
|
+
IsVivaBackendSite | false
|
|
112
|
+
SiteId | ca49054c-85f3-41eb-a290-46ffda8f219c
|
|
113
|
+
TargetedLicenseType | 0
|
|
114
|
+
Title | testcommsite
|
|
115
|
+
Url | https://contoso.sharepoint.com/sites/testcomms
|
|
116
|
+
VivaConnectionsDefaultStart | false
|
|
117
|
+
WebId | 256c4f0f-e372-47b4-a891-b4888e829e20
|
|
118
|
+
```
|
|
119
|
+
</TabItem>
|
|
120
|
+
</Tabs>
|
|
121
|
+
|
|
122
|
+
## More information
|
|
123
|
+
|
|
124
|
+
- SharePoint home sites [Viva Connections set up](https://learn.microsoft.com/en-us/viva/connections/set-up-admin-center)
|