├── Azure Activity
├── Azure RBAC Role Assignments with IdentityInfo and Roles.kql
├── Azure RBAC Role Assignments with IdentityInfo.kql
├── Azure RBAC Role Assignments.kql
├── azuredeploy-update-AzureRBACRolesWatchlist.json
└── readme.md
├── KQL
├── GitHubAuditLogPolling.kql
├── Total Cost Estimates by Subscription with table breakdown.txt
└── mfa changes by watchlist
├── agent-management
├── readme.md
└── update-extension.ps1
├── ama-management
├── readme.md
└── update-ama.ps1
├── analytics_rules
├── create-scheduledRuleFromTemplate.ps1
├── export-analyticRuleTemplates.ps1
└── readme.md
├── data_collection_rules
├── logstash-syslog-dcr.json
├── readme.md
├── update-dcrDataCollectionEndpoint.ps1
├── update-dcrStreamColumns.ps1
├── update-dcrTimestampFormat.ps1
├── update-dcrTransform.ps1
├── update-dcrdatastream.ps1
└── update-dcrworkspace.ps1
├── dataconnectors
├── GitHubAuditLogs
│ ├── CCP
│ │ ├── GitHubAuditLogs_CCP.json
│ │ └── readme.md
│ └── eventhub
│ │ ├── eventhub.json
│ │ └── readme.md
├── cross-tenant-logging
│ └── azure-activity-logs
│ │ ├── azuredeploy.json
│ │ └── readme.md
└── okta
│ ├── AzureFunctionOktaSSO.zip
│ ├── okta_custom_table.json
│ └── oktaccp.json
├── enable-sentinel-mdfc-sub-con
├── azuredeploy.json
├── custom-role.json
├── customRoleDeploy.json
├── defenderForCloudConnectorCoverage.json
├── logic-app.svg
├── logicapp.puml
├── readme.md
└── workbook.png
├── images
├── dcr-json.png
├── dcr-verify.png
├── dcr-view.png
├── dcr.gif
└── github_pat.png
├── logstash-cef-adx
├── create-adx-table.txt
├── logstash.conf
└── readme.md
├── logstash
├── logstash-syslog-dcr.json
└── readme.md
├── playbooks
├── get-incident-chatgpt4
│ ├── azuredeploy.json
│ └── readme.md
└── get-incidentdetails
│ ├── azuredeploy.json
│ └── readme.md
├── readme.md
├── table_tools
└── copy-logAnalyticsTable.ps1
├── temp.json
└── workbooks
├── Archiving, Basic Logs, and Retention.json
└── Security Agent Workbook.json
/Azure Activity/Azure RBAC Role Assignments with IdentityInfo and Roles.kql:
--------------------------------------------------------------------------------
1 | AzureActivity
2 | | where OperationNameValue =~ 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE'
3 | | where ActivityStatusValue =~ 'Success' and ActivitySubstatusValue =~ 'Created'
4 | | project TimeGenerated, Caller, CallerIpAddress, SubscriptionId, ResourceGroup, CorrelationId, ActivityStatusValue, ActivitySubstatusValue
5 | // Join AzureActivity start events to get role assignment details, these are not included in the Success event
6 | | join kind=leftouter (
7 | AzureActivity
8 | | where OperationNameValue =~ 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE'
9 | | where ActivityStatusValue =~ 'Start'
10 | | project-away Caller, CallerIpAddress, SubscriptionId, ResourceGroup
11 | )
12 | on CorrelationId
13 | | project-away CorrelationId1, ActivityStatusValue1
14 | | extend AssignedPrincipalId = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).PrincipalId)
15 | | extend AssignedPrincipalType = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).PrincipalType)
16 | | extend RoleDefinitionId = tostring(split(tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).RoleDefinitionId), '/')[-1])
17 | | extend Scope = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).Scope)
18 | // Join the IdentityInfo to get the assigned identity UPN
19 | | join kind=inner (
20 | IdentityInfo
21 | | where TimeGenerated >= ago(30d)
22 | | project AssignedPrincipalTenantId = TenantId, AssignedPrincipalUPN = AccountUPN, AssignedPrincipalId = AccountObjectId
23 | ) on AssignedPrincipalId
24 | | project-away AssignedPrincipalId1
25 | | join kind=inner (
26 | _GetWatchlist('AzureRoles')
27 | | extend RoleDefinitionId = tostring(RoleDefinitionId)
28 | | project RoleDefinitionId, RoleName, RoleType, RoleDescription
29 | ) on RoleDefinitionId
30 | | project TimeGenerated, Actor = Caller, CallerIpAddress, CorrelationId, SubscriptionId, ResourceGroup, AssignedPrincipalUPN, AssignedPrincipalId, AssignedPrincipalType, AssignedPrincipalTenantId, RoleName, RoleType, RoleDefinitionId, RoleDescription, Scope, Src = SourceSystem, OperationNameValue, ActivityStatusValue, ActivitySubstatusValue
31 |
--------------------------------------------------------------------------------
/Azure Activity/Azure RBAC Role Assignments with IdentityInfo.kql:
--------------------------------------------------------------------------------
1 | AzureActivity
2 | | where OperationNameValue =~ 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE'
3 | | where ActivityStatusValue =~ 'Success' and ActivitySubstatusValue =~ 'Created'
4 | | project TimeGenerated, Caller, CallerIpAddress, SubscriptionId, ResourceGroup, CorrelationId, ActivityStatusValue, ActivitySubstatusValue
5 | // Join AzureActivity start events to get role assignment details, these are not included in the Success event
6 | | join kind=leftouter (
7 | AzureActivity
8 | | where OperationNameValue =~ 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE'
9 | | where ActivityStatusValue =~ 'Start'
10 | | project-away Caller, CallerIpAddress, SubscriptionId, ResourceGroup
11 | )
12 | on CorrelationId
13 | | project-away CorrelationId1, ActivityStatusValue1
14 | | extend AssignedPrincipalId = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).PrincipalId)
15 | | extend AssignedPrincipalType = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).PrincipalType)
16 | | extend RoleDefinitionId = split(tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).RoleDefinitionId), '/')[-1]
17 | | extend Scope = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).Scope)
18 | // Join the IdentityInfo to get the assigned identity UPN
19 | | join kind=inner (
20 | IdentityInfo
21 | | where TimeGenerated >= ago(30d)
22 | | project AssignedPrincipalTenantId = TenantId, AssignedPrincipalUPN = AccountUPN, AssignedPrincipalId = AccountObjectId
23 | ) on AssignedPrincipalId
24 | | project-away AssignedPrincipalId1
25 | | project TimeGenerated, Actor = Caller, CallerIpAddress, CorrelationId, SubscriptionId, ResourceGroup, AssignedPrincipalUPN, AssignedPrincipalId, AssignedPrincipalType, AssignedPrincipalTenantId, RoleDefinitionId, Scope, Src = SourceSystem, OperationNameValue, ActivityStatusValue, ActivitySubstatusValue
26 |
--------------------------------------------------------------------------------
/Azure Activity/Azure RBAC Role Assignments.kql:
--------------------------------------------------------------------------------
1 | AzureActivity
2 | | where OperationNameValue =~ 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE'
3 | | where ActivityStatusValue =~ 'Success' and ActivitySubstatusValue =~ 'Created'
4 | | project TimeGenerated, Caller, CallerIpAddress, SubscriptionId, ResourceGroup, CorrelationId, ActivityStatusValue, ActivitySubstatusValue
5 | // Join AzureActivity start events to get role assignment details, these are not included in the Success event
6 | | join kind=leftouter (
7 | AzureActivity
8 | | where OperationNameValue =~ 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE'
9 | | where ActivityStatusValue =~ 'Start'
10 | | project-away Caller, CallerIpAddress, SubscriptionId, ResourceGroup
11 | )
12 | on CorrelationId
13 | | project-away CorrelationId1, ActivityStatusValue1
14 | | extend AssignedPrincipalId = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).PrincipalId)
15 | | extend AssignedPrincipalType = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).PrincipalType)
16 | | extend RoleDefinitionId = split(tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).RoleDefinitionId), '/')[-1]
17 | | extend Scope = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).requestbody)).Properties)).Scope)
18 | | project TimeGenerated, Actor = Caller, CallerIpAddress, CorrelationId, SubscriptionId, ResourceGroup, AssignedPrincipalId, AssignedPrincipalType, RoleDefinitionId, Scope, Src = SourceSystem, OperationNameValue, ActivityStatusValue, ActivitySubstatusValue
19 |
--------------------------------------------------------------------------------
/Azure Activity/azuredeploy-update-AzureRBACRolesWatchlist.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "title": "",
6 | "description": "",
7 | "prerequisites": "",
8 | "postDeployment": [],
9 | "prerequisitesDeployTemplateFile": "",
10 | "lastUpdateTime": "",
11 | "entities": [],
12 | "tags": [],
13 | "support": {
14 | "tier": "community",
15 | "armtemplate": "Generated from https://github.com/Azure/Azure-Sentinel/tree/master/Tools/Playbook-ARM-Template-Generator"
16 | },
17 | "author": {
18 | "name": ""
19 | }
20 | },
21 | "parameters": {
22 | "PlaybookName": {
23 | "defaultValue": "update-AzureRBACRolesWatchlist",
24 | "type": "string"
25 | },
26 | "Sentinel Resource Group": {
27 | "type": "String",
28 | "metadata": {
29 | "description": "Enter value for Sentinel Resource Group"
30 | }
31 | },
32 | "Sentinel Subscription ID": {
33 | "type": "String",
34 | "metadata": {
35 | "description": "Enter value for Sentinel Subscription ID"
36 | }
37 | },
38 | "Sentinel Workspace ID": {
39 | "type": "String",
40 | "metadata": {
41 | "description": "Enter value for Sentinel Workspace ID"
42 | }
43 | }
44 | },
45 | "variables": {
46 | "MicrosoftSentinelConnectionName": "[concat('MicrosoftSentinel-', parameters('PlaybookName'))]"
47 | },
48 | "resources": [
49 | {
50 | "properties": {
51 | "provisioningState": "Succeeded",
52 | "state": "Enabled",
53 | "definition": {
54 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
55 | "contentVersion": "1.0.0.0",
56 | "parameters": {
57 | "$connections": {
58 | "defaultValue": {},
59 | "type": "Object"
60 | },
61 | "Sentinel Resource Group": {
62 | "type": "String",
63 | "defaultValue": "[parameters('Sentinel Resource Group')]"
64 | },
65 | "Sentinel Subscription ID": {
66 | "type": "String",
67 | "defaultValue": "[parameters('Sentinel Subscription ID')]"
68 | },
69 | "Sentinel Workspace ID": {
70 | "type": "String",
71 | "defaultValue": "[parameters('Sentinel Workspace ID')]"
72 | }
73 | },
74 | "triggers": {
75 | "Recurrence": {
76 | "recurrence": {
77 | "frequency": "Day",
78 | "interval": 1
79 | },
80 | "evaluatedRecurrence": {
81 | "frequency": "Day",
82 | "interval": 1
83 | },
84 | "type": "Recurrence"
85 | }
86 | },
87 | "actions": {
88 | "Create_CSV_table": {
89 | "runAfter": {
90 | "Parse_JSON": [
91 | "Succeeded"
92 | ]
93 | },
94 | "type": "Table",
95 | "inputs": {
96 | "columns": [
97 | {
98 | "header": "RoleName",
99 | "value": "@item()?['properties']?['roleName']"
100 | },
101 | {
102 | "header": "RoleDefinitionId",
103 | "value": "@item()?['name']"
104 | },
105 | {
106 | "header": "RoleType",
107 | "value": "@item()?['properties']?['type']"
108 | },
109 | {
110 | "header": "RoleDescription",
111 | "value": "@item()?['properties']?['description']"
112 | }
113 | ],
114 | "format": "CSV",
115 | "from": "@body('Parse_JSON')?['value']"
116 | }
117 | },
118 | "Get_Azure_RBAC_Roles": {
119 | "runAfter": {},
120 | "type": "Http",
121 | "inputs": {
122 | "authentication": {
123 | "type": "ManagedServiceIdentity"
124 | },
125 | "method": "GET",
126 | "queries": {
127 | "api-version": "2022-04-01",
128 | "select": "roleName,name,type,description"
129 | },
130 | "uri": "https://management.azure.com/providers/Microsoft.Authorization/roleDefinitions"
131 | },
132 | "runtimeConfiguration": {
133 | "contentTransfer": {
134 | "transferMode": "Chunked"
135 | }
136 | }
137 | },
138 | "Parse_JSON": {
139 | "runAfter": {
140 | "Get_Azure_RBAC_Roles": [
141 | "Succeeded"
142 | ]
143 | },
144 | "type": "ParseJson",
145 | "inputs": {
146 | "content": "@body('Get_Azure_RBAC_Roles')",
147 | "schema": {
148 | "properties": {
149 | "value": {
150 | "items": {
151 | "properties": {
152 | "id": {
153 | "type": "string"
154 | },
155 | "name": {
156 | "type": "string"
157 | },
158 | "properties": {
159 | "properties": {
160 | "assignableScopes": {
161 | "items": {
162 | "type": "string"
163 | },
164 | "type": "array"
165 | },
166 | "createdBy": {},
167 | "createdOn": {
168 | "type": "string"
169 | },
170 | "description": {
171 | "type": "string"
172 | },
173 | "permissions": {
174 | "items": {
175 | "properties": {
176 | "actions": {
177 | "items": {
178 | "type": "string"
179 | },
180 | "type": "array"
181 | },
182 | "dataActions": {
183 | "type": "array"
184 | },
185 | "notActions": {
186 | "type": "array"
187 | },
188 | "notDataActions": {
189 | "type": "array"
190 | }
191 | },
192 | "required": [
193 | "actions",
194 | "dataActions",
195 | "notActions",
196 | "notDataActions"
197 | ],
198 | "type": "object"
199 | },
200 | "type": "array"
201 | },
202 | "roleName": {
203 | "type": "string"
204 | },
205 | "type": {
206 | "type": "string"
207 | },
208 | "updatedBy": {},
209 | "updatedOn": {
210 | "type": "string"
211 | }
212 | },
213 | "type": "object"
214 | },
215 | "type": {
216 | "type": "string"
217 | }
218 | },
219 | "required": [
220 | "id",
221 | "name",
222 | "properties",
223 | "type"
224 | ],
225 | "type": "object"
226 | },
227 | "type": "array"
228 | }
229 | },
230 | "type": "object"
231 | }
232 | }
233 | },
234 | "Watchlists_-_Create_a_new_Watchlist": {
235 | "runAfter": {
236 | "Create_CSV_table": [
237 | "Succeeded"
238 | ]
239 | },
240 | "type": "ApiConnection",
241 | "inputs": {
242 | "body": {
243 | "description": "Azure RBAC Definitions",
244 | "displayName": "Azure Role Definitions",
245 | "itemsSearchKey": "RoleDefinitionId",
246 | "rawContent": "@{body('Create_CSV_table')}"
247 | },
248 | "host": {
249 | "connection": {
250 | "name": "@parameters('$connections')['azuresentinel']['connectionId']"
251 | }
252 | },
253 | "method": "put",
254 | "path": "/Watchlists/subscriptions/@{encodeURIComponent(parameters('Sentinel Subscription ID'))}/resourceGroups/@{encodeURIComponent(parameters('Sentinel Resource Group'))}/workspaces/@{encodeURIComponent(parameters('Sentinel Workspace ID'))}/watchlists/@{encodeURIComponent('AzureRoles')}"
255 | }
256 | }
257 | },
258 | "outputs": {}
259 | },
260 | "parameters": {
261 | "$connections": {
262 | "value": {
263 | "azuresentinel": {
264 | "connectionId": "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]",
265 | "connectionName": "[variables('MicrosoftSentinelConnectionName')]",
266 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]",
267 | "connectionProperties": {
268 | "authentication": {
269 | "type": "ManagedServiceIdentity"
270 | }
271 | }
272 | }
273 | }
274 | }
275 | }
276 | },
277 | "name": "[parameters('PlaybookName')]",
278 | "type": "Microsoft.Logic/workflows",
279 | "location": "[resourceGroup().location]",
280 | "tags": {
281 | "hidden-SentinelTemplateName": "update-AzureRBACRolesWatchlist",
282 | "hidden-SentinelTemplateVersion": "1.0"
283 | },
284 | "identity": {
285 | "type": "SystemAssigned"
286 | },
287 | "apiVersion": "2017-07-01",
288 | "dependsOn": [
289 | "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]"
290 | ]
291 | },
292 | {
293 | "type": "Microsoft.Web/connections",
294 | "apiVersion": "2016-06-01",
295 | "name": "[variables('MicrosoftSentinelConnectionName')]",
296 | "location": "[resourceGroup().location]",
297 | "kind": "V1",
298 | "properties": {
299 | "displayName": "[variables('MicrosoftSentinelConnectionName')]",
300 | "customParameterValues": {},
301 | "parameterValueType": "Alternative",
302 | "api": {
303 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]"
304 | }
305 | }
306 | }
307 | ]
308 | }
309 |
--------------------------------------------------------------------------------
/Azure Activity/readme.md:
--------------------------------------------------------------------------------
1 | # Azure Activity RBAC Hunting and Enrichment
2 | This page contains a collection KQL queries and enrichment automation to hunt for Azure RBAC changes.
3 |
4 | ## Current Challenges
5 | The Azure Activity logs currently only contain GUIDs of Azure RBAC roles and identities. Furthermore, RBAC changes are represented in two separate events in the logs. This solution provides data enrichment using:
6 | - Logic App to pull in Azure Role Information
7 | - KQL Queries to pull in Identity information from the UEBA tables
8 |
9 | ## Data Enrichment
10 |
11 | ### update-AzureRBACRolesWatchlist
12 | The update-AzureRBACRolesWatchlist logic apps gets current role defintions in your tenant and creates/updates a watchlist with role definitions.
13 | > The logic app will run once a day by default
14 |
15 | 1. [
](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2FAzure%2520Activity%2Fazuredeploy-update-AzureRBACRolesWatchlist.json)
16 |
17 | 2. Assign the **Microsoft Sentinel Contributor** role to the logic app system assigned managed identity
18 |
19 | ## KQL Queries
20 |
21 | | Query | Description |
22 | |---|---|
23 | | [Azure RBAC Role Assignments](./Azure%20RBAC%20Role%20Assignments.kql)| Gets Azure RBAC role changes |
24 | | [Azure RBAC Role Assignments with IdentityInfo](./Azure%20RBAC%20Role%20Assignments%20with%20IdentityInfo.kql)| Gets Azure RBAC role changes and assigned identity information from the IdentityInfo table. Requires UEBA |
25 | | [Azure RBAC Role Assignments with IdentityInfo and Roles](./Azure%20RBAC%20Role%20Assignments%20with%20IdentityInfo%20and%20Roles.kql)| Gets Azure RBAC role changes, assigned identity information from the IdentityInfo table and role information from the AzureRoles watchlist. Requires UEBA and the logic app |
26 |
--------------------------------------------------------------------------------
/KQL/GitHubAuditLogPolling.kql:
--------------------------------------------------------------------------------
1 | // GitHubAuditLogPolling_CL Parser
2 | GitHubAuditLogPolling_CL
3 | | extend
4 | TimeGenerated = unixtime_milliseconds_todatetime(tolong(parse_json(RawData).created_at)),
5 | timestamp = parse_json(RawData).['@timestamp'],
6 | document_id = tostring(parse_json(RawData)._document_id),
7 | action = tostring(parse_json(RawData).action),
8 | actor = tostring(parse_json(RawData).actor),
9 | actor_id = tostring(parse_json(RawData).actor_id),
10 | actor_ip = tostring(parse_json(RawData).actor_ip),
11 | actor_location = parse_json(RawData).actor_location,
12 | application_name = tostring(parse_json(RawData).application_name),
13 | business = tostring(parse_json(RawData).business),
14 | business_id = tostring(parse_json(RawData).business_id),
15 | hashed_token = tostring(parse_json(RawData).hashed_token),
16 | integration = tostring(parse_json(RawData).integration),
17 | oauth_application_id = tostring(parse_json(RawData).oauth_application_id),
18 | operation_type = tostring(parse_json(RawData).operation_type),
19 | org = tostring(parse_json(RawData).org),
20 | org_id = tostring(parse_json(RawData).org_id),
21 | programmatic_access_type = tostring(parse_json(RawData).programmatic_access_type),
22 | public_repo = tostring(parse_json(RawData).public_repo),
23 | query_string = tostring(parse_json(RawData).query_string),
24 | rate_limit_remaining = tostring(parse_json(RawData).rate_limit_remaining),
25 | repo = tostring(parse_json(RawData).repo),
26 | repo_id = tostring(parse_json(RawData).repo_id),
27 | request_body = tostring(parse_json(RawData).request_body),
28 | request_method = tostring(parse_json(RawData).request_method),
29 | route = tostring(parse_json(RawData).route),
30 | status_code = tostring(parse_json(RawData).status_code),
31 | token_id = tostring(parse_json(RawData).token_id),
32 | token_scopes = tostring(parse_json(RawData).token_scopes),
33 | url_path = tostring(parse_json(RawData).url_path),
34 | user = tostring(parse_json(RawData).user),
35 | user_agent = tostring(parse_json(RawData).user_agent),
36 | user_id = tostring(parse_json(RawData).user_id)
37 |
--------------------------------------------------------------------------------
/KQL/Total Cost Estimates by Subscription with table breakdown.txt:
--------------------------------------------------------------------------------
1 | let Price = 3;
2 | let billedSizeBySub = materialize (union withsource = TableName1 *
3 | | where _IsBillable == True
4 | | where _SubscriptionId contains "ada06e68-375e-4210-be3a-c6cacebf41c5"
5 | | summarize totalTableEntries = count(), tableSize = sum(_BilledSize), lastLogReceived = datetime_diff("second",now(), max(TimeGenerated)),
6 | estimate = sumif(_BilledSize, _IsBillable==true)
7 | by _SubscriptionId, ResourceGroup, TableName1, _IsBillable
8 | | extend SubscriptionId = iif(isempty(_SubscriptionId),'Non Subscription Data',_SubscriptionId)
9 | | project SubscriptionId, ResourceGroup = tolower(ResourceGroup), TableName1, _IsBillable, tableSize, ['Estimated Price'] = (estimate/(1024*1024*1024)) * Price,totalTableEntries, lastLogReceived);
10 | billedSizeBySub
11 | | union
12 | (billedSizeBySub
13 | | summarize resourceGroupCount = tostring(dcount(ResourceGroup)), tableCount = tostring(dcount(TableName1)), tableSize = sum(tableSize), ['Estimated Price'] = sum(['Estimated Price']), totalTableEntries = sum(totalTableEntries)
14 | )
15 | | extend ResourceGroup = iif(isempty(ResourceGroup),resourceGroupCount,ResourceGroup)
16 | | extend TableName1 = iif(isempty(TableName1),tableCount,TableName1)
17 | | extend FinalTotals = iif(isempty(SubscriptionId),'Final Totals:','')
18 | | project FinalTotals, SubscriptionId, ResourceGroup, ['tableName'] = TableName1, _IsBillable, tableSize, ['Estimated Price'], totalTableEntries, lastLogReceived
19 | | order by ['Estimated Price'], totalTableEntries, FinalTotals asc
20 |
--------------------------------------------------------------------------------
/KQL/mfa changes by watchlist:
--------------------------------------------------------------------------------
1 | AuditLogs
2 | | where OperationName in ('User changed default security info','User deleted security info','User registered all required security info', 'User registered security info')
3 | | where Result == "success"
4 | | extend userPrincipalName = parse_json(tostring(InitiatedBy.user)).userPrincipalName
5 | | extend IPAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
6 | | where userPrincipalName in (
7 | (_GetWatchlist('watchlist')
8 | | project userPrincipalName)
9 | )
10 | | project TimeGenerated, userPrincipalName, IPAddress, OperationName, ResultReason, LoggedByService, AdditionalDetails
11 |
--------------------------------------------------------------------------------
/agent-management/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Update Extensions
3 |
4 | - [Overview](#overview)
5 | - [Usage](#usage)
6 | - [Examples](#examples)
7 | * [Azure Virtual Machines](#azure-virtual-machines)
8 | * [Update Azure Virtual Machines to a specific version of the Azure Monitor Agent](#update-azure-virtual-machines-to-a-specific-version-of-the-azure-monitor-agent)
9 |
10 | # Overview
11 |
12 | [**update-extension.ps1**](https://github.com/seanstark/sentinel-tools/blob/main/agent-management/update-extension.ps1) is a powershell script you can use to update extensions on Azure Virtual Machines and Azure Arc Machines. The script will handle both linux and windows servers with the below features.
13 |
14 | - Update the extension to a specific version
15 | - Update the extension to the latest version
16 | - Report on current versions without updating
17 |
18 | # Usage
19 |
20 | > Azure Arc does not return a detailed status of the update request
21 | >
22 | > The rest API only allows upgrading to Major+Minor versions. Therefore you can't upgrade from 1.2 to 1.2.2 as an example, only to 1.3
23 |
24 | - The script takes input from an object of machines from either [**Get-AzVM**](https://learn.microsoft.com/powershell/module/az.compute/get-azvm?view) or [**Get-AzConnectedMachine**](https://learn.microsoft.com/powershell/module/az.connectedmachine/get-azconnectedmachine). This will give you the flexibility to scope updates to specific machines.
25 |
26 | - You can specify the versions you want to update to using the **linuxTargetVersion** and **windowsTargetVersion** parameters.
27 |
28 | - If you specify the **latestVersion** parameter the script will automatically use the latest version available in the region where the machine resides.
29 |
30 | - If you specify the **report** parameter the script will only report on versions installed and will not update
31 |
32 | # Examples
33 |
34 | ## Azure Virtual Machines
35 |
36 | ### Update Azure Virtual Machines to a specific version of the Azure Monitor Agent
37 | ```
38 | .\update-extension.ps1 -machines $(Get-AzVM) -linuxTargetVersion 1.22.2 -windowsTargetVersion 1.10.0.0 -extPublisherName 'Microsoft.Azure.Monitor' -windowsExtType 'AzureMonitorWindowsAgent' -linuxExtType 'AzureMonitorLinuxAgent'
39 | ```
40 |
--------------------------------------------------------------------------------
/agent-management/update-extension.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update Azure Virtual Machines and Azure Arc Machines to the desired or latest version of an extension.
4 |
5 | .PARAMETER machines
6 | Specify an object of machines from Get-AzVM or Get-AzConnectedMachine
7 |
8 | .PARAMETER linuxTargetVersion
9 | Specify the version of the extension to ugprade to for Linux. See https://learn.microsoft.com/azure/azure-monitor/agents/azure-monitor-agent-extension-versions for AMA
10 |
11 | .PARAMETER windowsTargetVersion
12 | Specify the version of the extension to ugprade to for Windows. See https://learn.microsoft.com/azure/azure-monitor/agents/azure-monitor-agent-extension-versions for AMA
13 |
14 | .PARAMETER latestVersion
15 | Specify the latestVersion switch to use the latest version available for Linux and Windows. This is unique to each region.
16 |
17 | .PARAMETER report
18 | Specify the report switch to only report on machines with current versions
19 |
20 | .PARAMETER extPublisherName
21 | Specify the Extension Publisher name. Defaults to Microsoft.Azure.Monitor. See https://learn.microsoft.com/azure/azure-arc/servers/manage-vm-extensions
22 |
23 | .PARAMETER windowsExtType
24 | Specify the Windows Extension Type name. Defaults to AzureMonitorWindowsAgent. See https://learn.microsoft.com/azure/azure-arc/servers/manage-vm-extensions
25 |
26 | .PARAMETER linuxExtType
27 | Specify the Linux Extension Type name. Defaults to AzureMonitorLinuxAgent. See https://learn.microsoft.com/azure/azure-arc/servers/manage-vm-extensions
28 |
29 | .EXAMPLE
30 | Update Azure Virtual Machines to a specific version of the Azure Monitor Agent
31 | .\update-ama.ps1 -machines $(Get-AzVM) -linuxTargetVersion 1.22.2 -windowsTargetVersion 1.10.0.0 -extPublisherName 'Microsoft.Azure.Monitor' -windowsExtType 'AzureMonitorWindowsAgent' -linuxExtType 'AzureMonitorLinuxAgent'
32 | #>
33 |
34 | param(
35 | [Parameter(Mandatory=$true)]
36 | [object]$machines,
37 |
38 | [Parameter(Mandatory=$true, ParameterSetName = 'TargetVersion')]
39 | [string]$linuxTargetVersion,
40 |
41 | [Parameter(Mandatory=$true, ParameterSetName = 'TargetVersion')]
42 | [string]$windowsTargetVersion,
43 |
44 | [Parameter(Mandatory=$true, ParameterSetName = 'LatestVersion')]
45 | [switch]$latestVersion,
46 |
47 | [Parameter(Mandatory=$false)]
48 | [switch]$report,
49 |
50 | [Parameter(Mandatory=$false)]
51 | [string]$extPublisherName = 'Microsoft.Azure.Monitor',
52 |
53 | [Parameter(Mandatory=$false)]
54 | [string]$windowsExtType = 'AzureMonitorWindowsAgent',
55 |
56 | [Parameter(Mandatory=$false)]
57 | [string]$linuxExtType = 'AzureMonitorLinuxAgent'
58 | )
59 |
60 | $requiredModules = 'Az.Accounts', 'Az.Compute', 'Az.ConnectedMachine'
61 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
62 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
63 | ForEach ($module in $modulesToInstall){
64 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
65 | Install-Module $module -force
66 | }
67 |
68 | # This isn't used for anything other than to query for arc extension versions
69 | $subscriptionId = (Get-AzContext | Select -ExpandProperty Subscription).id
70 |
71 | function Get-latestVersion{
72 | Param($versions)
73 | $latest = $versions | % {[version]$_} | Sort-Object -Descending | Select -First 1
74 | $latest.ToString()
75 | }
76 |
77 | #Get Latest Versions for each machine region
78 | If($latestVersion){
79 |
80 | $regionLatestVersions = @()
81 | $regions = $machines.location | Sort-Object | Get-Unique
82 | ForEach ($region in $regions){
83 | Write-Verbose ('Getting Region Latest Extension Versions for {0}' -f $region )
84 | # Azure Native Virtual Machines
85 | $windowsVersions = Get-AzVMExtensionImage -PublisherName $extPublisherName -Type $windowsExtType -Location $region
86 | $linuxVersions = Get-AzVMExtensionImage -PublisherName $extPublisherName -Type $linuxExtType -Location $region
87 |
88 | # Azure Arc Machines
89 | If($machines[0].Type -like 'Microsoft.HybridCompute/machines'){
90 | Write-Verbose ('Getting Azure Arc Region Latest Extension Versions for {0}' -f $region )
91 | $uri = ('https://management.azure.com/subscriptions/{0}/providers/Microsoft.HybridCompute/locations/{1}/publishers/{2}/extensionTypes/{3}/versions?api-version=2022-12-27-preview' -f $subscriptionId, $region, $extPublisherName, $windowsExtType)
92 | $windowsVersions = (Invoke-AzRestMethod -Uri $uri -Method GET).Content | ConvertFrom-Json | Select -ExpandProperty properties
93 |
94 | $uri = ('https://management.azure.com/subscriptions/{0}/providers/Microsoft.HybridCompute/locations/{1}/publishers/{2}/extensionTypes/{3}/versions?api-version=2022-12-27-preview' -f $subscriptionId, $region, $extPublisherName, $linuxExtType)
95 | $linuxVersions = (Invoke-AzRestMethod -Uri $uri -Method GET).Content | ConvertFrom-Json | Select -ExpandProperty properties
96 | }
97 |
98 | #Update Object
99 | $regionLatestVersions += [PSCustomObject]@{
100 | location = $region
101 | linuxLatestVersion = Get-latestVersion $linuxVersions.Version
102 | windowsLatestVersion = Get-latestVersion $windowsVersions.Version
103 | }
104 | }
105 | }
106 |
107 | $agentsToUpgrade = @()
108 |
109 | Write-Host ('Evaluating {0} machines' -f $machines.Count)
110 |
111 | ForEach ($machine in $machines){
112 | # Cannot trust the OsType is properly detected, check for AzureMonitorWindowsAgent and AzureMonitorLinuxAgent
113 | $agent = $null
114 |
115 | Write-Verbose ('Evaluating {0}' -f $machine.Name)
116 |
117 | If($machine.Type -like 'Microsoft.Compute/virtualMachines'){
118 | $state = (($machine | Get-AzVM -Status).statuses | Where Code -like 'PowerState*').DisplayStatus
119 | $windowsAgent = Get-AzVMExtension -VMName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name $windowsExtType -ErrorAction SilentlyContinue
120 | $linuxAgent = Get-AzVMExtension -VMName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name $linuxExtType -ErrorAction SilentlyContinue
121 | }
122 | If($machine.Type -like 'Microsoft.HybridCompute/machines'){
123 | $state = $machine.Status
124 | $windowsAgent = Get-AzConnectedMachineExtension -MachineName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name $windowsExtType -ErrorAction SilentlyContinue
125 | $linuxAgent = Get-AzConnectedMachineExtension -MachineName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name $linuxExtType -ErrorAction SilentlyContinue
126 | }
127 |
128 | # If latestVersion is flagged, get the latest published version for the region where the machine resides
129 | If($latestVersion){
130 | Write-Verbose ('Latest Version Parameter Specified. Getting Latest Version for Region: {0}' -f $machine.Location)
131 | $linuxTargetVersion = $regionLatestVersions | Where-Object {$_.location -like $machine.Location} | Select -ExpandProperty linuxLatestVersion
132 | $windowsTargetVersion = $regionLatestVersions | Where-Object {$_.location -like $machine.Location} | Select -ExpandProperty windowsLatestVersion
133 | }
134 |
135 | # Build Agent Objects
136 | If ($windowsAgent){
137 | $agent = $windowsAgent
138 | $agent | Add-Member -MemberType NoteProperty -Name TargetVersion -Value $windowsTargetVersion -Force
139 | $agent | Add-Member -MemberType NoteProperty -Name extensionTarget -Value $windowsAgent.extensionTarget -Force
140 | }
141 | If ($linuxAgent){
142 | $agent = $linuxAgent
143 | $agent | Add-Member -MemberType NoteProperty -Name TargetVersion -Value $linuxTargetVersion -Force
144 | $agent | Add-Member -MemberType NoteProperty -Name extensionTarget -Value $linuxAgent.extensionTarget -Force
145 | }
146 |
147 | # Add Additional Attributes
148 | If ($agent){
149 | #Fix Target Version, can only be major and minor version
150 | $agent | Add-Member -MemberType NoteProperty -Name TargetMajorMinorVersion -Value ('{0}.{1}'-f ([Version]($agent.TargetVersion)).Major, ([Version]($agent.TargetVersion)).Minor) -Force
151 | $agent | Add-Member -MemberType NoteProperty -Name CurrentVersion -Value $agent.TypeHandlerVersion -Force
152 | $agent | Add-Member -MemberType NoteProperty -Name MachineType -Value $machine.Type -Force
153 | $agent | Add-Member -MemberType NoteProperty -Name MachineState -Value $state -Force
154 | $agent | Add-Member -MemberType NoteProperty -Name MachineName -Value $machine.Name -Force
155 | $agent | Add-Member -MemberType NoteProperty -Name SubscriptionId -Value $machine.id.split('/')[2] -Force
156 | $agentsToUpgrade += $agent
157 |
158 | Write-Verbose ($agent | Select MachineName, SubscriptionId, ResourceGroupName, MachineState, MachineType, Name, CurrentVersion, TargetVersion, extensionTarget, EnableAutomaticUpgrade, ProvisioningState)
159 | }
160 | }
161 |
162 | If ($report){
163 | Write-Host 'Report only specified'
164 | $agentsToUpgrade | Select MachineName, SubscriptionId, ResourceGroupName, MachineState, MachineType, Name, CurrentVersion, TargetVersion, extensionTarget, EnableAutomaticUpgrade, ProvisioningState | ft
165 | }else {
166 | #Get only running or connected machines
167 | $agentsToUpgrade = $agentsToUpgrade | Where-Object {$_.MachineState -like 'VM running' -or $_.MachineState -like 'Connected'}
168 | #Get only machines that do not match the target version
169 | $agentsToUpgrade = $agentsToUpgrade | Where-Object {[Version]$_.CurrentVersion -lt [Version]$_.TargetVersion}
170 |
171 | Write-Host ('{0} out of {1} machines to upgrade' -f $agentsToUpgrade.count, $machines.Count)
172 |
173 | ForEach ($agent in $agentsToUpgrade){
174 | If($agent.MachineType -like 'Microsoft.Compute/virtualMachines'){
175 | $uri = ('https://management.azure.com{0}?api-version=2022-11-01' -f $agent.Id)
176 | $method = 'PATCH'
177 | $body = @{
178 | properties = @{
179 | publisher = $agent.Publisher
180 | type = $agent.ExtensionType
181 | typeHandlerVersion = $agent.TargetMajorMinorVersion
182 | }
183 | }
184 | }
185 | If($agent.MachineType -like 'Microsoft.HybridCompute/machines'){
186 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.HybridCompute/machines/{2}/upgradeExtensions?api-version=2022-12-27-preview' -f $agent.SubscriptionId, $agent.ResourceGroupName, $agent.MachineName)
187 | $method = 'POST'
188 | $targetType = ('{0}.{1}' -f $agent.Publisher, $agent.Name)
189 | $body = @{
190 | extensionTargets = @{
191 | "$targetType"= @{
192 | targetVersion = $agent.TargetMajorMinorVersion
193 | }
194 | }
195 | }
196 | }
197 | Write-Host ('Updating {0} from version {1} to latest version: {2} ' -f $agent.MachineName, $agent.CurrentVersion, $agent.TargetVersion)
198 | Write-Verbose $($body | ConvertTo-Json)
199 | Write-Verbose $uri
200 | $request = Invoke-AzRestMethod -Uri $uri -Method $method -Payload $($body | ConvertTo-Json)
201 | $reqContent = $request.Content | ConvertFrom-Json
202 | $request | Add-Member -MemberType NoteProperty -Name provisioningState -Value $reqContent.properties.provisioningState -Force
203 | $request | Add-Member -MemberType NoteProperty -Name publisher -Value $reqContent.properties.publisher -Force
204 | $request | Add-Member -MemberType NoteProperty -Name type -Value $reqContent.properties.type -Force
205 | $request | Add-Member -MemberType NoteProperty -Name typeHandlerVersion -Value $reqContent.properties.typeHandlerVersion -Force
206 | $request | Add-Member -MemberType NoteProperty -Name machineName -Value $agent.MachineName -Force
207 | $request | Select machineName, StatusCode, Method, provisioningState, publisher, type, typeHandlerVersion
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/ama-management/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Update Azure Monitor Agent
3 |
4 | - [Overview](#overview)
5 | - [Usage](#usage)
6 | - [Examples](#examples)
7 | * [Azure Virtual Machines](#azure-virtual-machines)
8 | * [Update Azure Virtual Machines to a specific version](#update-azure-virtual-machines-to-a-specific-version)
9 | * [Update Azure Virtual Machines to the latest version of Windows and Linux](#update-azure-virtual-machines-to-the-latest-version-of-windows-and-linux)
10 | * [Generate a report of Azure Virtual Machines with current versions](#generate-a-report-of-azure-virtual-machines-with-current-versions)
11 | * [Azure Arc Machines](#azure-arc-machines)
12 | * [Update Azure Arc Machines to a specific version](#update-azure-arc-machines-to-a-specific-version)
13 | * [Update Azure Arc Machines to the latest version of Windows and Linux](#update-azure-arc-machines-to-the-latest-version-of-windows-and-linux)
14 | * [Generate a report of Azure Arc Machines with current versions](#generate-a-report-of-azure-arc-machines-with-current-versions)
15 |
16 | # Overview
17 |
18 | [**update-ama.ps1**](https://github.com/seanstark/sentinel-tools/blob/main/ama-management/update-ama.ps1) is a powershell script you can use to update the Azure Monitor Agent on Azure Virtual Machines and Azure Arc Machines. The script will handle both linux and windows servers with the below features.
19 |
20 | - Update the Azure Monitor Agent to a specific version
21 | - Update the Azure Monitor Agent to the latest version
22 | - Report on current versions without updating
23 |
24 | # Usage
25 |
26 | > Azure Arc does not return a detailed status of the update request
27 | >
28 | > The rest API only allows upgrading to Major+Minor versions. Therefore you can't upgrade from 1.2 to 1.2.2 as an example, only to 1.3
29 |
30 | - The script takes input from an object of machines from either [**Get-AzVM**](https://learn.microsoft.com/powershell/module/az.compute/get-azvm?view) or [**Get-AzConnectedMachine**](https://learn.microsoft.com/powershell/module/az.connectedmachine/get-azconnectedmachine). This will give you the flexibility to scope updates to specific machines.
31 |
32 | - You can specify the versions you want to update to using the **linuxTargetVersion** and **windowsTargetVersion** parameters.
33 | - > To get a list of versions see [Azure Monitor agent extension versions](https://learn.microsoft.com/en-us/azure/azure-monitor/agents/azure-monitor-agent-extension-versions)
34 |
35 | - If you specify the **latestVersion** parameter the script will automatically use the latest version available in the region where the machine resides.
36 |
37 | - If you specify the **report** parameter the script will only report on versions installed and will not update
38 |
39 | # Examples
40 |
41 | ## Azure Virtual Machines
42 |
43 | ### Update Azure Virtual Machines to a specific version
44 | ```
45 | .\update-ama.ps1 -machines $(Get-AzVM) -linuxTargetVersion 1.22.2 -windowsTargetVersion 1.10.0.0
46 | ```
47 |
48 | ### Update Azure Virtual Machines to the latest version of Windows and Linux
49 | ```
50 | .\update-ama.ps1 -machines $(Get-AzVM) -latestVersion
51 | ```
52 |
53 | ### Generate a report of Azure Virtual Machines with current versions
54 | ```
55 | .\update-ama.ps1 -machines $(Get-AzVM) -latestVersion -report
56 | ```
57 |
58 | ## Azure Arc Machines
59 |
60 | ### Update Azure Arc Machines to a specific version
61 | ```
62 | .\update-ama.ps1 -machines $(Get-AzConnectedMachine) -linuxTargetVersion 1.22.2 -windowsTargetVersion 1.10.0.0
63 | ```
64 |
65 | ### Update Azure Arc Machines to the latest version of Windows and Linux
66 | ```
67 | .\update-ama.ps1 -machines $(Get-AzConnectedMachine) -latestVersion
68 | ```
69 |
70 | ### Generate a report of Azure Arc Machines with current versions
71 | ```
72 | .\update-ama.ps1 -machines $(Get-AzConnectedMachine) -latestVersion -report
73 | ```
74 |
--------------------------------------------------------------------------------
/ama-management/update-ama.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update Azure Virtual Machines and Azure Arc Machines to the desired or latest version of the Azure Monitor Agent.
4 |
5 | .PARAMETER machines
6 | Specify an object of machines from Get-AzVM or Get-AzConnectedMachine
7 |
8 | .PARAMETER linuxTargetVersion
9 | Specify the version of the Azure Monitor Agent to ugprade to for Linux. See https://learn.microsoft.com/azure/azure-monitor/agents/azure-monitor-agent-extension-versions
10 |
11 | .PARAMETER windowsTargetVersion
12 | Specify the version of the Azure Monitor Agent to ugprade to for Windows. See https://learn.microsoft.com/azure/azure-monitor/agents/azure-monitor-agent-extension-versions
13 |
14 | .PARAMETER latestVersion
15 | Specify the latestVersion switch to use the latest version available for Linux and Windows. This is unique to each region.
16 |
17 | .PARAMETER report
18 | Specify the report switch to only report on machines with current versions
19 |
20 | .EXAMPLE
21 | Update Azure Virtual Machines to a specific version
22 | .\update-ama.ps1 -machines $(Get-AzVM) -linuxTargetVersion 1.22.2 -windowsTargetVersion 1.10.0.0
23 |
24 | .EXAMPLE
25 | Update Azure Virtual Machines to the latest version of Windows and Linux
26 | .\update-ama.ps1 -machines $(Get-AzVM) -latestVersion
27 |
28 | .EXAMPLE
29 | Generate a report of Azure Virtual Machines with current versions
30 | .\update-ama.ps1 -machines $(Get-AzVM) -latestVersion -report
31 |
32 | .EXAMPLE
33 | Update Azure Arc Machines to a specific version
34 | .\update-ama.ps1 -machines $(Get-AzConnectedMachine) -linuxTargetVersion 1.22.2 -windowsTargetVersion 1.10.0.0
35 |
36 | .EXAMPLE
37 | Update Azure Arc Machines to the latest version of Windows and Linux
38 | .\update-ama.ps1 -machines $(Get-AzConnectedMachine) -latestVersion
39 |
40 | .EXAMPLE
41 | Generate a report of Azure Arc Machines with current versions
42 | .\update-ama.ps1 -machines $(Get-AzConnectedMachine) -latestVersion -report
43 | #>
44 |
45 | param(
46 | [Parameter(Mandatory=$true)]
47 | [object]$machines,
48 |
49 | [Parameter(Mandatory=$true, ParameterSetName = 'TargetVersion')]
50 | [string]$linuxTargetVersion,
51 |
52 | [Parameter(Mandatory=$true, ParameterSetName = 'TargetVersion')]
53 | [string]$windowsTargetVersion,
54 |
55 | [Parameter(Mandatory=$true, ParameterSetName = 'LatestVersion')]
56 | [switch]$latestVersion,
57 |
58 | [Parameter(Mandatory=$false)]
59 | [switch]$report,
60 |
61 | [Parameter(Mandatory=$false)]
62 | [string]$extPublisherName = 'Microsoft.Azure.Monitor',
63 |
64 | [Parameter(Mandatory=$false)]
65 | [string]$windowsExtType = 'AzureMonitorWindowsAgent',
66 |
67 | [Parameter(Mandatory=$false)]
68 | [string]$linuxExtType = 'AzureMonitorLinuxAgent'
69 | )
70 |
71 | $requiredModules = 'Az.Accounts', 'Az.Compute', 'Az.ConnectedMachine'
72 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
73 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
74 | ForEach ($module in $modulesToInstall){
75 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
76 | Install-Module $module -force
77 | }
78 |
79 | # This isn't used for anything other than to query for arc extension versions
80 | $subscriptionId = (Get-AzContext | Select -ExpandProperty Subscription).id
81 |
82 | function Get-latestVersion{
83 | Param($versions)
84 | $latest = $versions | % {[version]$_} | Sort-Object -Descending | Select -First 1
85 | $latest.ToString()
86 | }
87 |
88 | #Get Latest Versions for each machine region
89 | If($latestVersion){
90 |
91 | $regionLatestVersions = @()
92 | $regions = $machines.location | Sort-Object | Get-Unique
93 | ForEach ($region in $regions){
94 | Write-Verbose ('Getting Region Latest Extension Versions for {0}' -f $region )
95 | # Azure Native Virtual Machines
96 | $windowsVersions = Get-AzVMExtensionImage -PublisherName $extPublisherName -Type $windowsExtType -Location $region
97 | $linuxVersions = Get-AzVMExtensionImage -PublisherName $extPublisherName -Type $linuxExtType -Location $region
98 |
99 | # Azure Arc Machines
100 | If($machines[0].Type -like 'Microsoft.HybridCompute/machines'){
101 | Write-Verbose ('Getting Azure Arc Region Latest Extension Versions for {0}' -f $region )
102 | $uri = ('https://management.azure.com/subscriptions/{0}/providers/Microsoft.HybridCompute/locations/{1}/publishers/{2}/extensionTypes/{3}/versions?api-version=2022-12-27-preview' -f $subscriptionId, $region, $extPublisherName, $windowsExtType)
103 | $windowsVersions = (Invoke-AzRestMethod -Uri $uri -Method GET).Content | ConvertFrom-Json | Select -ExpandProperty properties
104 |
105 | $uri = ('https://management.azure.com/subscriptions/{0}/providers/Microsoft.HybridCompute/locations/{1}/publishers/{2}/extensionTypes/{3}/versions?api-version=2022-12-27-preview' -f $subscriptionId, $region, $extPublisherName, $linuxExtType)
106 | $linuxVersions = (Invoke-AzRestMethod -Uri $uri -Method GET).Content | ConvertFrom-Json | Select -ExpandProperty properties
107 | }
108 |
109 | #Update Object
110 | $regionLatestVersions += [PSCustomObject]@{
111 | location = $region
112 | linuxLatestVersion = Get-latestVersion $linuxVersions.Version
113 | windowsLatestVersion = Get-latestVersion $windowsVersions.Version
114 | }
115 | }
116 | }
117 |
118 | $agentsToUpgrade = @()
119 |
120 | Write-Host ('Evaluating {0} machines' -f $machines.Count)
121 |
122 | ForEach ($machine in $machines){
123 | # Cannot trust the OsType is properly detected, check for AzureMonitorWindowsAgent and AzureMonitorLinuxAgent
124 | $agent = $null; $windowsAgent = $null; $linuxAgent = $null; $state= $null
125 |
126 | Write-Verbose ('Evaluating {0}' -f $machine.Name)
127 | # Write-Verbose $machine | out-string #Enable for debug logging
128 |
129 | If($machine.Type -like 'Microsoft.Compute/virtualMachines'){
130 | $state = (($machine | Get-AzVM -Status).statuses | Where Code -like 'PowerState*').DisplayStatus
131 | #Write-Verbose $state
132 | if ($state -like 'VM running'){
133 | Write-Verbose ('Machine State: {0}' -f $state)
134 | $windowsAgent = Get-AzVMExtension -VMName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name 'AzureMonitorWindowsAgent' -ErrorAction SilentlyContinue
135 | $linuxAgent = Get-AzVMExtension -VMName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name 'AzureMonitorLinuxAgent' -ErrorAction SilentlyContinue
136 | }
137 | }
138 | If($machine.Type -like 'Microsoft.HybridCompute/machines'){
139 | $state = $machine.Status
140 | #Write-Verbose $state
141 | if ($state -like 'Connected'){
142 | Write-Verbose ('Machine State: {0}' -f $state)
143 | $windowsAgent = Get-AzConnectedMachineExtension -MachineName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name 'AzureMonitorWindowsAgent' -ErrorAction SilentlyContinue
144 | $linuxAgent = Get-AzConnectedMachineExtension -MachineName $machine.Name -ResourceGroupName $machine.ResourceGroupName -Name 'AzureMonitorLinuxAgent' -ErrorAction SilentlyContinue
145 | }
146 | }
147 |
148 | # If latestVersion is flagged, get the latest published version for the region where the machine resides
149 | If($latestVersion -and ($state -like 'Connected' -or $state -like 'VM running')){
150 | Write-Verbose ('Latest Version Parameter Specified. Getting Latest Version for Region: {0}' -f $machine.Location)
151 | $linuxTargetVersion = $regionLatestVersions | Where-Object {$_.location -like $machine.Location} | Select -ExpandProperty linuxLatestVersion
152 | $windowsTargetVersion = $regionLatestVersions | Where-Object {$_.location -like $machine.Location} | Select -ExpandProperty windowsLatestVersion
153 | Write-Verbose ('Latest Windows Version: {0}, Latest Linux Version: {1}' -f $windowsTargetVersion, $linuxTargetVersion)
154 | }
155 |
156 | # Build Agent Objects
157 | If ($windowsAgent){
158 | $agent = $windowsAgent
159 | $agent | Add-Member -MemberType NoteProperty -Name TargetVersion -Value $windowsTargetVersion -Force
160 | $agent | Add-Member -MemberType NoteProperty -Name extensionTarget -Value $windowsAgent.extensionTarget -Force
161 | }
162 | If ($linuxAgent){
163 | $agent = $linuxAgent
164 | $agent | Add-Member -MemberType NoteProperty -Name TargetVersion -Value $linuxTargetVersion -Force
165 | $agent | Add-Member -MemberType NoteProperty -Name extensionTarget -Value $linuxAgent.extensionTarget -Force
166 | }
167 |
168 | # Add Additional Attributes
169 | If ($agent){
170 | #Fix Target Version, can only be major and minor version
171 | $agent | Add-Member -MemberType NoteProperty -Name TargetMajorMinorVersion -Value ('{0}.{1}'-f ([Version]($agent.TargetVersion)).Major, ([Version]($agent.TargetVersion)).Minor) -Force
172 | $agent | Add-Member -MemberType NoteProperty -Name CurrentVersion -Value $agent.TypeHandlerVersion -Force
173 | $agent | Add-Member -MemberType NoteProperty -Name MachineType -Value $machine.Type -Force
174 | $agent | Add-Member -MemberType NoteProperty -Name MachineState -Value $state -Force
175 | $agent | Add-Member -MemberType NoteProperty -Name MachineName -Value $machine.Name -Force
176 | $agent | Add-Member -MemberType NoteProperty -Name SubscriptionId -Value $machine.id.split('/')[2] -Force
177 | $agentsToUpgrade += $agent
178 |
179 | Write-Verbose ($agent | Select MachineName, SubscriptionId, ResourceGroupName, MachineState, MachineType, Name, CurrentVersion, TargetVersion, TargetMajorMinorVersion, extensionTarget, EnableAutomaticUpgrade, ProvisioningState)
180 | }
181 | }
182 |
183 | If ($report){
184 | Write-Host 'Report only specified'
185 | $agentsToUpgrade | Select MachineName, SubscriptionId, ResourceGroupName, MachineState, MachineType, Name, CurrentVersion, TargetVersion, TargetMajorMinorVersion, extensionTarget, EnableAutomaticUpgrade, ProvisioningState | ft
186 | }else {
187 | #Get only running or connected machines
188 | $agentsToUpgrade = $agentsToUpgrade | Where-Object {$_.MachineState -like 'VM running' -or $_.MachineState -like 'Connected'}
189 | #Get only machines that do not match the target version
190 | $agentsToUpgrade = $agentsToUpgrade | Where-Object {[Version]$_.CurrentVersion -lt [Version]$_.TargetVersion}
191 |
192 | Write-Host ('{0} out of {1} machines to upgrade' -f $agentsToUpgrade.count, $machines.Count)
193 |
194 | ForEach ($agent in $agentsToUpgrade){
195 | If($agent.MachineType -like 'Microsoft.Compute/virtualMachines'){
196 | $uri = ('https://management.azure.com{0}?api-version=2022-11-01' -f $agent.Id)
197 | $method = 'PATCH'
198 | $body = @{
199 | properties = @{
200 | publisher = $agent.Publisher
201 | type = $agent.ExtensionType
202 | typeHandlerVersion = $agent.TargetMajorMinorVersion
203 | }
204 | }
205 | }
206 | If($agent.MachineType -like 'Microsoft.HybridCompute/machines'){
207 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.HybridCompute/machines/{2}/upgradeExtensions?api-version=2022-12-27-preview' -f $agent.SubscriptionId, $agent.ResourceGroupName, $agent.MachineName)
208 | $method = 'POST'
209 | $targetType = ('{0}.{1}' -f $agent.Publisher, $agent.Name)
210 | $body = @{
211 | extensionTargets = @{
212 | "$targetType"= @{
213 | targetVersion = $agent.TargetMajorMinorVersion
214 | }
215 | }
216 | }
217 | }
218 | Write-Host ('Updating {0} from version {1} to latest version: {2}({3}) ' -f $agent.MachineName, $agent.CurrentVersion, $agent.TargetVersion, $agent.TargetMajorMinorVersion)
219 | Write-Verbose $($body | ConvertTo-Json)
220 | Write-Verbose $uri
221 | $request = Invoke-AzRestMethod -Uri $uri -Method $method -Payload $($body | ConvertTo-Json)
222 | $reqContent = $request.Content | ConvertFrom-Json
223 | $request | Add-Member -MemberType NoteProperty -Name provisioningState -Value $reqContent.properties.provisioningState -Force
224 | $request | Add-Member -MemberType NoteProperty -Name publisher -Value $reqContent.properties.publisher -Force
225 | $request | Add-Member -MemberType NoteProperty -Name type -Value $reqContent.properties.type -Force
226 | $request | Add-Member -MemberType NoteProperty -Name typeHandlerVersion -Value $reqContent.properties.typeHandlerVersion -Force
227 | $request | Add-Member -MemberType NoteProperty -Name machineName -Value $agent.MachineName -Force
228 | $request | Select machineName, StatusCode, Method, provisioningState, publisher, type, typeHandlerVersion
229 | }
230 | }
--------------------------------------------------------------------------------
/analytics_rules/create-scheduledRuleFromTemplate.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | You can leverage this script to create multiple scheduled analytics rules from the analytics rules templates on github https://github.com/Azure/Azure-Sentinel/tree/master/Detections.
4 |
5 | Known Limitations
6 | 1. Associated tables in the rule query need to exist first for the rule to be created. Tables are generally created when you start ingesting data.
7 | If the table does not exist the rule creation will fail during the script run
8 | 2. YAML files in the github repo may have incorrect query column to entity mappings defined. The rule creation will fail during the script run.
9 | If you run across either sumbit an issue via github on the YAML file or fork the github repo and submit a pull request - https://github.com/Azure/Azure-Sentinel#contributing
10 | 3. A fair number of rule templates do not have values for required data connectors. Be aware when using the dataconnector filter parameter you may not get a complete list of rules that leverage associated tables
11 |
12 | .PARAMETER subscriptionId
13 | Specify the subscriptionID GUID where your Sentinel Workspace Resides
14 | .PARAMETER resourceGroupName
15 | Specify the Resource Group Name where your Sentinel Workspace Resides
16 | .PARAMETER workspaceName
17 | Specify the Sentinel Workspace Name
18 | .PARAMETER githubToken
19 | Specify the GitHub Access Personal Access Token you created. Refer to the steps in [] to configure this token correctly.
20 | .PARAMETER apiVersion
21 | Optionally you can specify the API version of the Microsoft.SecurityInsights/alertRules endpoint
22 | .PARAMETER name
23 | Optionally you can enter the name of an Analytic Rule to Filter on. This parameter supports the -like operator for filtering
24 | .PARAMETER severity
25 | Optionally you can enter one or more rule severities separated by commas to filter rule templates on
26 | .PARAMETER detectionFolderName
27 | Optionally you can enter one or more child folders under https://github.com/Azure/Azure-Sentinel/tree/master/Detections separated by commas to filter rule templates on
28 | .PARAMETER techniques
29 | Optionally you can enter one or more techniques separated by commas to filter rule templates on
30 | .PARAMETER tactics
31 | Optionally you can enter one or more tactics separated by commas to filter rule templates on
32 | .PARAMETER dataConnector
33 | Optionally you can enter one or more dataConnector names separated by commas to filter rule templates on
34 | .PARAMETER dataType
35 | Optionally you can enter one or more data type names separated by commas to filter rule templates on. Data Type names refer to the table names in Sentinel
36 | .PARAMETER queryFilter
37 | Optionally you can enter one or more strings separated by commas to filter the query in the rule templates on. A common example is to filter based on tables names
38 | .PARAMETER tag
39 | Optionally you can enter one or more tag names separated by commas to filter rule templates on
40 | .PARAMETER enable
41 | Optionally you set the enable parameter to false to create rules but not enable them by default
42 | .PARAMETER reportOnly
43 | Optionally you can specify the reportOnly parameter to only report on what templates will be created
44 | .PARAMETER selectUI
45 | Optionally you can specify the selectUI parameter to show a UI to further select specific rules to import
46 |
47 | .EXAMPLE
48 | Create rules from all templates
49 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd'
50 | .EXAMPLE
51 | Create rules from all templates in a disabled state
52 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd' -enabled $false
53 | .EXAMPLE
54 | Create rules from all templates with "TI map" in the name
55 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd' -name '*TI map*
56 | .EXAMPLE
57 | The below example will open an out-grid UI where you can further select specific rules to import
58 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -selectUI
59 | .EXAMPLE
60 | Filter by detection or solution child folder Name.
61 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2EOUmy4P0Rb3yd' -detectionFolderName 'ASimAuthentication','ASimProcess'
62 | .EXAMPLE
63 | Filter by severity of alert rule templates
64 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -severity 'High','Medium'
65 | .EXAMPLE
66 | Filter by severity and tactic of alert rule templates
67 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -severity 'High','Medium' -tactic 'CredentialAccess'
68 | .EXAMPLE
69 | Run in report only mode
70 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -detectionFolderName 'ASimAuthentication','ASimProcess' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -reportOnly
71 |
72 | $rules | Select name, severity, tactics, techniques, requiredDataConnectors, templateURL
73 | .NOTES
74 | Author: seanstark-ms
75 | Website: https://starkonsec.medium.com/
76 | Link to GitHub Source: https://github.com/seanstark/sentinel-tools/tree/main/analytics_rules
77 | Requires PowerShell Version 7.0 and above
78 | Requires PowerShell Modules: 'PowerShellForGitHub', 'Az.Accounts', 'Az.SecurityInsights', 'powershell-yaml'
79 | #>
80 |
81 | param(
82 | [Parameter(Mandatory=$true)]
83 | [string]$subscriptionId,
84 |
85 | [Parameter(Mandatory=$true)]
86 | [string]$resourceGroupName,
87 |
88 | [Parameter(Mandatory=$true)]
89 | [string]$workspaceName,
90 |
91 | [Parameter(Mandatory=$true,
92 | HelpMessage='Specifiy your GitHub personal access token that has public_repo to the Microsoft Azure Organization')]
93 | [string]$githubToken,
94 |
95 | [Parameter(Mandatory=$false,
96 | HelpMessage='Specifiy the API version of the Microsoft.SecurityInsights/alertRules endpoint')]
97 | [string]$apiVersion = '2021-10-01-preview',
98 |
99 | [Parameter(Mandatory=$false,
100 | HelpMessage='Enter a name to filter on')]
101 | [string]$name,
102 |
103 | [Parameter(Mandatory=$false,
104 | HelpMessage='Enter one or more rule severities separated by commas to filter on')]
105 | [string[]]$severity,
106 |
107 | [Parameter(Mandatory=$false,
108 | HelpMessage='Enter one or more detection child folder names separated by commas to filter on')]
109 | [string[]]$detectionFolderName,
110 |
111 | [Parameter(Mandatory=$false,
112 | HelpMessage='Enter one or more techniques separated by commas to filter on')]
113 | [string[]]$techniques,
114 |
115 | [Parameter(Mandatory=$false,
116 | HelpMessage='Enter one or more tactics separated by commas to filter on')]
117 | [string[]]$tactics,
118 |
119 | [Parameter(Mandatory=$false,
120 | HelpMessage='Enter one or more dataconnector names separated by commas to filter on')]
121 | [string[]]$dataConnector,
122 |
123 | [Parameter(Mandatory=$false,
124 | HelpMessage='Enter one or more dataconnector names separated by commas to filter on')]
125 | [string[]]$dataType,
126 |
127 | [Parameter(Mandatory=$false,
128 | HelpMessage='Enter one or more dataconnector names separated by commas to filter on')]
129 | [string[]]$queryFilter,
130 |
131 | [Parameter(Mandatory=$false,
132 | HelpMessage='Enter one or more tag names separated by commas to filter on')]
133 | [string[]]$tag,
134 |
135 | [Parameter(Mandatory=$false,
136 | HelpMessage='Specify if the rule will be created in an enabled state. By default this is set to true')]
137 | [boolean]$enable = $true,
138 |
139 | [Parameter(Mandatory=$false,
140 | HelpMessage='Specify if you only want to report on alert rule templates that will be enabled')]
141 | [switch]$reportOnly,
142 |
143 | [Parameter(Mandatory=$false,
144 | HelpMessage='Open a UI to further select which Analytics Rules you want to import')]
145 | [switch]$selectUI
146 | )
147 |
148 | #Requires -Version 7.0
149 |
150 | # Check for required modules
151 | $requiredModules = 'PowerShellForGitHub', 'Az.Accounts', 'Az.SecurityInsights', 'powershell-yaml'
152 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
153 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
154 | ForEach ($module in $modulesToInstall){
155 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
156 | Install-Module $module -force
157 | }
158 |
159 | # Sentinel GitHub Repo URI and Analytic Rule Root Path
160 | $sentinelGitHuburi = 'https://github.com/Azure/Azure-Sentinel'
161 | $sentinelGitHubPaths = 'Detections','Solutions'
162 |
163 | #Setup GitHubAuthentication
164 | [pscredential]$gitHubCred = New-Object System.Management.Automation.PSCredential ('dummy', $(ConvertTo-SecureString $githubToken -AsPlainText -Force))
165 | Set-GitHubConfiguration -DisableLogging -DisableTelemetry
166 | Set-GitHubAuthentication -Credential $gitHubCred
167 |
168 | # Auth to Azure
169 | If(!(Get-AzContext)){
170 | Write-Host ('Connecting to Azure Subscription: {0}' -f $subscriptionId) -ForegroundColor Yellow
171 | Connect-AzAccount -Subscription $subscriptionId | Out-Null
172 | }
173 |
174 | #Set context to the subscriptionid
175 | Set-AzContext -Subscription $subscriptionId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null
176 |
177 | #Get the current Bearer Token
178 | $azToken = (Get-AzAccessToken).Token
179 |
180 | # Get all anlaytics rules from github
181 | $detectionFolders = @()
182 | ForEach ($sentinelGitHubPath in $sentinelGitHubPaths){
183 | If ($sentinelGitHubPath -like 'Solutions'){
184 | $solutions = Get-GitHubContent -Uri $sentinelGitHuburi -Path 'Solutions' -MediaType Object | Select -ExpandProperty entries | Where type -like 'dir' | Select name
185 | ForEach ($solution in $solutions.name){
186 | $detectionFolders += Get-GitHubContent -Uri $sentinelGitHuburi -Path "Solutions/$solution" -MediaType Object | Select -ExpandProperty entries | Where-Object {$_.type -like 'dir' -and $_.name -like 'Analytic Rules'} | Select @{Name = 'name'; Expression = {$solution}}, path
187 | }
188 | }else{
189 | $detectionFolders += Get-GitHubContent -Uri $sentinelGitHuburi -Path $sentinelGitHubPath -MediaType Object | Select -ExpandProperty entries | Where type -like 'dir' | Select name, path
190 | }
191 | }
192 |
193 | # Filter on specific detection folders if detectionFolderName parameter is defined
194 | If($detectionFolderName){
195 | $detectionFolders = $detectionFolders | where name -in $detectionFolderName
196 | }
197 |
198 | # Get all created by template analytic rules in Sentinel
199 | $existingRules = Get-AzSentinelAlertRule -resourceGroupName $resourceGroupName -workspaceName $workspaceName | Where-Object kind -like 'Scheduled'
200 |
201 | # Iterate through each detection folder in github and build an array of psobjects of the yaml files
202 | Write-Host 'Iterating through each detection folder to build an index of each analytic rule template. This will take a few minutes..' -ForegroundColor Yellow
203 | $alertRuleTemplates = @()
204 | ForEach ($detectionFolder in $($detectionFolders | Select -ExpandProperty path)){
205 |
206 | $yamlFiles = Get-GitHubContent -Uri $sentinelGitHuburi -Path $detectionFolder -MediaType Object | Select -ExpandProperty entries | Where name -Like '*.yaml'
207 |
208 | ForEach ($yamlFile in $yamlFiles){
209 |
210 | $alertRuleTemplate = ConvertFrom-Yaml (Invoke-RestMethod -uri $yamlFile.download_url)
211 |
212 | If ($alertRuleTemplate.kind -like 'Scheduled'){
213 |
214 | Write-Verbose ('Found Scheduled Rule Template: {0}, adding to index' -f $alertRuleTemplate.name)
215 |
216 | $alertRuleTemplates += ([PSCustomObject]@{
217 | id = $alertRuleTemplate.id
218 | name = $alertRuleTemplate.name
219 | kind = 'Scheduled'
220 | templateURL = $yamlFile.download_url
221 | templateFolder = $detectionFolder
222 | severity = $alertRuleTemplate.severity
223 | requiredDataConnectors = $alertRuleTemplate.requiredDataConnectors.connectorId -join ','
224 | techniques = $alertRuleTemplate.relevantTechniques -join ','
225 | tactics = $alertRuleTemplate.tactics -join ','
226 | properties = ($alertRuleTemplate | Select-object -ExcludeProperty name, id, kind)
227 | })
228 | }
229 | }
230 | }
231 |
232 | # This function is used to dynamically create an inclusive filter set when multiple filter parameters are defined
233 | function check-filterScript {
234 | param(
235 | [string]$filterToAdd
236 | )
237 | if ($filterScript){
238 | $newFilter = "$filterScript -and $filterToAdd"
239 | }else{
240 | $newFilter = $filterToAdd
241 | }
242 | [scriptblock]::Create($newFilter)
243 | }
244 |
245 | # This function is used to compare filter parameters with rule template properties that are an object "List" or Object[] type and contain multiple objects.
246 | # These are present in relevantTechniques, tactics, dataTypes, and requiredDataConnectors properties
247 | function match-Lists {
248 | param(
249 | [string[]]$filterParameter,
250 | $list
251 | )
252 | $matched = $false
253 |
254 | if($list){
255 | $list | ForEach-Object{
256 | if ($_ -in $filterParameter){
257 | $matched = $true
258 | }
259 | }
260 | }
261 |
262 | $matched
263 | }
264 |
265 | # Build dynamic filters on rule severities, relevantTechniques, tactics, and data connectors if parameters are defined
266 | $filterScript = $null
267 | If($severity){
268 | $filterScript = check-filterScript -filterToAdd ('$_.properties.severity -in {0}' -f $severity)
269 | }
270 | If($name){
271 | $filterScript = check-filterScript -filterToAdd ('$_.name -like "{0}"' -f $name)
272 | }
273 | If($techniques){
274 | $filterScript = check-filterScript -filterToAdd ('$(match-Lists -filterParameter $techniques -list $_.properties.relevantTechniques) -eq $true')
275 | }
276 | If($tactics){
277 | $filterScript = check-filterScript -filterToAdd ('$(match-Lists -filterParameter $tactics -list $_.properties.tactics) -eq $true')
278 | }
279 | If($dataConnector){
280 | $filterScript = check-filterScript -filterToAdd ('$(match-Lists -filterParameter $dataConnector -list $_.properties.requiredDataConnectors.connectorId) -eq $true')
281 | }
282 | If($dataType){
283 | $filterScript = check-filterScript -filterToAdd ('$(match-Lists -filterParameter $dataType -list $_.properties.requiredDataConnectors.dataTypes) -eq $true')
284 | }
285 | If($queryFilter){
286 | $filterScript = check-filterScript -filterToAdd ('$($_.properties.query | Select-String $queryFilter) -ne $null')
287 | }
288 | If($tag){
289 | $filterScript = check-filterScript -filterToAdd ('$(match-Lists -filterParameter $tag -list $_.properties.tags) -eq $true')
290 | }
291 |
292 | #Filter templates based on defined filter parameters
293 | If ($filterScript){
294 | Write-Verbose ('Filters that will be applied: {0}' -f $filterScript)
295 | $alertRuleTemplates = $alertRuleTemplates | where -FilterScript $filterScript
296 | }
297 |
298 | Write-Host ('Found a total of {0} Rule Templates from GitHub' -f $alertRuleTemplates.count) -ForegroundColor Cyan
299 |
300 | # Find which rules need to be created that don't already exist
301 | $rulesToCreate = $alertRuleTemplates | Where-object {$_.id -notin $existingRules.AlertRuleTemplateName -and $_.name -notin $existingRules.DisplayName}
302 |
303 | #Open the selection UI to further select specific rules to import
304 | if ($selectUI){
305 | Write-Host ('Opening UI for Rule Selection' -f $rulesToCreate.count) -ForegroundColor Cyan
306 | $rulesToCreate = $rulesToCreate | Out-GridView -PassThru -Title 'Select Rules to be Imported (For Multi-Select use CTRL)' | Select *
307 | }
308 |
309 | Write-Host ('Found a total of {0} Rule Templates to Enable' -f $rulesToCreate.count) -ForegroundColor Cyan
310 |
311 | If ($reportOnly){
312 | Write-Host 'Report Only Mode: Outputing rules that will be created' -ForegroundColor Yellow
313 | $rulesToCreate
314 | }
315 |
316 | #Updated Yaml Trigger Operators Mapping
317 | $triggerOperators = @{
318 | gt = 'GreaterThan'
319 | eq = 'Equal'
320 | lt = 'LessThan'
321 | ne = 'NotEqual'
322 | }
323 |
324 | #ISO 8601 Time Converstion Function
325 | Function ConvertFrom-YAMLTimeFormat {
326 | Param (
327 | [Parameter(Mandatory = $true)]
328 | [string]$timespan
329 | )
330 |
331 | if($timespan.contains("d"))
332 | {
333 | $result = "P$timespan".ToUpper()
334 | }
335 | if($timespan.contains("h"))
336 | {
337 | $result = "PT$timespan".ToUpper()
338 | }
339 | if($timespan.contains("m"))
340 | {
341 | $result = "PT$timespan".ToUpper()
342 | }
343 | $result
344 | }
345 |
346 | If ($reportOnly -eq $false){
347 |
348 | # Define Authorization headers for the Microsoft.SecurityInsights/alertRules API
349 | $headers = @{
350 | Authorization="Bearer $azToken"
351 | }
352 |
353 | # Create each rule from the template
354 | ForEach ($rule in $rulesToCreate){
355 |
356 | # Build an updated object for each yaml file to interact properly with the Microsoft.SecurityInsights/alertRules API endpoint.
357 | $newRuleGuid = (New-Guid).Guid
358 | $rule | Add-Member -NotePropertyName 'type' -NotePropertyValue 'Microsoft.SecurityInsights/alertRules' -Force
359 | $rule.properties | Add-Member -NotePropertyName 'templateVersion' -NotePropertyValue $rule.properties.version -Force
360 | $rule.properties| Add-Member -NotePropertyName 'alertRuleTemplateName' -NotePropertyValue $rule.id -Force
361 | $rule.properties| Add-Member -NotePropertyName 'suppressionDuration' -NotePropertyValue 'PT5H' -Force
362 | $rule.properties| Add-Member -NotePropertyName 'suppressionEnabled' -NotePropertyValue $false -Force
363 | $rule.properties| Add-Member -NotePropertyName 'displayName' -NotePropertyValue $rule.name -Force
364 | $rule.properties| Add-Member -NotePropertyName 'enabled' -NotePropertyValue $enable -Force
365 | $rule.properties.triggerOperator = $triggerOperators[$rule.properties.triggerOperator]
366 | $rule.properties.queryFrequency = ConvertFrom-YAMLTimeFormat $rule.properties.queryFrequency
367 | $rule.properties.queryPeriod = ConvertFrom-YAMLTimeFormat $rule.properties.queryPeriod
368 | $rule.name = $newRuleGuid
369 | $rule.id = "/subscriptions/$($subscriptionId)/resourceGroups/$($resourceGroupName)/providers/Microsoft.OperationalInsights/workspaces/$($workspaceName)/providers/Microsoft.SecurityInsights/alertRules/$($newRuleGuid)"
370 |
371 | Write-Host "Attempting to Create Rule: $($rule.properties.displayName)" -ForegroundColor Yellow
372 |
373 | # Create an Analytic Rule from the template using the rest API
374 | try{
375 | $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/resourceGroups/$($resourceGroupName)/providers/Microsoft.OperationalInsights/workspaces/$($workspaceName)/providers/Microsoft.SecurityInsights/alertRules/$($newRuleGuid)?api-version=$($apiVersion)"
376 | $newRule = Invoke-RestMethod -Uri $uri -Headers $headers -Method Put -Body $($rule | ConvertTo-Json -Depth 5) -ContentType 'application/json'
377 | }catch{
378 | $outputErrorMessage = ($PSItem.ErrorDetails | ConvertFrom-Json).error.message
379 | $outputErrorCode = ($PSItem.ErrorDetails | ConvertFrom-Json).error.code
380 | Write-Host "Error Creating Rule: $outputErrorMessage" -ForegroundColor Red
381 | }finally{
382 | $outputObject = New-Object PSObject -Property @{
383 | ruleName = $rule.properties.displayName
384 | ruleid = $newRule.name
385 | ruletype = $newRule.kind
386 | created = If($outputErrorCode -or $outputErrorMessage){$false}else{$true}
387 | errorCode = $outputErrorCode
388 | errorMessage = $outputErrorMessage
389 | properties = ($newRule | Select-object -ExcludeProperty name, id, kind)
390 | }
391 | }
392 | $outputObject
393 | #Cleanup rule and error variables
394 | Clear-Variable outputErrorMessage, outputErrorCode, outputObject -ErrorAction SilentlyContinue
395 | $error.Clear()
396 | }
397 | }
--------------------------------------------------------------------------------
/analytics_rules/export-analyticRuleTemplates.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | You can leverage this script to create export scheduled analytics rules from the analytics rules templates on github https://github.com/Azure/Azure-Sentinel
4 |
5 | .PARAMETER githubToken
6 | Specify the GitHub Access Personal Access Token you created. public_repo access required
7 |
8 | .EXAMPLE
9 | Create rules from all templates
10 | $rules = .\export-analyticRuleTemplates.ps1 -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd'
11 |
12 | .NOTES
13 | Author: seanstark-ms
14 | Link to GitHub Source: https://github.com/seanstark/sentinel-tools/tree/main/analytics_rules
15 | Requires PowerShell Version 7.0 and above
16 | Requires PowerShell Modules: 'PowerShellForGitHub', 'powershell-yaml'
17 | #>
18 |
19 | param(
20 | [Parameter(Mandatory=$true,
21 | HelpMessage='Specifiy your GitHub personal access token that has public_repo to the Microsoft Azure Organization')]
22 | [string]$githubToken
23 | )
24 |
25 | #Requires -Version 7.0
26 |
27 | # Check for required modules
28 | $requiredModules = 'PowerShellForGitHub', 'powershell-yaml'
29 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
30 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
31 | ForEach ($module in $modulesToInstall){
32 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
33 | Install-Module $module -force
34 | }
35 |
36 | # Sentinel GitHub Repo URI and Analytic Rule Root Path
37 | $sentinelGitHuburi = 'https://github.com/Azure/Azure-Sentinel'
38 | $sentinelGitHubPaths = 'Detections','Solutions'
39 |
40 | #Setup GitHubAuthentication
41 | [pscredential]$gitHubCred = New-Object System.Management.Automation.PSCredential ('dummy', $(ConvertTo-SecureString $githubToken -AsPlainText -Force))
42 | Set-GitHubConfiguration -DisableLogging -DisableTelemetry
43 | Set-GitHubAuthentication -Credential $gitHubCred
44 |
45 | # Get all anlaytics rules from github
46 | Write-Host 'Iterating through each detection folders to build directory structure. This will take a few minutes..' -ForegroundColor Yellow
47 | $detectionFolders = @()
48 | ForEach ($sentinelGitHubPath in $sentinelGitHubPaths){
49 | If ($sentinelGitHubPath -like 'Solutions'){
50 | $solutions = Get-GitHubContent -Uri $sentinelGitHuburi -Path 'Solutions' -MediaType Object | Select -ExpandProperty entries | Where type -like 'dir' | Select name
51 | ForEach ($solution in $solutions.name){
52 | $detectionFolders += Get-GitHubContent -Uri $sentinelGitHuburi -Path "Solutions/$solution" -MediaType Object | Select -ExpandProperty entries | Where-Object {$_.type -like 'dir' -and $_.name -like 'Analytic Rules'} | Select @{Name = 'name'; Expression = {$solution}}, path
53 | }
54 | }else{
55 | $detectionFolders += Get-GitHubContent -Uri $sentinelGitHuburi -Path $sentinelGitHubPath -MediaType Object | Select -ExpandProperty entries | Where type -like 'dir' | Select name, path
56 | }
57 | }
58 |
59 | # Filter on specific detection folders if detectionFolderName parameter is defined
60 | If($detectionFolderName){
61 | $detectionFolders = $detectionFolders | where name -in $detectionFolderName
62 | }
63 |
64 | # Iterate through each detection folder in github and build an array of psobjects of the yaml files
65 | Write-Host 'Iterating through each detection folder to build an index of each analytic rule template. This will take a few minutes..' -ForegroundColor Yellow
66 | $alertRuleTemplates = @()
67 | ForEach ($detectionFolder in $($detectionFolders | Select -ExpandProperty path)){
68 |
69 | $yamlFiles = Get-GitHubContent -Uri $sentinelGitHuburi -Path $detectionFolder -MediaType Object | Select -ExpandProperty entries | Where name -Like '*.yaml'
70 |
71 | ForEach ($yamlFile in $yamlFiles){
72 |
73 | $alertRuleTemplate = ConvertFrom-Yaml (Invoke-RestMethod -uri $yamlFile.download_url)
74 |
75 | If ($alertRuleTemplate.kind -like 'Scheduled'){
76 |
77 | Write-Verbose ('Found Scheduled Rule Template: {0}, adding to index' -f $alertRuleTemplate.name)
78 |
79 | $alertRuleTemplates += ([PSCustomObject]@{
80 | id = $alertRuleTemplate.id
81 | name = $alertRuleTemplate.name
82 | description = $alertRuleTemplate.description
83 | kind = 'Scheduled'
84 | templateURL = $yamlFile.download_url
85 | templateFolder = $detectionFolder
86 | severity = $alertRuleTemplate.severity
87 | requiredDataConnectors = $alertRuleTemplate.requiredDataConnectors.connectorId -join ',' | out-string
88 | techniques = $alertRuleTemplate.relevantTechniques -join ',' | out-string
89 | tactics = $alertRuleTemplate.tactics -join ',' | out-string
90 | query = $alertRuleTemplate.query
91 | entityMappings = $alertRuleTemplate.entityMappings | ConvertTo-Json -Depth 4 | out-string
92 | })
93 | }
94 | }
95 | }
96 | Write-Host "Found $($alertRuleTemplates.count) analytic rules" -ForegroundColor Yellow
97 | $alertRuleTemplates
98 |
--------------------------------------------------------------------------------
/analytics_rules/readme.md:
--------------------------------------------------------------------------------
1 | # Exporting Scheduled Analytics Rules From Templates
2 | You can export all rules from from the [Sentinel Github rule template repository](https://github.com/Azure/Azure-Sentinel) using the [export-analyticRuleTemplates.ps1](/analytics_rules/export-analyticRuleTemplates.ps1) script
3 |
4 | - [Configuration Requirements](#configuration-requirements)
5 | * [Github Personal Access Token](#github-personal-access-token)
6 | - [Example](#example)
7 |
8 | ## Example
9 | ```powershell
10 | $rules = .\export-analyticRuleTemplates.ps1 -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd'
11 |
12 | $rules | Export-Csv C:\analyticrules.csv
13 |
14 | ```
15 |
16 | # Creating Scheduled Analytics Rules From Templates
17 |
18 | - [Overview](#overview)
19 | - [Features](#features)
20 | - [Known Limitations](#known-limitations)
21 | - [Configuration Requirements](#configuration-requirements)
22 | * [Github Personal Access Token](#github-personal-access-token)
23 | * [Required PowerShell Modules](#required-powershell-modules)
24 | * [Required Sentinel Roles](#required-sentinel-roles)
25 | - [Running the Script](#running-the-script)
26 | + [Create rules from all templates](#create-rules-from-all-templates)
27 | + [Create rules from all templates in a disabled state](#create-rules-from-all-templates-in-a-disabled-state)
28 | + [Create rules from all templates with "TI map" in the name](#create-rules-from-all-templates-with-"ti-map"-in-the-name)
29 | + [Open a UI where you can further select specific rules to import](#open-a-ui-where-you-can-further-select-specific-rules-to-import)
30 | + [Run in report only mode](#run-in-report-only-mode)
31 | + [Filter by detection child folder name](#filter-by-detection-child-folder-name)
32 | + [Filter by severity of alert rule templates](#filter-by-severity-of-alert-rule-templates)
33 | + [Filter by severity and tactics of alert rule templates](#filter-by-severity-and-tactics-of-alert-rule-templates)
34 | + [Filter by tags](#filter-by-tags)
35 |
36 | ## Overview
37 | [**create-scheduledRuleFromTemplate.ps1**](/analytics_rules/create-scheduledRuleFromTemplate.ps1) is a PowerShell script you can leverage to import (create) multiple scheduled analytics rules from the [Sentinel Github rule template repository](https://github.com/Azure/Azure-Sentinel/tree/master/Detections) and from Analytics rules in the [Solutions folder](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions)
38 |
39 | This script was written to account for current limitations when leveraging the **AzSentinel** or **Az.SecurityInsights** PowerShell modules. Most of which are related to an incomplete set of properties being returned such as tactics and techniques from the API endpoints.
40 |
41 | ## Features
42 |
43 | - Create multiple scheduled analytics rules from rule templates
44 | - Filter rule templates on name, severity, tactics, techniques, tags, datatypes, queries, and data connectors
45 | - Run in report only mode to output templates based on the filters you defined
46 | - Create rules from templates in an enabled or disabled state
47 | - Open an out-gridview UI to further select specific rules to import
48 |
49 | ## Known Limitations
50 |
51 | - Associated tables in the rule query need to exist first for the rule to be created. Tables are generally created when you start ingesting data. If the table does not exist the rule creation will fail during the script run
52 | - YAML files in the github repo may have incorrect query column to entity mappings defined. The rule creation will fail during the script run. If you run across either submit an issue via github on the YAML file or fork the github repo and submit a pull request - https://github.com/Azure/Azure-Sentinel#contributing
53 | - A fair number of rule templates do not have values for required data connectors. Be aware when using the dataconnector filter parameter you may not get a complete list of rules that leverage associated tables
54 | - YAML file definitions continue to evolve, new attributes such as tags do not persist across all rule templates. Be aware when using some of these filters you may not get an accurate result
55 |
56 | ## Configuration Requirements
57 |
58 | ### Github Personal Access Token
59 | You will need to setup a GitHub **personal access token** in order for the PowerShell script to gather the rule template details. This is required to avoid GitHub API limits.
60 |
61 | 1. Navigate to https://github.com/settings/tokens/new
62 | 2. Generate a new token with the public_repo scope
63 | 3. I would also recommend setting the expiration to 7 days
64 | 4. Copy the generated token value for use the **-githubToken** parameter
65 |
66 | > 
67 |
68 |
69 | Depending on your organizaiton affiliation you may need to authorize the token for use with SSO to the Azure organization
70 |
71 | 
72 |
73 | ### Required PowerShell Modules
74 | The script will check and install any missing modules. For reference the below is required
75 | - PowerShellForGitHub
76 | - Az.Accounts
77 | - Az.SecurityInsights
78 | - powershell-yaml
79 |
80 | ### Required Sentinel Roles
81 | - Microsoft Sentinel Contributor
82 |
83 | ## Running the Script
84 | Below are some examples on running the script. In the examples below the script output is assigned to a variable $rules.
85 | I would recommend assigning the script output to a variable to easily review the results as some rule creations may fail.
86 |
87 | ```powershell
88 | $rules | Where created -eq $false | Select ruleName, created, errorCode, errorMessage
89 |
90 | $rules | Where created -eq $true
91 |
92 | ```
93 |
94 | > Rules will be created in an **enabled** state by default
95 |
96 | > Note: `-githubToken` example is not a valid token
97 |
98 | ### Create rules from all templates
99 | ```powershell
100 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd'
101 | ```
102 | ### Create rules from all templates in a disabled state
103 | ```powershell
104 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd' -enabled $false
105 | ```
106 | ### Create rules from all templates with "TI map" in the name
107 | ```powershell
108 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFB2pTrEEOUmy4P0Rb3yd' -name '*TI map*
109 | ```
110 | ### Open a UI where you can further select specific rules to import
111 | The below example will open an out-grid UI where you can further select specific rules to import
112 | ```powershell
113 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -selectUI
114 | ```
115 | ### Run in report only mode
116 | ```powershell
117 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -reportOnly
118 |
119 | $rules | Select name, severity, tactics, techniques, requiredDataConnectors, templateURL
120 | ```
121 | ### Filter by detection or solution child folder Name
122 | ```powershell
123 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2EOUmy4P0Rb3yd' -detectionFolderName 'ASimAuthentication','ASimProcess'
124 | ```
125 | ### Filter by severity of alert rule templates
126 | ```powershell
127 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2EOUmy4P0Rb3yd' -detectionFolderName 'ASimAuthentication','ASimProcess'
128 | ```
129 | ### Filter by severity and tactics of alert rule templates
130 | ```powershell
131 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -severity 'High','Medium'
132 | ```
133 | ### Filter by tags
134 | The below example returns all templates tagged with Log4j
135 | ```powershell
136 | $rules = .\create-scheduledRuleFromTemplate.ps1 -subscriptionId 'ada06e68-375e-4564-be3a-c6cacebf41c5' -resourceGroupName 'sentinel-prd' -workspaceName 'sentinel-prd' -githubToken 'ghp_ECgzFoyPsbSKrFoK5B2pOUmy4P0Rb3yd' -tag 'Log4j'
137 | ```
138 |
--------------------------------------------------------------------------------
/data_collection_rules/logstash-syslog-dcr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "dataCollectionRuleName": {
6 | "type": "String",
7 | "defaultValue": "logstash-sentinel",
8 | "metadata": {
9 | "description": "Specify the name of the Data Collection Rule to create."
10 | }
11 | },
12 | "location": {
13 | "defaultValue": "",
14 | "type": "String",
15 | "metadata": {
16 | "description": "Specify the location in which to create the Data Collection Rule."
17 | }
18 | },
19 | "workspaceResourceId": {
20 | "type": "String",
21 | "metadata": {
22 | "description": "Specify the Azure resource ID of the Log Analytics workspace to use."
23 | }
24 | },
25 | "dataCollectionEndpointResourceId": {
26 | "type": "String",
27 | "metadata": {
28 | "description": "Specify the Azure resource ID of the Data Collection Endpoint to use."
29 | }
30 | },
31 | "transformKql": {
32 | "defaultValue": "source | project TimeGenerated = ls_timestamp, HostName = hostname, Facility = facility, SeverityLevel = severity, SyslogMessage = message, ProcessID = pid, ProcessName = process, Computer = hostname, HostIP = ip, EventTime = timestamp, SourceSystem = service",
33 | "type": "String",
34 | "metadata": {
35 | "description": "The KQL statement to transform the data on ingest. The TimeGenerated datetime field is required"
36 | }
37 | }
38 | },
39 | "resources": [
40 | {
41 | "type": "Microsoft.Insights/dataCollectionRules",
42 | "apiVersion": "2021-09-01-preview",
43 | "name": "[parameters('dataCollectionRuleName')]",
44 | "location": "[parameters('location')]",
45 | "properties": {
46 | "dataCollectionEndpointId": "[parameters('dataCollectionEndpointResourceId')]",
47 | "streamDeclarations": {
48 | "Custom-Microsoft-Syslog": {
49 | "columns": [
50 | {
51 | "name": "ls_timestamp",
52 | "type": "datetime"
53 | },
54 | {
55 | "name": "hostname",
56 | "type": "string"
57 | },
58 | {
59 | "name": "facility",
60 | "type": "string"
61 | },
62 | {
63 | "name": "severity",
64 | "type": "string"
65 | },
66 | {
67 | "name": "message",
68 | "type": "string"
69 | },
70 | {
71 | "name": "pid",
72 | "type": "int"
73 | },
74 | {
75 | "name": "process",
76 | "type": "string"
77 | },
78 | {
79 | "name": "ip",
80 | "type": "string"
81 | },
82 | {
83 | "name": "timestamp",
84 | "type": "datetime"
85 | },
86 | {
87 | "name": "service",
88 | "type": "string"
89 | }
90 | ]
91 | }
92 | },
93 | "destinations": {
94 | "logAnalytics": [
95 | {
96 | "workspaceResourceId": "[parameters('workspaceResourceId')]",
97 | "name": "Custom-Microsoft-Syslog-Workspace"
98 | }
99 | ]
100 | },
101 | "dataFlows": [
102 | {
103 | "streams": [
104 | "Custom-Microsoft-Syslog"
105 | ],
106 | "destinations": [
107 | "Custom-Microsoft-Syslog-Workspace"
108 | ],
109 | "transformKql": "[parameters('transformKql')]",
110 | "outputStream": "Microsoft-Syslog"
111 | }
112 | ]
113 | }
114 | }
115 | ],
116 | "outputs": {
117 | "dataCollectionRuleId": {
118 | "type": "String",
119 | "value": "[resourceId('Microsoft.Insights/dataCollectionRules', parameters('dataCollectionRuleName'))]"
120 | }
121 | }
122 | }
--------------------------------------------------------------------------------
/data_collection_rules/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Overview
3 | A breif intro to the various tools that reside here.
4 |
5 | > Are you looking for simplier Azure Policies for assigning data collection rules without all the image, region, and publisher policy rules?
6 | Check out [my policy repo](https://github.com/seanstark/Azure-Policy/tree/main/policyDefinitions/monitoring)
7 |
8 | - [update-dcrdatastream](#update-dcrdatastream)
9 |
10 | ## update-dcrdatastream
11 | This script will update a data collection rule to send events to the SecurityEvents Table.
12 | This was created since the Azure Monitor UI in the Azure Portal currently does not support this for windows based event collection.
13 | > Technically this can be used for any use case in updating the data flows (destination tables) with a DCR.
14 | You just need to specify the -currentDataStream and -newDataStream parameters. (These are not table names, but refer to data streams names)
15 |
16 | ### Usage
17 | 1. Create a data collection rule via Azure Monitor for Windows Events
18 | * https://learn.microsoft.com/en-us/azure/azure-monitor/agents/data-collection-rule-azure-monitor-agent?tabs=portal
19 |
20 | 2. Run the script against the the applicable rule like below
21 |
22 | ```
23 | .\update-dcrdatastream.ps1 -subscriptionId ada06e68-375e-4210-be3a-c6cacebf41c5 `
24 | -resourceGroup sentinel-dcrs -ruleName windows-security-events
25 | ```
26 | 
27 |
28 | 3. You can verify the DCR was modified by checking the output of the script. You should see a StatusCode of 200 and the streams updated like below.
29 |
30 | 
31 |
32 | 4. You can also verify the DCR was updated by checking the data collection rule stream via the Azure Portal
33 | 1. Navigate to [Data Collection Rules](https://portal.azure.com/#view/Microsoft_Azure_Monitoring/AzureMonitoringBrowseBlade/~/dataCollectionRules) in the Azure Portal under Azure Monitor
34 | 2. Select the Data Collection Rule you just updated
35 |
36 | 
37 |
38 | 3. Select JSON View and verify the stream(s) have been updated
39 |
40 | 
41 |
--------------------------------------------------------------------------------
/data_collection_rules/update-dcrDataCollectionEndpoint.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update a data collection rule with an associated data collection endpoint
4 |
5 | .PARAMETER subscriptionId
6 | Specify the subscriptionID GUID where your data collection rule resides
7 |
8 | .PARAMETER resourceGroup
9 | Specify the Resource Group Name where your data collection rule resides
10 |
11 | .PARAMETER ruleName
12 | Specify the data collection rule name
13 |
14 | .PARAMETER apiVersion
15 | Optionally you can specify the api version to use for Microsoft.Insights/dataCollectionRules
16 |
17 | .PARAMETER dataCollectionEndpointId
18 | The full Resource ID of the data collection endpoint
19 |
20 | .EXAMPLE
21 | .\update-dcrDataCollectionEndpoint.ps1 -subscriptionId 'ada078449-375e-4210-be3a-c6cacebf41c5' -resourceGroup 'sentinel-dcrs' -ruleName 'windows-events' -dataCollectionEndpointId '/subscriptions/ada111e68-375e-4330-be3a-c6caddbf41c5/resourceGroups/data-collection-end/providers/Microsoft.Insights/dataCollectionEndpoints/dce-customlog-westus2'
22 | #>
23 |
24 | param(
25 | [Parameter(Mandatory=$true)]
26 | [string]$subscriptionId,
27 |
28 | [Parameter(Mandatory=$true)]
29 | [string]$resourceGroup,
30 |
31 | [Parameter(Mandatory=$true)]
32 | [string]$ruleName,
33 |
34 | [Parameter(Mandatory=$false)]
35 | [string]$apiVersion = '2022-06-01',
36 |
37 | [Parameter(Mandatory=$true)]
38 | [string]$dataCollectionEndpointId
39 | )
40 |
41 | $requiredModules = 'Az.Accounts'
42 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
43 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
44 | ForEach ($module in $modulesToInstall){
45 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
46 | Install-Module $module -force
47 | }
48 |
49 | If(!(Get-AzContext)){
50 | Write-Host ('Connecting to Azure Subscription: {0}' -f $subscriptionId) -ForegroundColor Yellow
51 | Connect-AzAccount -Subscription $subscriptionId | Out-Null
52 | }
53 |
54 | #Get Data Collection Rule
55 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/dataCollectionRules/{2}?api-version={3}' -f $subscriptionId, $resourceGroup, $ruleName, $apiVersion)
56 | $dcr = (Invoke-AzRestMethod -Uri $uri).content | ConvertFrom-Json -Depth 20
57 |
58 | #Update the data collection endpoint
59 | If ($dcr.properties.dataCollectionEndpointId){
60 | $dcr.properties.dataCollectionEndpointId = $dataCollectionEndpointId
61 | }else{
62 | $dcr.properties | Add-Member -MemberType NoteProperty -Name 'dataCollectionEndpointId' -Value $dataCollectionEndpointId -Force
63 | }
64 |
65 | $newDCR = $dcr | ConvertTo-Json -Depth 20
66 |
67 | # Update the DCR
68 | Invoke-AzRestMethod -Uri $uri -Method PUT -Payload $newDCR
69 |
--------------------------------------------------------------------------------
/data_collection_rules/update-dcrStreamColumns.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update a data collection rule stream declaration columns
4 |
5 | .PARAMETER subscriptionId
6 | Specify the subscriptionID GUID where your data collection rule resides
7 |
8 | .PARAMETER resourceGroup
9 | Specify the Resource Group Name where your data collection rule resides
10 |
11 | .PARAMETER ruleName
12 | Specify the data collection rule name
13 |
14 | .PARAMETER apiVersion
15 | Optionally you can specify the api version to use for Microsoft.Insights/dataCollectionRules
16 |
17 | .PARAMETER streamName
18 | The stream declaration name to update.
19 |
20 | .PARAMETER columnsToAdd
21 | Specify an array of columns to add to the stream. Column names cannot contain spaces. This needs to be in a json formated list, example: '{"name": "Test1", "type": "string"}', '{"name": "Test2", "type": "string"}'
22 |
23 | .PARAMETER columnsToRemove
24 | Specify an array of columns to remove from the stream. This needs to be a list of names, example: 'Message', 'Host'
25 |
26 | .EXAMPLE
27 | .\update-dcrStreamColumns.ps1 -subscriptionId 'ada078449-375e-4210-be3a-c6cacebf41c5' -resourceGroup 'sentinel-dcrs' -ruleName 'windows-events' -columnsToAdd '{"name": "Test1", "type": "string"}', '{"name": "Test2", "type": "string"}'
28 |
29 | .EXAMPLE
30 | .\update-dcrStreamColumns.ps1 -subscriptionId 'ada078449-375e-4210-be3a-c6cacebf41c5' -resourceGroup 'sentinel-dcrs' -ruleName 'windows-events' -columnsToRemove 'Message', 'Host'
31 | #>
32 |
33 | param(
34 | [Parameter(Mandatory=$true)]
35 | [string]$subscriptionId,
36 |
37 | [Parameter(Mandatory=$true)]
38 | [string]$resourceGroup,
39 |
40 | [Parameter(Mandatory=$true)]
41 | [string]$ruleName,
42 |
43 | [Parameter(Mandatory=$true)]
44 | [string]$streamName,
45 |
46 | [Parameter(Mandatory=$false)]
47 | [string[]]$columnsToAdd,
48 |
49 | [Parameter(Mandatory=$false)]
50 | [string[]]$columnsToRemove,
51 |
52 | [Parameter(Mandatory=$false)]
53 | [string]$apiVersion = '2022-06-01'
54 | )
55 |
56 | $requiredModules = 'Az.Accounts'
57 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
58 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
59 | ForEach ($module in $modulesToInstall){
60 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
61 | Install-Module $module -force
62 | }
63 |
64 | If(!(Get-AzContext)){
65 | Write-Host ('Connecting to Azure Subscription: {0}' -f $subscriptionId) -ForegroundColor Yellow
66 | Connect-AzAccount -Subscription $subscriptionId | Out-Null
67 | }
68 |
69 | #Get Data Collection Rule
70 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/dataCollectionRules/{2}?api-version={3}' -f $subscriptionId, $resourceGroup, $ruleName, $apiVersion)
71 | $dcr = (Invoke-AzRestMethod -Uri $uri).content | ConvertFrom-Json -Depth 20
72 |
73 | #Update the data collection endpoint
74 | If (!($dcr.properties.streamDeclarations."$streamName")){
75 | $json = '{"streamDeclarations": {"' + $streamName + '": {"columns": []}}}'
76 | $dcr.properties = @($dcr.properties) + $($json | ConvertFrom-Json)
77 | }
78 |
79 | If ($dcr.properties.streamDeclarations."$streamName"){
80 | #Columns to Add
81 | If ($columnsToAdd){
82 | ForEach ($columnToAdd in $columnsToAdd){
83 | $dcr.properties.streamDeclarations."$streamName".columns = @($dcr.properties.streamDeclarations."$streamName".columns) + $($columnToAdd | ConvertFrom-Json)
84 | }
85 | }
86 | #Columns to Remove
87 | If ($columnsToRemove) {
88 | Write-Host 'Removing'
89 | $dcr.properties.streamDeclarations."$streamName".columns = @($dcr.properties.streamDeclarations."$streamName".columns | where name -NotIn $columnsToRemove)
90 | }
91 | }
92 |
93 | $newDCR = $dcr | ConvertTo-Json -Depth 20
94 |
95 | # Update the DCR
96 | Invoke-AzRestMethod -Uri $uri -Method PUT -Payload $dcr
97 |
98 |
99 | $dcr = '
100 | {
101 | "properties": {
102 | "immutableId": "dcr-b995a3a48f34461b9255238f4b1f628a",
103 | "dataCollectionEndpointId": "/subscriptions/ada06e68-375e-4210-be3a-c6cacebf41c5/resourceGroups/data-collection-end/providers/Microsoft.Insights/dataCollectionEndpoints/dce-customlog-westus3",
104 | "streamDeclarations": {
105 | "Custom-Microsoft-Syslog": {
106 | "columns": [
107 | {
108 | "name": "ls_timestamp",
109 | "type": "datetime"
110 | },
111 | {
112 | "name": "hostname",
113 | "type": "string"
114 | },
115 | {
116 | "name": "facility",
117 | "type": "string"
118 | },
119 | {
120 | "name": "severity",
121 | "type": "string"
122 | },
123 | {
124 | "name": "message",
125 | "type": "string"
126 | },
127 | {
128 | "name": "pid",
129 | "type": "int"
130 | },
131 | {
132 | "name": "process_name",
133 | "type": "string"
134 | },
135 | {
136 | "name": "ip",
137 | "type": "string"
138 | },
139 | {
140 | "name": "timestamp",
141 | "type": "datetime"
142 | },
143 | {
144 | "name": "service",
145 | "type": "string"
146 | }
147 | ]
148 | }
149 | },
150 | "destinations": {
151 | "logAnalytics": [
152 | {
153 | "workspaceResourceId": "/subscriptions/ada06e68-375e-4210-be3a-c6cacebf41c5/resourcegroups/sentinel-prd/providers/microsoft.operationalinsights/workspaces/test-new",
154 | "workspaceId": "488aedbf-638c-4844-aeaa-888b16f278db",
155 | "name": "Custom-Microsoft-Syslog-Workspace"
156 | }
157 | ]
158 | },
159 | "dataFlows": [
160 | {
161 | "streams": [
162 | "Custom-Microsoft-Syslog"
163 | ],
164 | "destinations": [
165 | "Custom-Microsoft-Syslog-Workspace"
166 | ],
167 | "transformKql": "source | project TimeGenerated = ls_timestamp, HostName = hostname, Facility = facility, SeverityLevel = severity, SyslogMessage = message, ProcessID = pid, ProcessName = process_name, Computer = hostname, HostIP = ip, EventTime = timestamp, SourceSystem = service",
168 | "outputStream": "Microsoft-Syslog"
169 | }
170 | ],
171 | "provisioningState": "Succeeded"
172 | },
173 | "location": "westus3",
174 | "id": "/subscriptions/ada06e68-375e-4210-be3a-c6cacebf41c5/resourceGroups/sentinel-dcrs/providers/Microsoft.Insights/dataCollectionRules/logstash-sentinel-redux",
175 | "name": "logstash-sentinel-redux",
176 | "type": "Microsoft.Insights/dataCollectionRules",
177 | "etag": "\"01009632-0000-4d00-0000-64931dfd0000\"",
178 | "systemData": {
179 | "createdBy": "sean.stark@msdx250797.onmicrosoft.com",
180 | "createdByType": "User",
181 | "createdAt": "2023-06-15T18:56:27.7371654Z",
182 | "lastModifiedBy": "sean.stark@msdx250797.onmicrosoft.com",
183 | "lastModifiedByType": "User",
184 | "lastModifiedAt": "2023-06-21T15:57:48.0839327Z"
185 | }
186 | }'
--------------------------------------------------------------------------------
/data_collection_rules/update-dcrTimestampFormat.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update data collection rule timestamps for custom log files
4 | .PARAMETER subscriptionId
5 | Specify the subscriptionID GUID where your data collection rule resides
6 | .PARAMETER resourceGroup
7 | Specify the Resource Group Name where your data collection rule resides
8 | .PARAMETER ruleName
9 | Specify the data collection rule name
10 | .PARAMETER apiVersion
11 | Optionally you can specify the api version to use for Microsoft.Insights/dataCollectionRules
12 | .PARAMETER currentTimeFormat
13 | Optionally you can specify the current rule recordStartTimestampFormat. This configured to ISO 8601 by default
14 | .PARAMETER newTimeFormat
15 | Optionally you can specify the recordStartTimestampFormat to update to. This configured to yyyy-MM-ddTHH:mm:ssK by default
16 | .EXAMPLE
17 | .\update-dcrTimestampFormat.ps1 -subscriptionId 'ada06e68-375e-4210-be3a-c6cacebf41c5' -resourceGroup 'sentinel-dcrs' -ruleName 'windows-events'
18 | #>
19 |
20 | param(
21 | [Parameter(Mandatory=$true)]
22 | [string]$subscriptionId,
23 |
24 | [Parameter(Mandatory=$true)]
25 | [string]$resourceGroup,
26 |
27 | [Parameter(Mandatory=$true)]
28 | [string]$ruleName,
29 |
30 | [Parameter(Mandatory=$false)]
31 | [string]$apiVersion = '2021-09-01-preview',
32 |
33 | [Parameter(Mandatory=$false)]
34 | [string]$currentTimeFormat = 'ISO 8601',
35 |
36 | [Parameter(Mandatory=$false)]
37 | [string]$newTimeFormat = 'yyyy-MM-ddTHH:mm:ssK'
38 | )
39 |
40 | $requiredModules = 'Az.Accounts'
41 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
42 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
43 | ForEach ($module in $modulesToInstall){
44 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
45 | Install-Module $module -force
46 | }
47 |
48 | If(!(Get-AzContext)){
49 | Write-Host ('Connecting to Azure Subscription: {0}' -f $subscriptionId) -ForegroundColor Yellow
50 | Connect-AzAccount -Subscription $subscriptionId | Out-Null
51 | }
52 |
53 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/dataCollectionRules/{2}?api-version={3}' -f $subscriptionId, $resourceGroup, $ruleName, $apiVersion)
54 |
55 | #Get Data Collection Rule
56 | $dcr = (Invoke-AzRestMethod -Uri $uri).content
57 |
58 | # Update Data Collection Rule Data Flow Streams from Microsoft-Event to Microsoft-SecurityEvent
59 | $newDCR = $dcr.replace(('"recordStartTimestampFormat":"{0}"' -f $currentTimeFormat), ('"recordStartTimestampFormat":"{0}"' -f $newTimeFormat))
60 |
61 | # Update the DCR
62 | Invoke-AzRestMethod -Uri $uri -Method PUT -Payload $newDCR
63 |
--------------------------------------------------------------------------------
/data_collection_rules/update-dcrTransform.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update a data collection rule with an associated kql transform statement
4 |
5 | .PARAMETER subscriptionId
6 | Specify the subscriptionID GUID where your data collection rule resides
7 |
8 | .PARAMETER resourceGroup
9 | Specify the Resource Group Name where your data collection rule resides
10 |
11 | .PARAMETER ruleName
12 | Specify the data collection rule name
13 |
14 | .PARAMETER streamName
15 | The stream declaration name to update.
16 |
17 | .PARAMETER outputStream
18 | The outputStream name, the table name
19 |
20 | .PARAMETER transformKql
21 | Specify the KQL transform statement in a single line
22 |
23 | .PARAMETER apiVersion
24 | Optionally you can specify the api version to use for Microsoft.Insights/dataCollectionRules
25 |
26 | .EXAMPLE
27 | .\update-dcrTransform.ps1 -subscriptionId 'ada078449-375e-4210-be3a-c6cacebf41c5' -resourceGroup 'sentinel-dcrs' -ruleName 'windows-events' -transformKql 'source | extend TimeGenerated = todatetime(parse_json(RawData).timestamp) | extend SyslogMessage = RawData"'
28 | #>
29 |
30 | param(
31 | [Parameter(Mandatory=$true)]
32 | [string]$subscriptionId,
33 |
34 | [Parameter(Mandatory=$true)]
35 | [string]$resourceGroup,
36 |
37 | [Parameter(Mandatory=$true)]
38 | [string]$ruleName,
39 |
40 | [Parameter(Mandatory=$true)]
41 | [string]$streamName,
42 |
43 | [Parameter(Mandatory=$true)]
44 | [string]$outputStream,
45 |
46 | [Parameter(Mandatory=$true)]
47 | [string]$transformKql,
48 |
49 | [Parameter(Mandatory=$false)]
50 | [string]$apiVersion = '2022-06-01'
51 | )
52 |
53 | $requiredModules = 'Az.Accounts'
54 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
55 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
56 | ForEach ($module in $modulesToInstall){
57 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
58 | Install-Module $module -force
59 | }
60 |
61 | If(!(Get-AzContext)){
62 | Write-Host ('Connecting to Azure Subscription: {0}' -f $subscriptionId) -ForegroundColor Yellow
63 | Connect-AzAccount -Subscription $subscriptionId | Out-Null
64 | }
65 |
66 | #Get Data Collection Rule
67 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/dataCollectionRules/{2}?api-version={3}' -f $subscriptionId, $resourceGroup, $ruleName, $apiVersion)
68 | $dcr = (Invoke-AzRestMethod -Uri $uri).content | ConvertFrom-Json -Depth 20
69 |
70 | #Update the data collection rule transformation
71 | If ($dcr.properties.dataFlows | where streams -eq $streamName){
72 | If (($dcr.properties.dataFlows | where streams -eq $streamName).transformKql){
73 | ($dcr.properties.dataFlows | where streams -eq $streamName).transformKql = $transformKql
74 | }else{
75 | ($dcr.properties.dataFlows | where streams -eq $streamName) | Add-Member -MemberType NoteProperty -Name 'transformKql' -Value $transformKql -Force
76 | ($dcr.properties.dataFlows | where streams -eq $streamName) | Add-Member -MemberType NoteProperty -Name 'outputStream' -Value $outputStream -Force
77 | }
78 | }
79 |
80 | $newDCR = $dcr | ConvertTo-Json -Depth 20
81 |
82 | # Update the DCR
83 | Invoke-AzRestMethod -Uri $uri -Method PUT -Payload $newDCR
--------------------------------------------------------------------------------
/data_collection_rules/update-dcrdatastream.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update data collection rule data streams destinations
4 |
5 | .PARAMETER subscriptionId
6 | Specify the subscriptionID GUID where your data collection rule resides
7 |
8 | .PARAMETER resourceGroup
9 | Specify the Resource Group Name where your data collection rule resides
10 |
11 | .PARAMETER ruleName
12 | Specify the data collection rule name
13 |
14 | .PARAMETER apiVersion
15 | Optionally you can specify the api version to use for Microsoft.Insights/dataCollectionRules
16 |
17 | .PARAMETER currentDataStream
18 | Optionally you can specify the current rule data stream name. This configured to Microsoft-Event by default
19 |
20 | .PARAMETER newDataStream
21 | Optionally you can specify the data stream name to update to. This configured to Microsoft-SecurityEvent by default
22 |
23 | .EXAMPLE
24 | .\update-dcrdatastream.ps1 -subscriptionId 'ada06e68-375e-4210-be3a-c6cacebf41c5' -resourceGroup 'sentinel-dcrs' -ruleName 'windows-events'
25 | #>
26 |
27 | param(
28 | [Parameter(Mandatory=$true)]
29 | [string]$subscriptionId,
30 |
31 | [Parameter(Mandatory=$true)]
32 | [string]$resourceGroup,
33 |
34 | [Parameter(Mandatory=$true)]
35 | [string]$ruleName,
36 |
37 | [Parameter(Mandatory=$false)]
38 | [string]$apiVersion = '2021-09-01-preview',
39 |
40 | [Parameter(Mandatory=$false)]
41 | [string]$currentDataStream = 'Microsoft-Event',
42 |
43 | [Parameter(Mandatory=$false)]
44 | [string]$newDataStream = 'Microsoft-SecurityEvent'
45 | )
46 |
47 | $requiredModules = 'Az.Accounts'
48 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
49 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
50 | ForEach ($module in $modulesToInstall){
51 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
52 | Install-Module $module -force
53 | }
54 |
55 | If(!(Get-AzContext)){
56 | Write-Host ('Connecting to Azure Subscription: {0}' -f $subscriptionId) -ForegroundColor Yellow
57 | Connect-AzAccount -Subscription $subscriptionId | Out-Null
58 | }
59 |
60 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/dataCollectionRules/{2}?api-version={3}' -f $subscriptionId, $resourceGroup, $ruleName, $apiVersion)
61 |
62 | #Get Data Collection Rule
63 | $dcr = (Invoke-AzRestMethod -Uri $uri).content
64 |
65 | # Update Data Collection Rule Data Flow Streams from Microsoft-Event to Microsoft-SecurityEvent
66 | $newDCR = $dcr.replace(('"streams":["{0}"]' -f $currentDataStream), ('"streams":["{0}"]' -f $newDataStream))
67 |
68 | # Update the DCR
69 | Invoke-AzRestMethod -Uri $uri -Method PUT -Payload $newDCR
70 |
--------------------------------------------------------------------------------
/data_collection_rules/update-dcrworkspace.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will update a data collection rule destination log analytics workspace
4 |
5 | .PARAMETER subscriptionId
6 | Specify the subscriptionID GUID where your data collection rule resides
7 |
8 | .PARAMETER resourceGroup
9 | Specify the Resource Group Name where your data collection rule resides
10 |
11 | .PARAMETER ruleName
12 | Specify the data collection rule name
13 |
14 | .PARAMETER apiVersion
15 | Optionally you can specify the api version to use for Microsoft.Insights/dataCollectionRules
16 |
17 | .PARAMETER currentWorkspaceId
18 | The currently configured log analytics workspace Id
19 |
20 | .PARAMETER newWorkspaceResourceId
21 | The full Resource Id of the new log analytics workspace change to
22 |
23 | .EXAMPLE
24 | .\update-dcrworkspace.ps1 -subscriptionId 'ada078449-375e-4210-be3a-c6cacebf41c5' -resourceGroup 'sentinel-dcrs' -ruleName 'windows-events' -currentWorkspaceId 'b6222115-73bc-4c99-b795-4560c061aced' -newWorkspaceResourceId '/subscriptions/166c8347-0480-4aa7-b984-75f0fda42c69/resourceGroups/sentinel/providers/Microsoft.OperationalInsights/workspaces/sentinel'
25 | #>
26 |
27 | param(
28 | [Parameter(Mandatory=$true)]
29 | [string]$subscriptionId,
30 |
31 | [Parameter(Mandatory=$true)]
32 | [string]$resourceGroup,
33 |
34 | [Parameter(Mandatory=$true)]
35 | [string]$ruleName,
36 |
37 | [Parameter(Mandatory=$false)]
38 | [string]$apiVersion = '2021-04-01',
39 |
40 | [Parameter(Mandatory=$true)]
41 | [string]$currentWorkspaceId,
42 |
43 | [Parameter(Mandatory=$true)]
44 | [string]$newWorkspaceResourceId
45 | )
46 |
47 | $requiredModules = 'Az.Accounts'
48 | $availableModules = Get-Module -ListAvailable -Name $requiredModules
49 | $modulesToInstall = $requiredModules | where-object {$_ -notin $availableModules.Name}
50 | ForEach ($module in $modulesToInstall){
51 | Write-Host "Installing Missing PowerShell Module: $module" -ForegroundColor Yellow
52 | Install-Module $module -force
53 | }
54 |
55 | If(!(Get-AzContext)){
56 | Write-Host ('Connecting to Azure Subscription: {0}' -f $subscriptionId) -ForegroundColor Yellow
57 | Connect-AzAccount -Subscription $subscriptionId | Out-Null
58 | }
59 |
60 | #Get the new workspace Id
61 | $uri = ('https://management.azure.com{0}?api-version=2021-12-01-preview' -f $newWorkspaceResourceId)
62 |
63 | $newWorkspaceId = ((Invoke-AzRestMethod -Uri $uri).content | ConvertFrom-Json).properties.customerId
64 |
65 | $uri = ('https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Insights/dataCollectionRules/{2}?api-version={3}' -f $subscriptionId, $resourceGroup, $ruleName, $apiVersion)
66 |
67 | #Get Data Collection Rule
68 | $dcr = (Invoke-AzRestMethod -Uri $uri).content | ConvertFrom-Json -Depth 20
69 |
70 | #Update to the new workspace
71 | $destName = ($dcr.properties.destinations.logAnalytics | where workspaceId -Like $currentWorkspaceId).name
72 | $newDestName = 'la--{0}' -f $(get-random)
73 | ($dcr.properties.destinations.logAnalytics | where workspaceId -Like $currentWorkspaceId).workspaceResourceId = $newWorkspaceResourceId
74 | ($dcr.properties.destinations.logAnalytics | where workspaceId -Like $currentWorkspaceId).name = $newDestName
75 | ($dcr.properties.destinations.logAnalytics | where workspaceId -Like $currentWorkspaceId).workspaceId = $newWorkspaceId
76 | $newDCR = $dcr | ConvertTo-Json -Depth 20
77 | $newDCR = $newDCR.Replace($destName, $newDestName)
78 |
79 | # Update the DCR
80 | Invoke-AzRestMethod -Uri $uri -Method PUT -Payload $newDCR
81 |
82 |
--------------------------------------------------------------------------------
/dataconnectors/GitHubAuditLogs/CCP/GitHubAuditLogs_CCP.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "workspace": {
6 | "type": "string",
7 | "defaultValue": ""
8 | }
9 | },
10 | "resources": [
11 | {
12 | "id": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/',resourceGroup().name,'/providers/Microsoft.OperationalInsights/workspaces/',parameters('workspace'),'/providers/Microsoft.SecurityInsights/dataConnectors/',guid(resourceGroup().id, deployment().name))]",
13 | "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',guid(resourceGroup().id, deployment().name))]",
14 | "apiVersion": "2021-03-01-preview",
15 | "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors",
16 | "kind": "APIPolling",
17 | "properties": {
18 | "connectorUiConfig": {
19 | "id": "GitHubEntAuditLogPolling",
20 | "title": "GitHub Enterprise Audit Log",
21 | "publisher": "Microsoft-Custom",
22 | "descriptionMarkdown": "The GitHub audit log connector provides the capability to ingest GitHub audit log at Enterprise level into Microsoft Sentinel. By connecting GitHub audit logs into Microsoft Sentinel, you can view this data in workbooks, use it to create custom alerts, and improve your investigation process. \n\n ",
23 | "graphQueriesTableName": "GitHubEntAuditLogPolling_CL",
24 | "graphQueries": [
25 | {
26 | "metricName": "Total events received",
27 | "legend": "GitHub audit log events",
28 | "baseQuery": "{{graphQueriesTableName}}"
29 | }
30 | ],
31 | "sampleQueries": [
32 | {
33 | "description": "All logs",
34 | "query": "{{graphQueriesTableName}}\n | take 10"
35 | }
36 | ],
37 | "dataTypes": [
38 | {
39 | "name": "{{graphQueriesTableName}}",
40 | "lastDataReceivedQuery": "{{graphQueriesTableName}}\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)"
41 | }
42 | ],
43 | "connectivityCriterias": [
44 | {
45 | "type": "SentinelKindsV2",
46 | "value": []
47 | }
48 | ],
49 | "availability": {
50 | "status": 1,
51 | "isPreview": true
52 | },
53 | "permissions": {
54 | "resourceProvider": [
55 | {
56 | "provider": "Microsoft.OperationalInsights/workspaces",
57 | "permissionsDisplayText": "read and write permissions are required.",
58 | "providerDisplayName": "Workspace",
59 | "scope": "Workspace",
60 | "requiredPermissions": {
61 | "write": true,
62 | "read": true,
63 | "delete": true
64 | }
65 | }
66 | ],
67 | "customs": [
68 | {
69 | "name": "GitHub API personal access token",
70 | "description": "You need a GitHub personal access token to enable polling for the Enterprise audit log. You need to use a classic token; you must be an enterprise admin and you must use an access token with the read:audit_log scope."
71 | },
72 | {
73 | "name": "GitHub Enterprise type",
74 | "description": "This connector will only function with GitHub Enterprise Cloud; it will not support GitHub Enterprise Server. "
75 | }
76 | ]
77 | },
78 | "instructionSteps": [
79 | {
80 | "title": "Connect the GitHub Enterprise (All Orgs) Audit Log to Microsoft Sentinel",
81 | "description": "Enable GitHub audit logs. \n Follow [this guide](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) to create or find your personal access token.",
82 | "instructions": [
83 | {
84 | "parameters": {
85 | "enable": "true",
86 | "userRequestPlaceHoldersInput": [
87 | {
88 | "displayText": "Enterprise Name",
89 | "requestObjectKey": "apiEndpoint",
90 | "placeHolderName": "{{placeHolder1}}",
91 | "placeHolderValue": ""
92 | }
93 | ]
94 | },
95 | "type": "APIKey"
96 | }
97 | ]
98 | }
99 | ]
100 | },
101 | "pollingConfig": {
102 | "owner": "ASI",
103 | "version": "2.0",
104 | "source": "PaaS",
105 | "templateFilePath": "",
106 | "templateFileName": "",
107 | "auth": {
108 | "authType": "APIKey",
109 | "APIKeyName": "Authorization",
110 | "APIKeyIdentifier": "token"
111 | },
112 | "request": {
113 | "apiEndpoint": "https://api.github.com/enterprises/{{placeHolder1}}/audit-log",
114 | "rateLimitQPS": 50,
115 | "queryWindowInMin": 10,
116 | "httpMethod": "Get",
117 | "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ",
118 | "retryCount": 3,
119 | "timeoutInSeconds": 60,
120 | "headers": {
121 | "Accept": "application/json",
122 | "X-GitHub-Api-Version": "2022-11-28",
123 | "user-agent": "scuba"
124 | },
125 | "queryParameters": {
126 | "phrase": "created:{_QueryWindowStartTime}..{_QueryWindowEndTime}",
127 | "include": "all"
128 | }
129 | },
130 | "paging": {
131 | "pagingType": "LinkHeader",
132 | "pageSizeParaName": "per_page"
133 | },
134 | "response": {
135 | "eventsJsonPaths": [
136 | "$"
137 | ]
138 | }
139 | }
140 | }
141 | }
142 | ]
143 | }
144 |
--------------------------------------------------------------------------------
/dataconnectors/GitHubAuditLogs/CCP/readme.md:
--------------------------------------------------------------------------------
1 | # GitHub Enterprise Audit Logs Sentinel CCP
2 |
3 | - [Solution Overview](#solution-overview)
4 | * [Step 1 - Deploy the Data Connector](#step-1---deploy-the-data-connector)
5 | * [Step 2 - Configure GitHub Enterprise](#step-2---configure-github-enterprise)
6 | * [Step 3 - Create a GitHub Personal Access Token](#step-3---create-a-github-personal-access-token)
7 | * [Step 4 - Configure the Sentinel Data Connector](#step-4---configure-the-sentinel-data-connector)
8 |
9 | # Solution Overview
10 | This solution will ingest audit logs from GitHub Enterprise for all organizations to Microsoft Sentinel using the Codeless Connector Platform data connector. The data will end up in the **GitHubEntAuditLogPolling_CL** table.
11 | > ⚠️ If you are using IP Restrictions in GitHub you will need to whitelist the CIDR ranges for CCP under the **SCUBA** tag here - [Home Page - Azure IP Ranges](https://azureipranges.azurewebsites.net/)
12 |
13 | ## Step 1 - Deploy the Data Connector
14 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fdataconnectors%2FGitHubAuditLogs%2FCCP%2FGitHubAuditLogs_CCP.json)
15 |
16 | ## Step 2 - Configure GitHub Enterprise
17 | > This step is optional but recommend
18 |
19 | 1. From the [GitHub Enterprise](https://github.com/enterprises) navigate to **Settings** > **Audit Log**
20 | 2. Navigate to the **Settings** tab, turn on **Enable source IP disclosure** and **Enable API Request Events**
21 |
22 | 
23 |
24 | ## Step 3 - Create a GitHub Personal Access Token
25 | You will need a GitHub personal access token to enable polling for the Enterprise audit log. You need to use a **classic token**; you must be an enterprise admin and you must use an access token with the **read:audit_log** scope
26 |
27 | 1. In the upper-right corner of any page, click your profile photo, then click **Settings**
28 | 2. In the left sidebar, click **Developer settings**
29 | 3. In the left sidebar, click **Personal access tokens** > **Tokens (classic)**
30 | 4. Click **Generate new token (classic)**
31 | 5. Give the token a name and add the **read:audit_log** scope
32 |
33 | 
34 |
35 | 7. Copy the access token to a safe location
36 |
37 | ## Step 4 - Configure the Sentinel Data Connector
38 | 1. From Microsoft Sentinel navigate to **Data Connectors**
39 | 2. You should see a **GitHub Enterprise Audit Log (Preview)** data connector, click ***Open connector page**
40 | 3. Enter your **enterprise name** and **GitHub personal access token** in the **API Key** field. Click **Connect**
41 | 4. Generally the intial data will show up in the **GitHubEntAuditLogPolling_CL** table around 20 minutes after the data connector is configured
42 |
--------------------------------------------------------------------------------
/dataconnectors/GitHubAuditLogs/eventhub/eventhub.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "eventHubName": {
6 | "defaultValue": "githubauditlogs",
7 | "type": "String"
8 | },
9 | "eventHubNamespace": {
10 | "defaultValue": "githubauditlogs",
11 | "type": "String"
12 | },
13 | "eventHubLocation": {
14 | "defaultValue": "westus3",
15 | "metadata": {
16 | "description": "Location for the Event Hub. Must be a supported region https://learn.microsoft.com/en-us/azure/azure-monitor/logs/ingest-logs-event-hub#supported-regions "
17 | },
18 | "type": "string"
19 | }
20 | },
21 | "resources": [
22 | {
23 | "apiVersion": "2023-01-01-preview",
24 | "location": "[parameters('eventHubLocation')]",
25 | "name": "[variables('_eventHubNamespace')]",
26 | "properties": {
27 | "disableLocalAuth": false,
28 | "isAutoInflateEnabled": false,
29 | "kafkaEnabled": false,
30 | "maximumThroughputUnits": 0,
31 | "minimumTlsVersion": "1.2",
32 | "publicNetworkAccess": "Enabled",
33 | "zoneRedundant": true
34 | },
35 | "sku": {
36 | "capacity": 1,
37 | "name": "Standard",
38 | "tier": "Standard"
39 | },
40 | "type": "Microsoft.EventHub/namespaces"
41 | },
42 | {
43 | "apiVersion": "2023-01-01-preview",
44 | "dependsOn": [
45 | "[resourceId('Microsoft.EventHub/namespaces', variables('_eventHubNamespace'))]"
46 | ],
47 | "location": "[parameters('eventHubLocation')]",
48 | "name": "[concat(variables('_eventHubNamespace'), '/RootManageSharedAccessKey')]",
49 | "properties": {
50 | "rights": [
51 | "Listen",
52 | "Manage",
53 | "Send"
54 | ]
55 | },
56 | "type": "Microsoft.EventHub/namespaces/authorizationrules"
57 | },
58 | {
59 | "apiVersion": "2023-01-01-preview",
60 | "dependsOn": [
61 | "[resourceId('Microsoft.EventHub/namespaces', variables('_eventHubNamespace'))]"
62 | ],
63 | "location": "[parameters('eventHubLocation')]",
64 | "name": "[concat(variables('_eventHubNamespace'), '/', parameters('eventHubName'))]",
65 | "properties": {
66 | "messageRetentionInDays": 1,
67 | "partitionCount": 2,
68 | "retentionDescription": {
69 | "cleanupPolicy": "Delete",
70 | "retentionTimeInHours": 1
71 | },
72 | "status": "Active"
73 | },
74 | "type": "Microsoft.EventHub/namespaces/eventhubs"
75 | },
76 | {
77 | "apiVersion": "2023-01-01-preview",
78 | "dependsOn": [
79 | "[resourceId('Microsoft.EventHub/namespaces', variables('_eventHubNamespace'))]"
80 | ],
81 | "location": "[parameters('eventHubLocation')]",
82 | "name": "[concat(variables('_eventHubNamespace'), '/default')]",
83 | "properties": {
84 | "defaultAction": "Allow",
85 | "ipRules": [
86 | ],
87 | "publicNetworkAccess": "Enabled",
88 | "trustedServiceAccessEnabled": false,
89 | "virtualNetworkRules": [
90 | ]
91 | },
92 | "type": "Microsoft.EventHub/namespaces/networkrulesets"
93 | },
94 | {
95 | "apiVersion": "2023-01-01-preview",
96 | "dependsOn": [
97 | "[resourceId('Microsoft.EventHub/namespaces/eventhubs', variables('_eventHubNamespace'), parameters('eventHubName'))]",
98 | "[resourceId('Microsoft.EventHub/namespaces', variables('_eventHubNamespace'))]"
99 | ],
100 | "location": "[parameters('eventHubLocation')]",
101 | "name": "[concat(variables('_eventHubNamespace'), '/', parameters('eventHubName'), '/', parameters('eventHubName'), 'Send')]",
102 | "properties": {
103 | "rights": [
104 | "Send"
105 | ]
106 | },
107 | "type": "Microsoft.EventHub/namespaces/eventhubs/authorizationrules"
108 | },
109 | {
110 | "apiVersion": "2023-01-01-preview",
111 | "dependsOn": [
112 | "[resourceId('Microsoft.EventHub/namespaces/eventhubs', variables('_eventHubNamespace'), parameters('eventHubName'))]",
113 | "[resourceId('Microsoft.EventHub/namespaces', variables('_eventHubNamespace'))]"
114 | ],
115 | "location": "[parameters('eventHubLocation')]",
116 | "name": "[concat(variables('_eventHubNamespace'), '/', parameters('eventHubName'), '/$Default')]",
117 | "properties": {
118 | },
119 | "type": "Microsoft.EventHub/namespaces/eventhubs/consumergroups"
120 | }
121 | ],
122 | "variables": {
123 | "_eventHubNamespace": "[concat(parameters('eventHubNamespace'), '-', uniqueString(parameters('eventHubNamespace')))]"
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/dataconnectors/GitHubAuditLogs/eventhub/readme.md:
--------------------------------------------------------------------------------
1 | ## ⚠️ Work In Progress, not ready yet
2 |
3 | ## Solution Overview
4 | This solution will stream [audit logs](https://docs.github.com/en/enterprise-cloud@latest/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/about-the-audit-log-for-your-enterprise) from GitHub Enterprise for all organizations to an Azure Event Hub. Events that are sent to the Event Hub will then be ingested to the Microsoft Sentinel workspace via the method documented here - [Ingest events from Azure Event Hubs into Azure Monitor Logs](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/ingest-logs-event-hub)
5 |
6 | > This solution also avoids any of the rate limits imposed with directly pulling audit logs from the rest api and exposing any PAT tokens from GitHub
7 |
8 | ## Requirements
9 | To send events from Azure Event Hubs to Sentinel you wil require the below
10 |
11 | - Log Analytics workspace where you have at least contributor rights.
12 | - Your Log Analytics workspace needs to be linked to a **dedicated cluster** or to have a **commitment tier**.
13 | - Event Hubs namespace that permits public network access. Private Link and Network Security Perimeters (NSP) are currently not supported.
14 | - ❗The Event Hub must be in a **supported region** documented here [Supported Regions](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/ingest-logs-event-hub#supported-regions).
15 |
16 | ## Step 1 - Deploy the Event Hub
17 | > ❗The Event Hub must be in a supported region documented here [Supported Regions](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/ingest-logs-event-hub#supported-regions). West US 2 is not supported.
18 |
19 | 1. [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fdataconnectors%2FGitHubAuditLogs%2Feventhub.json)
20 |
21 | 2. Collect the following information from the Event Hub
22 | 3. Navigate to the Event Hub Namespace **Event Hubs** > Select the **Event Hub** (githubauditlogs)
23 | 4. Copy down the Event Hub instance name, which should be githubauditlogs by default
24 |
25 | 
26 |
27 | 6. Select **Shared access policies** and select the **githubauditlogsSend** policy
28 |
29 | 
30 |
31 | 8. Copy down the Connection string–primary key in a safe location
32 |
33 | ## Step 2 - Create the Data Collection Endpoint and Data Collection Rule
34 |
35 |
36 | ## Step 3 - Configure GitHub Enterprise
37 | > Steps 2 is optional but recommend
38 |
39 | 1. From the [GitHub Enterprise](https://github.com/enterprises) navigate to **Settings** > **Audit Log**
40 | 2. Navigate to the **Settings** tab, turn on **Enable source IP disclosure** and **Enable API Request Events**
41 |
42 | 
43 |
44 | 4. Navigate to the **Log Streaming** tab
45 | 5. Enter your **Azure Event Hubs Instance** name and **Connection String** collected from **Step 1**
46 |
47 | 
48 |
49 | ## Step 4 - Verify Logs
50 | 1. After Step 1 - 3 are completed you should see events hitting your Event Hub Namespace and logs in your log analytics workspace
51 |
--------------------------------------------------------------------------------
/dataconnectors/cross-tenant-logging/azure-activity-logs/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "diagnosticsSettingsName": {
6 | "defaultValue": "send-to-sentinel",
7 | "type": "String",
8 | "metadata": {
9 | "description": "Enter value for diagnosticsSettingsName"
10 | }
11 | },
12 | "logCategoryAdministrative": {
13 | "defaultValue": true,
14 | "type": "Bool",
15 | "metadata": {
16 | "description": "Enter value for logCategoryAdministrative"
17 | }
18 | },
19 | "logCategoryAlert": {
20 | "defaultValue": true,
21 | "type": "Bool",
22 | "metadata": {
23 | "description": "Enter value for logCategoryAlert"
24 | }
25 | },
26 | "logCategoryAutoscale": {
27 | "defaultValue": true,
28 | "type": "Bool",
29 | "metadata": {
30 | "description": "Enter value for logCategoryAutoscale"
31 | }
32 | },
33 | "logCategoryPolicy": {
34 | "defaultValue": true,
35 | "type": "Bool",
36 | "metadata": {
37 | "description": "Enter value for logCategoryPolicy"
38 | }
39 | },
40 | "logCategoryRecommendation": {
41 | "defaultValue": true,
42 | "type": "Bool",
43 | "metadata": {
44 | "description": "Enter value for logCategoryRecommendation"
45 | }
46 | },
47 | "logCategoryResourceHealth": {
48 | "defaultValue": true,
49 | "type": "Bool",
50 | "metadata": {
51 | "description": "Enter value for logCategoryResourceHealth"
52 | }
53 | },
54 | "logCategorySecurity": {
55 | "defaultValue": true,
56 | "type": "Bool",
57 | "metadata": {
58 | "description": "Enter value for logCategorySecurity"
59 | }
60 | },
61 | "logCategoryServiceHealth": {
62 | "defaultValue": true,
63 | "type": "Bool",
64 | "metadata": {
65 | "description": "Enter value for logCategoryServiceHealth"
66 | }
67 | },
68 | "PlaybookName": {
69 | "defaultValue": "configure-azure-activity-logging",
70 | "type": "String"
71 | },
72 | "workspaceResourceId": {
73 | "type": "String",
74 | "metadata": {
75 | "description": "The full resourceID of the log analytics workspace"
76 | }
77 | }
78 | },
79 | "variables": {
80 | "ArmConnectionName": "[concat('Arm-', parameters('PlaybookName'))]"
81 | },
82 | "resources": [
83 | {
84 | "type": "Microsoft.Logic/workflows",
85 | "apiVersion": "2017-07-01",
86 | "name": "[parameters('PlaybookName')]",
87 | "location": "[resourceGroup().location]",
88 | "dependsOn": [
89 | "[resourceId('Microsoft.Web/connections', variables('ArmConnectionName'))]"
90 | ],
91 | "tags": {
92 | "hidden-SentinelTemplateName": "configure-cross-tenant-logging",
93 | "hidden-SentinelTemplateVersion": "1.0"
94 | },
95 | "identity": {
96 | "type": "SystemAssigned"
97 | },
98 | "properties": {
99 | "definition": {
100 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
101 | "actions": {
102 | "For_each_Subscription_-_Configure_Azure_Activity_Logs": {
103 | "actions": {
104 | "HTTP_-_Configure_Diagnostic_Settings": {
105 | "inputs": {
106 | "authentication": {
107 | "type": "ManagedServiceIdentity"
108 | },
109 | "body": {
110 | "properties": {
111 | "logs": [
112 | {
113 | "category": "Administrative",
114 | "categoryGroup": null,
115 | "enabled": "@parameters('logCategoryAdministrative')"
116 | },
117 | {
118 | "category": "Security",
119 | "categoryGroup": null,
120 | "enabled": "@parameters('logCategorySecurity')"
121 | },
122 | {
123 | "category": "ServiceHealth",
124 | "categoryGroup": null,
125 | "enabled": "@parameters('logCategoryServiceHealth')"
126 | },
127 | {
128 | "category": "Alert",
129 | "categoryGroup": null,
130 | "enabled": "@parameters('logCategoryAlert')"
131 | },
132 | {
133 | "category": "Recommendation",
134 | "categoryGroup": null,
135 | "enabled": "@parameters('logCategoryRecommendation')"
136 | },
137 | {
138 | "category": "Policy",
139 | "categoryGroup": null,
140 | "enabled": "@parameters('logCategoryPolicy')"
141 | },
142 | {
143 | "category": "Autoscale",
144 | "categoryGroup": null,
145 | "enabled": "@parameters('logCategoryAutoscale')"
146 | },
147 | {
148 | "category": "ResourceHealth",
149 | "categoryGroup": null,
150 | "enabled": "@parameters('logCategoryResourceHealth')"
151 | }
152 | ],
153 | "workspaceId": "@{parameters('workspaceResourceId')}"
154 | }
155 | },
156 | "method": "PUT",
157 | "queries": {
158 | "api-version": "2021-05-01-preview"
159 | },
160 | "uri": "https://management.azure.com/subscriptions/@{items('For_each_Subscription_-_Configure_Azure_Activity_Logs')?['subscriptionId']}/providers/microsoft.insights/diagnosticSettings/@{parameters('diagnosticsSettingsName')}"
161 | },
162 | "runAfter": {},
163 | "type": "Http"
164 | }
165 | },
166 | "foreach": "@body('List_subscriptions_-_All_Tenants')?['value']",
167 | "runAfter": {
168 | "List_subscriptions_-_All_Tenants": [
169 | "Succeeded"
170 | ]
171 | },
172 | "type": "Foreach"
173 | },
174 | "List_subscriptions_-_All_Tenants": {
175 | "inputs": {
176 | "host": {
177 | "connection": {
178 | "name": "@parameters('$connections')['arm']['connectionId']"
179 | }
180 | },
181 | "method": "get",
182 | "path": "/subscriptions",
183 | "queries": {
184 | "x-ms-api-version": "2016-06-01"
185 | }
186 | },
187 | "runAfter": {},
188 | "type": "ApiConnection"
189 | }
190 | },
191 | "contentVersion": "1.0.0.0",
192 | "outputs": {},
193 | "parameters": {
194 | "$connections": {
195 | "defaultValue": {},
196 | "type": "Object"
197 | },
198 | "diagnosticsSettingsName": {
199 | "defaultValue": "[parameters('diagnosticsSettingsName')]",
200 | "type": "string"
201 | },
202 | "logCategoryAdministrative": {
203 | "defaultValue": "[parameters('logCategoryAdministrative')]",
204 | "type": "bool"
205 | },
206 | "logCategoryAlert": {
207 | "defaultValue": "[parameters('logCategoryAlert')]",
208 | "type": "bool"
209 | },
210 | "logCategoryAutoscale": {
211 | "defaultValue": "[parameters('logCategoryAutoscale')]",
212 | "type": "bool"
213 | },
214 | "logCategoryPolicy": {
215 | "defaultValue": "[parameters('logCategoryPolicy')]",
216 | "type": "bool"
217 | },
218 | "logCategoryRecommendation": {
219 | "defaultValue": "[parameters('logCategoryRecommendation')]",
220 | "type": "bool"
221 | },
222 | "logCategoryResourceHealth": {
223 | "defaultValue": "[parameters('logCategoryResourceHealth')]",
224 | "type": "bool"
225 | },
226 | "logCategorySecurity": {
227 | "defaultValue": "[parameters('logCategorySecurity')]",
228 | "type": "bool"
229 | },
230 | "logCategoryServiceHealth": {
231 | "defaultValue": "[parameters('logCategoryServiceHealth')]",
232 | "type": "bool"
233 | },
234 | "workspaceResourceId": {
235 | "defaultValue": "[parameters('workspaceResourceId')]",
236 | "type": "string"
237 | }
238 | },
239 | "triggers": {
240 | "Recurrence": {
241 | "evaluatedRecurrence": {
242 | "frequency": "Day",
243 | "interval": 1
244 | },
245 | "recurrence": {
246 | "frequency": "Day",
247 | "interval": 1
248 | },
249 | "type": "Recurrence"
250 | }
251 | }
252 | },
253 | "parameters": {
254 | "$connections": {
255 | "value": {
256 | "arm": {
257 | "connectionId": "[resourceId('Microsoft.Web/connections', variables('ArmConnectionName'))]",
258 | "connectionName": "[variables('ArmConnectionName')]",
259 | "connectionProperties": {
260 | "authentication": {
261 | "type": "ManagedServiceIdentity"
262 | }
263 | },
264 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Arm')]"
265 | }
266 | }
267 | }
268 | },
269 | "provisioningState": "Succeeded",
270 | "state": "Enabled"
271 | }
272 | },
273 | {
274 | "type": "Microsoft.Web/connections",
275 | "apiVersion": "2016-06-01",
276 | "name": "[variables('ArmConnectionName')]",
277 | "location": "[resourceGroup().location]",
278 | "kind": "V1",
279 | "properties": {
280 | "api": {
281 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Arm')]"
282 | },
283 | "customParameterValues": {},
284 | "displayName": "[variables('ArmConnectionName')]",
285 | "parameterValueType": "Alternative"
286 | }
287 | }
288 | ]
289 | }
290 |
--------------------------------------------------------------------------------
/dataconnectors/cross-tenant-logging/azure-activity-logs/readme.md:
--------------------------------------------------------------------------------
1 | # Azure Activity Logs Cross Tenant Logging
2 |
3 | - [Solution Overview](#solution-overview)
4 | - [Step 1 - Deploy the Logic App](#step-1-deploy-the-logic-app)
5 | - [Step 2 - Configure the Logic App](#step-2-configure-the-logic-app)
6 | - [Step 3 - Configure Azure Lighthouse](#step-3-configure-azure-lighthouse)
7 | - [Step 4 - Test The Logic App](#step-4-test-the-logic-app)
8 |
9 | # Solution Overview
10 | This solution will configure cross-tenant logging of Azure Activity Logs to a centralized log analytics workspace in a primary tenant's workspace using Azure Lighthouse delegated roles. The overall steps are to deploy the logic app in the tenant hosting the log analytics workspace and then deploy an Azure Lighthouse delegation to all other tenants to allow the logic app to create the logging profile.
11 |
12 | - The logic app runs once a day by default
13 | - The logic app will configure Azure Activity logs on any subscription where the system assigned managed identity has been delegated rights to
14 |
15 | # Step 1 - Deploy the Logic App
16 |
17 | 1. Deploy this in the tenant hosting the log analytics workspace
18 |
19 | > [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fdataconnectors%2Fcross-tenant-logging%2Fazure-activity-logs%2Fazuredeploy.json)
20 |
21 | > ⚠️ Make you specify the full log analytics workspace resource id. Example: /subscriptions/\/resourcegroups/\/providers/microsoft.operationalinsights/workspaces/\
22 |
23 | # Step 2 - Configure the Logic App
24 | 1. Navigate to **Logic App** > **Identity** Section
25 | 2. Confirm a system assigned managed identity was created and **copy** the **Object (principal) ID**
26 | 3. Click **Azure role assignments**
27 | 4. Click **Add role assignment**
28 | 5. For the Scope select **Resource Group**
29 | 6. Select the **Subscription** and **Resource Group** where your **Sentinel Workspace** Resides
30 | 7. Select the **Log Analytics Contributor Role**
31 |
32 | > 
33 |
34 | 8. Select **Save**
35 | > ℹ️ If you would like the logic app to also configure logging in the current tenant add the **Monitoring Contributor** role to applicable subscriptions or managment groups in the current tenant
36 |
37 | # Step 3 - Configure Azure Lighthouse
38 | 1. From the Azure Portal navigate to **Azure Lighthouse**
39 | 2. Select **Manage your customers**
40 | 3. Select **Create ARM Template**
41 | 4. Enter a **name** for the Azure Lighthouse offer (Sentinel Cross Tenant Logging)
42 | 5. Enter a **description** (Configures Cross Tenant Logging of Azure resource provider logs to Sentinel)
43 | 6. For the **Delegate scope** select **Subscription**
44 | 7. Click Add Authorization
45 | 8. Select **Service principal** and select the **system assigned managed identity of the logic app**
46 |
47 | >
48 |
49 | 9. Select the **Monitoring Contributor** role
50 | 10. Select **Permanent** for the Access Type
51 |
52 | >
53 |
54 | 11. Click **Add**
55 | 12. Click **View Template**
56 | 13. Next deploy the ARM template to your other Tenant Subscriptions
57 |
58 | # Step 4 - Test The Logic App
59 | 1. Navigate to **Logic App** > Select Run
60 | 2. Wait for the Logic App run to complete and verify the diagnostics settings were configured
61 |
--------------------------------------------------------------------------------
/dataconnectors/okta/AzureFunctionOktaSSO.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanstark/sentinel-tools/1832eec427e35a748d236ac2506b30da25f7dfbc/dataconnectors/okta/AzureFunctionOktaSSO.zip
--------------------------------------------------------------------------------
/dataconnectors/okta/okta_custom_table.json:
--------------------------------------------------------------------------------
1 | $tableParams = @'
2 | {
3 | "properties": {
4 | "schema": {
5 | "name": "Okta_App_Team_CL"
6 | "columns": [
7 | {
8 | "isDefaultDisplay": false,
9 | "isHidden": false,
10 | "name": "ActingAppName",
11 | "type": "string"
12 | },
13 | {
14 | "isDefaultDisplay": false,
15 | "isHidden": false,
16 | "name": "ActingAppType",
17 | "type": "string"
18 | },
19 | {
20 | "isDefaultDisplay": false,
21 | "isHidden": false,
22 | "name": "ActorDetailEntry",
23 | "type": "dynamic"
24 | },
25 | {
26 | "isDefaultDisplay": false,
27 | "isHidden": false,
28 | "name": "ActorDisplayName",
29 | "type": "string"
30 | },
31 | {
32 | "isDefaultDisplay": false,
33 | "isHidden": false,
34 | "name": "ActorSessionId",
35 | "type": "string"
36 | },
37 | {
38 | "isDefaultDisplay": false,
39 | "isHidden": false,
40 | "name": "ActorUserId",
41 | "type": "string"
42 | },
43 | {
44 | "isDefaultDisplay": false,
45 | "isHidden": false,
46 | "name": "ActorUserIdType",
47 | "type": "string"
48 | },
49 | {
50 | "isDefaultDisplay": false,
51 | "isHidden": false,
52 | "name": "ActorUsername",
53 | "type": "string"
54 | },
55 | {
56 | "isDefaultDisplay": false,
57 | "isHidden": false,
58 | "name": "ActorUsernameType",
59 | "type": "string"
60 | },
61 | {
62 | "isDefaultDisplay": false,
63 | "isHidden": false,
64 | "name": "ActorUserType",
65 | "type": "string"
66 | },
67 | {
68 | "isDefaultDisplay": false,
69 | "isHidden": false,
70 | "name": "AuthenticationContextAuthenticationProvider",
71 | "type": "string"
72 | },
73 | {
74 | "isDefaultDisplay": false,
75 | "isHidden": false,
76 | "name": "AuthenticationContextAuthenticationStep",
77 | "type": "int"
78 | },
79 | {
80 | "isDefaultDisplay": false,
81 | "isHidden": false,
82 | "name": "AuthenticationContextCredentialProvider",
83 | "type": "string"
84 | },
85 | {
86 | "isDefaultDisplay": false,
87 | "isHidden": false,
88 | "name": "AuthenticationContextInterface",
89 | "type": "string"
90 | },
91 | {
92 | "isDefaultDisplay": false,
93 | "isHidden": false,
94 | "name": "AuthenticationContextIssuerId",
95 | "type": "string"
96 | },
97 | {
98 | "isDefaultDisplay": false,
99 | "isHidden": false,
100 | "name": "AuthenticationContextIssuerType",
101 | "type": "string"
102 | },
103 | {
104 | "isDefaultDisplay": false,
105 | "isHidden": false,
106 | "name": "DebugData",
107 | "type": "dynamic"
108 | },
109 | {
110 | "isDefaultDisplay": false,
111 | "isHidden": false,
112 | "name": "DvcAction",
113 | "type": "string"
114 | },
115 | {
116 | "isDefaultDisplay": false,
117 | "isHidden": false,
118 | "name": "EventMessage",
119 | "type": "string"
120 | },
121 | {
122 | "isDefaultDisplay": false,
123 | "isHidden": false,
124 | "name": "EventOriginalResultDetails",
125 | "type": "string"
126 | },
127 | {
128 | "isDefaultDisplay": false,
129 | "isHidden": false,
130 | "name": "EventOriginalType",
131 | "type": "string"
132 | },
133 | {
134 | "isDefaultDisplay": false,
135 | "isHidden": false,
136 | "name": "EventOriginalUid",
137 | "type": "string"
138 | },
139 | {
140 | "isDefaultDisplay": false,
141 | "isHidden": false,
142 | "name": "EventResult",
143 | "type": "string"
144 | },
145 | {
146 | "isDefaultDisplay": false,
147 | "isHidden": false,
148 | "name": "EventSeverity",
149 | "type": "string"
150 | },
151 | {
152 | "isDefaultDisplay": false,
153 | "isHidden": false,
154 | "name": "HttpUserAgent",
155 | "type": "string"
156 | },
157 | {
158 | "isDefaultDisplay": false,
159 | "isHidden": false,
160 | "name": "LegacyEventType",
161 | "type": "string"
162 | },
163 | {
164 | "isDefaultDisplay": false,
165 | "isHidden": false,
166 | "name": "LogonMethod",
167 | "type": "string"
168 | },
169 | {
170 | "isDefaultDisplay": false,
171 | "isHidden": false,
172 | "name": "OriginalActorAlternateId",
173 | "type": "string"
174 | },
175 | {
176 | "isDefaultDisplay": false,
177 | "isHidden": false,
178 | "name": "OriginalClientDevice",
179 | "type": "string"
180 | },
181 | {
182 | "isDefaultDisplay": false,
183 | "isHidden": false,
184 | "name": "OriginalOutcomeResult",
185 | "type": "string"
186 | },
187 | {
188 | "isDefaultDisplay": false,
189 | "isHidden": false,
190 | "name": "OriginalSeverity",
191 | "type": "string"
192 | },
193 | {
194 | "isDefaultDisplay": false,
195 | "isHidden": false,
196 | "name": "OriginalTarget",
197 | "type": "dynamic"
198 | },
199 | {
200 | "isDefaultDisplay": false,
201 | "isHidden": false,
202 | "name": "OriginalUserId",
203 | "type": "string"
204 | },
205 | {
206 | "isDefaultDisplay": false,
207 | "isHidden": false,
208 | "name": "OriginalUserType",
209 | "type": "string"
210 | },
211 | {
212 | "isDefaultDisplay": false,
213 | "isHidden": false,
214 | "name": "Request",
215 | "type": "dynamic"
216 | },
217 | {
218 | "isDefaultDisplay": false,
219 | "isHidden": false,
220 | "name": "SecurityContextAsNumber",
221 | "type": "int"
222 | },
223 | {
224 | "isDefaultDisplay": false,
225 | "isHidden": false,
226 | "name": "SecurityContextAsOrg",
227 | "type": "string"
228 | },
229 | {
230 | "isDefaultDisplay": false,
231 | "isHidden": false,
232 | "name": "SecurityContextDomain",
233 | "type": "string"
234 | },
235 | {
236 | "isDefaultDisplay": false,
237 | "isHidden": false,
238 | "name": "SecurityContextIsProxy",
239 | "type": "boolean"
240 | },
241 | {
242 | "isDefaultDisplay": false,
243 | "isHidden": false,
244 | "name": "SrcDeviceType",
245 | "type": "string"
246 | },
247 | {
248 | "isDefaultDisplay": false,
249 | "isHidden": false,
250 | "name": "SrcDvcId",
251 | "type": "string"
252 | },
253 | {
254 | "isDefaultDisplay": false,
255 | "isHidden": false,
256 | "name": "SrcDvcOs",
257 | "type": "string"
258 | },
259 | {
260 | "isDefaultDisplay": false,
261 | "isHidden": false,
262 | "name": "SrcGeoCity",
263 | "type": "string"
264 | },
265 | {
266 | "isDefaultDisplay": false,
267 | "isHidden": false,
268 | "name": "SrcGeoCountry",
269 | "type": "string"
270 | },
271 | {
272 | "isDefaultDisplay": false,
273 | "isHidden": false,
274 | "name": "SrcGeoLatitude",
275 | "type": "real"
276 | },
277 | {
278 | "isDefaultDisplay": false,
279 | "isHidden": false,
280 | "name": "SrcGeoLongtitude",
281 | "type": "real"
282 | },
283 | {
284 | "isDefaultDisplay": false,
285 | "isHidden": false,
286 | "name": "SrcGeoPostalCode",
287 | "type": "string"
288 | },
289 | {
290 | "isDefaultDisplay": false,
291 | "isHidden": false,
292 | "name": "SrcGeoRegion",
293 | "type": "string"
294 | },
295 | {
296 | "isDefaultDisplay": false,
297 | "isHidden": false,
298 | "name": "SrcIpAddr",
299 | "type": "string"
300 | },
301 | {
302 | "isDefaultDisplay": false,
303 | "isHidden": false,
304 | "name": "SrcIsp",
305 | "type": "string"
306 | },
307 | {
308 | "isDefaultDisplay": false,
309 | "isHidden": false,
310 | "name": "SrcZone",
311 | "type": "string"
312 | },
313 | {
314 | "isDefaultDisplay": false,
315 | "isHidden": false,
316 | "name": "TimeGenerated",
317 | "type": "datetime"
318 | },
319 | {
320 | "isDefaultDisplay": false,
321 | "isHidden": false,
322 | "name": "TransactionDetail",
323 | "type": "dynamic"
324 | },
325 | {
326 | "isDefaultDisplay": false,
327 | "isHidden": false,
328 | "name": "TransactionId",
329 | "type": "string"
330 | },
331 | {
332 | "isDefaultDisplay": false,
333 | "isHidden": false,
334 | "name": "TransactionType",
335 | "type": "string"
336 | },
337 | {
338 | "isDefaultDisplay": false,
339 | "isHidden": false,
340 | "name": "Version",
341 | "type": "string"
342 | },
343 | {
344 | "isDefaultDisplay": false,
345 | "isHidden": false,
346 | "name": "SrcDvcIdType",
347 | "type": "string"
348 | }
349 | ]
350 | }
351 | }
352 | }
353 | '@
354 |
--------------------------------------------------------------------------------
/dataconnectors/okta/oktaccp.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0",
4 | "parameters": {
5 | "apikey": {
6 | "defaultValue": "-NA-",
7 | "minLength": 1,
8 | "type": "SecureString"
9 | },
10 | "domainname": {
11 | "defaultValue": "Enter Okta domainname value",
12 | "minLength": 1,
13 | "type": "String"
14 | },
15 | "workspace": {
16 | "defaultValue": "Sentinel Workspace Name",
17 | "type": "String"
18 | },
19 | "dataCollectionEndpoint": {
20 | "defaultValue": "Data Collection Endpoint name (found via the DCR Overview Page)",
21 | "type": "String"
22 | },
23 | "dataCollectionRuleImmutableId": {
24 | "defaultValue": "DCR immutableId (found via the JSON view on the DCR)",
25 | "type": "String"
26 | }
27 | },
28 | "variables": {
29 | "_dataConnectorContentIdConnections2": "OktaSSOv2Connections",
30 | "_oktaDomainHostName": "[first(split(parameters('domainname'), '.'))]"
31 | },
32 | "resources": [
33 | {
34 | "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors",
35 | "apiVersion": "2023-02-01-preview",
36 | "name": "[concat(parameters('workspace'), '/Microsoft.SecurityInsights/OktaDCV1_2_', variables('_oktaDomainHostName'))]",
37 | "location": "westus2",
38 | "kind": "RestApiPoller",
39 | "properties": {
40 | "connectorDefinitionName": "OktaSSOv2",
41 | "dcrConfig": {
42 | "dataCollectionEndpoint": "[parameters('dataCollectionEndpoint')]",
43 | "dataCollectionRuleImmutableId": "[parameters('dataCollectionRuleImmutableId')]",
44 | "streamName": "Custom-OktaSSO_CL"
45 | },
46 | "addOnAttributes": {
47 | "Domain": "[parameters('domainname')]"
48 | },
49 | "dataType": "Okta System Log API",
50 | "response": {
51 | "eventsJsonPaths": [
52 | "$"
53 | ],
54 | "format": "json"
55 | },
56 | "paging": {
57 | "pagingType": "LinkHeader"
58 | },
59 | "auth": {
60 | "apiKeyName": "Authorization",
61 | "ApiKey": "[parameters('apikey')]",
62 | "apiKeyIdentifier": "SSWS",
63 | "type": "APIKey"
64 | },
65 | "request": {
66 | "apiEndpoint": "[concat('https://', parameters('domainname'), '/api/v1/logs')]",
67 | "rateLimitQPS": 10,
68 | "queryWindowInMin": 5,
69 | "httpMethod": "GET",
70 | "retryCount": 3,
71 | "timeoutInSeconds": 60,
72 | "headers": {
73 | "Accept": "application/json",
74 | "User-Agent": "Scuba"
75 | },
76 | "startTimeAttributeName": "since",
77 | "endTimeAttributeName": "until"
78 | },
79 | "isActive": true
80 | }
81 | }
82 | ]
83 | }
84 |
--------------------------------------------------------------------------------
/enable-sentinel-mdfc-sub-con/custom-role.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "/providers/Microsoft.Authorization/roleDefinitions/46e686a4-16b0-401b-945d-e83196987077",
3 | "properties": {
4 | "roleName": "Microsoft Sentinel Defender for Cloud Connector Contributor",
5 | "description": "Microsoft Sentinel Defender for Cloud Connector Contributor",
6 | "assignableScopes": [
7 | "/providers/Microsoft.Management/managementGroups/869d26a0-a8cb-4068-aba6-c3284b615873"
8 | ],
9 | "permissions": [
10 | {
11 | "actions": [
12 | "Microsoft.Security/register/action",
13 | "Microsoft.Security/*/read",
14 | "Microsoft.Resources/subscriptions/read",
15 | "Microsoft.SecurityInsights/dataConnectors/read",
16 | "Microsoft.SecurityInsights/dataConnectors/write",
17 | "Microsoft.Security/settings/write"
18 | ],
19 | "notActions": [],
20 | "dataActions": [],
21 | "notDataActions": []
22 | }
23 | ]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/enable-sentinel-mdfc-sub-con/customRoleDeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "roleName": {
6 | "defaultValue": "Microsoft Sentinel Defender for Cloud Connector Contributor",
7 | "type": "String",
8 | "metadata": {
9 | "description": "Friendly name of the role definition"
10 | }
11 | },
12 | "roleDescription": {
13 | "defaultValue": "Microsoft Sentinel Defender for Cloud Connector Contributor",
14 | "type": "String",
15 | "metadata": {
16 | "description": "Detailed description of the role definition"
17 | }
18 | }
19 | },
20 | "variables": {
21 | "roleDefName": "[guid(managementGroup().id, string(parameters('roleName')))]"
22 | },
23 | "resources": [
24 | {
25 | "type": "Microsoft.Authorization/roleDefinitions",
26 | "apiVersion": "2022-04-01",
27 | "name": "[variables('roleDefName')]",
28 | "properties": {
29 | "roleName": "[parameters('roleName')]",
30 | "description": "[parameters('roleDescription')]",
31 | "type": "customRole",
32 | "permissions": [
33 | {
34 | "actions": [
35 | "Microsoft.Security/register/action",
36 | "Microsoft.Security/*/read",
37 | "Microsoft.Resources/subscriptions/read",
38 | "Microsoft.SecurityInsights/dataConnectors/read",
39 | "Microsoft.SecurityInsights/dataConnectors/write",
40 | "Microsoft.Security/settings/write"
41 | ],
42 | "notActions": [],
43 | "dataActions": [],
44 | "notDataActions": []
45 | }
46 | ],
47 | "assignableScopes": [
48 | "[managementGroup().id]"
49 | ]
50 | }
51 | }
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/enable-sentinel-mdfc-sub-con/logicapp.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | !$defaultBg = '#d9dadb'
3 | !$borderColor = '#999a9b'
4 | !$darkColor = '#494a4b'
5 | skinparam ActivityDiamondBackgroundColor $defaultBg
6 | skinparam ActivityDiamondBorderColor $borderColor
7 | skinparam ActivityBorderColor $borderColor
8 | skinparam ArrowColor $darkColor
9 | skinparam ActivityBarColor $darkColor
10 | skinparam ActivityStartColor $darkColor
11 | skinparam ActivityEndColor $darkColor
12 | start
13 | fork
14 | :Recurrence;
15 | endfork
16 | #eadef8:InitializeVariable
17 | Initialize Variable - Run Time;
18 | #eadef8:InitializeVariable
19 | Initialize Variable - sentinel-subscriptionid;
20 | #eadef8:InitializeVariable
21 | Initialize Variable - sentinel-workspacename;
22 | #eadef8:InitializeVariable
23 | Initialize Variable - sentinel-resourcegroupname;
24 | #eadef8:InitializeVariable
25 | Initialize variable - taskOutput;
26 | #eadef8:InitializeVariable
27 | Initialize Variable - new-connected-subscriptions;
28 | #eadef8:InitializeVariable
29 | Initialize variable - subscriptionsToCheck;
30 | #eadef8:InitializeVariable
31 | Initialize variable - alertSyncEnabled;
32 | #c4e2ff:ApiConnection
33 | List subscriptions;
34 | :Select
35 | Parse Subscription List;
36 | if (Check for Excluded Subscriptions) then (yes)
37 | :Query
38 | Filter out excluded subscriptions;
39 | #eadef8:SetVariable
40 | Set variable - subscriptionsToCheck (excluded filtered);
41 |
42 | else (no)
43 | #eadef8:SetVariable
44 | Set variable - subscriptionsToCheck;
45 |
46 | endif
47 | fork
48 | if (Check for valid list of Subscriptions - Enable Bi-Directional Alert Sync) then (yes)
49 | while (Enable Bi-Directional Alert Sync)
50 | #d9eced:Http
51 | Check for Alert Sync Settings;
52 | #eadef8:SetVariable
53 | Set variable - alertSyncEnabled;
54 | if (Check Alert Sync Settings) then (yes)
55 |
56 | else (no)
57 | #d9eced:Http
58 | Enable Alert Sync;
59 | :AppendToArrayVariable
60 | Append to array variable;
61 |
62 | endif
63 | endwhile
64 |
65 | else (no)
66 |
67 | endif
68 | if (Check for Output) then (yes)
69 | if (Condition - Log Results) then (yes)
70 | while (Send to Log Analytics)
71 | #c4e2ff:ApiConnection
72 | Send Data;
73 | endwhile
74 |
75 | else (no)
76 |
77 | endif
78 | if (Condition - Send Email) then (yes)
79 | :Table
80 | Create HTML table;
81 | #c4e2ff:ApiConnection
82 | Send an email (V2);
83 |
84 | else (no)
85 |
86 | endif
87 |
88 | else (no)
89 |
90 | endif
91 | forkagain
92 | if (Check for valid list of Subscriptions - Enable Data Connector Group) then (yes)
93 | #c4e2ff:ApiConnection
94 | Get All Data Connectors;
95 | :ParseJson
96 | Parse Data Connector JSON;
97 | :Query
98 | Return Defender for Cloud Data Connectors;
99 | :Select
100 | Parse Enabled Subscription Ids;
101 | :Query
102 | Filter Subscriptions Not Connected;
103 | if (Check for Subscriptions to Enable) then (yes)
104 | while (Enable disconnected Subscriptions)
105 | #d9eced:Http
106 | Enable the Data Connector;
107 | :AppendToArrayVariable
108 | Append to array variable 2;
109 | endwhile
110 |
111 | else (no)
112 |
113 | endif
114 |
115 | else (no)
116 |
117 | endif
118 | if (Check for Output) then (yes)
119 | if (Condition - Log Results) then (yes)
120 | while (Send to Log Analytics)
121 | #c4e2ff:ApiConnection
122 | Send Data;
123 | endwhile
124 |
125 | else (no)
126 |
127 | endif
128 | if (Condition - Send Email) then (yes)
129 | :Table
130 | Create HTML table;
131 | #c4e2ff:ApiConnection
132 | Send an email (V2);
133 |
134 | else (no)
135 |
136 | endif
137 |
138 | else (no)
139 |
140 | endif
141 | forkagain
142 | if (Check for valid list of Subscriptions - Register Subscriptions with Defender) then (yes)
143 | while (Register Subscriptions with Defender for Cloud)
144 | #d9eced:Http
145 | Check for Microsoft.Security Resource Provider Registration;
146 | if (Check for Registered Resource Provider) then (yes)
147 | #c4e2ff:ApiConnection
148 | Register resource provider - Microsoft.Security;
149 | :AppendToArrayVariable
150 | Append to array variable 3;
151 |
152 | else (no)
153 |
154 | endif
155 | endwhile
156 |
157 | else (no)
158 |
159 | endif
160 | if (Check for Output) then (yes)
161 | if (Condition - Log Results) then (yes)
162 | while (Send to Log Analytics)
163 | #c4e2ff:ApiConnection
164 | Send Data;
165 | endwhile
166 |
167 | else (no)
168 |
169 | endif
170 | if (Condition - Send Email) then (yes)
171 | :Table
172 | Create HTML table;
173 | #c4e2ff:ApiConnection
174 | Send an email (V2);
175 |
176 | else (no)
177 |
178 | endif
179 |
180 | else (no)
181 |
182 | endif
183 | endfork
184 | stop
185 | @enduml
186 |
--------------------------------------------------------------------------------
/enable-sentinel-mdfc-sub-con/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Microsoft Sentinel Defender for Cloud Data Connector At Scale
3 |
4 | > ℹ️ This solution is no longer needed. Defender for Cloud is now integrated into the Defender XDR at the tenant level. You can integrate Defender XDR with Sentinel to sync all alerts and incidents.
5 | > See [Microsoft Defender for Cloud in Microsoft Defender XDR](https://learn.microsoft.com/en-us/microsoft-365/security/defender/microsoft-365-security-center-defender-cloud?view=o365-worldwide)
6 |
7 | This workflow will enable the Microsoft Defender for Cloud data connector in Microsoft Sentinel automatically for all subscriptions you have the logic app scoped to. The solution also provides the ability to:
8 |
9 | - Exclude Subscriptions
10 | - Log results to your Sentinel Workspace
11 | - Leverage a workbook to track and audit connector changes
12 | - Send Email Notifications
13 |
14 | > TLDR Version
15 |
16 | > [Step 1 - Deploy Custom Role](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fenable-sentinel-mdfc-sub-con%2FcustomRoleDeploy.json) ---> [Step 2 - Deploy Logic App](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fenable-sentinel-mdfc-sub-con%2FcustomRoleDeploy.json) ---> [Step 3 - Deploy Workbook](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fenable-sentinel-mdfc-sub-con%2FdefenderForCloudConnectorCoverage.json)
17 |
18 | > ----
19 |
20 | - [Requirements](#requirements)
21 | - [Setup and Configuration](#setup-and-configuration)
22 | - [Create a Custom Role](#create-a-custom-role)
23 | - [Deploy the Logic App](#deploy-the-logic-app)
24 | - [Authorize Permissions](#authorize-permissions)
25 | - [Assign the Role to the Logic App System Managed Identity](#assign-the-role-to-the-logic-app-system-managed-identity)
26 | - [Enable the Logic App](#enable-the-logic-app)
27 | - [Working with Parameters](#working-with-parameters)
28 | - [Workbook](#workbook)
29 | - [Logic App Overview](#logic-app-overview)
30 | - [Credentials Used](#credentials-used)
31 | - [Workflow](#workflow)
32 | - [Troubleshooting](#troubleshooting)
33 |
34 | ## Requirements
35 |
36 | - The custom role described in [Create a Custom Role](##create-a-custom-role)
37 | - Rights to assign the role and scope to either subscriptions or management groups
38 | - Rights to complete the deployment, Log Analytics Contributor, Microsoft Sentinel Contributor, and Contributor.
39 | - Rights to create a custom role at the desired scope, such as Owner or User Access Administrator
40 |
41 | ## Setup and Configuration
42 |
43 | ### Create a Custom Role
44 | I highly reccomend creating the custom role to follow the principle of least privilege.
45 | There is no need to assign the built-in roles that provide more permissions than required.
46 |
47 | > You will need permissions to create custom roles at the management group level, such as Owner or User Access Administrator
48 |
49 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fenable-sentinel-mdfc-sub-con%2FcustomRoleDeploy.json)
50 |
51 | **Manual Steps**
52 |
53 | 1. Follow the steps outlined in [Create or update Azure custom roles using the Azure portal](https://learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles-portal#start-from-scratch)
54 |
55 | 3. For the name I reccomend using **Microsoft Sentinel Defender for Cloud Connector Contributor**
56 |
57 | 4. For the role definition use the [custom role defintion](https://github.com/seanstark/sentinel-tools/blob/main/enable-sentinel-mdfc-sub-con/custom-role.json) json
58 |
59 | ### Deploy the Logic App
60 |
61 | > The Logic App will be deployed in a disabled state by default
62 |
63 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fenable-sentinel-mdfc-sub-con%2Fazuredeploy.json)
64 |
65 | ### Authorize Permissions
66 | Most of the logic leverages the System Managed Identity to perform tasks. However if you plan on sending email notifications you will need to authorize the Office 365 Outlook connector.
67 |
68 | 1. Navigate to the Logic App in the Azure Portal
69 | 2. Select API connections on the left hand side
70 | 3. Select the Office365-'logic app name' api connection
71 | 4. Select Edit API connection
72 | 5. Select Authorize and complete the authorization process
73 | 6. Make sure to click Save
74 |
75 | ### Assign the Role to the Logic App System Managed Identity
76 | > - If you would like to test the logic app first on a subset of subscriptions only assign the role to that scope
77 | > - If you can't use the custom role you will need to assign the System Managed Identity the Security Admin and Microsoft Sentinel Contributor.
78 | > - In the example below the role is assigned at the root managment group level.
79 | > - I would reccomend waiting 15 minutes after assigning the role before executing your first run of the logic.
80 |
81 | 1. Navigate to [Azure Management Groups](https://azmg.cmd.ms/)
82 | 2. Select your **Tenant Root Group**
83 | 3. Select **Access Control (IAM)**
84 | 4. Select **Add** > **Add Role Assignment**
85 | 5. Select the custom role you created earlier (Microsoft Sentinel Defender for Cloud Connector Contributor)
86 | 6. Under the **Members** tab select **Managed Identity**
87 | 7. Click Select **Members** and select the Logic App Managed Identity
88 | 8. Click **Next** > **Review + Assign**
89 |
90 | ### Enable the Logic App
91 | 1. From the Logic App **Overview** pane, select **Enable** in the top menu bar
92 | 2. If you would like to run the logic immediately you can select **Run Trigger**
93 |
94 | ### Working with Parameters
95 |
96 | There are several parameters you can update in the logic app
97 |
98 | 1. Navigate to the Logic App in the Azure Portal and select **Logic App Designer**
99 | 2. Select **Parameters**
100 | 3. Update the parameter according to the table below
101 | 4. Make sure to the **Save** the Logic App
102 |
103 | > true or false values are case sensitive
104 |
105 | | Parameter | Use Case | Type | Value Example |
106 | |----------------|-------------------------------|------| -----------------------------|
107 | | emailRecipients | Email Addresses to send notifications to. Semi-colon seperated values | string | ``` user1@domain.com;dl@domain.com ``` |
108 | | excludedSubscriptions | Exclude subscriptions from being enabled | array | ``` ["ada06e68-375e-4210-b43a-c6fgdcebf41c5","ada0dht8-375e-4210-be3a-c6cacebf41c5"] ``` |
109 | | logResults | Log results to your Sentinel Workspace | string | ``` true or false ``` |
110 | | sendEmail | Send email notifications on subscriptions that were enabled | string | ``` true or false ``` |
111 | | sentinel-resourcegroupname | The reource group name where Sentinel Resides | string | ``` sentinel-prd ``` |
112 | | sentinel-subscriptionid | The subscription ID where Sentinel resides | string | ``` ada06e68-375e-4210-b43a-c6fgdcebf41c5 ``` |
113 | | sentinel-workspacename | The workspace name of Sentinel | string | ``` sentinel-wrk-prd ``` |
114 |
115 | ## Workbook
116 | To fully leverage this workbook you will need to enable logging within the Logic App by setting the **logResults** parameter to **true**. You will also need to ensure Azure Activity logs are being sent to your Sentinel workspace.
117 |
118 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fenable-sentinel-mdfc-sub-con%2FdefenderForCloudConnectorCoverage.json)
119 |
120 | 
121 |
122 | ## Logic App Overview
123 |
124 | ### Credentials Used
125 |
126 | | Credential | Type | Use Case |
127 | |----------------|--------------|-------------|
128 | | Logic App System Managed Identity | System Managed Identity | Get Azure Subscriptions, Defender for Cloud Settings, Sentinel Data Connectors. Put Defender for Cloud Settings, Enable Bi-Directional Incident Sync, Create the Sentinel Data Connector, Register the Microsoft.Security Resource Provider
129 | | Log Analytics API Key | Workspace Shared Key | Log Results to a custom table in your Sentinel Workspace |
130 | | Azure AD Account | User | Sending Email Notifications after execution, graph api permissions to an Office 365 Mailbox |
131 |
132 | ### Workflow
133 |
134 | The Logic app runs on a re-occuring schedule every 12 hours by default. The overall sequence of events are as follows.
135 |
136 | 
137 |
138 | ## Troubleshooting
139 |
140 | - The Logic App won't present any errors when the system managed identity doesn't have permissions to list subscriptions in the tenant.
141 | - Ensure the system managed identity is assign the custom role and applied to either management group or subscription scopes.
142 | - Ensure the Logic App API Connections are properly authorized
143 | - Ensure the loganalyticsdatacollector-'logic app name' API connection has the correct workspaceid and workspace shared key for logging results
144 |
145 |
146 |
--------------------------------------------------------------------------------
/enable-sentinel-mdfc-sub-con/workbook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanstark/sentinel-tools/1832eec427e35a748d236ac2506b30da25f7dfbc/enable-sentinel-mdfc-sub-con/workbook.png
--------------------------------------------------------------------------------
/images/dcr-json.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanstark/sentinel-tools/1832eec427e35a748d236ac2506b30da25f7dfbc/images/dcr-json.png
--------------------------------------------------------------------------------
/images/dcr-verify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanstark/sentinel-tools/1832eec427e35a748d236ac2506b30da25f7dfbc/images/dcr-verify.png
--------------------------------------------------------------------------------
/images/dcr-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanstark/sentinel-tools/1832eec427e35a748d236ac2506b30da25f7dfbc/images/dcr-view.png
--------------------------------------------------------------------------------
/images/dcr.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanstark/sentinel-tools/1832eec427e35a748d236ac2506b30da25f7dfbc/images/dcr.gif
--------------------------------------------------------------------------------
/images/github_pat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanstark/sentinel-tools/1832eec427e35a748d236ac2506b30da25f7dfbc/images/github_pat.png
--------------------------------------------------------------------------------
/logstash-cef-adx/logstash.conf:
--------------------------------------------------------------------------------
1 | input {
2 | tcp {
3 | port => 5514
4 | type => syslog
5 | tags => [ "PAN-OS_syslog" ]
6 | }
7 | udp {
8 | port => 5514
9 | type => syslog
10 | tags => [ "PAN-OS_syslog" ]
11 | }
12 | }
13 |
14 | filter {
15 | if "PAN-OS_syslog" in [tags] {
16 |
17 | # Log types are "TRAFFIC", "THREAT", "CONFIG", "SYSTEM" and "HIP-MATCH".
18 |
19 | # Traffic log fields: https://docs.paloaltonetworks.com/pan-os/9-0/pan-os-admin/monitoring/use-syslog-for-monitoring/syslog-field-descriptions/traffic-log-fields.html
20 | if ([message] =~ /TRAFFIC/) {
21 | csv {
22 | source => "message"
23 | columns => [
24 | "FUTURE_USE", "ReceiveTime", "SerialNumber", "Type", "Threat_ContentType", "FUTURE_USE",
25 | "GeneratedTime", "SourceIP", "DestinationIP", "NATSourceIP", "NATDestinationIP", "RuleName",
26 | "SourceUser", "DestinationUser", "Application", "VirtualSystem", "SourceZone", "DestinationZone",
27 | "InboundInterface", "OutboundInterface", "LogAction", "FUTURE_USE", "SessionID",
28 | "RepeatCount", "SourcePort", "DestinationPort", "NATSourcePort", "NATDestinationPort", "Flags",
29 | "Protocol", "Action", "Bytes", "BytesSent", "BytesReceived", "Packets", "StartTime", "ElapsedTime",
30 | "Category", "FUTURE_USE", "SequenceNumber", "ActionFlags", "SourceLocation",
31 | "DestinationLocation", "FUTURE_USE", "PacketsSent", "PacketsReceived", "SessionEndReason",
32 | "DeviceGroupHierarchyLevel1", "DeviceGroupHierarchyLevel2", "DeviceGroupHierarchyLevel3",
33 | "DeviceGroupHierarchyLevel4", "VirtualSystemName", "DeviceName", "ActionSource", "SourceVMUUID",
34 | "DestinationVMUUID", "TunnelID_IMSI", "MonitorTag_IMEI", "ParentSessionID", "ParentStartTime",
35 | "TunnelType", "SCTPAssociationID", "SCTPChunks", "SCTPChunksSent", "SCTPChunksReceived"
36 | ]
37 | }
38 |
39 | mutate {
40 | convert => [ "Bytes", "integer" ]
41 | convert => [ "BytesReceived", "integer" ]
42 | convert => [ "BytesSent", "integer" ]
43 | convert => [ "ElapsedTime", "integer" ]
44 | convert => [ "GeoIP.dma_code", "integer" ]
45 | convert => [ "GeoIP.latitude", "float" ]
46 | convert => [ "GeoIP.longitude", "float" ]
47 | convert => [ "NATDestinationPort", "integer" ]
48 | convert => [ "NATSourcePort", "integer" ]
49 | convert => [ "Packets", "integer" ]
50 | convert => [ "PacketsReceived", "integer" ]
51 | convert => [ "PacketsSent", "integer" ]
52 | convert => [ "SequenceNumber", "integer" ]
53 |
54 | add_tag => [ "PAN-OS_traffic"]
55 | }
56 | }
57 |
58 | # Threat log fields: https://docs.paloaltonetworks.com/pan-os/9-0/pan-os-admin/monitoring/use-syslog-for-monitoring/syslog-field-descriptions/threat-log-fields.html
59 | else if ([message] =~ /THREAT/) {
60 | csv {
61 | source => "message"
62 | columns => [
63 | "FUTURE_USE", "ReceiveTime", "SerialNumber", "Type", "Threat_ContentType", "FUTURE_USE",
64 | "GeneratedTime", "SourceIP", "DestinationIP", "NATSourceIP", "NATDestinationIP", "RuleName",
65 | "SourceUser", "DestinationUser", "Application", "VirtualSystem", "SourceZone", "DestinationZone",
66 | "InboundInterface", "OutboundInterface", "LogAction", "FUTURE_USE", "SessionID",
67 | "RepeatCount", "SourcePort", "DestinationPort", "NATSourcePort", "NATDestinationPort", "Flags",
68 | "Protocol", "Action", "URL_Filename", "ThreatID", "Category", "Severity", "Direction",
69 | "SequenceNumber", "ActionFlags", "SourceLocation", "DestinationLocation", "FUTURE_USE",
70 | "ContentType", "PCAP_ID", "FileDigest", "Cloud", "URLIndex", "UserAgent", "FileType",
71 | "X-Forwarded-For", "Referer", "Sender", "Subject", "Recipient", "ReportID",
72 | "DeviceGroupHierarchyLevel1", "DeviceGroupHierarchyLevel2", "DeviceGroupHierarchyLevel3",
73 | "DeviceGroupHierarchyLevel4", "VirtualSystemName", "DeviceName", "FUTURE_USE", "SourceVMUUID",
74 | "DestinationVMUUID", "HTTPMethod", "TunnelID_IMSI", "MonitorTag_IMEI", "ParentSessionID",
75 | "ParentStartTime", "TunnelType", "ThreatCategory", "ContentVersion", "FUTURE_USE" ,
76 | "SCTPAssociationID", "PayloadProtocolID", "HTTPHeaders"
77 | ]
78 | }
79 |
80 | mutate {
81 | convert => [ "GeoIP.dma_code", "integer" ]
82 | convert => [ "GeoIP.latitude", "float" ]
83 | convert => [ "GeoIP.longitude", "float" ]
84 | convert => [ "NATDestinationPort", "integer" ]
85 | convert => [ "NATSourcePort", "integer" ]
86 | convert => [ "SequenceNumber", "integer" ]
87 |
88 | add_tag => ["PAN-OS_threat"]
89 | }
90 | }
91 |
92 | mutate {
93 | # Original message has been fully parsed, so remove it.
94 | remove_field => [ "message" ]
95 | }
96 |
97 | # Geolocate logs that have SourceIP if that SourceIP is a non-RFC1918 address
98 | if [SourceIP] and [SourceIP] !~ "(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)|(^169\.254\.)" {
99 | geoip {
100 | source => "SourceIP"
101 | target => "SourceIPGeo"
102 | }
103 |
104 | # Delete 0,0 in SourceIPGeo.location if equal to 0,0
105 | if ([SourceIPGeo.location] and [SourceIPGeo.location] =~ "0,0") {
106 | mutate {
107 | replace => [ "SourceIPGeo.location", "" ]
108 | }
109 | }
110 | }
111 |
112 | # Geolocate logs that have DestinationIP and if that DestinationIP is a non-RFC1918 address
113 | if [DestinationIP] and [DestinationIP] !~ "(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)|(^169\.254\.)" {
114 | geoip {
115 | source => "DestinationIP"
116 | target => "DestinationIPGeo"
117 | }
118 |
119 | # Delete 0,0 in DestinationIPGeo.location if equal to 0,0
120 | if ([DestinationIPGeo.location] and [DestinationIPGeo.location] =~ "0,0") {
121 | mutate {
122 | replace => [ "DestinationIPGeo.location", "" ]
123 | }
124 | }
125 | }
126 |
127 | # Takes the 5-tuple of source address, source port, destination address, destination port, and protocol and does a SHA1 hash to fingerprint the flow. This is a useful
128 | # way to be able to do top N terms queries on flows, not just on one field.
129 | if [SourceIP] and [DestinationIP] {
130 | fingerprint {
131 | concatenate_sources => true
132 | method => "SHA1"
133 | key => "logstash"
134 | source => [ "SourceIP", "SourcePort", "DestinationIP", "DestinationPort", "Protocol" ]
135 | }
136 | }
137 |
138 | }
139 | }
140 |
141 | output {
142 | kusto {
143 | path => "/tmp/kusto/%{+YYYY-MM-dd-HH-mm-ss}.txt"
144 | ingest_url => ""
145 | app_id => ""
146 | app_key => ""
147 | app_tenant => ""
148 | database => ""
149 | table => "CommonSecurityLog"
150 | json_mapping => "CEF"
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/logstash-cef-adx/readme.md:
--------------------------------------------------------------------------------
1 | placeholder
2 |
--------------------------------------------------------------------------------
/logstash/logstash-syslog-dcr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "dataCollectionRuleName": {
6 | "type": "String",
7 | "defaultValue": "logstash-sentinel",
8 | "metadata": {
9 | "description": "Specify the name of the Data Collection Rule to create."
10 | }
11 | },
12 | "location": {
13 | "defaultValue": "",
14 | "type": "String",
15 | "metadata": {
16 | "description": "Specify the location in which to create the Data Collection Rule."
17 | }
18 | },
19 | "workspaceResourceId": {
20 | "type": "String",
21 | "metadata": {
22 | "description": "Specify the Azure resource ID of the Log Analytics workspace to use."
23 | }
24 | },
25 | "dataCollectionEndpointResourceId": {
26 | "type": "String",
27 | "metadata": {
28 | "description": "Specify the Azure resource ID of the Data Collection Endpoint to use."
29 | }
30 | },
31 | "transformKql": {
32 | "defaultValue": "source | project TimeGenerated = ls_timestamp, HostName = hostname, Facility = facility, SeverityLevel = severity, SyslogMessage = message, ProcessID = pid, ProcessName = process_name, Computer = hostname, HostIP = ip, EventTime = timestamp, SourceSystem = service",
33 | "type": "String",
34 | "metadata": {
35 | "description": "The KQL statement to transform the data on ingest. The TimeGenerated datetime field is required"
36 | }
37 | }
38 | },
39 | "resources": [
40 | {
41 | "type": "Microsoft.Insights/dataCollectionRules",
42 | "apiVersion": "2021-09-01-preview",
43 | "name": "[parameters('dataCollectionRuleName')]",
44 | "location": "[parameters('location')]",
45 | "properties": {
46 | "dataCollectionEndpointId": "[parameters('dataCollectionEndpointResourceId')]",
47 | "streamDeclarations": {
48 | "Custom-Microsoft-Syslog": {
49 | "columns": [
50 | {
51 | "name": "ls_timestamp",
52 | "type": "datetime"
53 | },
54 | {
55 | "name": "hostname",
56 | "type": "string"
57 | },
58 | {
59 | "name": "facility",
60 | "type": "string"
61 | },
62 | {
63 | "name": "severity",
64 | "type": "string"
65 | },
66 | {
67 | "name": "message",
68 | "type": "string"
69 | },
70 | {
71 | "name": "pid",
72 | "type": "int"
73 | },
74 | {
75 | "name": "process_name",
76 | "type": "string"
77 | },
78 | {
79 | "name": "ip",
80 | "type": "string"
81 | },
82 | {
83 | "name": "timestamp",
84 | "type": "datetime"
85 | },
86 | {
87 | "name": "service",
88 | "type": "string"
89 | }
90 | ]
91 | }
92 | },
93 | "destinations": {
94 | "logAnalytics": [
95 | {
96 | "workspaceResourceId": "[parameters('workspaceResourceId')]",
97 | "name": "Custom-Microsoft-Syslog-Workspace"
98 | }
99 | ]
100 | },
101 | "dataFlows": [
102 | {
103 | "streams": [
104 | "Custom-Microsoft-Syslog"
105 | ],
106 | "destinations": [
107 | "Custom-Microsoft-Syslog-Workspace"
108 | ],
109 | "transformKql": "[parameters('transformKql')]",
110 | "outputStream": "Microsoft-Syslog"
111 | }
112 | ]
113 | }
114 | }
115 | ],
116 | "outputs": {
117 | "dataCollectionRuleId": {
118 | "type": "String",
119 | "value": "[resourceId('Microsoft.Insights/dataCollectionRules', parameters('dataCollectionRuleName'))]"
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/logstash/readme.md:
--------------------------------------------------------------------------------
1 | ## Sending logs to Sentinel using Logstash
2 |
3 | ## Step 1 - Deploy the Data Collection Rule
4 | Deploy the custom data collection rule in Azure
5 |
6 | | Syslog |
7 | | ------ |
8 | | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Flogstash%2Flogstash-syslog-dcr.json) |
9 |
10 | ## Step 2 - Create a logstash configuration file
11 | Default directory is /etc/logstash/conf.d/
12 |
13 | ### Logstash Configuration Example
14 |
15 | #### Syslog
16 |
17 | ``` ruby
18 | input {
19 | syslog {
20 | port => 11536
21 | type => syslog
22 | }
23 | }
24 | filter {
25 | grok {
26 | match => {
27 | "message" => "%{SYSLOGTIMESTAMP:ls_timestamp} %{SYSLOGHOST:hostname} %{DATA:proc}(?:\[%{POSINT:pid}\])?:%{SPACE}%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:severity} \[%{DATA:proctag}\]%{SPACE}%{GREEDYDATA:message}"
28 | }
29 | }
30 | mutate {
31 | replace => {
32 | "service" => "logstash"
33 | "ip" => "%{[host][ip]}"
34 | "hostname" => "%{[host][hostname]}"
35 | "severity" => "%{[log][syslog][severity][name]}"
36 | "facility" => "%{[log][syslog][facility][name]}"
37 | "pid" => "%{[process][pid]}"
38 | "process_name" => "%{[process][name]}"
39 | }
40 | convert => {
41 | "[pid]" => "integer"
42 | }
43 | }
44 | if [process_name] == "%{[process][name]}" {
45 | mutate {
46 | remove_field => ["process_name", "pid"]
47 | }
48 | }
49 | }
50 | output {
51 | microsoft-sentinel-logstash-output-plugin {
52 | client_app_Id => ""
53 | client_app_secret => ""
54 | tenant_id => ""
55 | data_collection_endpoint => ""
56 | dcr_immutable_id => ""
57 | dcr_stream_name => "Custom-Microsoft-Syslog"
58 | }
59 | }
60 | ```
61 |
62 | ## Step 3 - Configure rsyslog to forward logs to logstash
63 |
64 | Update your rsyslog.conf or other rsyslog config files to sent to the syslog input port you defined above in the logstash config file. Example:
65 |
66 | ```
67 | # Send all syslog to logstash
68 | *.* @0.0.0.0:11536
69 | ```
70 |
71 | ## Step 4 - Test the configuration
72 |
73 | Run logstash interactively to view console output during testing to ensure logs are being sent to the log analytics workspace
74 |
75 | 1. Stop the logstash service first
76 | ```
77 | service logstash stop
78 | ```
79 |
80 | 2. Start logstash in interactive mode.
81 |
82 | ( Specify the full path to the configuration file after -f )
83 | ```
84 | /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/sentinel.conf
85 | ```
86 |
87 | 4. Verify in the console output logstash is listening on the defined local port and logs are getting sent to log analytics.
88 |
89 | 
90 |
91 | 5. You can stop the interactive process by entering ctrl + C
92 |
93 | ## Step 5 - Run logstash as service
94 |
95 | 1. Start logstash as service
96 |
97 | Logstash will use any configuration files you have in the logstash config directory, /etc/logstash/conf.d/ by default.
98 | ```
99 | serivce logstash start
100 | ```
101 |
102 | 3. Verify the service is running
103 | ```
104 | serivce logstash status
105 | ```
106 |
107 | ## Troubleshooting
108 |
109 | ### Debug Logging
110 | You can turn on debug logging with logstash via different methods. The methods below will work when running logstash in interactive mode.
111 |
112 | 1. Stop the logstash service first
113 | ```
114 | service logstash stop
115 | ```
116 |
117 | 2. Add the below to your logstash configuration output section
118 | ``` ruby
119 | stdout {
120 | codec => rubydebug{}
121 | }
122 | ```
123 |
124 | 3. Start logstash in interactive moode.
125 |
126 | ( Specify the full path to the configuration file after -f )
127 | ```
128 | /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/sentinel.conf
129 | ```
130 |
131 | 5. To enable further debug output, open another ssh sessions and run the below
132 | ```
133 | curl -XPUT 'localhost:9600/_node/logging?pretty' -H 'Content-Type: application/json' -d'
134 | {
135 | "logger.logstash.inputs.syslog" : "DEBUG",
136 | "logger.logstash.filters.grok" : "DEBUG",
137 | "logger.logstash.outputs.microsoftsentineloutput" : "DEBUG"
138 | }
139 | '
140 | ```
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/playbooks/get-incident-chatgpt4/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "title": "",
6 | "description": "",
7 | "prerequisites": "",
8 | "postDeployment": [],
9 | "prerequisitesDeployTemplateFile": "",
10 | "lastUpdateTime": "",
11 | "entities": [],
12 | "tags": [],
13 | "support": {
14 | "tier": "community",
15 | "armtemplate": "Generated from https://github.com/Azure/Azure-Sentinel/tree/master/Tools/Playbook-ARM-Template-Generator"
16 | },
17 | "author": {
18 | "name": ""
19 | }
20 | },
21 | "parameters": {
22 | "PlaybookName": {
23 | "defaultValue": "get-incident-chatgpt4",
24 | "type": "string"
25 | }
26 | },
27 | "variables": {
28 | "MicrosoftSentinelConnectionName": "[concat('MicrosoftSentinel-', parameters('PlaybookName'))]",
29 | "Openaigpt4ipConnectionName": "[concat('Openaigpt4ip-', parameters('PlaybookName'))]"
30 | },
31 | "resources": [
32 | {
33 | "properties": {
34 | "provisioningState": "Succeeded",
35 | "state": "Enabled",
36 | "definition": {
37 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
38 | "contentVersion": "1.0.0.0",
39 | "parameters": {
40 | "$connections": {
41 | "defaultValue": {},
42 | "type": "Object"
43 | }
44 | },
45 | "triggers": {
46 | "Microsoft_Sentinel_incident": {
47 | "type": "ApiConnectionWebhook",
48 | "inputs": {
49 | "body": {
50 | "callback_url": "@{listCallbackUrl()}"
51 | },
52 | "host": {
53 | "connection": {
54 | "name": "@parameters('$connections')['azuresentinel']['connectionId']"
55 | }
56 | },
57 | "path": "/incident-creation"
58 | }
59 | }
60 | },
61 | "actions": {
62 | "For_each": {
63 | "foreach": "@triggerBody()?['object']?['properties']?['Alerts']",
64 | "actions": {
65 | "Create_completion_-_OpenAI": {
66 | "runAfter": {
67 | "Defender_365_-_Get_Incident": [
68 | "Succeeded"
69 | ]
70 | },
71 | "type": "ApiConnection",
72 | "inputs": {
73 | "body": {
74 | "model": "text-davinci-003"
75 | },
76 | "host": {
77 | "connection": {
78 | "name": "@parameters('$connections')['openaigpt4ip']['connectionId']"
79 | }
80 | },
81 | "method": "post",
82 | "path": "/v1/completions"
83 | }
84 | },
85 | "Defender_365_-_Get_Incident": {
86 | "runAfter": {},
87 | "type": "Http",
88 | "inputs": {
89 | "authentication": {
90 | "audience": "https://graph.microsoft.com",
91 | "type": "ManagedServiceIdentity"
92 | },
93 | "method": "GET",
94 | "uri": "https://graph.microsoft.com/v1.0/security/alerts_v2/@{last(split(first(split(items('For_each')?['properties']?['alertLink'],'?tid')),'/'))}"
95 | }
96 | }
97 | },
98 | "runAfter": {},
99 | "type": "Foreach"
100 | }
101 | },
102 | "outputs": {}
103 | },
104 | "parameters": {
105 | "$connections": {
106 | "value": {
107 | "azuresentinel": {
108 | "connectionId": "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]",
109 | "connectionName": "[variables('MicrosoftSentinelConnectionName')]",
110 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]",
111 | "connectionProperties": {
112 | "authentication": {
113 | "type": "ManagedServiceIdentity"
114 | }
115 | }
116 | },
117 | "openaigpt4ip": {
118 | "connectionId": "[resourceId('Microsoft.Web/connections', variables('Openaigpt4ipConnectionName'))]",
119 | "connectionName": "[variables('Openaigpt4ipConnectionName')]",
120 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Openaigpt4ip')]"
121 | }
122 | }
123 | }
124 | }
125 | },
126 | "name": "[parameters('PlaybookName')]",
127 | "type": "Microsoft.Logic/workflows",
128 | "location": "[resourceGroup().location]",
129 | "tags": {
130 | "hidden-SentinelTemplateName": "get-incident-chatgpt4",
131 | "hidden-SentinelTemplateVersion": "1.0"
132 | },
133 | "identity": {
134 | "type": "SystemAssigned"
135 | },
136 | "apiVersion": "2017-07-01",
137 | "dependsOn": [
138 | "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]",
139 | "[resourceId('Microsoft.Web/connections', variables('Openaigpt4ipConnectionName'))]"
140 | ]
141 | },
142 | {
143 | "type": "Microsoft.Web/connections",
144 | "apiVersion": "2016-06-01",
145 | "name": "[variables('MicrosoftSentinelConnectionName')]",
146 | "location": "[resourceGroup().location]",
147 | "kind": "V1",
148 | "properties": {
149 | "displayName": "[variables('MicrosoftSentinelConnectionName')]",
150 | "customParameterValues": {},
151 | "parameterValueType": "Alternative",
152 | "api": {
153 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]"
154 | }
155 | }
156 | },
157 | {
158 | "type": "Microsoft.Web/connections",
159 | "apiVersion": "2016-06-01",
160 | "name": "[variables('Openaigpt4ipConnectionName')]",
161 | "location": "[resourceGroup().location]",
162 | "kind": "V1",
163 | "properties": {
164 | "displayName": "[variables('Openaigpt4ipConnectionName')]",
165 | "customParameterValues": {},
166 | "api": {
167 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Openaigpt4ip')]"
168 | }
169 | }
170 | }
171 | ]
172 | }
173 |
--------------------------------------------------------------------------------
/playbooks/get-incident-chatgpt4/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Deploy the Logic App
3 |
4 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fplaybooks%2Fget-incidentdetails%2Fazuredeploy.json)
5 |
6 | # Grant the System Managed Identity SecurityAlert.Read.All rights
7 |
8 | ``` Powershell
9 | Install-Module -Name AzureAD
10 |
11 | $msgraph = Get-AzureADServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
12 | $permission = $msgraph.AppRoles | where Value -Like 'SecurityAlert.Read.All' | Select-Object -First 1
13 |
14 | $msi = Get-AzureADServicePrincipal -ObjectId
15 | New-AzureADServiceAppRoleAssignment -Id $permission.Id -ObjectId $msi.ObjectId -PrincipalId $msi.ObjectId -ResourceId $msgraph.ObjectId
16 | ```
17 |
--------------------------------------------------------------------------------
/playbooks/get-incidentdetails/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "title": "",
6 | "description": "",
7 | "prerequisites": "",
8 | "postDeployment": [],
9 | "prerequisitesDeployTemplateFile": "",
10 | "lastUpdateTime": "",
11 | "entities": [],
12 | "tags": [],
13 | "support": {
14 | "tier": "community",
15 | "armtemplate": "Generated from https://github.com/Azure/Azure-Sentinel/tree/master/Tools/Playbook-ARM-Template-Generator"
16 | },
17 | "author": {
18 | "name": ""
19 | }
20 | },
21 | "parameters": {
22 | "PlaybookName": {
23 | "defaultValue": "get-incidentdetails",
24 | "type": "string"
25 | }
26 | },
27 | "variables": {
28 | "MicrosoftSentinelConnectionName": "[concat('MicrosoftSentinel-', parameters('PlaybookName'))]"
29 | },
30 | "resources": [
31 | {
32 | "properties": {
33 | "provisioningState": "Succeeded",
34 | "state": "Enabled",
35 | "definition": {
36 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
37 | "contentVersion": "1.0.0.0",
38 | "parameters": {
39 | "$connections": {
40 | "defaultValue": {},
41 | "type": "Object"
42 | }
43 | },
44 | "triggers": {
45 | "Microsoft_Sentinel_incident": {
46 | "type": "ApiConnectionWebhook",
47 | "inputs": {
48 | "body": {
49 | "callback_url": "@{listCallbackUrl()}"
50 | },
51 | "host": {
52 | "connection": {
53 | "name": "@parameters('$connections')['azuresentinel']['connectionId']"
54 | }
55 | },
56 | "path": "/incident-creation"
57 | }
58 | }
59 | },
60 | "actions": {
61 | "For_each": {
62 | "foreach": "@triggerBody()?['object']?['properties']?['Alerts']",
63 | "actions": {
64 | "Defender_365_-_Get_Incident": {
65 | "runAfter": {},
66 | "type": "Http",
67 | "inputs": {
68 | "authentication": {
69 | "audience": "https://graph.microsoft.com",
70 | "type": "ManagedServiceIdentity"
71 | },
72 | "method": "GET",
73 | "uri": "https://graph.microsoft.com/v1.0/security/alerts_v2/@{items('For_each')?['properties']?['systemAlertId']}"
74 | }
75 | }
76 | },
77 | "runAfter": {},
78 | "type": "Foreach"
79 | }
80 | },
81 | "outputs": {}
82 | },
83 | "parameters": {
84 | "$connections": {
85 | "value": {
86 | "azuresentinel": {
87 | "connectionId": "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]",
88 | "connectionName": "[variables('MicrosoftSentinelConnectionName')]",
89 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]",
90 | "connectionProperties": {
91 | "authentication": {
92 | "type": "ManagedServiceIdentity"
93 | }
94 | }
95 | }
96 | }
97 | }
98 | }
99 | },
100 | "name": "[parameters('PlaybookName')]",
101 | "type": "Microsoft.Logic/workflows",
102 | "location": "[resourceGroup().location]",
103 | "tags": {
104 | "hidden-SentinelTemplateName": "get-incidentdetails",
105 | "hidden-SentinelTemplateVersion": "1.0"
106 | },
107 | "identity": {
108 | "type": "SystemAssigned"
109 | },
110 | "apiVersion": "2017-07-01",
111 | "dependsOn": [
112 | "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]"
113 | ]
114 | },
115 | {
116 | "type": "Microsoft.Web/connections",
117 | "apiVersion": "2016-06-01",
118 | "name": "[variables('MicrosoftSentinelConnectionName')]",
119 | "location": "[resourceGroup().location]",
120 | "kind": "V1",
121 | "properties": {
122 | "displayName": "[variables('MicrosoftSentinelConnectionName')]",
123 | "customParameterValues": {},
124 | "parameterValueType": "Alternative",
125 | "api": {
126 | "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]"
127 | }
128 | }
129 | }
130 | ]
131 | }
132 |
--------------------------------------------------------------------------------
/playbooks/get-incidentdetails/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Deploy the Logic App
3 |
4 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fseanstark%2Fsentinel-tools%2Fmain%2Fplaybooks%2Fget-incidentdetails%2Fazuredeploy.json)
5 |
6 | # Grant the System Managed Identity SecurityAlert.Read.All rights
7 |
8 | ``` Powershell
9 | Install-Module -Name AzureAD
10 |
11 | $msgraph = Get-AzureADServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
12 | $permission = $msgraph.AppRoles | where Value -Like 'SecurityAlert.Read.All' | Select-Object -First 1
13 |
14 | $msi = Get-AzureADServicePrincipal -ObjectId
15 | New-AzureADServiceAppRoleAssignment -Id $permission.Id -ObjectId $msi.ObjectId -PrincipalId $msi.ObjectId -ResourceId $msgraph.ObjectId
16 | ```
17 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Various Sentinel Tools
--------------------------------------------------------------------------------
/table_tools/copy-logAnalyticsTable.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script will create a new table from existing table with the same properties and schema.
4 |
5 | .PARAMETER sourceTableName
6 | Specify the exisiting table name
7 |
8 | .PARAMETER newTableName
9 | Specify the name of the new table
10 |
11 | .PARAMETER subscriptionID
12 | Specify the subscriptionID of your log analytics workspace
13 |
14 | .PARAMETER resourceGroupName
15 | Specify the Resource Group Name of your log analytics workspace
16 |
17 | .PARAMETER workspaceName
18 | Specify the log analytics workspace name where the source table resides
19 |
20 | .PARAMETER apiVersion
21 | Specify the apiVersion to use, not required
22 |
23 | .EXAMPLE
24 | Create a new log analytics table from an existing one
25 | .\copy-logAnalyticsTable.ps1 -sourceTableName 'Okta_CL' -newTableName 'Okta_AppTeam_CL' -subscriptionID 'ada06dd8-375e-4210-be3a-c6cdde3341c5' -resourceGroupName 'sentinel' -workspaceName 'sentinel-prd'
26 | #>
27 |
28 | param(
29 | [Parameter(Mandatory=$true)]
30 | [string]$sourceTableName,
31 |
32 | [Parameter(Mandatory=$true)]
33 | [string]$newTableName,
34 |
35 | [Parameter(Mandatory=$true)]
36 | [string]$subscriptionID,
37 |
38 | [Parameter(Mandatory=$true)]
39 | [string]$resourceGroupName,
40 |
41 | [Parameter(Mandatory=$true)]
42 | [string]$workspaceName,
43 |
44 | [Parameter(Mandatory=$false)]
45 | [string]$apiVersion = '2021-12-01-preview'
46 |
47 | )
48 |
49 | #Get source table
50 | $sourceTable = Invoke-AzRestMethod -Path ('/subscriptions/{0}/resourcegroups/{1}/providers/microsoft.operationalinsights/workspaces/{2}/tables/{3}?api-version=2021-12-01-preview' -f $subscriptionID, $resourceGroupName, $workspaceName, $sourceTableName, $apiVersion) -Method GET
51 |
52 | # Convert From Json
53 | $sourceTable = $sourceTable.Content | ConvertFrom-Json
54 |
55 | # Check that new table name ends in CL
56 | If (!($newTableName.EndsWith('_CL'))){
57 | $newTableName = $newTableName + "_CL"
58 | }
59 |
60 | # Udpate Schema with new table name and look for default workspace retention properties
61 | $sourceTable.properties.schema.Name = $newTableName
62 | If ($sourceTable.properties.retentionInDaysAsDefault){
63 | $sourceTable.properties.totalRetentionInDays = $null
64 | $sourceTable.properties.retentionInDays = -1
65 | }
66 |
67 | #Create the new table
68 | Invoke-AzRestMethod -Path ('/subscriptions/{0}/resourcegroups/{1}/providers/microsoft.operationalinsights/workspaces/{2}/tables/{3}?api-version=2021-12-01-preview' -f $subscriptionID, $resourceGroupName, $workspaceName, $newTableName, $apiVersion) -Method PUT -Payload $($sourceTable | ConvertTo-Json -Depth 6)
69 |
--------------------------------------------------------------------------------