@pnp/cli-microsoft365 10.0.0-beta.48e9f7d → 10.0.0-beta.787c5f1
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 +11 -12
- package/dist/Command.js +4 -1
- package/dist/cli/cli.js +14 -0
- package/dist/config.js +60 -5
- package/dist/m365/base/PowerAutomateCommand.js +1 -1
- package/dist/m365/base/SpoCommand.js +1 -1
- package/dist/m365/cli/commands/cli-consent.js +2 -2
- package/dist/m365/cli/commands/cli-doctor.js +2 -2
- package/dist/m365/cli/commands/cli-reconsent.js +2 -3
- package/dist/m365/cli/commands/config/config-set.js +12 -4
- package/dist/m365/commands/login.js +28 -9
- package/dist/m365/commands/setup.js +256 -33
- package/dist/m365/commands/setupPresets.js +2 -4
- package/dist/m365/connection/commands/connection-list.js +4 -4
- package/dist/m365/entra/commands/app/app-add.js +52 -288
- package/dist/m365/flow/commands/environment/environment-get.js +1 -1
- package/dist/m365/flow/commands/environment/environment-list.js +1 -1
- package/dist/m365/flow/commands/flow-disable.js +1 -1
- package/dist/m365/flow/commands/flow-enable.js +1 -1
- package/dist/m365/flow/commands/flow-export.js +17 -16
- package/dist/m365/flow/commands/flow-get.js +1 -1
- package/dist/m365/flow/commands/flow-list.js +1 -1
- package/dist/m365/flow/commands/flow-remove.js +1 -1
- package/dist/m365/flow/commands/owner/owner-ensure.js +1 -1
- package/dist/m365/flow/commands/owner/owner-list.js +1 -1
- package/dist/m365/flow/commands/owner/owner-remove.js +1 -1
- package/dist/m365/flow/commands/recyclebinitem/recyclebinitem-list.js +47 -0
- package/dist/m365/flow/commands/recyclebinitem/recyclebinitem-restore.js +48 -0
- package/dist/m365/flow/commands/run/run-cancel.js +1 -1
- package/dist/m365/flow/commands/run/run-get.js +1 -1
- package/dist/m365/flow/commands/run/run-list.js +1 -1
- package/dist/m365/flow/commands/run/run-resubmit.js +2 -2
- package/dist/m365/flow/commands.js +2 -0
- package/dist/m365/spo/commands/contenttype/contenttype-field-remove.js +8 -8
- package/dist/m365/spo/commands/contenttype/contenttype-field-set.js +2 -2
- package/dist/m365/spo/commands/file/file-roleassignment-add.js +17 -54
- package/dist/m365/spo/commands/file/file-roleassignment-remove.js +13 -40
- package/dist/m365/spo/commands/file/file-roleinheritance-break.js +5 -13
- package/dist/m365/spo/commands/file/file-roleinheritance-reset.js +5 -13
- package/dist/m365/spo/commands/folder/folder-sharinglink-add.js +143 -0
- package/dist/m365/spo/commands/folder/folder-sharinglink-clear.js +111 -0
- package/dist/m365/spo/commands/folder/folder-sharinglink-remove.js +95 -0
- package/dist/m365/spo/commands/list/list-get.js +17 -4
- package/dist/m365/spo/commands/page/page-section-add.js +185 -34
- package/dist/m365/spo/commands/site/SiteAdmin.js +2 -0
- package/dist/m365/spo/commands/site/site-admin-add.js +252 -0
- package/dist/m365/spo/commands/site/site-admin-list.js +2 -27
- package/dist/m365/spo/commands/user/user-get.js +67 -9
- package/dist/m365/spo/commands.js +4 -0
- package/dist/m365/spp/commands/contentcenter/contentcenter-list.js +56 -0
- package/dist/m365/spp/commands.js +5 -0
- package/dist/settingsNames.js +6 -1
- package/dist/utils/entraApp.js +283 -0
- package/dist/utils/spo.js +75 -7
- package/dist/utils/zod.js +1 -1
- package/docs/docs/_clisettings.mdx +6 -1
- package/docs/docs/cmd/flow/recyclebinitem/recyclebinitem-list.mdx +132 -0
- package/docs/docs/cmd/flow/recyclebinitem/recyclebinitem-restore.mdx +55 -0
- package/docs/docs/cmd/setup.mdx +17 -6
- package/docs/docs/cmd/spo/contenttype/contenttype-field-remove.mdx +7 -7
- package/docs/docs/cmd/spo/contenttype/contenttype-field-set.mdx +2 -2
- package/docs/docs/cmd/spo/folder/folder-sharinglink-add.mdx +125 -0
- package/docs/docs/cmd/spo/folder/folder-sharinglink-clear.mdx +50 -0
- package/docs/docs/cmd/spo/folder/folder-sharinglink-remove.mdx +50 -0
- package/docs/docs/cmd/spo/page/page-section-add.mdx +57 -2
- package/docs/docs/cmd/spo/site/site-admin-add.mdx +67 -0
- package/docs/docs/cmd/spo/user/user-get.mdx +35 -9
- package/docs/docs/cmd/spp/contentcenter/contentcenter-list.mdx +289 -0
- package/npm-shrinkwrap.json +203 -375
- package/package.json +16 -17
|
@@ -9,10 +9,10 @@ import { v4 } from 'uuid';
|
|
|
9
9
|
import auth from '../../../../Auth.js';
|
|
10
10
|
import request from '../../../../request.js';
|
|
11
11
|
import { accessToken } from '../../../../utils/accessToken.js';
|
|
12
|
-
import {
|
|
12
|
+
import { entraApp } from '../../../../utils/entraApp.js';
|
|
13
13
|
import GraphCommand from '../../../base/GraphCommand.js';
|
|
14
|
-
import commands from '../../commands.js';
|
|
15
14
|
import aadCommands from '../../aadCommands.js';
|
|
15
|
+
import commands from '../../commands.js';
|
|
16
16
|
class EntraAppAddCommand extends GraphCommand {
|
|
17
17
|
get name() {
|
|
18
18
|
return commands.APP_ADD;
|
|
@@ -27,7 +27,6 @@ class EntraAppAddCommand extends GraphCommand {
|
|
|
27
27
|
super();
|
|
28
28
|
_EntraAppAddCommand_instances.add(this);
|
|
29
29
|
this.appName = '';
|
|
30
|
-
this.appPermissions = [];
|
|
31
30
|
__classPrivateFieldGet(this, _EntraAppAddCommand_instances, "m", _EntraAppAddCommand_initTelemetry).call(this);
|
|
32
31
|
__classPrivateFieldGet(this, _EntraAppAddCommand_instances, "m", _EntraAppAddCommand_initOptions).call(this);
|
|
33
32
|
__classPrivateFieldGet(this, _EntraAppAddCommand_instances, "m", _EntraAppAddCommand_initValidators).call(this);
|
|
@@ -35,16 +34,38 @@ class EntraAppAddCommand extends GraphCommand {
|
|
|
35
34
|
}
|
|
36
35
|
async commandAction(logger, args) {
|
|
37
36
|
await this.showDeprecationWarning(logger, aadCommands.APP_ADD, commands.APP_ADD);
|
|
37
|
+
if (!args.options.name && this.manifest) {
|
|
38
|
+
args.options.name = this.manifest.name;
|
|
39
|
+
}
|
|
40
|
+
this.appName = args.options.name;
|
|
38
41
|
try {
|
|
39
|
-
const apis = await
|
|
40
|
-
|
|
42
|
+
const apis = await entraApp.resolveApis({
|
|
43
|
+
options: args.options,
|
|
44
|
+
manifest: this.manifest,
|
|
45
|
+
logger,
|
|
46
|
+
verbose: this.verbose,
|
|
47
|
+
debug: this.debug
|
|
48
|
+
});
|
|
49
|
+
let appInfo = await entraApp.createAppRegistration({
|
|
50
|
+
options: args.options,
|
|
51
|
+
apis,
|
|
52
|
+
logger,
|
|
53
|
+
verbose: this.verbose,
|
|
54
|
+
debug: this.debug
|
|
55
|
+
});
|
|
41
56
|
// based on the assumption that we're adding Microsoft Entra app to the current
|
|
42
57
|
// directory. If we in the future extend the command with allowing
|
|
43
58
|
// users to create Microsoft Entra app in a different directory, we'll need to
|
|
44
59
|
// adjust this
|
|
45
60
|
appInfo.tenantId = accessToken.getTenantIdFromAccessToken(auth.connection.accessTokens[auth.defaultResource].accessToken);
|
|
46
61
|
appInfo = await this.updateAppFromManifest(args, appInfo);
|
|
47
|
-
appInfo = await
|
|
62
|
+
appInfo = await entraApp.grantAdminConsent({
|
|
63
|
+
appInfo,
|
|
64
|
+
appPermissions: entraApp.appPermissions,
|
|
65
|
+
adminConsent: args.options.grantAdminConsent,
|
|
66
|
+
logger,
|
|
67
|
+
debug: this.debug
|
|
68
|
+
});
|
|
48
69
|
appInfo = await this.configureUri(args, appInfo, logger);
|
|
49
70
|
appInfo = await this.configureSecret(args, appInfo, logger);
|
|
50
71
|
const _appInfo = await this.saveAppInfo(args, appInfo, logger);
|
|
@@ -62,128 +83,45 @@ class EntraAppAddCommand extends GraphCommand {
|
|
|
62
83
|
this.handleRejectedODataJsonPromise(err);
|
|
63
84
|
}
|
|
64
85
|
}
|
|
65
|
-
async
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
signInAudience: args.options.multitenant ? 'AzureADMultipleOrgs' : 'AzureADMyOrg'
|
|
69
|
-
};
|
|
70
|
-
if (!applicationInfo.displayName && this.manifest) {
|
|
71
|
-
applicationInfo.displayName = this.manifest.name;
|
|
72
|
-
}
|
|
73
|
-
this.appName = applicationInfo.displayName;
|
|
74
|
-
if (apis.length > 0) {
|
|
75
|
-
applicationInfo.requiredResourceAccess = apis;
|
|
76
|
-
}
|
|
77
|
-
if (args.options.redirectUris) {
|
|
78
|
-
applicationInfo[args.options.platform] = {
|
|
79
|
-
redirectUris: args.options.redirectUris.split(',').map(u => u.trim())
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
if (args.options.implicitFlow) {
|
|
83
|
-
if (!applicationInfo.web) {
|
|
84
|
-
applicationInfo.web = {};
|
|
85
|
-
}
|
|
86
|
-
applicationInfo.web.implicitGrantSettings = {
|
|
87
|
-
enableAccessTokenIssuance: true,
|
|
88
|
-
enableIdTokenIssuance: true
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
if (args.options.certificateFile || args.options.certificateBase64Encoded) {
|
|
92
|
-
const certificateBase64Encoded = await this.getCertificateBase64Encoded(args, logger);
|
|
93
|
-
const newKeyCredential = {
|
|
94
|
-
type: "AsymmetricX509Cert",
|
|
95
|
-
usage: "Verify",
|
|
96
|
-
displayName: args.options.certificateDisplayName,
|
|
97
|
-
key: certificateBase64Encoded
|
|
98
|
-
};
|
|
99
|
-
applicationInfo.keyCredentials = [newKeyCredential];
|
|
100
|
-
}
|
|
101
|
-
if (args.options.allowPublicClientFlows) {
|
|
102
|
-
applicationInfo.isFallbackPublicClient = true;
|
|
86
|
+
async configureSecret(args, appInfo, logger) {
|
|
87
|
+
if (!args.options.withSecret || (appInfo.secrets && appInfo.secrets.length > 0)) {
|
|
88
|
+
return appInfo;
|
|
103
89
|
}
|
|
104
90
|
if (this.verbose) {
|
|
105
|
-
await logger.logToStderr(`
|
|
106
|
-
}
|
|
107
|
-
const createApplicationRequestOptions = {
|
|
108
|
-
url: `${this.resource}/v1.0/myorganization/applications`,
|
|
109
|
-
headers: {
|
|
110
|
-
accept: 'application/json;odata.metadata=none'
|
|
111
|
-
},
|
|
112
|
-
responseType: 'json',
|
|
113
|
-
data: applicationInfo
|
|
114
|
-
};
|
|
115
|
-
return request.post(createApplicationRequestOptions);
|
|
116
|
-
}
|
|
117
|
-
async grantAdminConsent(appInfo, adminConsent, logger) {
|
|
118
|
-
if (!adminConsent || this.appPermissions.length === 0) {
|
|
119
|
-
return appInfo;
|
|
91
|
+
await logger.logToStderr(`Configure Microsoft Entra app secret...`);
|
|
120
92
|
}
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
123
|
-
|
|
93
|
+
const secret = await this.createSecret({ appObjectId: appInfo.id });
|
|
94
|
+
if (!appInfo.secrets) {
|
|
95
|
+
appInfo.secrets = [];
|
|
124
96
|
}
|
|
125
|
-
|
|
126
|
-
this.appPermissions.forEach(async (permission) => {
|
|
127
|
-
if (permission.scope.length > 0) {
|
|
128
|
-
tasks.push(this.grantOAuth2Permission(sp.id, permission.resourceId, permission.scope.join(' ')));
|
|
129
|
-
if (this.debug) {
|
|
130
|
-
await logger.logToStderr(`Admin consent granted for following resource ${permission.resourceId}, with delegated permissions: ${permission.scope.join(',')}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
permission.resourceAccess.filter(access => access.type === "Role").forEach(async (access) => {
|
|
134
|
-
tasks.push(this.addRoleToServicePrincipal(sp.id, permission.resourceId, access.id));
|
|
135
|
-
if (this.debug) {
|
|
136
|
-
await logger.logToStderr(`Admin consent granted for following resource ${permission.resourceId}, with application permission: ${access.id}`);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
await Promise.all(tasks);
|
|
97
|
+
appInfo.secrets.push(secret);
|
|
141
98
|
return appInfo;
|
|
142
99
|
}
|
|
143
|
-
async
|
|
100
|
+
async createSecret({ appObjectId, displayName = undefined, expirationDate = undefined }) {
|
|
101
|
+
let secretExpirationDate = expirationDate;
|
|
102
|
+
if (!secretExpirationDate) {
|
|
103
|
+
secretExpirationDate = new Date();
|
|
104
|
+
secretExpirationDate.setFullYear(secretExpirationDate.getFullYear() + 1);
|
|
105
|
+
}
|
|
106
|
+
const secretName = displayName ?? 'Default';
|
|
144
107
|
const requestOptions = {
|
|
145
|
-
url: `${this.resource}/v1.0/myorganization/
|
|
146
|
-
headers: {
|
|
147
|
-
'Content-Type': 'application/json'
|
|
148
|
-
},
|
|
149
|
-
responseType: 'json',
|
|
150
|
-
data: {
|
|
151
|
-
appRoleId: appRoleId,
|
|
152
|
-
principalId: objectId,
|
|
153
|
-
resourceId: resourceId
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
return request.post(requestOptions);
|
|
157
|
-
}
|
|
158
|
-
async grantOAuth2Permission(appId, resourceId, scopeName) {
|
|
159
|
-
const grantAdminConsentApplicationRequestOptions = {
|
|
160
|
-
url: `${this.resource}/v1.0/myorganization/oauth2PermissionGrants`,
|
|
108
|
+
url: `${this.resource}/v1.0/myorganization/applications/${appObjectId}/addPassword`,
|
|
161
109
|
headers: {
|
|
162
|
-
|
|
110
|
+
'content-type': 'application/json'
|
|
163
111
|
},
|
|
164
112
|
responseType: 'json',
|
|
165
113
|
data: {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
scope: scopeName
|
|
114
|
+
passwordCredential: {
|
|
115
|
+
displayName: secretName,
|
|
116
|
+
endDateTime: secretExpirationDate.toISOString()
|
|
117
|
+
}
|
|
171
118
|
}
|
|
172
119
|
};
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
url: `${this.resource}/v1.0/myorganization/servicePrincipals`,
|
|
178
|
-
headers: {
|
|
179
|
-
'content-type': 'application/json'
|
|
180
|
-
},
|
|
181
|
-
data: {
|
|
182
|
-
appId: appId
|
|
183
|
-
},
|
|
184
|
-
responseType: 'json'
|
|
120
|
+
const response = await request.post(requestOptions);
|
|
121
|
+
return {
|
|
122
|
+
displayName: secretName,
|
|
123
|
+
value: response.secretText
|
|
185
124
|
};
|
|
186
|
-
return request.post(requestOptions);
|
|
187
125
|
}
|
|
188
126
|
async updateAppFromManifest(args, appInfo) {
|
|
189
127
|
if (!args.options.manifest) {
|
|
@@ -423,180 +361,6 @@ class EntraAppAddCommand extends GraphCommand {
|
|
|
423
361
|
await request.patch(requestOptions);
|
|
424
362
|
return appInfo;
|
|
425
363
|
}
|
|
426
|
-
async resolveApis(args, logger) {
|
|
427
|
-
if (!args.options.apisDelegated && !args.options.apisApplication
|
|
428
|
-
&& (typeof this.manifest?.requiredResourceAccess === 'undefined' || this.manifest.requiredResourceAccess.length === 0)) {
|
|
429
|
-
return [];
|
|
430
|
-
}
|
|
431
|
-
if (this.verbose) {
|
|
432
|
-
await logger.logToStderr('Resolving requested APIs...');
|
|
433
|
-
}
|
|
434
|
-
const servicePrincipals = await odata.getAllItems(`${this.resource}/v1.0/myorganization/servicePrincipals?$select=appId,appRoles,id,oauth2PermissionScopes,servicePrincipalNames`);
|
|
435
|
-
let resolvedApis = [];
|
|
436
|
-
try {
|
|
437
|
-
if (args.options.apisDelegated || args.options.apisApplication) {
|
|
438
|
-
resolvedApis = await this.getRequiredResourceAccessForApis(servicePrincipals, args.options.apisDelegated, 'Scope', logger);
|
|
439
|
-
if (this.verbose) {
|
|
440
|
-
await logger.logToStderr(`Resolved delegated permissions: ${JSON.stringify(resolvedApis, null, 2)}`);
|
|
441
|
-
}
|
|
442
|
-
const resolvedApplicationApis = await this.getRequiredResourceAccessForApis(servicePrincipals, args.options.apisApplication, 'Role', logger);
|
|
443
|
-
if (this.verbose) {
|
|
444
|
-
await logger.logToStderr(`Resolved application permissions: ${JSON.stringify(resolvedApplicationApis, null, 2)}`);
|
|
445
|
-
}
|
|
446
|
-
// merge resolved application APIs onto resolved delegated APIs
|
|
447
|
-
resolvedApplicationApis.forEach(resolvedRequiredResource => {
|
|
448
|
-
const requiredResource = resolvedApis.find(api => api.resourceAppId === resolvedRequiredResource.resourceAppId);
|
|
449
|
-
if (requiredResource) {
|
|
450
|
-
requiredResource.resourceAccess.push(...resolvedRequiredResource.resourceAccess);
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
resolvedApis.push(resolvedRequiredResource);
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
const manifestApis = this.manifest.requiredResourceAccess;
|
|
459
|
-
manifestApis.forEach(manifestApi => {
|
|
460
|
-
resolvedApis.push(manifestApi);
|
|
461
|
-
const app = servicePrincipals.find(servicePrincipals => servicePrincipals.appId === manifestApi.resourceAppId);
|
|
462
|
-
if (app) {
|
|
463
|
-
manifestApi.resourceAccess.forEach((res => {
|
|
464
|
-
const resourceAccessPermission = {
|
|
465
|
-
id: res.id,
|
|
466
|
-
type: res.type
|
|
467
|
-
};
|
|
468
|
-
const oAuthValue = app.oauth2PermissionScopes.find(scp => scp.id === res.id)?.value;
|
|
469
|
-
this.updateAppPermissions(app.id, resourceAccessPermission, oAuthValue);
|
|
470
|
-
}));
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
if (this.verbose) {
|
|
475
|
-
await logger.logToStderr(`Merged delegated and application permissions: ${JSON.stringify(resolvedApis, null, 2)}`);
|
|
476
|
-
await logger.logToStderr(`App role assignments: ${JSON.stringify(this.appPermissions.flatMap(permission => permission.resourceAccess.filter(access => access.type === "Role")), null, 2)}`);
|
|
477
|
-
await logger.logToStderr(`OAuth2 permissions: ${JSON.stringify(this.appPermissions.flatMap(permission => permission.scope), null, 2)}`);
|
|
478
|
-
}
|
|
479
|
-
return resolvedApis;
|
|
480
|
-
}
|
|
481
|
-
catch (e) {
|
|
482
|
-
throw e;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
async getRequiredResourceAccessForApis(servicePrincipals, apis, scopeType, logger) {
|
|
486
|
-
if (!apis) {
|
|
487
|
-
return [];
|
|
488
|
-
}
|
|
489
|
-
const resolvedApis = [];
|
|
490
|
-
const requestedApis = apis.split(',').map(a => a.trim());
|
|
491
|
-
for (const api of requestedApis) {
|
|
492
|
-
const pos = api.lastIndexOf('/');
|
|
493
|
-
const permissionName = api.substr(pos + 1);
|
|
494
|
-
const servicePrincipalName = api.substr(0, pos);
|
|
495
|
-
if (this.debug) {
|
|
496
|
-
await logger.logToStderr(`Resolving ${api}...`);
|
|
497
|
-
await logger.logToStderr(`Permission name: ${permissionName}`);
|
|
498
|
-
await logger.logToStderr(`Service principal name: ${servicePrincipalName}`);
|
|
499
|
-
}
|
|
500
|
-
const servicePrincipal = servicePrincipals.find(sp => (sp.servicePrincipalNames.indexOf(servicePrincipalName) > -1 ||
|
|
501
|
-
sp.servicePrincipalNames.indexOf(`${servicePrincipalName}/`) > -1));
|
|
502
|
-
if (!servicePrincipal) {
|
|
503
|
-
throw `Service principal ${servicePrincipalName} not found`;
|
|
504
|
-
}
|
|
505
|
-
const scopesOfType = scopeType === 'Scope' ? servicePrincipal.oauth2PermissionScopes : servicePrincipal.appRoles;
|
|
506
|
-
const permission = scopesOfType.find(scope => scope.value === permissionName);
|
|
507
|
-
if (!permission) {
|
|
508
|
-
throw `Permission ${permissionName} for service principal ${servicePrincipalName} not found`;
|
|
509
|
-
}
|
|
510
|
-
let resolvedApi = resolvedApis.find(a => a.resourceAppId === servicePrincipal.appId);
|
|
511
|
-
if (!resolvedApi) {
|
|
512
|
-
resolvedApi = {
|
|
513
|
-
resourceAppId: servicePrincipal.appId,
|
|
514
|
-
resourceAccess: []
|
|
515
|
-
};
|
|
516
|
-
resolvedApis.push(resolvedApi);
|
|
517
|
-
}
|
|
518
|
-
const resourceAccessPermission = {
|
|
519
|
-
id: permission.id,
|
|
520
|
-
type: scopeType
|
|
521
|
-
};
|
|
522
|
-
resolvedApi.resourceAccess.push(resourceAccessPermission);
|
|
523
|
-
this.updateAppPermissions(servicePrincipal.id, resourceAccessPermission, permission.value);
|
|
524
|
-
}
|
|
525
|
-
return resolvedApis;
|
|
526
|
-
}
|
|
527
|
-
updateAppPermissions(spId, resourceAccessPermission, oAuth2PermissionValue) {
|
|
528
|
-
// During API resolution, we store globally both app role assignments and oauth2permissions
|
|
529
|
-
// So that we'll be able to parse them during the admin consent process
|
|
530
|
-
let existingPermission = this.appPermissions.find(oauth => oauth.resourceId === spId);
|
|
531
|
-
if (!existingPermission) {
|
|
532
|
-
existingPermission = {
|
|
533
|
-
resourceId: spId,
|
|
534
|
-
resourceAccess: [],
|
|
535
|
-
scope: []
|
|
536
|
-
};
|
|
537
|
-
this.appPermissions.push(existingPermission);
|
|
538
|
-
}
|
|
539
|
-
if (resourceAccessPermission.type === 'Scope' && oAuth2PermissionValue && !existingPermission.scope.find(scp => scp === oAuth2PermissionValue)) {
|
|
540
|
-
existingPermission.scope.push(oAuth2PermissionValue);
|
|
541
|
-
}
|
|
542
|
-
if (!existingPermission.resourceAccess.find(res => res.id === resourceAccessPermission.id)) {
|
|
543
|
-
existingPermission.resourceAccess.push(resourceAccessPermission);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
async configureSecret(args, appInfo, logger) {
|
|
547
|
-
if (!args.options.withSecret || (appInfo.secrets && appInfo.secrets.length > 0)) {
|
|
548
|
-
return appInfo;
|
|
549
|
-
}
|
|
550
|
-
if (this.verbose) {
|
|
551
|
-
await logger.logToStderr(`Configure Microsoft Entra app secret...`);
|
|
552
|
-
}
|
|
553
|
-
const secret = await this.createSecret({ appObjectId: appInfo.id });
|
|
554
|
-
if (!appInfo.secrets) {
|
|
555
|
-
appInfo.secrets = [];
|
|
556
|
-
}
|
|
557
|
-
appInfo.secrets.push(secret);
|
|
558
|
-
return appInfo;
|
|
559
|
-
}
|
|
560
|
-
async createSecret({ appObjectId, displayName = undefined, expirationDate = undefined }) {
|
|
561
|
-
let secretExpirationDate = expirationDate;
|
|
562
|
-
if (!secretExpirationDate) {
|
|
563
|
-
secretExpirationDate = new Date();
|
|
564
|
-
secretExpirationDate.setFullYear(secretExpirationDate.getFullYear() + 1);
|
|
565
|
-
}
|
|
566
|
-
const secretName = displayName ?? 'Default';
|
|
567
|
-
const requestOptions = {
|
|
568
|
-
url: `${this.resource}/v1.0/myorganization/applications/${appObjectId}/addPassword`,
|
|
569
|
-
headers: {
|
|
570
|
-
'content-type': 'application/json'
|
|
571
|
-
},
|
|
572
|
-
responseType: 'json',
|
|
573
|
-
data: {
|
|
574
|
-
passwordCredential: {
|
|
575
|
-
displayName: secretName,
|
|
576
|
-
endDateTime: secretExpirationDate.toISOString()
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
const response = await request.post(requestOptions);
|
|
581
|
-
return {
|
|
582
|
-
displayName: secretName,
|
|
583
|
-
value: response.secretText
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
async getCertificateBase64Encoded(args, logger) {
|
|
587
|
-
if (args.options.certificateBase64Encoded) {
|
|
588
|
-
return args.options.certificateBase64Encoded;
|
|
589
|
-
}
|
|
590
|
-
if (this.debug) {
|
|
591
|
-
await logger.logToStderr(`Reading existing ${args.options.certificateFile}...`);
|
|
592
|
-
}
|
|
593
|
-
try {
|
|
594
|
-
return fs.readFileSync(args.options.certificateFile, { encoding: 'base64' });
|
|
595
|
-
}
|
|
596
|
-
catch (e) {
|
|
597
|
-
throw new Error(`Error reading certificate file: ${e}. Please add the certificate using base64 option '--certificateBase64Encoded'.`);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
364
|
async saveAppInfo(args, appInfo, logger) {
|
|
601
365
|
if (!args.options.save) {
|
|
602
366
|
return appInfo;
|
|
@@ -28,7 +28,7 @@ class FlowEnvironmentGetCommand extends PowerAutomateCommand {
|
|
|
28
28
|
if (this.verbose) {
|
|
29
29
|
await logger.logToStderr(`Retrieving information about Microsoft Flow environment ${args.options.name ?? ''}...`);
|
|
30
30
|
}
|
|
31
|
-
let requestUrl = `${
|
|
31
|
+
let requestUrl = `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/environments/`;
|
|
32
32
|
if (args.options.name) {
|
|
33
33
|
requestUrl += `${formatting.encodeQueryParameter(args.options.name)}`;
|
|
34
34
|
}
|
|
@@ -16,7 +16,7 @@ class FlowEnvironmentListCommand extends PowerAutomateCommand {
|
|
|
16
16
|
await logger.logToStderr(`Retrieving list of Microsoft Flow environments...`);
|
|
17
17
|
}
|
|
18
18
|
try {
|
|
19
|
-
const res = await odata.getAllItems(`${
|
|
19
|
+
const res = await odata.getAllItems(`${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/environments?api-version=2016-11-01`);
|
|
20
20
|
if (res.length > 0) {
|
|
21
21
|
if (args.options.output !== 'json') {
|
|
22
22
|
res.forEach(e => {
|
|
@@ -26,7 +26,7 @@ class FlowDisableCommand extends PowerAutomateCommand {
|
|
|
26
26
|
await logger.logToStderr(`Disables Microsoft Flow ${args.options.name}...`);
|
|
27
27
|
}
|
|
28
28
|
const requestOptions = {
|
|
29
|
-
url: `${
|
|
29
|
+
url: `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/${args.options.asAdmin ? 'scopes/admin/' : ''}environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.name)}/stop?api-version=2016-11-01`,
|
|
30
30
|
headers: {
|
|
31
31
|
accept: 'application/json'
|
|
32
32
|
},
|
|
@@ -26,7 +26,7 @@ class FlowEnableCommand extends PowerAutomateCommand {
|
|
|
26
26
|
await logger.logToStderr(`Enables Microsoft Flow ${args.options.name}...`);
|
|
27
27
|
}
|
|
28
28
|
const requestOptions = {
|
|
29
|
-
url: `${
|
|
29
|
+
url: `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/${args.options.asAdmin ? 'scopes/admin/' : ''}environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.name)}/start?api-version=2016-11-01`,
|
|
30
30
|
headers: {
|
|
31
31
|
accept: 'application/json'
|
|
32
32
|
},
|
|
@@ -11,6 +11,7 @@ import { formatting } from '../../../utils/formatting.js';
|
|
|
11
11
|
import { validation } from '../../../utils/validation.js';
|
|
12
12
|
import PowerPlatformCommand from '../../base/PowerPlatformCommand.js';
|
|
13
13
|
import commands from '../commands.js';
|
|
14
|
+
import PowerAutomateCommand from '../../base/PowerAutomateCommand.js';
|
|
14
15
|
class FlowExportCommand extends PowerPlatformCommand {
|
|
15
16
|
get name() {
|
|
16
17
|
return commands.EXPORT;
|
|
@@ -27,15 +28,15 @@ class FlowExportCommand extends PowerPlatformCommand {
|
|
|
27
28
|
}
|
|
28
29
|
async commandAction(logger, args) {
|
|
29
30
|
let filenameFromApi = '';
|
|
30
|
-
const formatArgument = args.options.format
|
|
31
|
+
const formatArgument = args.options.format?.toLowerCase() || '';
|
|
31
32
|
if (this.verbose) {
|
|
32
33
|
await logger.logToStderr(`Retrieving package resources for Microsoft Flow ${args.options.name}...`);
|
|
33
34
|
}
|
|
34
35
|
try {
|
|
35
36
|
let res;
|
|
36
37
|
if (formatArgument === 'json') {
|
|
37
|
-
if (this.
|
|
38
|
-
await logger.logToStderr('format = json, skipping listing package resources step');
|
|
38
|
+
if (this.verbose) {
|
|
39
|
+
await logger.logToStderr('format = json, skipping listing package resources step.');
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
else {
|
|
@@ -61,7 +62,7 @@ class FlowExportCommand extends PowerPlatformCommand {
|
|
|
61
62
|
}
|
|
62
63
|
let requestOptions = {
|
|
63
64
|
url: formatArgument === 'json' ?
|
|
64
|
-
|
|
65
|
+
`${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.name)}?api-version=2016-11-01`
|
|
65
66
|
: `${this.resource}/providers/Microsoft.BusinessAppPlatform/environments/${formatting.encodeQueryParameter(args.options.environmentName)}/exportPackage?api-version=2016-11-01`,
|
|
66
67
|
headers: {
|
|
67
68
|
accept: 'application/json'
|
|
@@ -76,17 +77,17 @@ class FlowExportCommand extends PowerPlatformCommand {
|
|
|
76
77
|
? res.resources[key].suggestedCreationType = 'Update'
|
|
77
78
|
: res.resources[key].suggestedCreationType = 'Existing';
|
|
78
79
|
});
|
|
79
|
-
requestOptions
|
|
80
|
-
|
|
80
|
+
requestOptions.data = {
|
|
81
|
+
includedResourceIds: [
|
|
81
82
|
`/providers/Microsoft.Flow/flows/${args.options.name}`
|
|
82
83
|
],
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
details: {
|
|
85
|
+
displayName: args.options.packageDisplayName,
|
|
86
|
+
description: args.options.packageDescription,
|
|
87
|
+
creator: args.options.packageCreatedBy,
|
|
88
|
+
sourceEnvironment: args.options.packageSourceEnvironment
|
|
88
89
|
},
|
|
89
|
-
|
|
90
|
+
resources: res.resources
|
|
90
91
|
};
|
|
91
92
|
}
|
|
92
93
|
res = formatArgument === 'json' ? await request.get(requestOptions) : await request.post(requestOptions);
|
|
@@ -99,13 +100,13 @@ class FlowExportCommand extends PowerPlatformCommand {
|
|
|
99
100
|
// Replace all illegal characters from the file name
|
|
100
101
|
const illegalCharsRegEx = /[\\\/:*?"<>|]/g;
|
|
101
102
|
filenameFromApi = filenameFromApi.replace(illegalCharsRegEx, '_');
|
|
102
|
-
if (this.
|
|
103
|
-
await logger.logToStderr(`Filename from PowerApps API: ${filenameFromApi}
|
|
103
|
+
if (this.verbose) {
|
|
104
|
+
await logger.logToStderr(`Filename from PowerApps API: ${filenameFromApi}.`);
|
|
104
105
|
await logger.logToStderr('');
|
|
105
106
|
}
|
|
106
107
|
requestOptions = {
|
|
107
108
|
url: formatArgument === 'json' ?
|
|
108
|
-
|
|
109
|
+
`${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.name)}/exportToARMTemplate?api-version=2016-11-01`
|
|
109
110
|
: downloadFileUrl,
|
|
110
111
|
// Set responseType to arraybuffer, otherwise binary data will be encoded
|
|
111
112
|
// to utf8 and binary data is corrupt
|
|
@@ -124,7 +125,7 @@ class FlowExportCommand extends PowerPlatformCommand {
|
|
|
124
125
|
fs.writeFileSync(path, file, 'binary');
|
|
125
126
|
if (!args.options.path || this.verbose) {
|
|
126
127
|
if (this.verbose) {
|
|
127
|
-
await logger.logToStderr(`File saved to path '${path}'
|
|
128
|
+
await logger.logToStderr(`File saved to path '${path}'.`);
|
|
128
129
|
}
|
|
129
130
|
else {
|
|
130
131
|
await logger.log(path);
|
|
@@ -29,7 +29,7 @@ class FlowGetCommand extends PowerAutomateCommand {
|
|
|
29
29
|
await logger.logToStderr(`Retrieving information about Microsoft Flow ${args.options.name}...`);
|
|
30
30
|
}
|
|
31
31
|
const requestOptions = {
|
|
32
|
-
url: `${
|
|
32
|
+
url: `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/${args.options.asAdmin ? 'scopes/admin/' : ''}environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.name)}?api-version=2016-11-01&$expand=swagger,properties.connectionreferences.apidefinition,properties.definitionsummary.operations.apioperation,operationDefinition,plan,properties.throttleData,properties.estimatedsuspensiondata,properties.licenseData`,
|
|
33
33
|
headers: {
|
|
34
34
|
accept: 'application/json'
|
|
35
35
|
},
|
|
@@ -67,7 +67,7 @@ class FlowListCommand extends PowerAutomateCommand {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
getApiUrl(environmentName, asAdmin, includeSolutionFlows, filter) {
|
|
70
|
-
const baseEndpoint = `${
|
|
70
|
+
const baseEndpoint = `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple`;
|
|
71
71
|
const environmentSegment = `/environments/${formatting.encodeQueryParameter(environmentName)}`;
|
|
72
72
|
const adminSegment = `/scopes/admin${environmentSegment}/v2`;
|
|
73
73
|
const flowsEndpoint = '/flows?api-version=2016-11-01';
|
|
@@ -30,7 +30,7 @@ class FlowRemoveCommand extends PowerAutomateCommand {
|
|
|
30
30
|
}
|
|
31
31
|
const removeFlow = async () => {
|
|
32
32
|
const requestOptions = {
|
|
33
|
-
url: `${
|
|
33
|
+
url: `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/${args.options.asAdmin ? 'scopes/admin/' : ''}environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.name)}?api-version=2016-11-01`,
|
|
34
34
|
fullResponse: true,
|
|
35
35
|
headers: {
|
|
36
36
|
accept: 'application/json'
|
|
@@ -52,7 +52,7 @@ class FlowOwnerEnsureCommand extends PowerAutomateCommand {
|
|
|
52
52
|
type = 'Group';
|
|
53
53
|
}
|
|
54
54
|
const requestOptions = {
|
|
55
|
-
url: `${
|
|
55
|
+
url: `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/${args.options.asAdmin ? 'scopes/admin/' : ''}environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.flowName)}/modifyPermissions?api-version=2016-11-01`,
|
|
56
56
|
headers: {
|
|
57
57
|
accept: 'application/json'
|
|
58
58
|
},
|
|
@@ -32,7 +32,7 @@ class FlowOwnerListCommand extends PowerAutomateCommand {
|
|
|
32
32
|
if (this.verbose) {
|
|
33
33
|
await logger.logToStderr(`Listing owners for flow ${args.options.flowName} in environment ${args.options.environmentName}`);
|
|
34
34
|
}
|
|
35
|
-
const response = await odata.getAllItems(`${
|
|
35
|
+
const response = await odata.getAllItems(`${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/${args.options.asAdmin ? 'scopes/admin/' : ''}environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.flowName)}/permissions?api-version=2016-11-01`);
|
|
36
36
|
if (!cli.shouldTrimOutput(args.options.output)) {
|
|
37
37
|
await logger.log(response);
|
|
38
38
|
}
|
|
@@ -47,7 +47,7 @@ class FlowOwnerRemoveCommand extends PowerAutomateCommand {
|
|
|
47
47
|
idToRemove = await entraGroup.getGroupIdByDisplayName(args.options.groupName);
|
|
48
48
|
}
|
|
49
49
|
const requestOptions = {
|
|
50
|
-
url: `${
|
|
50
|
+
url: `${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/${args.options.asAdmin ? 'scopes/admin/' : ''}environments/${formatting.encodeQueryParameter(args.options.environmentName)}/flows/${formatting.encodeQueryParameter(args.options.flowName)}/modifyPermissions?api-version=2016-11-01`,
|
|
51
51
|
headers: {
|
|
52
52
|
accept: 'application/json'
|
|
53
53
|
},
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import PowerAutomateCommand from '../../../base/PowerAutomateCommand.js';
|
|
2
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { zod } from '../../../../utils/zod.js';
|
|
5
|
+
import commands from '../../commands.js';
|
|
6
|
+
import { formatting } from '../../../../utils/formatting.js';
|
|
7
|
+
import { odata } from '../../../../utils/odata.js';
|
|
8
|
+
import { cli } from '../../../../cli/cli.js';
|
|
9
|
+
const options = globalOptionsZod
|
|
10
|
+
.extend({
|
|
11
|
+
environmentName: zod.alias('e', z.string())
|
|
12
|
+
})
|
|
13
|
+
.strict();
|
|
14
|
+
class FlowRecycleBinItemListCommand extends PowerAutomateCommand {
|
|
15
|
+
get name() {
|
|
16
|
+
return commands.RECYCLEBINITEM_LIST;
|
|
17
|
+
}
|
|
18
|
+
get description() {
|
|
19
|
+
return 'Lists all soft-deleted Power Automate flows within an environment';
|
|
20
|
+
}
|
|
21
|
+
defaultProperties() {
|
|
22
|
+
return ['name', 'displayName'];
|
|
23
|
+
}
|
|
24
|
+
get schema() {
|
|
25
|
+
return options;
|
|
26
|
+
}
|
|
27
|
+
async commandAction(logger, args) {
|
|
28
|
+
try {
|
|
29
|
+
if (this.verbose) {
|
|
30
|
+
await logger.logToStderr(`Getting list of soft-deleted flows in environment ${args.options.environmentName}...`);
|
|
31
|
+
}
|
|
32
|
+
const flows = await odata.getAllItems(`${PowerAutomateCommand.resource}/providers/Microsoft.ProcessSimple/scopes/admin/environments/${formatting.encodeQueryParameter(args.options.environmentName)}/v2/flows?api-version=2016-11-01&include=softDeletedFlows`);
|
|
33
|
+
const deletedFlows = flows.filter(flow => flow.properties.state === 'Deleted');
|
|
34
|
+
if (cli.shouldTrimOutput(args.options.output)) {
|
|
35
|
+
deletedFlows.forEach(flow => {
|
|
36
|
+
flow.displayName = flow.properties.displayName;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
await logger.log(deletedFlows);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
this.handleRejectedODataJsonPromise(err);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export default new FlowRecycleBinItemListCommand();
|
|
47
|
+
//# sourceMappingURL=recyclebinitem-list.js.map
|