@pnp/cli-microsoft365 10.6.0-beta.36bb898 → 10.6.0-beta.a7465c9

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.
Files changed (30) hide show
  1. package/.eslintrc.cjs +3 -1
  2. package/allCommands.json +1 -1
  3. package/allCommandsFull.json +1 -1
  4. package/dist/Command.js +4 -26
  5. package/dist/config.js +1 -0
  6. package/dist/m365/adaptivecard/commands/adaptivecard-send.js +2 -1
  7. package/dist/m365/base/SpoCommand.js +2 -1
  8. package/dist/m365/commands/request.js +3 -12
  9. package/dist/m365/commands/setup.js +2 -0
  10. package/dist/m365/entra/commands/administrativeunit/administrativeunit-add.js +10 -5
  11. package/dist/m365/entra/commands/app/app-add.js +5 -0
  12. package/dist/m365/entra/commands/app/app-set.js +23 -0
  13. package/dist/m365/entra/commands/group/group-add.js +1 -0
  14. package/dist/m365/entra/commands/group/group-set.js +12 -6
  15. package/dist/m365/entra/commands/user/user-add.js +1 -0
  16. package/dist/m365/entra/commands/user/user-list.js +2 -1
  17. package/dist/m365/external/commands/item/item-add.js +2 -1
  18. package/dist/m365/graph/commands/openextension/openextension-add.js +73 -0
  19. package/dist/m365/graph/commands.js +1 -0
  20. package/dist/m365/spo/commands/web/web-roleassignment-add.js +22 -47
  21. package/dist/m365/spo/commands/web/web-roleassignment-remove.js +17 -32
  22. package/dist/m365/tenant/commands/people/people-profilecardproperty-add.js +4 -3
  23. package/dist/m365/tenant/commands/people/people-profilecardproperty-set.js +4 -3
  24. package/dist/m365/todo/commands/list/list-remove.js +1 -1
  25. package/dist/utils/entraApp.js +3 -1
  26. package/dist/utils/optionsUtils.js +28 -0
  27. package/dist/utils/spo.js +6 -3
  28. package/docs/docs/cmd/graph/directoryextension/directoryextension-get.mdx +2 -3
  29. package/docs/docs/cmd/graph/openextension/openextension-add.mdx +120 -0
  30. package/package.json +1 -1
@@ -4,15 +4,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
4
4
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
5
  };
6
6
  var _SpoWebRoleAssignmentRemoveCommand_instances, _SpoWebRoleAssignmentRemoveCommand_initTelemetry, _SpoWebRoleAssignmentRemoveCommand_initOptions, _SpoWebRoleAssignmentRemoveCommand_initValidators, _SpoWebRoleAssignmentRemoveCommand_initOptionSets;
7
- import { cli } from '../../../../cli/cli.js';
8
7
  import request from '../../../../request.js';
9
8
  import { validation } from '../../../../utils/validation.js';
10
9
  import SpoCommand from '../../../base/SpoCommand.js';
11
10
  import commands from '../../commands.js';
12
- import spoGroupGetCommand from '../group/group-get.js';
13
- import spoUserGetCommand from '../user/user-get.js';
14
11
  import { entraGroup } from '../../../../utils/entraGroup.js';
15
12
  import { spo } from '../../../../utils/spo.js';
