Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions apps/sim/blocks/blocks/microsoft_ad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
condition: { field: 'operation', value: ['list_users', 'list_groups'] },
mode: 'advanced',
},
{
id: 'nextLink',
title: 'Next Page',
type: 'short-input',
placeholder: "Paste the previous response's nextLink to fetch the next page",
condition: {
field: 'operation',
value: ['list_users', 'list_groups', 'list_group_members'],
},
mode: 'advanced',
},
// Group ID field
{
id: 'groupId',
Expand All @@ -212,7 +223,6 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
'get_group',
'update_group',
'delete_group',
'list_group_members',
'add_group_member',
'remove_group_member',
],
Expand Down Expand Up @@ -296,6 +306,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
options: [
{ label: 'Private', id: 'Private' },
{ label: 'Public', id: 'Public' },
{ label: 'Hidden Membership (Microsoft 365 groups only)', id: 'HiddenMembership' },
],
value: () => 'Private',
condition: { field: 'operation', value: 'create_group' },
Expand Down Expand Up @@ -334,6 +345,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
if (params.top) result.top = Number(params.top)
if (params.filter) result.filter = params.filter
if (params.search) result.search = params.search
if (params.nextLink) result.nextLink = params.nextLink
if (params.operation === 'update_user') {
if (params.accountEnabled) result.accountEnabled = params.accountEnabled === 'true'
} else if (params.operation === 'create_user') {
Expand Down Expand Up @@ -375,6 +387,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
top: { type: 'string' },
filter: { type: 'string' },
search: { type: 'string' },
nextLink: { type: 'string' },
groupId: { type: 'string' },
groupDisplayName: { type: 'string' },
groupMailNickname: { type: 'string' },
Expand All @@ -390,7 +403,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
response: {
type: 'json',
description:
'Azure AD operation response. User operations return id, displayName, userPrincipalName, mail, jobTitle, department. Group operations return id, displayName, description, mailEnabled, securityEnabled, groupTypes. Member operations return id, displayName, mail, odataType.',
'Azure AD operation response. User operations return id, displayName, userPrincipalName, mail, jobTitle, department. Group operations return id, displayName, description, mailEnabled, securityEnabled, groupTypes. Member operations return id, displayName, mail, odataType. List operations also return nextLink for fetching additional pages.',
},
},
}
Expand Down Expand Up @@ -487,7 +500,21 @@ export const MicrosoftAdBlockMeta = {
description:
'List the members of an Azure AD group for an access review. Use for periodic attestation of privileged or sensitive groups.',
content:
'# Audit Group Membership\n\nProduce a current membership snapshot for a group.\n\n## Steps\n1. Resolve the target group with Get Group or List Groups (filter or search by name).\n2. Call List Group Members for the group id, raising Max Results if the group is large.\n3. For each member, optionally call Get User to enrich with job title, department, and account-enabled status.\n\n## Output\nReturn a table of members with id, display name, email, department, and whether the account is enabled. Highlight disabled or stale accounts that still hold membership and should be reviewed for removal.',
'# Audit Group Membership\n\nProduce a current membership snapshot for a group.\n\n## Steps\n1. Resolve the target group with Get Group or List Groups (filter or search by name).\n2. Call List Group Members for the group id, raising Max Results if the group is large. If the response includes a Next Page link, keep calling List Group Members with that link until it comes back empty to capture every member.\n3. For each member, optionally call Get User to enrich with job title, department, and account-enabled status.\n\n## Output\nReturn a table of members with id, display name, email, department, and whether the account is enabled. Highlight disabled or stale accounts that still hold membership and should be reviewed for removal.',
},
{
name: 'search-directory-users',
description:
'Search Azure AD (Entra ID) for users matching a name, department, or other attribute. Use for directory lookups and reporting.',
content:
"# Search Directory Users\n\nFind users in the directory by attribute instead of enumerating everyone.\n\n## Steps\n1. Use List Users with Search set to the name or email fragment, or Filter set to an OData expression (e.g. `department eq 'Sales'`) for attribute-based lookups. Search and Filter cannot be combined in one call.\n2. If the result set is large, follow the Next Page link returned in the response to page through additional results.\n3. Optionally call Get User for a specific match to retrieve full profile detail.\n\n## Output\nReturn the matching users with id, display name, user principal name, department, and account-enabled status. State clearly when Max Results or pagination limits mean the list may be incomplete.",
},
{
name: 'manage-group-membership',
description:
'Add or remove specific users from an Azure AD (Entra ID) group on demand, outside of onboarding/offboarding flows. Use for ad hoc access changes and team restructuring.',
content:
"# Manage Group Membership\n\nApply a one-off membership change to a group.\n\n## Steps\n1. Resolve the group with Get Group or List Groups, and resolve each affected user with Get User or List Users.\n2. Call Add Group Member or Remove Group Member with the group id and each user id.\n3. Confirm the change with List Group Members.\n\n## Output\nReturn which users were added or removed and the group's current member count. Report any member that failed to add or remove (e.g. already a member, or not found) instead of silently skipping it.",
},
],
} as const satisfies BlockMeta
3 changes: 2 additions & 1 deletion apps/sim/tools/microsoft_ad/create_group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ export const createGroupTool: ToolConfig<
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Group visibility: "Private" or "Public"',
description:
'Group visibility: "Private" or "Public" (can be changed later), or "HiddenMembership" (Microsoft 365 groups only; can only be set at creation and never changed afterward)',
},
},
request: {
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/microsoft_ad/create_user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const createUserTool: ToolConfig<
},
},
request: {
url: 'https://graph.microsoft.com/v1.0/users',
url: 'https://graph.microsoft.com/v1.0/users?$select=id,displayName,givenName,surname,userPrincipalName,mail,jobTitle,department,officeLocation,mobilePhone,accountEnabled',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
Expand Down
19 changes: 17 additions & 2 deletions apps/sim/tools/microsoft_ad/list_group_members.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
MicrosoftAdListGroupMembersResponse,
} from '@/tools/microsoft_ad/types'
import { MEMBER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'

export const listGroupMembersTool: ToolConfig<
Expand All @@ -27,19 +28,27 @@ export const listGroupMembersTool: ToolConfig<
},
groupId: {
type: 'string',
required: true,
required: false,
visibility: 'user-or-llm',
description: 'Group ID',
description: 'Group ID. Not needed when Next Page is provided to fetch a later page.',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of members to return (default 100, max 999)',
},
nextLink: {
Comment thread
waleedlatif1 marked this conversation as resolved.
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrl(params.nextLink)
const groupId = params.groupId?.trim()
if (!groupId) throw new Error('Group ID is required')
const queryParts = ['$select=id,displayName,mail']
Expand All @@ -64,6 +73,7 @@ export const listGroupMembersTool: ToolConfig<
output: {
members,
memberCount: members.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
Expand All @@ -74,5 +84,10 @@ export const listGroupMembersTool: ToolConfig<
properties: MEMBER_OUTPUT_PROPERTIES,
},
memberCount: { type: 'number', description: 'Number of members returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
23 changes: 19 additions & 4 deletions apps/sim/tools/microsoft_ad/list_groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
MicrosoftAdListGroupsResponse,
} from '@/tools/microsoft_ad/types'
import { GROUP_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'

export const listGroupsTool: ToolConfig<
Expand Down Expand Up @@ -43,20 +44,28 @@ export const listGroupsTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Search string to filter groups by displayName or description',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrl(params.nextLink)
const queryParts: string[] = []
queryParts.push(
'$select=id,displayName,description,mail,mailEnabled,mailNickname,securityEnabled,groupTypes,visibility,createdDateTime'
)
if (params.top) queryParts.push(`$top=${params.top}`)
if (params.search && params.filter) {
throw new Error('$search and $filter cannot be used together in Microsoft Graph API')
}
if (params.filter) queryParts.push(`$filter=${encodeURIComponent(params.filter)}`)
if (params.search) {
queryParts.push(`$search="${encodeURIComponent(params.search)}"`)
const term = params.search.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
queryParts.push(
`$search=${encodeURIComponent(`"displayName:${term}" OR "description:${term}"`)}`
)
queryParts.push('$count=true')
}
return `https://graph.microsoft.com/v1.0/groups?${queryParts.join('&')}`
Expand Down Expand Up @@ -86,6 +95,7 @@ export const listGroupsTool: ToolConfig<
output: {
groups,
groupCount: groups.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
Expand All @@ -96,5 +106,10 @@ export const listGroupsTool: ToolConfig<
properties: GROUP_OUTPUT_PROPERTIES,
},
groupCount: { type: 'number', description: 'Number of groups returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
21 changes: 17 additions & 4 deletions apps/sim/tools/microsoft_ad/list_users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
MicrosoftAdListUsersResponse,
} from '@/tools/microsoft_ad/types'
import { USER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'

export const listUsersTool: ToolConfig<MicrosoftAdListUsersParams, MicrosoftAdListUsersResponse> = {
Expand Down Expand Up @@ -40,20 +41,26 @@ export const listUsersTool: ToolConfig<MicrosoftAdListUsersParams, MicrosoftAdLi
visibility: 'user-or-llm',
description: 'Search string to filter users by displayName or mail',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrl(params.nextLink)
const queryParts: string[] = []
queryParts.push(
'$select=id,displayName,givenName,surname,userPrincipalName,mail,jobTitle,department,officeLocation,mobilePhone,accountEnabled'
)
if (params.top) queryParts.push(`$top=${params.top}`)
if (params.search && params.filter) {
throw new Error('$search and $filter cannot be used together in Microsoft Graph API')
}
if (params.filter) queryParts.push(`$filter=${encodeURIComponent(params.filter)}`)
if (params.search) {
queryParts.push(`$search="${encodeURIComponent(params.search)}"`)
const term = params.search.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
queryParts.push(`$search=${encodeURIComponent(`"displayName:${term}" OR "mail:${term}"`)}`)
Comment thread
waleedlatif1 marked this conversation as resolved.
queryParts.push('$count=true')
}
return `https://graph.microsoft.com/v1.0/users?${queryParts.join('&')}`
Expand Down Expand Up @@ -84,6 +91,7 @@ export const listUsersTool: ToolConfig<MicrosoftAdListUsersParams, MicrosoftAdLi
output: {
users,
userCount: users.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
Expand All @@ -94,5 +102,10 @@ export const listUsersTool: ToolConfig<MicrosoftAdListUsersParams, MicrosoftAdLi
properties: USER_OUTPUT_PROPERTIES,
},
userCount: { type: 'number', description: 'Number of users returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
8 changes: 7 additions & 1 deletion apps/sim/tools/microsoft_ad/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface MicrosoftAdListUsersParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
search?: string
nextLink?: string
}

export interface MicrosoftAdGetUserParams extends MicrosoftAdBaseParams {
Expand Down Expand Up @@ -48,6 +49,7 @@ export interface MicrosoftAdListGroupsParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
search?: string
nextLink?: string
}

export interface MicrosoftAdGetGroupParams extends MicrosoftAdBaseParams {
Expand Down Expand Up @@ -77,8 +79,9 @@ export interface MicrosoftAdDeleteGroupParams extends MicrosoftAdBaseParams {
}

export interface MicrosoftAdListGroupMembersParams extends MicrosoftAdBaseParams {
groupId: string
groupId?: string
top?: number
nextLink?: string
}

export interface MicrosoftAdAddGroupMemberParams extends MicrosoftAdBaseParams {
Expand Down Expand Up @@ -129,6 +132,7 @@ export interface MicrosoftAdListUsersResponse extends ToolResponse {
output: {
users: Array<Record<string, unknown>>
userCount: number
nextLink: string | null
}
}

Expand Down Expand Up @@ -162,6 +166,7 @@ export interface MicrosoftAdListGroupsResponse extends ToolResponse {
output: {
groups: Array<Record<string, unknown>>
groupCount: number
nextLink: string | null
}
}

Expand Down Expand Up @@ -195,6 +200,7 @@ export interface MicrosoftAdListGroupMembersResponse extends ToolResponse {
output: {
members: Array<Record<string, unknown>>
memberCount: number
nextLink: string | null
}
}

Expand Down
Loading