-
Notifications
You must be signed in to change notification settings - Fork 328
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New command: entra roledefinition add
- Loading branch information
Showing
5 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
127 changes: 127 additions & 0 deletions
127
docs/docs/cmd/entra/roledefinition/roledefinition-add.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import Global from '/docs/cmd/_global.mdx'; | ||
import Tabs from '@theme/Tabs'; | ||
import TabItem from '@theme/TabItem'; | ||
|
||
# entra roledefinition add | ||
|
||
Creates a custom Microsoft Entra ID role definition | ||
|
||
## Usage | ||
|
||
```sh | ||
m365 entra roledefinition add [options] | ||
``` | ||
|
||
## Options | ||
|
||
```md definition-list | ||
`-n, --displayName <displayName>` | ||
: The display name for the role definition. | ||
|
||
`--allowedResourceActions <allowedResourceActions>` | ||
: Comma-separated list of resource actions allowed for the role. | ||
|
||
`--description [description]` | ||
: The description for the role definition. | ||
|
||
`--enabled [enabled]` | ||
: Indicates if the role is enabled for the assignment. If not specified, the role is enabled by default. | ||
|
||
`--version [version]` | ||
: The version of the role definition. | ||
``` | ||
|
||
<Global /> | ||
|
||
## Examples | ||
|
||
Create a custom Microsoft Entra ID role | ||
|
||
```sh | ||
m365 entra roledefinition add --displayName 'Application Remover' --description 'Allows to remove any Entra ID application' --allowedResourceActions 'microsoft.directory/applications/delete' | ||
``` | ||
|
||
Create a custom Microsoft Entra ID role, but disable it for the assignment | ||
|
||
```sh | ||
m365 entra roledefinition add --displayName 'Application Remover' --version '1.0' --enabled false --allowedResourceActions 'microsoft.directory/applications/delete,microsoft.directory/applications/owners/update' | ||
``` | ||
|
||
## Response | ||
|
||
<Tabs> | ||
<TabItem value="JSON"> | ||
|
||
```json | ||
{ | ||
"id": "3844129d-f748-4c03-8165-4412ee9b4ceb", | ||
"description": null, | ||
"displayName": "Custom Role", | ||
"isBuiltIn": false, | ||
"isEnabled": true, | ||
"resourceScopes": [ | ||
"/" | ||
], | ||
"templateId": "3844129d-f748-4c03-8165-4412ee9b4ceb", | ||
"version": "1", | ||
"rolePermissions": [ | ||
{ | ||
"allowedResourceActions": [ | ||
"microsoft.directory/groups.unified/create", | ||
"microsoft.directory/groups.unified/delete" | ||
], | ||
"condition": null | ||
} | ||
] | ||
} | ||
``` | ||
|
||
</TabItem> | ||
<TabItem value="Text"> | ||
|
||
```text | ||
description : null | ||
displayName : Custom Role | ||
id : 3844129d-f748-4c03-8165-4412ee9b4ceb | ||
isBuiltIn : false | ||
isEnabled : true | ||
resourceScopes : ["/"] | ||
rolePermissions: [{"allowedResourceActions":["microsoft.directory/groups.unified/create","microsoft.directory/groups.unified/delete"],"condition":null}] | ||
templateId : 3844129d-f748-4c03-8165-4412ee9b4ceb | ||
version : 1 | ||
``` | ||
|
||
</TabItem> | ||
<TabItem value="CSV"> | ||
|
||
```csv | ||
id,description,displayName,isBuiltIn,isEnabled,templateId,version | ||
3844129d-f748-4c03-8165-4412ee9b4ceb,,Custom Role,0,1,3844129d-f748-4c03-8165-4412ee9b4ceb,1 | ||
``` | ||
|
||
</TabItem> | ||
<TabItem value="Markdown"> | ||
|
||
```md | ||
# entra roledefinition add --displayName "Custom Role" --allowedResourceActions "microsoft.directory/groups.unified/create,microsoft.directory/groups.unified/delete" --version 1 | ||
|
||
Date: 12/15/2024 | ||
|
||
## Custom Role (3844129d-f748-4c03-8165-4412ee9b4ceb) | ||
|
||
Property | Value | ||
---------|------- | ||
id | 3844129d-f748-4c03-8165-4412ee9b4ceb | ||
displayName | Custom Role | ||
isBuiltIn | false | ||
isEnabled | true | ||
templateId | 3844129d-f748-4c03-8165-4412ee9b4ceb | ||
version | 1 | ||
``` | ||
|
||
</TabItem> | ||
</Tabs> | ||
|
||
## More information | ||
|
||
- https://learn.microsoft.com/graph/api/rbacapplication-post-roledefinitions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
src/m365/entra/commands/roledefinition/roledefinition-add.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import assert from 'assert'; | ||
import sinon from 'sinon'; | ||
import auth from '../../../../Auth.js'; | ||
import commands from '../../commands.js'; | ||
import request from '../../../../request.js'; | ||
import { Logger } from '../../../../cli/Logger.js'; | ||
import { telemetry } from '../../../../telemetry.js'; | ||
import { pid } from '../../../../utils/pid.js'; | ||
import { session } from '../../../../utils/session.js'; | ||
import command from './roledefinition-add.js'; | ||
import { sinonUtil } from '../../../../utils/sinonUtil.js'; | ||
import { CommandError } from '../../../../Command.js'; | ||
import { z } from 'zod'; | ||
import { CommandInfo } from '../../../../cli/CommandInfo.js'; | ||
import { cli } from '../../../../cli/cli.js'; | ||
|
||
describe(commands.ROLEDEFINITION_ADD, () => { | ||
const roleDefinitionResponse = { | ||
"id": "e1ede50a-487c-49b3-a43e-cda270d3341f", | ||
"description": null, | ||
"displayName": "Custom Role", | ||
"isBuiltIn": false, | ||
"isEnabled": true, | ||
"resourceScopes": [ | ||
"/" | ||
], | ||
"templateId": "e1ede50a-487c-49b3-a43e-cda270d3341f", | ||
"version": null, | ||
"rolePermissions": [ | ||
{ | ||
"allowedResourceActions": [ | ||
"microsoft.directory/groups.unified/create", | ||
"microsoft.directory/groups.unified/delete" | ||
], | ||
"condition": null | ||
} | ||
] | ||
}; | ||
|
||
const roleDefinitionWithDeatilsResponse = { | ||
"id": "abcde50a-487c-49b3-a43e-cda270d3341f", | ||
"description": "Allows creating and deleting unified groups", | ||
"displayName": "Custom Role", | ||
"isBuiltIn": false, | ||
"isEnabled": false, | ||
"resourceScopes": [ | ||
"/" | ||
], | ||
"templateId": "abcnpm instade50a-487c-49b3-a43e-cda270d3341f", | ||
"version": "1", | ||
"rolePermissions": [ | ||
{ | ||
"allowedResourceActions": [ | ||
"microsoft.directory/groups.unified/create", | ||
"microsoft.directory/groups.unified/delete" | ||
], | ||
"condition": null | ||
} | ||
] | ||
}; | ||
|
||
let log: string[]; | ||
let logger: Logger; | ||
let loggerLogSpy: sinon.SinonSpy; | ||
let commandInfo: CommandInfo; | ||
let commandOptionsSchema: z.ZodTypeAny; | ||
|
||
before(() => { | ||
sinon.stub(auth, 'restoreAuth').resolves(); | ||
sinon.stub(telemetry, 'trackEvent').returns(); | ||
sinon.stub(pid, 'getProcessName').returns(''); | ||
sinon.stub(session, 'getId').returns(''); | ||
auth.connection.active = true; | ||
commandInfo = cli.getCommandInfo(command); | ||
commandOptionsSchema = commandInfo.command.getSchemaToParse()!; | ||
}); | ||
|
||
beforeEach(() => { | ||
log = []; | ||
logger = { | ||
log: async (msg: string) => { | ||
log.push(msg); | ||
}, | ||
logRaw: async (msg: string) => { | ||
log.push(msg); | ||
}, | ||
logToStderr: async (msg: string) => { | ||
log.push(msg); | ||
} | ||
}; | ||
loggerLogSpy = sinon.spy(logger, 'log'); | ||
}); | ||
|
||
afterEach(() => { | ||
sinonUtil.restore([ | ||
request.post, | ||
cli.promptForConfirmation | ||
]); | ||
}); | ||
|
||
after(() => { | ||
sinon.restore(); | ||
auth.connection.active = false; | ||
}); | ||
|
||
it('has correct name', () => { | ||
assert.strictEqual(command.name, commands.ROLEDEFINITION_ADD); | ||
}); | ||
|
||
it('has a description', () => { | ||
assert.notStrictEqual(command.description, null); | ||
}); | ||
|
||
it('fails validation if displayName is not provided', () => { | ||
const actual = commandOptionsSchema.safeParse({ allowedResourceActions: "microsoft.directory/groups.unified/create"}); | ||
assert.notStrictEqual(actual.success, true); | ||
}); | ||
|
||
it('fails validation if allowedResourceActions is not provided', () => { | ||
const actual = commandOptionsSchema.safeParse({ displayName: "Custom Role" }); | ||
assert.notStrictEqual(actual.success, true); | ||
}); | ||
|
||
it('creates a custom role definition with a specific display name and resource actions', async () => { | ||
sinon.stub(request, 'post').callsFake(async (opts) => { | ||
if (opts.url === 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions') { | ||
return roleDefinitionResponse; | ||
} | ||
|
||
throw 'Invalid request'; | ||
}); | ||
|
||
await command.action(logger, { options: { displayName: 'Custom Role', allowedResourceActions: "microsoft.directory/groups.unified/create,microsoft.directory/groups.unified/delete" } }); | ||
assert(loggerLogSpy.calledOnceWithExactly(roleDefinitionResponse)); | ||
}); | ||
|
||
it('creates a custom role definition with a specific display name, description, version and resource actions', async () => { | ||
sinon.stub(request, 'post').callsFake(async (opts) => { | ||
if (opts.url === 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions') { | ||
return roleDefinitionWithDeatilsResponse; | ||
} | ||
|
||
throw 'Invalid request'; | ||
}); | ||
|
||
await command.action(logger, { | ||
options: { | ||
displayName: 'Custom Role', | ||
description: 'Allows creating and deleting unified groups', | ||
allowedResourceActions: "microsoft.directory/groups.unified/create,microsoft.directory/groups.unified/delete", | ||
enabled: false, | ||
version: "1", | ||
verbose: true | ||
} | ||
}); | ||
assert(loggerLogSpy.calledOnceWithExactly(roleDefinitionWithDeatilsResponse)); | ||
}); | ||
|
||
it('correctly handles API OData error', async () => { | ||
sinon.stub(request, 'post').rejects({ | ||
error: { | ||
'odata.error': { | ||
code: '-1, InvalidOperationException', | ||
message: { | ||
value: 'Invalid request' | ||
} | ||
} | ||
} | ||
}); | ||
|
||
await assert.rejects(command.action(logger, { | ||
options: { | ||
displayName: 'Custom Role', | ||
allowedResourceActions: "microsoft.directory/groups.unified/create" | ||
} | ||
}), new CommandError('Invalid request')); | ||
}); | ||
}); |
Oops, something went wrong.