13
+ import { cli } from '../../../../cli/cli.js';
16
14
  class SpoWebRoleAssignmentRemoveCommand extends SpoCommand {
17
15
  get name() {
18
16
  return commands.WEB_ROLEASSIGNMENT_REMOVE;
@@ -45,10 +43,12 @@ class SpoWebRoleAssignmentRemoveCommand extends SpoCommand {
45
43
  }
46
44
  try {
47
45
  if (options.upn) {
48
- options.principalId = await this.getUserPrincipalId(options);
46
+ const principalId = await this.getUserPrincipalId(options, logger);
47
+ await this.removeRoleAssignmentWithOptions(options.webUrl, principalId, logger);
49
48
  }
50
49
  else if (options.groupName) {
51
- options.principalId = await this.getGroupPrincipalId(options);
50
+ const principalId = await this.getGroupPrincipalId(options, logger);
51
+ await this.removeRoleAssignmentWithOptions(options.webUrl, principalId, logger);
52
52
  }
53
53
  else if (options.entraGroupId || options.entraGroupName) {
54
54
  if (this.verbose) {
@@ -58,17 +58,19 @@ class SpoWebRoleAssignmentRemoveCommand extends SpoCommand {
58
58
  ? await entraGroup.getGroupById(options.entraGroupId)
59
59
  : await entraGroup.getGroupByDisplayName(options.entraGroupName);
60
60
  const siteUser = await spo.ensureEntraGroup(options.webUrl, group);
61
- options.principalId = siteUser.Id;
61
+ await this.removeRoleAssignmentWithOptions(options.webUrl, siteUser.Id, logger);
62
62
  }
63
- await this.removeRoleAssignmentWithOptions(logger, options);
64
63
  }
65
64
  catch (err) {
66
65
  this.handleRejectedODataJsonPromise(err);
67
66
  }
68
67
  }
69
- async removeRoleAssignmentWithOptions(logger, options) {
68
+ async removeRoleAssignmentWithOptions(webUrl, principalId, logger) {
69
+ if (this.verbose) {
70
+ await logger.logToStderr('Removing role assignment...');
71
+ }
70
72
  const requestOptions = {
71
- url: `${options.webUrl}/_api/web/roleassignments/removeroleassignment(principalid='${options.principalId}')`,
73
+ url: `${webUrl}/_api/web/roleassignments/removeroleassignment(principalid='${principalId}')`,
72
74
  method: 'POST',
73
75
  headers: {
74
76
  'accept': 'application/json;odata=nometadata',
@@ -78,30 +80,13 @@ class SpoWebRoleAssignmentRemoveCommand extends SpoCommand {
78
80
  };
79
81
  await request.post(requestOptions);
80
82
  }
81
- async getGroupPrincipalId(options) {
82
- const groupGetCommandOptions = {
83
- webUrl: options.webUrl,
84
- name: options.groupName,
85
- output: 'json',
86
- debug: this.debug,
87
- verbose: this.verbose
88
- };
89
- const output = await cli.executeCommandWithOutput(spoGroupGetCommand, { options: { ...groupGetCommandOptions, _: [] } });
90
- const getGroupOutput = JSON.parse(output.stdout);
91
- return getGroupOutput.Id;
83
+ async getGroupPrincipalId(options, logger) {
84
+ const group = await spo.getGroupByName(options.webUrl, options.groupName, logger, this.verbose);
85
+ return group.Id;
92
86
  }
93
- async getUserPrincipalId(options) {
94
- const userGetCommandOptions = {
95
- webUrl: options.webUrl,
96
- email: options.upn,
97
- id: undefined,
98
- output: 'json',
99
- debug: this.debug,
100
- verbose: this.verbose
101
- };
102
- const output = await cli.executeCommandWithOutput(spoUserGetCommand, { options: { ...userGetCommandOptions, _: [] } });
103
- const getUserOutput = JSON.parse(output.stdout);
104
- return getUserOutput.Id;
87
+ async getUserPrincipalId(options, logger) {
88
+ const user = await spo.getUserByEmail(options.webUrl, options.upn, logger, this.verbose);
89
+ return user.Id;
105
90
  }
106
91
  }
107
92
  _SpoWebRoleAssignmentRemoveCommand_instances = new WeakSet(), _SpoWebRoleAssignmentRemoveCommand_initTelemetry = function _SpoWebRoleAssignmentRemoveCommand_initTelemetry() {
@@ -5,6 +5,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
5
5
  };
6
6
  var _TenantPeopleProfileCardPropertyAddCommand_instances, _TenantPeopleProfileCardPropertyAddCommand_initTelemetry, _TenantPeopleProfileCardPropertyAddCommand_initOptions, _TenantPeopleProfileCardPropertyAddCommand_initValidators;
7
7
  import request from '../../../../request.js';
8
+ import { optionsUtils } from '../../../../utils/optionsUtils.js';
8
9
  import GraphCommand from '../../../base/GraphCommand.js';
9
10
  import commands from '../../commands.js';
10
11
  import { profileCardPropertyNames } from './profileCardProperties.js';
@@ -71,7 +72,7 @@ class TenantPeopleProfileCardPropertyAddCommand extends GraphCommand {
71
72
  ];
72
73
  }
73
74
  getLocalizations(options) {
74
- const unknownOptions = Object.keys(this.getUnknownOptions(options));
75
+ const unknownOptions = Object.keys(optionsUtils.getUnknownOptions(options, this.options));
75
76
  if (unknownOptions.length === 0) {
76
77
  return [];
77
78
  }
@@ -88,7 +89,7 @@ class TenantPeopleProfileCardPropertyAddCommand extends GraphCommand {
88
89
  _TenantPeopleProfileCardPropertyAddCommand_instances = new WeakSet(), _TenantPeopleProfileCardPropertyAddCommand_initTelemetry = function _TenantPeopleProfileCardPropertyAddCommand_initTelemetry() {
89
90
  this.telemetry.push((args) => {
90
91
  // Add unknown options to telemetry
91
- const unknownOptions = Object.keys(this.getUnknownOptions(args.options));
92
+ const unknownOptions = Object.keys(optionsUtils.getUnknownOptions(args.options, this.options));
92
93
  const unknownOptionsObj = unknownOptions.reduce((obj, key) => ({ ...obj, [key]: true }), {});
93
94
  Object.assign(this.telemetryProperties, {
94
95
  displayName: typeof args.options.displayName !== 'undefined',
@@ -114,7 +115,7 @@ _TenantPeopleProfileCardPropertyAddCommand_instances = new WeakSet(), _TenantPeo
114
115
  if (!propertyName.startsWith('customattribute') && args.options.displayName !== undefined) {
115
116
  return `The option 'displayName' can only be used when adding customAttributes as profile card properties`;
116
117
  }
117
- const unknownOptions = Object.keys(this.getUnknownOptions(args.options));
118
+ const unknownOptions = Object.keys(optionsUtils.getUnknownOptions(args.options, this.options));
118
119
  if (!propertyName.startsWith('customattribute') && unknownOptions.length > 0) {
119
120
  return `Unknown options like ${unknownOptions.join(', ')} are only supported with customAttributes`;
120
121
  }
@@ -8,6 +8,7 @@ import GraphCommand from '../../../base/GraphCommand.js';
8
8
  import request from '../../../../request.js';
9
9
  import { profileCardPropertyNames as allProfileCardPropertyNames } from './profileCardProperties.js';
10
10
  import commands from '../../commands.js';
11
+ import { optionsUtils } from '../../../../utils/optionsUtils.js';
11
12
  class TenantPeopleProfileCardPropertySetCommand extends GraphCommand {
12
13
  get name() {
13
14
  return commands.PEOPLE_PROFILECARDPROPERTY_SET;
@@ -73,7 +74,7 @@ class TenantPeopleProfileCardPropertySetCommand extends GraphCommand {
73
74
  * @example Transform "--displayName-en-US 'Cost center'" to { languageTag: 'en-US', displayName: 'Cost center' }
74
75
  */
75
76
  getLocalizations(options) {
76
- const unknownOptions = this.getUnknownOptions(options);
77
+ const unknownOptions = optionsUtils.getUnknownOptions(options, this.options);
77
78
  const result = Object.keys(unknownOptions).map(o => ({
78
79
  languageTag: o.substring(o.indexOf('-') + 1),
79
80
  displayName: unknownOptions[o]
@@ -84,7 +85,7 @@ class TenantPeopleProfileCardPropertySetCommand extends GraphCommand {
84
85
  _TenantPeopleProfileCardPropertySetCommand_instances = new WeakSet(), _TenantPeopleProfileCardPropertySetCommand_initTelemetry = function _TenantPeopleProfileCardPropertySetCommand_initTelemetry() {
85
86
  this.telemetry.push((args) => {
86
87
  // Add unknown options to telemetry
87
- const unknownOptions = Object.keys(this.getUnknownOptions(args.options));
88
+ const unknownOptions = Object.keys(optionsUtils.getUnknownOptions(args.options, this.options));
88
89
  const unknownOptionsObj = unknownOptions.reduce((obj, key) => ({ ...obj, [key]: true }), {});
89
90
  Object.assign(this.telemetryProperties, {
90
91
  displayName: typeof args.options.displayName !== 'undefined',
@@ -104,7 +105,7 @@ _TenantPeopleProfileCardPropertySetCommand_instances = new WeakSet(), _TenantPeo
104
105
  return `'${args.options.name}' is not a valid value for option name. Allowed values are: ${this.profileCardPropertyNames.join(', ')}.`;
105
106
  }
106
107
  // Unknown options are allowed only if they start with 'displayName-'
107
- const unknownOptionKeys = Object.keys(this.getUnknownOptions(args.options));
108
+ const unknownOptionKeys = Object.keys(optionsUtils.getUnknownOptions(args.options, this.options));
108
109
  const invalidOptionKey = unknownOptionKeys.find(o => !o.startsWith('displayName-'));
109
110
  if (invalidOptionKey) {
110
111
  return `Invalid option: '${invalidOptionKey}'`;
@@ -46,7 +46,7 @@ class TodoListRemoveCommand extends DelegatedGraphCommand {
46
46
  responseType: 'json'
47
47
  };
48
48
  const response = await request.get(requestOptions);
49
- return response.value && response.value.length === 1 ? response.value[0].id : null;
49
+ return response.value && response.value.length === 1 ? response.value[0].id : undefined;
50
50
  }
51
51
  async removeList(args) {
52
52
  try {
@@ -3,6 +3,7 @@ import request from '../request.js';
3
3
  import { odata } from './odata.js';
4
4
  import { formatting } from './formatting.js';
5
5
  import { cli } from '../cli/cli.js';
6
+ import { optionsUtils } from './optionsUtils.js';
6
7
  async function getCertificateBase64Encoded({ options, logger, debug }) {
7
8
  if (options.certificateBase64Encoded) {
8
9
  return options.certificateBase64Encoded;
@@ -129,7 +130,7 @@ function updateAppPermissions({ spId, resourceAccessPermission, oAuth2Permission
129
130
  }
130
131
  export const entraApp = {
131
132
  appPermissions: [],
132
- createAppRegistration: async ({ options, apis, logger, verbose, debug }) => {
133
+ createAppRegistration: async ({ options, apis, logger, verbose, debug, unknownOptions }) => {
133
134
  const applicationInfo = {
134
135
  displayName: options.name,
135
136
  signInAudience: options.multitenant ? 'AzureADMultipleOrgs' : 'AzureADMyOrg'
@@ -164,6 +165,7 @@ export const entraApp = {
164
165
  if (options.allowPublicClientFlows) {
165
166
  applicationInfo.isFallbackPublicClient = true;
166
167
  }
168
+ optionsUtils.addUnknownOptionsToPayload(applicationInfo, unknownOptions);
167
169
  if (verbose) {
168
170
  await logger.logToStderr(`Creating Microsoft Entra app registration...`);
169
171
  }
@@ -0,0 +1,28 @@
1
+ const longOptionRegex = /--([^\s]+)/;
2
+ const shortOptionRegex = /-([a-z])\b/;
3
+ export const optionsUtils = {
4
+ getUnknownOptions(options, knownOptions) {
5
+ const unknownOptions = JSON.parse(JSON.stringify(options));
6
+ // remove minimist catch-all option
7
+ delete unknownOptions._;
8
+ knownOptions.forEach(o => {
9
+ const longOptionName = longOptionRegex.exec(o.option)[1];
10
+ delete unknownOptions[longOptionName];
11
+ // short names are optional so we need to check if the current command has
12
+ // one before continuing
13
+ const shortOptionMatch = shortOptionRegex.exec(o.option);
14
+ if (shortOptionMatch) {
15
+ const shortOptionName = shortOptionMatch[1];
16
+ delete unknownOptions[shortOptionName];
17
+ }
18
+ });
19
+ return unknownOptions;
20
+ },
21
+ addUnknownOptionsToPayload(payload, unknownOptions) {
22
+ const unknownOptionsNames = Object.getOwnPropertyNames(unknownOptions);
23
+ unknownOptionsNames.forEach(o => {
24
+ payload[o] = unknownOptions[o];
25
+ });
26
+ }
27
+ };
28
+ //# sourceMappingURL=optionsUtils.js.map
package/dist/utils/spo.js CHANGED
@@ -519,10 +519,11 @@ export const spo = {
519
519
  },
520
520
  /**
521
521
  * Retrieves the spo user by email.
522
+ * Returns a user object
522
523
  * @param webUrl Web url
523
524
  * @param email The email of the user
524
525
  * @param logger the Logger object
525
- * @param verbose set for verbose logging
526
+ * @param verbose Set for verbose logging
526
527
  */
527
528
  async getUserByEmail(webUrl, email, logger, verbose) {
528
529
  if (verbose && logger) {
@@ -593,10 +594,11 @@ export const spo = {
593
594
  },
594
595
  /**
595
596
  * Retrieves the spo group by name.
597
+ * Returns a group object
596
598
  * @param webUrl Web url
597
599
  * @param name The name of the group
598
600
  * @param logger the Logger object
599
- * @param verbose set for verbose logging
601
+ * @param verbose Set for verbose logging
600
602
  */
601
603
  async getGroupByName(webUrl, name, logger, verbose) {
602
604
  if (verbose && logger) {
@@ -615,10 +617,11 @@ export const spo = {
615
617
  },
616
618
  /**
617
619
  * Retrieves the role definition by name.
620
+ * Returns a RoleDefinition object
618
621
  * @param webUrl Web url
619
622
  * @param name the name of the role definition
620
623
  * @param logger the Logger object
621
- * @param verbose set for verbose logging
624
+ * @param verbose Set for verbose logging
622
625
  */
623
626
  async getRoleDefinitionByName(webUrl, name, logger, verbose) {
624
627
  if (verbose && logger) {
@@ -38,16 +38,15 @@ m365 graph directoryextension get [options]
38
38
  Get directory extension by id registered for an application specified by app id.
39
39
 
40
40
  ```sh
41
- m365 directoryextension get --id 1f0f15e3-925d-40f0-8fc8-9d3ad135bce0 --appId fd918e4b-c821-4efb-b50a-5eddd23afc6f
41
+ m365 graph directoryextension get --id 1f0f15e3-925d-40f0-8fc8-9d3ad135bce0 --appId fd918e4b-c821-4efb-b50a-5eddd23afc6f
42
42
  ```
43
43
 
44
44
  Get directory extension by name registered for an application specified by name.
45
45
 
46
46
  ```sh
47
- m365 directoryextension get --name extension_105be60b603845fea385e58772d9d630_GitHubWorkAccount --appName ContosoApp
47
+ m365 graph directoryextension get --name extension_105be60b603845fea385e58772d9d630_GitHubWorkAccount --appName ContosoApp
48
48
  ```
49
49
 
50
-
51
50
  ## Response
52
51
 
53
52
  <Tabs>
@@ -0,0 +1,120 @@
1
+ import Global from '/docs/cmd/_global.mdx';
2
+ import Tabs from '@theme/Tabs';
3
+ import TabItem from '@theme/TabItem';
4
+
5
+ # graph openextension add
6
+
7
+ Adds an open extension to a resource
8
+
9
+ ## Usage
10
+
11
+ ```sh
12
+ m365 graph openextension add [options]
13
+ ```
14
+
15
+ ## Options
16
+
17
+ ```md definition-list
18
+ `-n, --name <name>`
19
+ : The name of the open extension.
20
+
21
+ `-i, --resourceId <resourceId>`
22
+ : The Id of the resource for which the extension is created.
23
+
24
+ `-t, --resourceType <resourceType>`
25
+ : The resource type for which the extension is created. Allowed values are `user`, `group`, `device`, `organization`.
26
+ ```
27
+
28
+ <Global />
29
+
30
+ ## Remarks
31
+
32
+ This command allows using unknown options to add custom data to the open extension.
33
+
34
+ When adding an open extension to a user, it's possible to use the UPN as the resourceId.
35
+
36
+ :::warning[Escaping JSON in PowerShell]
37
+
38
+ When creating open extensions it's possible to enter a JSON string. In PowerShell 5 to 7.2 [specific escaping rules](./../../../user-guide/using-cli.mdx#escaping-double-quotes-in-powershell) apply due to an issue. Remember that you can also use [file tokens](./../../../user-guide/using-cli.mdx#passing-complex-content-into-cli-options) instead.
39
+
40
+ :::
41
+
42
+ ## Examples
43
+
44
+ Create a new open extension for a user specified by id. Extension properties are specified by unknown options.
45
+
46
+ ```sh
47
+ m365 graph openextension add --resourceId eb77fbcf-6fe8-458b-985d-1747284793bc --resourceType user --name 'com.contoso.roamingSettings' --theme dark --color red --language English
48
+ ```
49
+
50
+ Create a new open extension for a user specified by userName. Extension properties are specified by unknown options.
51
+
52
+ ```sh
53
+ m365 graph openextension add --resourceId adelev@contoso.com --resourceType user --name 'com.contoso.roamingSettings' --theme dark --color red --language English
54
+ ```
55
+
56
+ Create a new open extension for a group, one of the property represents a JSON object
57
+
58
+ ```sh
59
+ m365 graph openextension add --resourceId eb77fbcf-6fe8-458b-985d-1747284793bc --resourceType group --name 'com.contoso.roamingSettings' --settings '{"theme": "dark", "color": "red", "language": "English"}' --supportedSystem 'Linux'
60
+ ```
61
+
62
+ ## Response
63
+
64
+ <Tabs>
65
+ <TabItem value="JSON">
66
+
67
+ ```json
68
+ {
69
+ "extensionName": "com.contoso.roamingSettings",
70
+ "settings": {
71
+ "theme": "dark",
72
+ "color": "red",
73
+ "language": "English"
74
+ },
75
+ "supportedSystem": "Linux",
76
+ "id": "com.contoso.roamingSettings"
77
+ }
78
+ ```
79
+
80
+ </TabItem>
81
+ <TabItem value="Text">
82
+
83
+ ```text
84
+ extensionName : com.contoso.roamingSettings
85
+ id : com.contoso.roamingSettings
86
+ settings : {"theme":"dark","color":"red","language":"English"}
87
+ supportedSystem: Linux
88
+ ```
89
+
90
+ </TabItem>
91
+ <TabItem value="CSV">
92
+
93
+ ```csv
94
+ extensionName,supportedSystem,id
95
+ com.contoso.roamingSettings,Linux,com.contoso.roamingSettings
96
+ ```
97
+
98
+ </TabItem>
99
+ <TabItem value="Markdown">
100
+
101
+ ```md
102
+ # graph openextension add --name "com.contoso.roamingSettings" --resourceId "01b62bc5-9701-4f93-9587-9d1ea58a086d" --resourceType "user" --settings "{"theme": "dark", "color": "red", "language": "English"}" --supportedSystem "Linux"
103
+
104
+ Date: 3/13/2025
105
+
106
+ ## com.contoso.roamingSettings
107
+
108
+ Property | Value
109
+ ---------|-------
110
+ extensionName | com.contoso.roamingSettings
111
+ supportedSystem | Linux
112
+ id | com.contoso.roamingSettings
113
+ ```
114
+
115
+ </TabItem>
116
+ </Tabs>
117
+
118
+ ## More information
119
+
120
+ - Open extensions: https://learn.microsoft.com/graph/api/opentypeextension-post-opentypeextension
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnp/cli-microsoft365",
3
- "version": "10.6.0-beta.36bb898",
3
+ "version": "10.6.0-beta.a7465c9",
4
4
  "description": "Manage Microsoft 365 and SharePoint Framework projects on any platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/api.js",