Skip to content

Commit 784ff21

Browse files
Add Miro solution v3.0.0 for Microsoft Sentinel
Adds Miro solution with two CCF data connectors for ingesting Miro audit logs and content activity logs into Microsoft Sentinel. Components: - Miro Audit Logs connector (Enterprise Plan) - Miro Content Logs connector (Enterprise Plan + Enterprise Guard) - Solution metadata with 7 domain categories - Data collection rules and custom tables - ARM templates for deployment
1 parent d3446cf commit 784ff21

16 files changed

+2391
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
[
2+
{
3+
"name": "MiroAuditLogsDCR",
4+
"apiVersion": "2024-03-11",
5+
"type": "Microsoft.Insights/dataCollectionRules",
6+
"location": "{{location}}",
7+
"properties": {
8+
"streamDeclarations": {
9+
"Custom-MiroAuditLogs_CL": {
10+
"columns": [
11+
{
12+
"name": "id",
13+
"type": "string"
14+
},
15+
{
16+
"name": "event",
17+
"type": "string"
18+
},
19+
{
20+
"name": "category",
21+
"type": "string"
22+
},
23+
{
24+
"name": "type",
25+
"type": "string"
26+
},
27+
{
28+
"name": "createdAt",
29+
"type": "string"
30+
},
31+
{
32+
"name": "createdBy",
33+
"type": "dynamic"
34+
},
35+
{
36+
"name": "context",
37+
"type": "dynamic"
38+
},
39+
{
40+
"name": "details",
41+
"type": "dynamic"
42+
},
43+
{
44+
"name": "object",
45+
"type": "dynamic"
46+
}
47+
]
48+
}
49+
},
50+
"dataSources": {},
51+
"destinations": {
52+
"logAnalytics": [
53+
{
54+
"workspaceResourceId": "{{workspaceResourceId}}",
55+
"name": "clv2ws1"
56+
}
57+
]
58+
},
59+
"dataFlows": [
60+
{
61+
"streams": [
62+
"Custom-MiroAuditLogs_CL"
63+
],
64+
"destinations": [
65+
"clv2ws1"
66+
],
67+
"transformKql": "source\n| extend TimeGenerated = todatetime(createdAt)\n| extend createdAt = todatetime(createdAt)\n| extend logType = type\n| extend createdBy_email = iff(isnull(createdBy), \"\", tostring(createdBy.email))\n| extend createdBy_id = iff(isnull(createdBy), \"\", tostring(createdBy.id))\n| extend createdBy_name = iff(isnull(createdBy), \"\", tostring(createdBy.name))\n| extend createdBy_type = iff(isnull(createdBy), \"\", tostring(createdBy.type))\n| extend object_id = iff(isnull(object), \"\", tostring(object.id))\n| extend object_name = iff(isnull(object), \"\", tostring(object.name))\n| extend context_ip = iff(isnull(context), \"\", tostring(context.ip))\n| extend context_team_id = iff(isnull(context.team), \"\", tostring(context.team.id))\n| extend context_team_name = iff(isnull(context.team), \"\", tostring(context.team.name))\n| extend context_team_type = iff(isnull(context.team), \"\", tostring(context.team.type))\n| extend context_organization_id = iff(isnull(context.organization), \"\", tostring(context.organization.id))\n| extend context_organization_name = iff(isnull(context.organization), \"\", tostring(context.organization.name))\n| extend context_organization_type = iff(isnull(context.organization), \"\", tostring(context.organization.type))\n| project TimeGenerated, id, createdAt, event, category, logType, createdBy_email, createdBy_id, createdBy_name, createdBy_type, object_id, object_name, context_ip, context_team_id, context_team_name, context_team_type, context_organization_id, context_organization_name, context_organization_type, details",
68+
"outputStream": "Custom-MiroAuditLogs_CL"
69+
}
70+
],
71+
"dataCollectionEndpointId": "[concat('/subscriptions/',variables('subscriptionID'),'/resourceGroups/',resourceGroup().name,'/providers/Microsoft.Insights/dataCollectionEndpoints/',{{dataCollectionEndpointId}}"
72+
}
73+
}
74+
]
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
{
2+
"name": "MiroAuditLogsDataConnector",
3+
"apiVersion": "2025-09-01",
4+
"type": "Microsoft.SecurityInsights/dataConnectorDefinitions",
5+
"location": "{{location}}",
6+
"kind": "Customizable",
7+
"properties": {
8+
"connectorUiConfig": {
9+
"id": "MiroAuditLogsDataConnector",
10+
"title": "Miro Audit Logs (Enterprise Plan)",
11+
"publisher": "Miro",
12+
"descriptionMarkdown": "The [Miro Audit Logs](https://help.miro.com/hc/en-us/articles/360017571434-Audit-logs) data connector enables you to ingest organization-wide audit events from Miro into Microsoft Sentinel. Monitor user activities, security events, content access, team changes, and administrative actions to enhance your security operations and compliance capabilities.\n\n**Key features:**\n- Track user authentication and access patterns.\n- Monitor content creation, sharing, and deletion.\n- Audit team and organization configuration changes.\n- Detect suspicious activities and policy violations.\n- Meet compliance and regulatory requirements.\n\n**Requirements:**\n- **Miro Plan**: [Enterprise Plan](https://miro.com/pricing/).\n- **OAuth scope**: `auditlogs:read`.\n- **Role**: Company Admin in your Miro organization.\n\n💡 **Not on Enterprise Plan yet?** Upgrade to [Miro Enterprise](https://miro.com/enterprise/) to unlock audit logs and gain comprehensive visibility into your team's activities in Microsoft Sentinel.\n\nFor detailed instructions, refer to the [documentation](https://help.miro.com/hc/en-us/articles/31325908249362).",
13+
"graphQueriesTableName": "MiroAuditLogs_CL",
14+
"graphQueries": [
15+
{
16+
"metricName": "Miro Audit Logs",
17+
"legend": "MiroAuditLogs_CL",
18+
"baseQuery": "MiroAuditLogs_CL"
19+
}
20+
],
21+
"sampleQueries": [
22+
{
23+
"description": "Recent audit events",
24+
"query": "MiroAuditLogs_CL\n| sort by TimeGenerated desc\n| take 10"
25+
},
26+
{
27+
"description": "Events by category",
28+
"query": "MiroAuditLogs_CL\n| summarize count() by category\n| render piechart"
29+
}
30+
],
31+
"dataTypes": [
32+
{
33+
"name": "MiroAuditLogs_CL",
34+
"lastDataReceivedQuery": "MiroAuditLogs_CL\n| summarize Time = max(TimeGenerated)\n| where isnotempty(Time)"
35+
}
36+
],
37+
"availability": {
38+
"isPreview": false
39+
},
40+
"connectivityCriteria": [
41+
{
42+
"type": "HasDataConnectors"
43+
}
44+
],
45+
"permissions": {
46+
"resourceProvider": [
47+
{
48+
"provider": "Microsoft.OperationalInsights/workspaces",
49+
"permissionsDisplayText": "Read and Write permissions are required.",
50+
"providerDisplayName": "Workspace",
51+
"scope": "Workspace",
52+
"requiredPermissions": {
53+
"write": true,
54+
"read": true,
55+
"delete": true
56+
}
57+
}
58+
],
59+
"customs": [
60+
{
61+
"name": "Miro Enterprise Plan",
62+
"description": "Miro Enterprise Plan subscription is required."
63+
},
64+
{
65+
"name": "Miro OAuth Application",
66+
"description": "Miro OAuth application with auditlogs:read scope and Company Admin role is required."
67+
}
68+
]
69+
},
70+
"instructionSteps": [
71+
{
72+
"description": "**Step 1: Verify your Miro plan**\n\n1. Ensure your organization has an active [Miro Enterprise Plan](https://miro.com/pricing/).\n2. If you need to upgrade, contact [Miro Sales](https://miro.com/contact/sales/) or your account manager.\n3. You must be a **Company Admin** to set up this integration."
73+
},
74+
{
75+
"description": "**Step 2: Choose your setup option**\n\nThere are two ways to set up the Miro Audit Logs connector.\n\n**Option 1 (recommended):** Use Enterprise integrations\n- Simplest setup with automatic token generation.\n- Recommended for most users.\n- See Option 1 below.\n\n**Option 2 (alternative):** Create custom OAuth application\n- More control over OAuth app configuration.\n- For advanced users or custom integration needs.\n- See Option 2 below.\n\n**Note:** When using Option 1, the integration is automatically tied to the team with the largest number of users in your organization. When using Option 2, you can choose which team to install the app to. However, **the team selection does not affect which logs are collected**—both options provide organization-wide log access. All integration-relevant events from all teams are included in your logs."
76+
},
77+
{
78+
"description": "**Option 1: Enterprise integrations (recommended)**\n\n1. Open [Miro Company Settings](https://miro.com/app/settings/).\n2. Expand the **Apps and integrations** section.\n3. Click **Enterprise integrations**.\n4. Enable the **SIEM** toggle.\n5. Copy the **Access Token** value that appears.\n6. **Important:** Store the token securely—it provides full access to audit logs.\n7. The token will work until you disable the toggle.\n8. Proceed to Step 3."
79+
},
80+
{
81+
"description": "**Option 2: Custom OAuth application (alternative)**\n\n1. Go to [Miro App Settings](https://miro.com/app/settings/user-profile/apps).\n2. Click **Create new app**.\n3. Select **Non-expiring access token** option during app creation.\n4. Enable the OAuth scope: **`auditlogs:read`**.\n5. Click **Install app and get OAuth token**.\n6. Authorize the app to access your organization.\n7. Copy the **Access Token** that is displayed.\n8. **Important:** Store the token securely—it provides full access to audit logs.\n9. The token will work until you uninstall the app."
82+
},
83+
{
84+
"description": "**Step 3: Learn more**\n\nFor detailed information about Miro audit logs:\n- [Miro Audit Logs documentation](https://help.miro.com/hc/en-us/articles/360017571434-Audit-logs)\n- [Miro API reference](https://developers.miro.com/reference/enterprise-get-audit-logs)\n- [OAuth non-expiring tokens](https://developers.miro.com/reference/authorization-flow-for-expiring-access-tokens)\n- [Enterprise integrations settings](https://miro.com/app/settings/)"
85+
},
86+
{
87+
"description": "**Step 4: Connect to Miro**\n\nProvide your Miro access token below to complete the connection.",
88+
"instructions": [
89+
{
90+
"type": "Textbox",
91+
"parameters": {
92+
"label": "Access token",
93+
"placeholder": "Enter your Miro Access Token",
94+
"type": "password",
95+
"name": "AccessToken"
96+
}
97+
},
98+
{
99+
"type": "ConnectionToggleButton",
100+
"parameters": {
101+
"label": "toggle",
102+
"name": "toggle"
103+
}
104+
}
105+
],
106+
"title": "Connect to Miro to start collecting audit logs in Microsoft Sentinel."
107+
}
108+
]
109+
}
110+
}
111+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
[
2+
{
3+
"name": "MiroAuditLogsPoller",
4+
"apiVersion": "2025-09-01",
5+
"type": "Microsoft.SecurityInsights/dataConnectors",
6+
"location": "{{location}}",
7+
"kind": "RestApiPoller",
8+
"properties": {
9+
"connectorDefinitionName": "MiroAuditLogsDataConnector",
10+
"dcrConfig": {
11+
"dataCollectionEndpoint": "{{dataCollectionEndpoint}}",
12+
"dataCollectionRuleImmutableId": "{{auditLogsDataCollectionRuleImmutableId}}",
13+
"streamName": "Custom-MiroAuditLogs_CL"
14+
},
15+
"dataType": "Miro Audit Logs",
16+
"response": {
17+
"eventsJsonPaths": [
18+
"$.data"
19+
],
20+
"format": "json"
21+
},
22+
"paging": {
23+
"pagingType": "NextPageToken",
24+
"nextPageTokenJsonPath": "$.cursor",
25+
"nextPageParaName": "cursor",
26+
"pageSize": 100
27+
},
28+
"auth": {
29+
"type": "APIKey",
30+
"ApiKeyName": "Authorization",
31+
"APIKey": "Bearer {{AccessToken}}",
32+
"IsAPIKeyInPostPayload": false
33+
},
34+
"request": {
35+
"apiEndpoint": "https://api.miro.com/v2/audit/logs",
36+
"rateLimitQPS": 10,
37+
"queryWindowInMin": 5,
38+
"queryTimeFormat": "yyyy-MM-dd'T'HH:mm:ss'.00Z'",
39+
"httpMethod": "GET",
40+
"retryCount": 3,
41+
"timeoutInSeconds": 60,
42+
"headers": {
43+
"Accept": "application/json",
44+
"User-Agent": "Microsoft-Azure-Sentinel-MiroAuditLogsDataConnector"
45+
},
46+
"StartTimeAttributeName": "createdAfter",
47+
"EndTimeAttributeName": "createdBefore",
48+
"queryParameters": {
49+
"createdAfter": "{_QueryWindowStartTime}",
50+
"createdBefore": "{_QueryWindowEndTime}",
51+
"sorting": "ASC",
52+
"limit": "100"
53+
}
54+
},
55+
"isActive": true
56+
}
57+
}
58+
]
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{
2+
"name": "MiroAuditLogs_CL",
3+
"apiVersion": "2025-07-01",
4+
"type": "Microsoft.OperationalInsights/workspaces/tables",
5+
"location": "{{location}}",
6+
"kind": null,
7+
"properties": {
8+
"schema": {
9+
"name": "MiroAuditLogs_CL",
10+
"columns": [
11+
{
12+
"name": "TimeGenerated",
13+
"type": "datetime",
14+
"description": "The timestamp (UTC) reflecting the time when the event was ingested"
15+
},
16+
{
17+
"name": "id",
18+
"type": "string",
19+
"description": "Unique identifier of the audit event"
20+
},
21+
{
22+
"name": "createdAt",
23+
"type": "datetime",
24+
"description": "Timestamp when the audit event was created"
25+
},
26+
{
27+
"name": "event",
28+
"type": "string",
29+
"description": "Name of the audit event"
30+
},
31+
{
32+
"name": "category",
33+
"type": "string",
34+
"description": "Category of the audit event"
35+
},
36+
{
37+
"name": "logType",
38+
"type": "string",
39+
"description": "Type of the log entry. Mapped from API's 'type' field to avoid reserved 'Type' column name."
40+
},
41+
{
42+
"name": "createdBy_email",
43+
"type": "string",
44+
"description": "Email address of the user who triggered the event"
45+
},
46+
{
47+
"name": "createdBy_id",
48+
"type": "string",
49+
"description": "Unique identifier of the user who triggered the event"
50+
},
51+
{
52+
"name": "createdBy_name",
53+
"type": "string",
54+
"description": "Name of the user who triggered the event"
55+
},
56+
{
57+
"name": "createdBy_type",
58+
"type": "string",
59+
"description": "Type of the actor (user, service account, etc.)"
60+
},
61+
{
62+
"name": "object_id",
63+
"type": "string",
64+
"description": "Unique identifier of the object affected by the event"
65+
},
66+
{
67+
"name": "object_name",
68+
"type": "string",
69+
"description": "Name of the object affected by the event"
70+
},
71+
{
72+
"name": "context_ip",
73+
"type": "string",
74+
"description": "IP address from which the event originated"
75+
},
76+
{
77+
"name": "context_team_id",
78+
"type": "string",
79+
"description": "Unique identifier of the team context"
80+
},
81+
{
82+
"name": "context_team_name",
83+
"type": "string",
84+
"description": "Name of the team context"
85+
},
86+
{
87+
"name": "context_team_type",
88+
"type": "string",
89+
"description": "Type of the team"
90+
},
91+
{
92+
"name": "context_organization_id",
93+
"type": "string",
94+
"description": "Unique identifier of the organization"
95+
},
96+
{
97+
"name": "context_organization_name",
98+
"type": "string",
99+
"description": "Name of the organization"
100+
},
101+
{
102+
"name": "context_organization_type",
103+
"type": "string",
104+
"description": "Type of the organization"
105+
},
106+
{
107+
"name": "details",
108+
"type": "dynamic",
109+
"description": "Additional event details in JSON format"
110+
}
111+
]
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)