@pnp/cli-microsoft365 10.5.0-beta.395d550 → 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/.eslintrc.cjs +1 -0
- package/README.md +1 -1
- 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/graph/commands/directoryextension/directoryextension-add.js +85 -0
- package/dist/m365/graph/commands/directoryextension/directoryextension-get.js +82 -0
- package/dist/m365/graph/commands/directoryextension/directoryextension-remove.js +93 -0
- package/dist/m365/graph/commands.js +3 -0
- package/dist/m365/spo/commands/homesite/homesite-add.js +66 -0
- package/dist/m365/spo/commands/page/page-header-set.js +47 -10
- package/dist/m365/spo/commands/page/page-section-remove.js +81 -0
- package/dist/m365/spo/commands.js +2 -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/dist/request.js +5 -0
- package/dist/utils/directoryExtension.js +25 -0
- package/dist/utils/entraApp.js +28 -0
- package/docs/docs/cmd/graph/directoryextension/directoryextension-add.mdx +123 -0
- package/docs/docs/cmd/graph/directoryextension/directoryextension-get.mdx +118 -0
- package/docs/docs/cmd/graph/directoryextension/directoryextension-remove.mdx +57 -0
- package/docs/docs/cmd/login.mdx +9 -1
- package/docs/docs/cmd/spo/homesite/homesite-add.mdx +124 -0
- package/docs/docs/cmd/spo/page/page-section-remove.mdx +47 -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
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { cli } from '../../../../cli/cli.js';
|
|
2
|
+
import request from '../../../../request.js';
|
|
3
|
+
import { spo } from '../../../../utils/spo.js';
|
|
4
|
+
import { urlUtil } from '../../../../utils/urlUtil.js';
|
|
5
|
+
import { validation } from '../../../../utils/validation.js';
|
|
6
|
+
import SpoCommand from '../../../base/SpoCommand.js';
|
|
7
|
+
import commands from '../../commands.js';
|
|
8
|
+
import { Page } from './Page.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
11
|
+
import { zod } from '../../../../utils/zod.js';
|
|
12
|
+
const options = globalOptionsZod
|
|
13
|
+
.extend({
|
|
14
|
+
webUrl: zod.alias('u', z.string()
|
|
15
|
+
.refine(url => validation.isValidSharePointUrl(url) === true, url => ({
|
|
16
|
+
message: `'${url}' is not a valid SharePoint Online site URL.`
|
|
17
|
+
}))),
|
|
18
|
+
pageName: zod.alias('n', z.string()),
|
|
19
|
+
section: zod.alias('s', z.number()),
|
|
20
|
+
force: zod.alias('f', z.boolean().optional())
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
class SpoPageSectionRemoveCommand extends SpoCommand {
|
|
24
|
+
get name() {
|
|
25
|
+
return commands.PAGE_SECTION_REMOVE;
|
|
26
|
+
}
|
|
27
|
+
get description() {
|
|
28
|
+
return 'Removes the specified section from the modern page';
|
|
29
|
+
}
|
|
30
|
+
get schema() {
|
|
31
|
+
return options;
|
|
32
|
+
}
|
|
33
|
+
async commandAction(logger, args) {
|
|
34
|
+
if (args.options.force) {
|
|
35
|
+
await this.removeSection(logger, args);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove section ${args.options.section} from '${args.options.pageName}'?` });
|
|
39
|
+
if (result) {
|
|
40
|
+
await this.removeSection(logger, args);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async removeSection(logger, args) {
|
|
45
|
+
try {
|
|
46
|
+
if (this.verbose) {
|
|
47
|
+
await logger.logToStderr(`Removing modern page section ${args.options.pageName} - ${args.options.section}...`);
|
|
48
|
+
}
|
|
49
|
+
const reqDigest = await spo.getRequestDigest(args.options.webUrl);
|
|
50
|
+
const clientSidePage = await Page.getPage(args.options.pageName, args.options.webUrl, logger, this.debug, this.verbose);
|
|
51
|
+
const sectionToDelete = clientSidePage.sections
|
|
52
|
+
.findIndex(section => section.order === args.options.section);
|
|
53
|
+
if (sectionToDelete === -1) {
|
|
54
|
+
throw new Error(`Section ${args.options.section} not found`);
|
|
55
|
+
}
|
|
56
|
+
clientSidePage.sections.splice(sectionToDelete, 1);
|
|
57
|
+
const updatedContent = clientSidePage.toHtml();
|
|
58
|
+
const requestOptions = {
|
|
59
|
+
url: `${args.options
|
|
60
|
+
.webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${urlUtil.getServerRelativeSiteUrl(args.options.webUrl)}/sitepages/${args.options.pageName}')/ListItemAllFields`,
|
|
61
|
+
headers: {
|
|
62
|
+
'X-RequestDigest': reqDigest.FormDigestValue,
|
|
63
|
+
'content-type': 'application/json;odata=nometadata',
|
|
64
|
+
'X-HTTP-Method': 'MERGE',
|
|
65
|
+
'IF-MATCH': '*',
|
|
66
|
+
accept: 'application/json;odata=nometadata'
|
|
67
|
+
},
|
|
68
|
+
data: {
|
|
69
|
+
CanvasContent1: updatedContent
|
|
70
|
+
},
|
|
71
|
+
responseType: 'json'
|
|
72
|
+
};
|
|
73
|
+
return request.post(requestOptions);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
this.handleRejectedODataJsonPromise(err);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export default new SpoPageSectionRemoveCommand();
|
|
81
|
+
//# sourceMappingURL=page-section-remove.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`,
|
|
@@ -221,6 +222,7 @@ export default {
|
|
|
221
222
|
PAGE_SECTION_ADD: `${prefix} page section add`,
|
|
222
223
|
PAGE_SECTION_GET: `${prefix} page section get`,
|
|
223
224
|
PAGE_SECTION_LIST: `${prefix} page section list`,
|
|
225
|
+
PAGE_SECTION_REMOVE: `${prefix} page section remove`,
|
|
224
226
|
PAGE_TEMPLATE_LIST: `${prefix} page template list`,
|
|
225
227
|
PAGE_TEMPLATE_REMOVE: `${prefix} page template remove`,
|
|
226
228
|
PAGE_TEXT_ADD: `${prefix} page text add`,
|
|
@@ -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/dist/request.js
CHANGED
|
@@ -132,6 +132,7 @@ class Request {
|
|
|
132
132
|
throw 'Logger not set on the request object';
|
|
133
133
|
}
|
|
134
134
|
this.updateRequestForCloudType(options, auth.connection.cloudType);
|
|
135
|
+
this.removeDoubleSlashes(options);
|
|
135
136
|
try {
|
|
136
137
|
let accessToken = '';
|
|
137
138
|
if (options.headers && options.headers['x-anonymous']) {
|
|
@@ -187,6 +188,10 @@ class Request {
|
|
|
187
188
|
const cloudUrl = Auth.getEndpointForResource(hostname, cloudType);
|
|
188
189
|
options.url = options.url.replace(hostname, cloudUrl);
|
|
189
190
|
}
|
|
191
|
+
removeDoubleSlashes(options) {
|
|
192
|
+
options.url = options.url.substring(0, 8) +
|
|
193
|
+
options.url.substring(8).replace('//', '/');
|
|
194
|
+
}
|
|
190
195
|
createProxyConfigFromUrl(url) {
|
|
191
196
|
const parsedUrl = new URL(url);
|
|
192
197
|
const port = parsedUrl.port || (url.toLowerCase().startsWith('https') ? 443 : 80);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { formatting } from './formatting.js';
|
|
2
|
+
import { odata } from './odata.js';
|
|
3
|
+
export const directoryExtension = {
|
|
4
|
+
/**
|
|
5
|
+
* Get a directory extension by its name registered for an application.
|
|
6
|
+
* @param name Role definition display name.
|
|
7
|
+
* @param appObjectId Application object id.
|
|
8
|
+
* @param properties List of properties to include in the response.
|
|
9
|
+
* @returns The directory extensions.
|
|
10
|
+
* @throws Error when directory extension was not found.
|
|
11
|
+
*/
|
|
12
|
+
async getDirectoryExtensionByName(name, appObjectId, properties) {
|
|
13
|
+
let url = `https://graph.microsoft.com/v1.0/applications/${appObjectId}/extensionProperties?$filter=name eq '${formatting.encodeQueryParameter(name)}'`;
|
|
14
|
+
if (properties) {
|
|
15
|
+
url += `&$select=${properties.join(',')}`;
|
|
16
|
+
}
|
|
17
|
+
const extensionProperties = await odata.getAllItems(url);
|
|
18
|
+
if (extensionProperties.length === 0) {
|
|
19
|
+
throw `The specified directory extension '${name}' does not exist.`;
|
|
20
|
+
}
|
|
21
|
+
// there can be only one directory extension with a given name
|
|
22
|
+
return extensionProperties[0];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=directoryExtension.js.map
|
package/dist/utils/entraApp.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import request from '../request.js';
|
|
3
3
|
import { odata } from './odata.js';
|
|
4
|
+
import { formatting } from './formatting.js';
|
|
5
|
+
import { cli } from '../cli/cli.js';
|
|
4
6
|
async function getCertificateBase64Encoded({ options, logger, debug }) {
|
|
5
7
|
if (options.certificateBase64Encoded) {
|
|
6
8
|
return options.certificateBase64Encoded;
|
|
@@ -278,6 +280,32 @@ export const entraApp = {
|
|
|
278
280
|
await logger.logToStderr(`OAuth2 permissions: ${JSON.stringify(entraApp.appPermissions.flatMap(permission => permission.scope), null, 2)}`);
|
|
279
281
|
}
|
|
280
282
|
return resolvedApis;
|
|
283
|
+
},
|
|
284
|
+
async getAppRegistrationByAppId(appId, properties) {
|
|
285
|
+
let url = `https://graph.microsoft.com/v1.0/applications?$filter=appId eq '${appId}'`;
|
|
286
|
+
if (properties) {
|
|
287
|
+
url += `&$select=${properties.join(',')}`;
|
|
288
|
+
}
|
|
289
|
+
const apps = await odata.getAllItems(url);
|
|
290
|
+
if (apps.length === 0) {
|
|
291
|
+
throw `App with appId '${appId}' not found in Microsoft Entra ID`;
|
|
292
|
+
}
|
|
293
|
+
return apps[0];
|
|
294
|
+
},
|
|
295
|
+
async getAppRegistrationByAppName(appName, properties) {
|
|
296
|
+
let url = `https://graph.microsoft.com/v1.0/applications?$filter=displayName eq '${formatting.encodeQueryParameter(appName)}'`;
|
|
297
|
+
if (properties) {
|
|
298
|
+
url += `&$select=${properties.join(',')}`;
|
|
299
|
+
}
|
|
300
|
+
const apps = await odata.getAllItems(url);
|
|
301
|
+
if (apps.length === 0) {
|
|
302
|
+
throw `App with name '${appName}' not found in Microsoft Entra ID`;
|
|
303
|
+
}
|
|
304
|
+
if (apps.length > 1) {
|
|
305
|
+
const resultAsKeyValuePair = formatting.convertArrayToHashTable('id', apps);
|
|
306
|
+
return await cli.handleMultipleResultsFound(`Multiple apps with name '${appName}' found in Microsoft Entra ID.`, resultAsKeyValuePair);
|
|
307
|
+
}
|
|
308
|
+
return apps[0];
|
|
281
309
|
}
|
|
282
310
|
};
|
|
283
311
|
//# sourceMappingURL=entraApp.js.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import Global from '/docs/cmd/_global.mdx';
|
|
2
|
+
import Tabs from '@theme/Tabs';
|
|
3
|
+
import TabItem from '@theme/TabItem';
|
|
4
|
+
|
|
5
|
+
# graph directoryextension add
|
|
6
|
+
|
|
7
|
+
Creates a new directory extension
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
m365 graph directoryextension add [options]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Options
|
|
16
|
+
|
|
17
|
+
```md definition-list
|
|
18
|
+
`-n, --name <name>`
|
|
19
|
+
: The name of the directory extension.
|
|
20
|
+
|
|
21
|
+
`--appId [appId]`
|
|
22
|
+
: Application (client) ID of the Entra application where the directory extension is registered. Specify either `appId`, `appObjectId` or `appName`, but not multiple.
|
|
23
|
+
|
|
24
|
+
`--appObjectId [appObjectId]`
|
|
25
|
+
: Object ID of the Entra application where the directory extension is registered. Specify either `appId`, `appObjectId` or `appName`, but not multiple.
|
|
26
|
+
|
|
27
|
+
`--appName [appName]`
|
|
28
|
+
: The name of Entra application where the directory extension is registered. Specify either `appId`, `appObjectId` or `appName`, but not multiple.
|
|
29
|
+
|
|
30
|
+
`--dataType <dataType>`
|
|
31
|
+
: The data type of the value the extension property can hold. Possible values are: `Binary`, `Boolean`, `DateTime`, `Integer`, `LargeInteger` and `String`.
|
|
32
|
+
|
|
33
|
+
`--targetObjects <targetObjects>`
|
|
34
|
+
: Comma-separated list of Microsoft Graph resources that can use the extension. Possible values are: `User`, `Group`, `Application`, `AdministrativeUnit`, `Device` and `Organization`.
|
|
35
|
+
|
|
36
|
+
`--isMultiValued`
|
|
37
|
+
: Defines the directory extension as a multi-valued property.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
<Global />
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
Create a new directory extension of string type defined for user resource.
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
m365 graph directoryextension add --name gitHubWorkAccountName --appName ContosoApp --targetObjects User --dataType String
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Create a new multi-valued directory extension of integer type defined for device and application resource
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
m365 graph directoryextension add --name departmentIds --appId 1caf7dcd-7e83-4c3a-94f7-932a1299c844 --targetObjects 'Application,Device' --dataType Integer --isMultiValued
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Response
|
|
57
|
+
|
|
58
|
+
<Tabs>
|
|
59
|
+
<TabItem value="JSON">
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"id": "522817ae-5c95-4243-96c1-f85231fcbc1f",
|
|
64
|
+
"deletedDateTime": null,
|
|
65
|
+
"appDisplayName": "ContosoApp",
|
|
66
|
+
"dataType": "String",
|
|
67
|
+
"isMultiValued": false,
|
|
68
|
+
"isSyncedFromOnPremises": false,
|
|
69
|
+
"name": "extension_105be60b603845fea385e58772d9d630_githubworkaccount",
|
|
70
|
+
"targetObjects": [
|
|
71
|
+
"User"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
</TabItem>
|
|
77
|
+
<TabItem value="Text">
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
appDisplayName : ContosoApp
|
|
81
|
+
dataType : String
|
|
82
|
+
deletedDateTime : null
|
|
83
|
+
id : 01e77f60-3dde-4fa3-825b-cac8048994a8
|
|
84
|
+
isMultiValued : false
|
|
85
|
+
isSyncedFromOnPremises: false
|
|
86
|
+
name : extension_105be60b603845fea385e58772d9d630_githubworkaccount
|
|
87
|
+
targetObjects : ["User"]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
</TabItem>
|
|
91
|
+
<TabItem value="CSV">
|
|
92
|
+
|
|
93
|
+
```csv
|
|
94
|
+
id,deletedDateTime,appDisplayName,dataType,isMultiValued,isSyncedFromOnPremises,name
|
|
95
|
+
b0937b01-49ce-45c7-a52a-727954f092ea,,ContosoApp,String,,,extension_105be60b603845fea385e58772d9d630_githubworkaccount
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</TabItem>
|
|
99
|
+
<TabItem value="Markdown">
|
|
100
|
+
|
|
101
|
+
```md
|
|
102
|
+
# graph directoryextension add --debug "false" --verbose "false" --name "githubworkaccount" --appName "ContosoApp" --dataType "String" --targetObjects "User"
|
|
103
|
+
|
|
104
|
+
Date: 9/8/2024
|
|
105
|
+
|
|
106
|
+
## extension_105be60b603845fea385e58772d9d630_githubworkaccount (cec3c38e-3f4a-4ca8-9523-afa47016f51b)
|
|
107
|
+
|
|
108
|
+
Property | Value|
|
|
109
|
+
---------|-------|
|
|
110
|
+
id | cec3c38e-3f4a-4ca8-9523-afa47016f51b
|
|
111
|
+
appDisplayName | ContosoApp
|
|
112
|
+
dataType | String
|
|
113
|
+
isMultiValued | false
|
|
114
|
+
isSyncedFromOnPremises | false
|
|
115
|
+
name | extension\_105be60b603845fea385e58772d9d630\_githubworkaccount
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
</TabItem>
|
|
119
|
+
</Tabs>
|
|
120
|
+
|
|
121
|
+
## More information
|
|
122
|
+
|
|
123
|
+
- Directory extensions: https://learn.microsoft.com/graph/extensibility-overview#directory-microsoft-entra-id-extensions
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import Global from '/docs/cmd/_global.mdx';
|
|
2
|
+
import Tabs from '@theme/Tabs';
|
|
3
|
+
import TabItem from '@theme/TabItem';
|
|
4
|
+
|
|
5
|
+
# graph directoryextension get
|
|
6
|
+
|
|
7
|
+
Retrieves the definition of a directory extension
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
m365 graph directoryextension get [options]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Options
|
|
16
|
+
|
|
17
|
+
```md definition-list
|
|
18
|
+
`-i, --id [id]`
|
|
19
|
+
: The ID of the directory extension. Specify either `id` or `name`, but not both.
|
|
20
|
+
|
|
21
|
+
`-n, --name [name]`
|
|
22
|
+
: The name of the directory extension. Specify either `id` or `name`, but not both.
|
|
23
|
+
|
|
24
|
+
`--appId [appId]`
|
|
25
|
+
: Application (client) ID of the Entra application where the directory extension is registered. Specify either `appId`, `appObjectId` or `appName`, but not multiple.
|
|
26
|
+
|
|
27
|
+
`--appObjectId [appObjectId]`
|
|
28
|
+
: Object ID of the Entra application where the directory extension is registered. Specify either `appId`, `appObjectId` or `appName`, but not multiple.
|
|
29
|
+
|
|
30
|
+
`--appName [appName]`
|
|
31
|
+
: The name of Entra application where the directory extension is registered. Specify either `appId`, `appObjectId` or `appName`, but not multiple.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
<Global />
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
Get directory extension by id registered for an application specified by app id.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
m365 directoryextension get --id 1f0f15e3-925d-40f0-8fc8-9d3ad135bce0 --appId fd918e4b-c821-4efb-b50a-5eddd23afc6f
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Get directory extension by name registered for an application specified by name.
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
m365 directoryextension get --name extension_105be60b603845fea385e58772d9d630_GitHubWorkAccount --appName ContosoApp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Response
|
|
52
|
+
|
|
53
|
+
<Tabs>
|
|
54
|
+
<TabItem value="JSON">
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"id": "522817ae-5c95-4243-96c1-f85231fcbc1f",
|
|
59
|
+
"deletedDateTime": null,
|
|
60
|
+
"appDisplayName": "ContosoApp",
|
|
61
|
+
"dataType": "String",
|
|
62
|
+
"isMultiValued": false,
|
|
63
|
+
"isSyncedFromOnPremises": false,
|
|
64
|
+
"name": "extension_105be60b603845fea385e58772d9d630_githubworkaccount",
|
|
65
|
+
"targetObjects": [
|
|
66
|
+
"User"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
</TabItem>
|
|
72
|
+
<TabItem value="Text">
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
appDisplayName : ContosoApp
|
|
76
|
+
dataType : String
|
|
77
|
+
deletedDateTime : null
|
|
78
|
+
id : 01e77f60-3dde-4fa3-825b-cac8048994a8
|
|
79
|
+
isMultiValued : false
|
|
80
|
+
isSyncedFromOnPremises: false
|
|
81
|
+
name : extension_105be60b603845fea385e58772d9d630_githubworkaccount
|
|
82
|
+
targetObjects : ["User"]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
</TabItem>
|
|
86
|
+
<TabItem value="CSV">
|
|
87
|
+
|
|
88
|
+
```csv
|
|
89
|
+
id,deletedDateTime,appDisplayName,dataType,isMultiValued,isSyncedFromOnPremises,name
|
|
90
|
+
b0937b01-49ce-45c7-a52a-727954f092ea,,ContosoApp,String,,,extension_105be60b603845fea385e58772d9d630_githubworkaccount
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
</TabItem>
|
|
94
|
+
<TabItem value="Markdown">
|
|
95
|
+
|
|
96
|
+
```md
|
|
97
|
+
# graph directoryextension add --debug "false" --verbose "false" --name "githubworkaccount" --appName "ContosoApp" --dataType "String" --targetObjects "User"
|
|
98
|
+
|
|
99
|
+
Date: 3/2/2025
|
|
100
|
+
|
|
101
|
+
## extension_105be60b603845fea385e58772d9d630_githubworkaccount (cec3c38e-3f4a-4ca8-9523-afa47016f51b)
|
|
102
|
+
|
|
103
|
+
Property | Value|
|
|
104
|
+
---------|-------|
|
|
105
|
+
id | cec3c38e-3f4a-4ca8-9523-afa47016f51b
|
|
106
|
+
appDisplayName | ContosoApp
|
|
107
|
+
dataType | String
|
|
108
|
+
isMultiValued | false
|
|
109
|
+
isSyncedFromOnPremises | false
|
|
110
|
+
name | extension\_105be60b603845fea385e58772d9d630\_githubworkaccount
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
</TabItem>
|
|
114
|
+
</Tabs>
|
|
115
|
+
|
|
116
|
+
## More information
|
|
117
|
+
|
|
118
|
+
- Directory extensions: https://learn.microsoft.com/graph/extensibility-overview#directory-microsoft-entra-id-extensions
|