diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..f915119553 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-azurefunctions", + "ms-vscode.PowerShell" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 811c18f8d2..0e35d3e5a0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,12 +8,17 @@ "name": "Launch Program", "type": "node", "request": "launch", - "args": ["${relativeFile}"], - "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], + "args": [ + "${relativeFile}" + ], + "runtimeArgs": [ + "--nolazy", + "-r", + "ts-node/register" + ], "sourceMaps": true, "cwd": "${workspaceRoot}", - "protocol": "inspector", - + "protocol": "inspector" }, { "type": "node", @@ -26,7 +31,11 @@ "--colors", "${workspaceFolder}/.script/test" ], - "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], + "runtimeArgs": [ + "--nolazy", + "-r", + "ts-node/register" + ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" }, @@ -41,9 +50,21 @@ "--colors", "${file}" ], - "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], + "runtimeArgs": [ + "--nolazy", + "-r", + "ts-node/register" + ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" + }, + { + "name": "Attach to PowerShell Functions", + "type": "PowerShell", + "request": "attach", + "customPipeName": "AzureFunctionsPSWorker", + "runspaceId": 1, + "preLaunchTask": "func: host start" } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 79e520aee0..754c5a8fbb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,7 @@ { - "powershell.codeFormatting.addWhitespaceAroundPipe": true + "powershell.codeFormatting.addWhitespaceAroundPipe": true, + "azureFunctions.deploySubpath": "DataConnectors\\AADUserInfo", + "azureFunctions.projectLanguage": "PowerShell", + "azureFunctions.projectRuntime": "~3", + "debug.internalConsoleOptions": "neverOpen" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..d4f42dfc8b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,14 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "func", + "command": "host start", + "problemMatcher": "$func-powershell-watch", + "isBackground": true, + "options": { + "cwd": "${workspaceFolder}/DataConnectors\\AADUserInfo" + } + } + ] +} \ No newline at end of file diff --git a/DataConnectors/AADUserInfo/.funcignore b/DataConnectors/AADUserInfo/.funcignore new file mode 100644 index 0000000000..414df2f01a --- /dev/null +++ b/DataConnectors/AADUserInfo/.funcignore @@ -0,0 +1,4 @@ +.git* +.vscode +local.settings.json +test \ No newline at end of file diff --git a/DataConnectors/AADUserInfo/.gitignore b/DataConnectors/AADUserInfo/.gitignore new file mode 100644 index 0000000000..0fd34d4b3f --- /dev/null +++ b/DataConnectors/AADUserInfo/.gitignore @@ -0,0 +1,6 @@ + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json \ No newline at end of file diff --git a/DataConnectors/AADUserInfo/AADUserInfo/function.json b/DataConnectors/AADUserInfo/AADUserInfo/function.json new file mode 100644 index 0000000000..fa11c8e2e3 --- /dev/null +++ b/DataConnectors/AADUserInfo/AADUserInfo/function.json @@ -0,0 +1,10 @@ +{ + "bindings": [ + { + "name": "Timer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 */1440 * * * *" + } + ] +} diff --git a/DataConnectors/AADUserInfo/AADUserInfo/readme.md b/DataConnectors/AADUserInfo/AADUserInfo/readme.md new file mode 100644 index 0000000000..6f80bfdf0a --- /dev/null +++ b/DataConnectors/AADUserInfo/AADUserInfo/readme.md @@ -0,0 +1,11 @@ +# TimerTrigger - PowerShell + +The `TimerTrigger` makes it incredibly easy to have your functions executed on a schedule. This sample demonstrates a simple use case of calling your function every 5 minutes. + +## How it works + +For a `TimerTrigger` to work, you provide a schedule in the form of a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)(See the link for full details). A cron expression is a string with 6 separate expressions which represent a given schedule via patterns. The pattern we use to represent every 5 minutes is `0 */5 * * * *`. This, in plain text, means: "When seconds is equal to 0, minutes is divisible by 5, for any hour, day of the month, month, day of the week, or year". + +## Learn more + + Documentation diff --git a/DataConnectors/AADUserInfo/AADUserInfo/run.ps1 b/DataConnectors/AADUserInfo/AADUserInfo/run.ps1 new file mode 100644 index 0000000000..d381ed252e --- /dev/null +++ b/DataConnectors/AADUserInfo/AADUserInfo/run.ps1 @@ -0,0 +1,83 @@ +# Input bindings are passed in via param block. +param($Timer) + +# Get the current universal time in the default string format +$currentUTCtime = (Get-Date).ToUniversalTime() + +# The 'IsPastDue' porperty is 'true' when the current function invocation is later than scheduled. +if ($Timer.IsPastDue) { + Write-Host "PowerShell timer is running late!" +} + +Import-Module AzureAD -UseWindowsPowerShell + +# Function to build the Authorization signature for the Log Analytics Data Connector API +Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource) +{ + $xHeaders = "x-ms-date:" + $date + $stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource + + $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash) + $keyBytes = [Convert]::FromBase64String($sharedKey) + + $sha256 = New-Object System.Security.Cryptography.HMACSHA256 + $sha256.Key = $keyBytes + $calculatedHash = $sha256.ComputeHash($bytesToHash) + $encodedHash = [Convert]::ToBase64String($calculatedHash) + $authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash + + # Dispose SHA256 from heap before return. + $sha256.Dispose() + + return $authorization +} + +# Function to create and invoke an API POST request to the Log Analytics Data Connector API +Function Post-LogAnalyticsData($customerId, $sharedKey, $body, $logType) +{ + $method = "POST" + $contentType = "application/json" + $resource = "/api/logs" + $rfc1123date = [DateTime]::UtcNow.ToString("r") + $contentLength = $body.Length + $signature = Build-Signature ` + -customerId $customerId ` + -sharedKey $sharedKey ` + -date $rfc1123date ` + -contentLength $contentLength ` + -method $method ` + -contentType $contentType ` + -resource $resource + $uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01" + + $headers = @{ + "Authorization" = $signature; + "Log-Type" = $logType; + "x-ms-date" = $rfc1123date; + } + + $response = Invoke-RestMethod -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body + #$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing + return $response.StatusCode + +} + +# Get Managed Service Identity info from Azure Functions Application Settings +$msiEndpoint = $env:MSI_ENDPOINT +$msiSecret = $env:MSI_SECRET + +# Define the Log Analytics Workspace ID and Key +$CustomerId = $env:workspaceId +$SharedKey = $env:workspaceKey + +connect-azaccount -identity +$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext +$aadToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, "https://graph.windows.net").AccessToken +Connect-AzureAD -AadAccessToken $aadToken -AccountId $context.Account.Id -TenantId $context.tenant.id +$Users = Get-AzureADUser -All $True +$json = ConvertTo-Json $Users -Depth 3 + +Post-LogAnalyticsData -customerId $customerId -sharedKey $sharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType "AADUserInfov2" + +# Write an information log with the current time. +Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime" diff --git a/DataConnectors/AADUserInfo/AADUserInfo/sample.dat b/DataConnectors/AADUserInfo/AADUserInfo/sample.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/DataConnectors/AADUserInfo/AADUserInfo_template.zip b/DataConnectors/AADUserInfo/AADUserInfo_template.zip new file mode 100644 index 0000000000..9e7e05509c Binary files /dev/null and b/DataConnectors/AADUserInfo/AADUserInfo_template.zip differ diff --git a/DataConnectors/AADUserInfo/README.MD b/DataConnectors/AADUserInfo/README.MD new file mode 100644 index 0000000000..a8b4f92bb3 --- /dev/null +++ b/DataConnectors/AADUserInfo/README.MD @@ -0,0 +1,88 @@ +# Deploy a Function App for collecting Azure AD User Information data into Azure Sentinel +This function app run daily, query Azure AD for all users and write the information to Log Analyitcs. + +Note: There is a parser avaialbe [here](https://github.com/Azure/Azure-Sentinel/blob/master/Parsers/AADUserInfo/AADUserInfo.txt) +### Deploy the Function App +The easiest way is via the provided ARM templates: + +#### 1: Deploy via Azure ARM Template +1. Deploy the template. + + + + + +2. Deploy permissions for the function to the Key Vault. + + + + + +Alternatively you can deploy the elements manually. +#### 2: Deploy via VS Code +Note: You will need to prepare VS code for Azure function development. See https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-powershell#prerequisites +1. Download the [Zip](https://github.com/Azure/Azure-Sentinel/blob/master/DataConnectors/AADUserInfo/AADUserInfo_template.zip?raw=true) file of the Azure Funciton app from Github. +2. Extract to a location on your local host. +3. Open VS Code. +4. Click File -> Open Folder. +5. Select the top level folder from extracted files. +6. Type Crtl+Shift+P. +7. Click Azure Functions: Deploy to function app. You maybe asked to sign in to Azure. +8. Click Create New function app in Azure (advanced). +9. Provide a unique name like "AADUserInfo". Press Enter. +10. Click Windows. +11. Click Consumption. +12. Click PowerShell. +13. Click Create new Resource Group. +14. Press enter to accept the name. +15. Click Create a new storage Account. +16. Press enter to accept the name. +17. Click Create new Application Insights resource. +18. Press enter to accept the name. +19. Pick a location to deploy in. +20. Deployment will begin. +21. Wait for the deployment to complete, then click upload settings in the bottom right. +22. Click yes to all to upload. +23. Go to the Azure Portal. +24. Go to the resource group that was created. Click the Function. +25. Click Stop. +26. Click Identity. +27. Click On under system assigned. Click Save. Click Yes. +29. Click Configuration +30. Click General Settings. +31. Change Platform to 64 Bit. Click Save. + +### Create a Key Vault +1. Go to the Azure Portal. +2. Go to the resource group that was created. Click Add. +3. Type Key Vault. +4. Create a Key vault. +5. Go to the resource created. +6. Click Access Policies. +7. Click Add Access Policy. +8. Select Secret Management from Configure from template. +9. Click Select Principal. +10. Search for the name of the function app. Click Select. +11. Click Add. +12. Click Save. +13. Click Secrets. +14. Click Generate. +15. Enter WorkspaceKey. Paste in your Azure Sentinel Workspace Key. Click Create. +16. Click Generate. +17. Click WorkspaceKey and copy the current version string to a temporary location. + +### Configure Settings for the Function +1. Go to the Azure Portal. +2. Go to the resource group that was created. Click the Function. +3. Click Configuration. +4. Click edit next to workspaceKey. +5. Update the value using the string copied from KeyVault. +* @Microsoft.KeyVault(SecretUri=https:///secrets/workspaceKey/) +6. Click Ok. +7. Click edit next to workspaceId. +8. Update the value with your Sentinel Workspace Id. +9. Click Ok. +11. Click Save. + +If sucessfully deployed you should start to see events appear in your Azure Sentinel workpsace as soon as they are generated. +If you run into issues there are a number of options for [monitoring](https://docs.microsoft.com/en-us/azure/azure-functions/functions-monitoring?tabs=cmd) and [deugging](https://docs.microsoft.com/en-us/azure/azure-functions/functions-debug-powershell-local) your Function App. \ No newline at end of file diff --git a/DataConnectors/AADUserInfo/azuredeploy.json b/DataConnectors/AADUserInfo/azuredeploy.json new file mode 100644 index 0000000000..2e5e0fd102 --- /dev/null +++ b/DataConnectors/AADUserInfo/azuredeploy.json @@ -0,0 +1,251 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "FunctionName": { + "defaultValue": "AADUserInfo", + "type": "string" + }, + "workspaceID": { + "type": "string", + "defaultValue": "" + }, + "workspaceKey": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2015-05-01", + "name": "[parameters('FunctionName')]", + "location": "[resourceGroup().location]", + "kind": "web", + "properties": { + "Application_Type": "web", + "ApplicationId": "[parameters('FunctionName')]" + } + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2016-10-01", + "name": "[parameters('FunctionName')]", + "location": "[resourceGroup().location]", + "properties": { + "sku": { + "family": "A", + "name": "standard" + }, + "tenantId": "[subscription().tenantId]", + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": true, + "enableSoftDelete": true, + "accessPolicies": [ + ] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2016-10-01", + "name": "[concat(parameters('FunctionName'), '/workspaceKey')]", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('FunctionName'))]" + ], + "properties": { + "value": "[parameters('workspaceKey')]", + "contentType": "string", + "attributes": { + "enabled": true + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2019-06-01", + "name": "[tolower(parameters('FunctionName'))]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "kind": "StorageV2", + "properties": { + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [ + ], + "ipRules": [ + ], + "defaultAction": "Allow" + }, + "supportsHttpsTrafficOnly": true, + "encryption": { + "services": { + "file": { + "keyType": "Account", + "enabled": true + }, + "blob": { + "keyType": "Account", + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + } + } + }, + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2018-02-01", + "name": "[parameters('FunctionName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Y1", + "tier": "Dynamic" + }, + "kind": "functionapp", + "properties": { + "name": "[parameters('FunctionName')]", + "workerSize": "0", + "workerSizeId": "0", + "numberOfWorkers": "1" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2019-06-01", + "name": "[concat(parameters('FunctionName'), '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', tolower(parameters('FunctionName')))]" + ], + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [ + ] + }, + "deleteRetentionPolicy": { + "enabled": false + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2019-06-01", + "name": "[concat(parameters('FunctionName'), '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', tolower(parameters('FunctionName')))]" + ], + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [ + ] + } + } + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "name": "[parameters('FunctionName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', tolower(parameters('FunctionName')))]", + "[resourceId('Microsoft.Web/serverfarms', parameters('FunctionName'))]", + "[resourceId('Microsoft.KeyVault/vaults', parameters('FunctionName'))]", + "[resourceId('Microsoft.Insights/components', parameters('FunctionName'))]" + ], + "kind": "functionapp", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "name": "[parameters('FunctionName')]", + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('FunctionName'))]", + "httpsOnly": true, + "clientAffinityEnabled": true + }, + "resources": [ + { + "apiVersion": "2018-11-01", + "type": "config", + "name": "appsettings", + "dependsOn": [ + "[concat('Microsoft.Web/sites/', parameters('FunctionName'))]" + ], + "properties": { + "use32BitWorkerProcess": false, + "FUNCTIONS_EXTENSION_VERSION": "~3", + "FUNCTIONS_WORKER_RUNTIME": "powershell", + "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.insights/components', parameters('FunctionName')), '2015-05-01').InstrumentationKey]", + "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('microsoft.insights/components', parameters('FunctionName')), '2015-05-01').ConnectionString]", + "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(parameters('FunctionName')),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', toLower(parameters('FunctionName'))), '2019-06-01').keys[0].value, ';EndpointSuffix=core.windows.net')]", + "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(parameters('FunctionName')),';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', toLower(parameters('FunctionName'))), '2019-06-01').keys[0].value, ';EndpointSuffix=core.windows.net')]", + "WEBSITE_CONTENTSHARE": "[toLower(parameters('FunctionName'))]", + "workspaceID": "[parameters('workspaceID')]", + "workspaceKey": "[concat('@Microsoft.KeyVault(SecretUri=', reference(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('FunctionName'), 'workspaceKey')).SecretUriWithVersion, ')')]", + "WEBSITE_RUN_FROM_PACKAGE": "https://github.com/Azure/Azure-Sentinel/blob/master/DataConnectors/AADUserInfo/AADUserInfo_template.zip?raw=true" } + } + ] + }, + { + "type": "Microsoft.Web/sites/hostNameBindings", + "apiVersion": "2018-11-01", + "name": "[concat(parameters('FunctionName'), '/', parameters('FunctionName'), '.azurewebsites.net')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('FunctionName'))]" + ], + "properties": { + "siteName": "[parameters('FunctionName')]", + "hostNameType": "Verified" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-06-01", + "name": "[concat(parameters('FunctionName'), '/default/azure-webjobs-hosts')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('FunctionName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('FunctionName'))]" + ], + "properties": { + "publicAccess": "None" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-06-01", + "name": "[concat(parameters('FunctionName'), '/default/azure-webjobs-secrets')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('FunctionName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('FunctionName'))]" + ], + "properties": { + "publicAccess": "None" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2019-06-01", + "name": "[concat(parameters('FunctionName'), '/default/', tolower(parameters('FunctionName')))]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/fileServices', parameters('FunctionName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('FunctionName'))]" + ], + "properties": { + "shareQuota": 5120 + } + } + ] +} diff --git a/DataConnectors/AADUserInfo/azuredeploy_kv.json b/DataConnectors/AADUserInfo/azuredeploy_kv.json new file mode 100644 index 0000000000..e63c245c0b --- /dev/null +++ b/DataConnectors/AADUserInfo/azuredeploy_kv.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "FunctionName": { + "defaultValue": "AADUserInfo", + "type": "String" + } + }, + "variables": { + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2016-10-01", + "name": "[concat(parameters('FunctionName'), '/add')]", + "properties": { + "accessPolicies": [ + { + "tenantId": "[subscription().tenantId]", + "objectId": "[reference(concat(resourceId('Microsoft.Web/sites', parameters('FunctionName')), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2018-11-30').principalId]", + "permissions": { + "keys": [ + ], + "secrets": [ + "Get", + "List", + "Set", + "Delete", + "Recover", + "Backup", + "Restore" + ], + "certificates": [ + ] + } + } + ], + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": true + } + } + ] +} \ No newline at end of file diff --git a/DataConnectors/AADUserInfo/host.json b/DataConnectors/AADUserInfo/host.json new file mode 100644 index 0000000000..c1437cbc6e --- /dev/null +++ b/DataConnectors/AADUserInfo/host.json @@ -0,0 +1,18 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[1.*, 2.0.0)" + }, + "managedDependency": { + "enabled": true + } +} diff --git a/DataConnectors/AADUserInfo/profile.ps1 b/DataConnectors/AADUserInfo/profile.ps1 new file mode 100644 index 0000000000..1670fc99c2 --- /dev/null +++ b/DataConnectors/AADUserInfo/profile.ps1 @@ -0,0 +1,22 @@ +# Azure Functions profile.ps1 +# +# This profile.ps1 will get executed every "cold start" of your Function App. +# "cold start" occurs when: +# +# * A Function App starts up for the very first time +# * A Function App starts up after being de-allocated due to inactivity +# +# You can define helper functions, run commands, or specify environment variables +# NOTE: any variables defined that are not environment variables will get reset after the first execution + +# Authenticate with Azure PowerShell using MSI. +# Remove this if you are not planning on using MSI or Azure PowerShell. +if ($env:MSI_SECRET) { + Disable-AzContextAutosave -Scope Process | Out-Null + Connect-AzAccount -Identity +} + +# Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell. +# Enable-AzureRmAlias + +# You can also define functions or aliases that can be referenced in any of your PowerShell functions. diff --git a/DataConnectors/AADUserInfo/proxies.json b/DataConnectors/AADUserInfo/proxies.json new file mode 100644 index 0000000000..b385252f5e --- /dev/null +++ b/DataConnectors/AADUserInfo/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} +} diff --git a/DataConnectors/AADUserInfo/requirements.psd1 b/DataConnectors/AADUserInfo/requirements.psd1 new file mode 100644 index 0000000000..5dfa556d14 --- /dev/null +++ b/DataConnectors/AADUserInfo/requirements.psd1 @@ -0,0 +1,7 @@ +# This file enables modules to be automatically managed by the Functions service. +# See https://aka.ms/functionsmanageddependency for additional information. +# +@{ + 'Az' = '5.*' + 'AzureAD' = '2.*' +} \ No newline at end of file diff --git a/Parsers/AADUserInfo/AADUserInfo.txt b/Parsers/AADUserInfo/AADUserInfo.txt new file mode 100644 index 0000000000..cc98427d95 --- /dev/null +++ b/Parsers/AADUserInfo/AADUserInfo.txt @@ -0,0 +1,40 @@ +// Usage Instruction : +// Paste below query in log analytics, click on Save button and select as Function from drop down by specifying function name and alias as AADUserInfo. +// Function usually takes 10-15 minutes to activate. You can then use function alias from any other queries (e.g. AADUserInfo | take 10). +// Reference : Using functions in Azure monitor log queries : https://docs.microsoft.com/azure/azure-monitor/log-query/functions +AADUserInfo_CL +| summarize arg_max(TimeGenerated, *) by ObjectId_g +| project TimeGenerated, + ExtensionProperty_createdDateTime=columnifexists('ExtensionProperty_createdDateTime_s', ""), + ExtensionProperty_userIdentities=columnifexists('ExtensionProperty_userIdentities_s', ""), + ExtensionProperty_onPremisesDistinguishedName=columnifexists('ExtensionProperty_onPremisesDistinguishedName_s', ""), + ObjectId=columnifexists('ObjectId_g', ""), + ObjectType=columnifexists('ObjectType_s', ""), + AccountEnabled=columnifexists('AccountEnabled_b', ""), + DirSyncEnabled=columnifexists('DirSyncEnabled_b', ""), + DisplayName=columnifexists('DisplayName_s', ""), + GivenName=columnifexists('GivenName_s', ""), + MailNickName=columnifexists('MailNickName_s', ""), + OnPremisesSecurityIdentifier=columnifexists('OnPremisesSecurityIdentifier_s', ""), + PasswordPolicies=columnifexists('PasswordPolicies_s', ""), + ProxyAddresses=columnifexists('ProxyAddresses_s', ""), + RefreshTokensValidFromDateTime=columnifexists('RefreshTokensValidFromDateTime_t', ""), + SignInNames=columnifexists('SignInNames_s', ""), + Surname=columnifexists('Surname_s', ""), + UserPrincipalName=columnifexists('UserPrincipalName_s', ""), + UserType=columnifexists('UserType_s', ""), + CompanyName=columnifexists('CompanyName_s', ""), + Department=columnifexists('Department_s', ""), + JobTitle=columnifexists('JobTitle_s', ""), + Mail=columnifexists('Mail_s', ""), + SipProxyAddress=columnifexists('SipProxyAddress_s', ""), + UsageLocation=columnifexists('UsageLocation_s', ""), + PasswordProfile=columnifexists('PasswordProfile_s', ""), + UserState=columnifexists('UserState_s', ""), + City=columnifexists('City_s', ""), + Mobile=columnifexists('Mobile_s', ""), + PhysicalDeliveryOfficeName=columnifexists('PhysicalDeliveryOfficeName_s', ""), + PostalCode=columnifexists('PostalCode_s', ""), + State=columnifexists('State_s', ""), + StreetAddress=columnifexists('StreetAddress_s', ""), + Country=columnifexists('Country_s', "")