diff --git a/Logos/NetApp.svg b/Logos/NetApp.svg new file mode 100644 index 00000000000..f9f49b9771c --- /dev/null +++ b/Logos/NetApp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Solutions/NetApp Ransomware Resilience/Data/Solution_NetAppRansomwareResilience.json b/Solutions/NetApp Ransomware Resilience/Data/Solution_NetAppRansomwareResilience.json new file mode 100644 index 00000000000..f7a2190ce85 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Data/Solution_NetAppRansomwareResilience.json @@ -0,0 +1,20 @@ +{ + "Name": "NetApp Ransomware Resilience", + "Author": "NetApp - support@netapp.com", + "Title": "NetApp Ransomware Resilience", + "Logo": "\"NetApp", + "Description": "NetApp Ransomware Resilience - Comprehensive security solution for detecting and responding to ransomware threats across NetApp storage environments.", + "Playbooks": [ + "Playbooks/NetApp-RansomwareResilience-Auth-Playbook/azuredeploy.json", + "Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/azuredeploy.json", + "Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/azuredeploy.json", + "Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/azuredeploy.json", + "Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/azuredeploy.json", + "Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/azuredeploy.json" + ], + "Version": "3.0.0", + "BasePath": "C:\\GitHub\\Azure-Sentinel\\solutions\\NetApp Ransomware Resilience", + "Metadata": "SolutionMetadata.json", + "TemplateSpec": true, + "StaticDataConnectorIds": [] +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Package/3.0.0.zip b/Solutions/NetApp Ransomware Resilience/Package/3.0.0.zip new file mode 100644 index 00000000000..b1ae890f256 Binary files /dev/null and b/Solutions/NetApp Ransomware Resilience/Package/3.0.0.zip differ diff --git a/Solutions/NetApp Ransomware Resilience/Package/createUiDefinition.json b/Solutions/NetApp Ransomware Resilience/Package/createUiDefinition.json new file mode 100644 index 00000000000..dc29e9aa6f1 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Package/createUiDefinition.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "config": { + "isWizard": false, + "basics": { + "description": "\"NetApp\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/NetApp%20Ransomware%20Resilience/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nNetApp Ransomware Resilience - Comprehensive security solution for detecting and responding to ransomware threats across NetApp storage environments.\n\n**Playbooks:** 6\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "subscription": { + "resourceProviders": [ + "Microsoft.OperationsManagement/solutions", + "Microsoft.OperationalInsights/workspaces/providers/alertRules", + "Microsoft.Insights/workbooks", + "Microsoft.Logic/workflows" + ] + }, + "location": { + "metadata": { + "hidden": "Hiding location, we get it from the log analytics workspace" + }, + "visible": false + }, + "resourceGroup": { + "allowExisting": true + } + } + }, + "basics": [ + { + "name": "getLAWorkspace", + "type": "Microsoft.Solutions.ArmApiControl", + "toolTip": "This filters by workspaces that exist in the Resource Group selected", + "condition": "[greater(length(resourceGroup().name),0)]", + "request": { + "method": "GET", + "path": "[concat(subscription().id,'/providers/Microsoft.OperationalInsights/workspaces?api-version=2020-08-01')]" + } + }, + { + "name": "workspace", + "type": "Microsoft.Common.DropDown", + "label": "Workspace", + "placeholder": "Select a workspace", + "toolTip": "This dropdown will list only workspace that exists in the Resource Group selected", + "constraints": { + "allowedValues": "[map(filter(basics('getLAWorkspace').value, (filter) => contains(toLower(filter.id), toLower(resourceGroup().name))), (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.name, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "steps": [ + { + "name": "playbooks", + "label": "Playbooks", + "subLabel": { + "preValidation": "Configure the playbooks", + "postValidation": "Done" + }, + "bladeTitle": "Playbooks", + "elements": [ + { + "name": "playbooks-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This solution installs the Playbook templates to help implement your Security Orchestration, Automation and Response (SOAR) operations. After installing the solution, these will be deployed under Playbook Templates in the Automation blade in Microsoft Sentinel. They can be configured and managed from the Manage solution view in Content Hub." + } + }, + { + "name": "playbooks-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/tutorial-respond-threats-playbook?WT.mc_id=Portal-Microsoft_Azure_CreateUIDef" + } + } + } + ] + } + ], + "outputs": { + "workspace-location": "[first(map(filter(basics('getLAWorkspace').value, (filter) => and(contains(toLower(filter.id), toLower(resourceGroup().name)),equals(filter.name,basics('workspace')))), (item) => item.location))]", + "location": "[location()]", + "workspace": "[basics('workspace')]" + } + } +} diff --git a/Solutions/NetApp Ransomware Resilience/Package/mainTemplate.json b/Solutions/NetApp Ransomware Resilience/Package/mainTemplate.json new file mode 100644 index 00000000000..7071525f549 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Package/mainTemplate.json @@ -0,0 +1,3143 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "author": "NetApp - support@netapp.com", + "comments": "Solution template for NetApp Ransomware Resilience" + }, + "parameters": { + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + } + }, + "variables": { + "email": "support@netapp.com", + "_email": "[variables('email')]", + "_solutionName": "NetApp Ransomware Resilience", + "_solutionVersion": "3.0.0", + "solutionId": "netapp1234567890.azure-sentinel-solution-netapprrs", + "_solutionId": "[variables('solutionId')]", + "NetApp-RansomwareResilience-Auth-Playbook": "NetApp-RansomwareResilience-Auth-Playbook", + "_NetApp-RansomwareResilience-Auth-Playbook": "[variables('NetApp-RansomwareResilience-Auth-Playbook')]", + "TemplateEmptyArray": "[json('[]')]", + "playbookVersion1": "1.0", + "playbookContentId1": "NetApp-RansomwareResilience-Auth-Playbook", + "_playbookContentId1": "[variables('playbookContentId1')]", + "playbookId1": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId1'))]", + "playbookTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId1'))))]", + "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", + "_playbookcontentProductId1": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId1'),'-', variables('playbookVersion1'))))]", + "blanks": "[replace('b', 'b', '')]", + "NetApp-RansomwareResilience_Volume_Offline_Playbook": "NetApp-RansomwareResilience_Volume_Offline_Playbook", + "_NetApp-RansomwareResilience_Volume_Offline_Playbook": "[variables('NetApp-RansomwareResilience_Volume_Offline_Playbook')]", + "TemplateEmptyObject": "[json('{}')]", + "playbookVersion2": "1.0", + "playbookContentId2": "NetApp-RansomwareResilience_Volume_Offline_Playbook", + "_playbookContentId2": "[variables('playbookContentId2')]", + "playbookId2": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId2'))]", + "playbookTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId2'))))]", + "_playbookcontentProductId2": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId2'),'-', variables('playbookVersion2'))))]", + "NetApp-RansomwareResilience_Async_Poll_Playbook": "NetApp-RansomwareResilience_Async_Poll_Playbook", + "_NetApp-RansomwareResilience_Async_Poll_Playbook": "[variables('NetApp-RansomwareResilience_Async_Poll_Playbook')]", + "playbookVersion3": "1.0", + "playbookContentId3": "NetApp-RansomwareResilience_Async_Poll_Playbook", + "_playbookContentId3": "[variables('playbookContentId3')]", + "playbookId3": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId3'))]", + "playbookTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId3'))))]", + "_playbookcontentProductId3": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId3'),'-', variables('playbookVersion3'))))]", + "NetApp-RansomwareResilience_Volume_Snapshot_Playbook": "NetApp-RansomwareResilience_Volume_Snapshot_Playbook", + "_NetApp-RansomwareResilience_Volume_Snapshot_Playbook": "[variables('NetApp-RansomwareResilience_Volume_Snapshot_Playbook')]", + "playbookVersion4": "1.0", + "playbookContentId4": "NetApp-RansomwareResilience_Volume_Snapshot_Playbook", + "_playbookContentId4": "[variables('playbookContentId4')]", + "playbookId4": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId4'))]", + "playbookTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId4'))))]", + "_playbookcontentProductId4": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId4'),'-', variables('playbookVersion4'))))]", + "NetApp-RansomwareResilience_Enrich_StorageVM_Playbook": "NetApp-RansomwareResilience_Enrich_StorageVM_Playbook", + "_NetApp-RansomwareResilience_Enrich_StorageVM_Playbook": "[variables('NetApp-RansomwareResilience_Enrich_StorageVM_Playbook')]", + "playbookVersion5": "1.0", + "playbookContentId5": "NetApp-RansomwareResilience_Enrich_StorageVM_Playbook", + "_playbookContentId5": "[variables('playbookContentId5')]", + "playbookId5": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId5'))]", + "playbookTemplateSpecName5": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId5'))))]", + "_playbookcontentProductId5": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId5'),'-', variables('playbookVersion5'))))]", + "NetApp-RansomwareResilience_Enrich_IP_Playbook": "NetApp-RansomwareResilience_Enrich_IP_Playbook", + "_NetApp-RansomwareResilience_Enrich_IP_Playbook": "[variables('NetApp-RansomwareResilience_Enrich_IP_Playbook')]", + "playbookVersion6": "1.0", + "playbookContentId6": "NetApp-RansomwareResilience_Enrich_IP_Playbook", + "_playbookContentId6": "[variables('playbookContentId6')]", + "playbookId6": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId6'))]", + "playbookTemplateSpecName6": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId6'))))]", + "_playbookcontentProductId6": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId6'),'-', variables('playbookVersion6'))))]", + "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('playbookTemplateSpecName1')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "NetApp-RansomwareResilience-Auth Playbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('playbookVersion1')]", + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the Logic App playbook to be created" + } + }, + "KeyVaultName": { + "type": "String", + "defaultValue": "[concat('RRSkv', uniqueString(resourceGroup().id))]", + "metadata": { + "description": "Name of the shared Key Vault to store NetApp Ransomware Resilience credentials" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + }, + "ClientId": { + "type": "securestring", + "metadata": { + "description": "Client ID for NetApp Ransomware Resilience API authentication" + } + }, + "ClientSecret": { + "type": "securestring", + "metadata": { + "description": "Client Secret for NetApp Ransomware Resilience API authentication" + } + }, + "AccountId": { + "type": "securestring", + "metadata": { + "description": "Account ID for NetApp Ransomware Resilience API authentication" + } + } + }, + "variables": { + "KeyVaultConnectionName": "[[concat('keyvault-', parameters('PlaybookName'))]", + "connection-5": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', parameters('location'), '/managedApis/keyvault')]", + "_connection-5": "[[variables('connection-5')]", + "workspace-location-inline": "[concat('[resourceGroup().locatio', 'n]')]", + "workspace-name": "[parameters('workspace')]", + "workspaceResourceId": "[[resourceId('microsoft.OperationalInsights/Workspaces', variables('workspace-name'))]" + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[[parameters('KeyVaultName')]", + "location": "[[parameters('location')]", + "properties": { + "sku": { + "family": "A", + "name": "standard" + }, + "tenantId": "[[subscription().tenantId]", + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": true, + "enableSoftDelete": true, + "softDeleteRetentionInDays": 90, + "enableRbacAuthorization": false, + "accessPolicies": "[variables('TemplateEmptyArray')]" + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2019-09-01", + "name": "[[concat(parameters('KeyVaultName'), '/client-id')]", + "dependsOn": [ + "[[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "value": "[[parameters('ClientId')]" + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2019-09-01", + "name": "[[concat(parameters('KeyVaultName'), '/client-secret')]", + "dependsOn": [ + "[[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "value": "[[parameters('ClientSecret')]" + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2019-09-01", + "name": "[[concat(parameters('KeyVaultName'), '/account-id')]", + "dependsOn": [ + "[[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "value": "[[parameters('AccountId')]" + } + }, + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[[variables('KeyVaultConnectionName')]", + "location": "[[parameters('location')]", + "kind": "V1", + "dependsOn": [ + "[[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "displayName": "[[variables('KeyVaultConnectionName')]", + "parameterValueSet": { + "name": "oauthMI", + "values": { + "vaultName": { + "value": "[[parameters('KeyVaultName')]" + } + } + }, + "api": { + "id": "[[variables('_connection-5')]" + } + } + }, + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[[parameters('PlaybookName')]", + "location": "[[parameters('location')]", + "dependsOn": [ + "[[resourceId('Microsoft.Web/connections', variables('KeyVaultConnectionName'))]", + "[[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$connections": { + "type": "Object" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "properties": { + "account_id": { + "type": "string" + }, + "api_hostname": { + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "actions": { + "Get_Client_ID": { + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('client-id')}/value" + } + }, + "Get_Client_Secret": { + "runAfter": { + "Get_Client_ID": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('client-secret')}/value" + } + }, + "Get_Account_ID": { + "runAfter": { + "Get_Client_Secret": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('account-id')}/value" + } + }, + "HTTP_Token_Request": { + "runAfter": { + "Get_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://netapp-cloud-account.auth0.com/oauth/token", + "headers": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "body": "grant_type=client_credentials&client_id=@{body('Get_Client_ID')?['value']}&client_secret=@{body('Get_Client_Secret')?['value']}&audience=https://api.cloud.netapp.com" + } + }, + "Response": { + "runAfter": { + "HTTP_Token_Request": [ + "Succeeded" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "access_token": "@{body('HTTP_Token_Request')?['access_token']}", + "token_type": "@{body('HTTP_Token_Request')?['token_type']}", + "expires_in": "@{body('HTTP_Token_Request')?['expires_in']}", + "account_id": "@{body('Get_Account_ID')?['value']}" + } + } + } + } + }, + "parameters": { + "$connections": { + "value": { + "keyvault": { + "connectionId": "[[resourceId('Microsoft.Web/connections', variables('KeyVaultConnectionName'))]", + "connectionName": "[[variables('KeyVaultConnectionName')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + }, + "id": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', parameters('location'), '/managedApis/keyvault')]" + } + } + } + } + }, + "tags": { + "hidden-SentinelWorkspaceId": "[[variables('workspaceResourceId')]" + } + }, + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2019-09-01", + "name": "[[concat(parameters('KeyVaultName'), '/add')]", + "dependsOn": [ + "[[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]", + "[[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "accessPolicies": [ + { + "tenantId": "[[subscription().tenantId]", + "objectId": "[[reference(resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')), '2019-05-01', 'Full').identity.principalId]", + "permissions": { + "secrets": [ + "get" + ] + } + } + ] + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Playbook-', last(split(variables('playbookId1'),'/'))))]", + "properties": { + "parentId": "[variables('playbookId1')]", + "contentId": "[variables('_playbookContentId1')]", + "kind": "Playbook", + "version": "[variables('playbookVersion1')]", + "source": { + "kind": "Solution", + "name": "NetApp Ransomware Resilience", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "NetApp", + "email": "[variables('_email')]" + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + } + } + } + ], + "metadata": { + "title": "NetApp Ransomware Resilience Authentication Playbook", + "description": "This playbook creates a shared Key Vault for NetApp Ransomware Resilience credentials and provides authentication services to all NetApp Ransomware Resilience playbooks in the solution.", + "prerequisites": [ + "1. Valid client_id, client_secret, and account_id for NetApp Ransomware Resilience API authentication", + "2. API endpoint that accepts OAuth2 client credentials flow" + ], + "postDeployment": [ + "1. Update the client_id, client_secret, and account_id values in the Key Vault", + "2. Test the Logic App to ensure proper token retrieval", + "3. The Key Vault created by this playbook will be shared across all NetApp Ransomware Resilience playbooks", + "4. Other playbooks will call this Auth playbook to obtain authentication tokens" + ], + "lastUpdateTime": "2025-09-17T00:00:00Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Authentication" + ], + "releaseNotes": { + "version": "1.0", + "title": "[variables('blanks')]", + "notes": [ + "Initial version" + ] + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId1')]", + "contentKind": "Playbook", + "displayName": "NetApp-RansomwareResilience-Auth", + "contentProductId": "[variables('_playbookcontentProductId1')]", + "id": "[variables('_playbookcontentProductId1')]", + "version": "[variables('playbookVersion1')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('playbookTemplateSpecName2')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "NetApp-RansomwareResilience-Volume-Offline Playbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('playbookVersion2')]", + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Volume-Offline", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience async poll playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": { + "workspace-location-inline": "[concat('[resourceGroup().locatio', 'n]')]", + "workspace-name": "[parameters('workspace')]", + "workspaceResourceId": "[[resourceId('microsoft.OperationalInsights/Workspaces', variables('workspace-name'))]" + }, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[[parameters('PlaybookName')]", + "location": "[[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + }, + "asyncPollPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAsyncPollPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "volume_id": { + "type": "string" + }, + "agent_id": { + "type": "string" + }, + "system_id": { + "type": "string" + }, + "callbackData": { + "type": "object" + }, + "poll": { + "type": "boolean" + } + }, + "required": [ + "volume_id", + "agent_id", + "system_id" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "VolumeId", + "type": "string", + "value": "@triggerBody()?['volume_id']" + }, + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "DoPoll", + "type": "boolean", + "value": "@coalesce(triggerBody()?['poll'], true)" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "JobId", + "type": "string" + }, + { + "name": "JobAgentId", + "type": "string" + }, + { + "name": "PollResponse", + "type": "object", + "value": "[variables('TemplateEmptyObject')]" + }, + { + "name": "SubmissionStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Submit_Volume_Offline": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/storage/take-volume-offline", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "Content-Type": "application/json", + "accept": "application/json" + }, + "body": { + "volume_id": "@variables('VolumeId')", + "agent_id": "@variables('AgentId')", + "system_id": "@variables('SystemId')" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Capture_Submission_Status_Code": { + "runAfter": { + "Submit_Volume_Offline": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "SubmissionStatusCode", + "value": "@outputs('Submit_Volume_Offline')['statusCode']" + } + }, + "Parse_Submission_Response": { + "runAfter": { + "Capture_Submission_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Submit_Volume_Offline')", + "schema": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "source": { + "type": "string" + }, + "agent_id": { + "type": "string" + } + } + } + } + }, + "Store_JobId": { + "runAfter": { + "Parse_Submission_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobId", + "value": "@body('Parse_Submission_Response')?['job_id']" + } + }, + "Store_Job_AgentId": { + "runAfter": { + "Store_JobId": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobAgentId", + "value": "@body('Parse_Submission_Response')?['agent_id']" + } + }, + "Check_If_Should_Poll": { + "runAfter": { + "Store_Job_AgentId": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@variables('DoPoll')", + true + ] + }, + { + "or": [ + { + "equals": [ + "@variables('SubmissionStatusCode')", + 200 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 201 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 202 + ] + } + ] + }, + { + "not": { + "equals": [ + "@variables('JobId')", + "" + ] + } + } + ] + }, + "actions": { + "Call_Async_Poll_Playbook": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAsyncPollPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "job_id": "@variables('JobId')", + "agent_id": "@variables('JobAgentId')", + "system_id": "@variables('SystemId')", + "source": "ontap", + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "original_volume_id": "@variables('VolumeId')" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + } + } + }, + "Parse_Poll_Response": { + "runAfter": { + "Check_If_Should_Poll": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@if(equals(actions('Call_Async_Poll_Playbook')['status'], 'Succeeded'), body('Call_Async_Poll_Playbook'), json('{\"success\": false, \"status\": \"FAILED\", \"error\": \"Poll playbook failed or timed out\"}'))" + }, + "Store_Poll_Response": { + "runAfter": { + "Parse_Poll_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "PollResponse", + "value": "@outputs('Parse_Poll_Response')" + } + }, + "Finalize_Execution": { + "runAfter": { + "Store_Poll_Response": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Check_If_Should_Poll": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Submit_Volume_Offline": [ + "Failed", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "execution_completed": true, + "timestamp": "@utcNow()" + } + }, + "Return_Response": { + "runAfter": { + "Finalize_Execution": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), 500, if(or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)), 200, if(equals(variables('SubmissionStatusCode'), 0), 500, variables('SubmissionStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut'))), or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), 'Failed submitting volume offline request', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), 'submit', 'success'))", + "submission": { + "statusCode": "@variables('SubmissionStatusCode')", + "jobId": "@variables('JobId')", + "raw": "@body('Submit_Volume_Offline')", + "details": "@if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), body('Submit_Volume_Offline'), null)" + }, + "polling": { + "invoked": "@and(variables('DoPoll'), not(empty(variables('JobId'))))", + "playbook": "@parameters('asyncPollPlaybookName')", + "jobFinalStatus": "@coalesce(variables('PollResponse')?['status'], 'unknown')", + "jobResponse": "@variables('PollResponse')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + } + } + }, + "tags": { + "hidden-SentinelWorkspaceId": "[[variables('workspaceResourceId')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Playbook-', last(split(variables('playbookId2'),'/'))))]", + "properties": { + "parentId": "[variables('playbookId2')]", + "contentId": "[variables('_playbookContentId2')]", + "kind": "Playbook", + "version": "[variables('playbookVersion2')]", + "source": { + "kind": "Solution", + "name": "NetApp Ransomware Resilience", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "NetApp", + "email": "[variables('_email')]" + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + } + } + } + ], + "metadata": { + "title": "NetApp Ransomware Resilience Volume Offline Playbook", + "description": "This playbook takes a NetApp volume offline using the updated NetApp Ransomware Resilience take-volume-offline API endpoint and optionally polls for completion.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. NetApp Ransomware Resilience Async Poll Playbook must be deployed first and ensure that it is functioning correctly", + "3. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "4. Caller must provide volume_id, agent_id, and system_id parameters" + ], + "postDeployment": [ + "1. Test the volume offline functionality with valid volume_id, agent_id, and system_id" + ], + "lastUpdateTime": "2025-09-17T00:00:00Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Volume", + "Offline" + ], + "releaseNotes": { + "version": "1.0", + "title": "[variables('blanks')]", + "notes": [ + "Initial version" + ] + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId2')]", + "contentKind": "Playbook", + "displayName": "NetApp-RansomwareResilience-Volume-Offline", + "contentProductId": "[variables('_playbookcontentProductId2')]", + "id": "[variables('_playbookcontentProductId2')]", + "version": "[variables('playbookVersion2')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('playbookTemplateSpecName3')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "NetApp-RansomwareResilience-Async-Poll Playbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('playbookVersion3')]", + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": { + "workspace-location-inline": "[concat('[resourceGroup().locatio', 'n]')]", + "workspace-name": "[parameters('workspace')]", + "workspaceResourceId": "[[resourceId('microsoft.OperationalInsights/Workspaces', variables('workspace-name'))]" + }, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[[parameters('PlaybookName')]", + "location": "[[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "The NetApp Ransomware Resilience job ID to poll" + }, + "agent_id": { + "type": "string", + "description": "NetApp Ransomware Resilience agent ID" + }, + "system_id": { + "type": "string", + "description": "System ID for the working environment" + }, + "source": { + "type": "string", + "description": "Job source type" + }, + "caller_info": { + "type": "object", + "description": "Optional caller information for tracking" + } + }, + "required": [ + "job_id", + "agent_id", + "system_id", + "source" + ] + } + } + } + }, + "actions": { + "Initialize_variables": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "JobId", + "type": "string", + "value": "@triggerBody()?['job_id']" + }, + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "Source", + "type": "string", + "value": "@triggerBody()?['source']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "PollCount", + "type": "integer", + "value": 0 + }, + { + "name": "MaxPollAttempts", + "type": "integer", + "value": 10 + }, + { + "name": "PollIntervalSeconds", + "type": "integer", + "value": 30 + }, + { + "name": "JobStatus", + "type": "string", + "value": "pending" + }, + { + "name": "JobResponse", + "type": "object", + "value": "[variables('TemplateEmptyObject')]" + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + } + } + }, + "Set_Account_ID": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Call_Auth_Playbook')?['account_id']" + } + }, + "Initial_Status_Check": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/job/status?source=@{variables('Source')}&job_id=@{variables('JobId')}&agent_id=@{variables('AgentId')}&system_id=@{variables('SystemId')}", + "headers": { + "Authorization": "Bearer @{body('Call_Auth_Playbook')?['access_token']}", + "accept": "application/json" + }, + "retryPolicy": { + "type": "exponential", + "count": 2, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT10S" + }, + "timeout": "PT30S" + } + }, + "Update_Initial_Status": { + "runAfter": { + "Initial_Status_Check": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobStatus", + "value": "@body('Initial_Status_Check')?['status']" + } + }, + "Update_Initial_Response": { + "runAfter": { + "Update_Initial_Status": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobResponse", + "value": "@body('Initial_Status_Check')" + } + }, + "Polling_Loop": { + "runAfter": { + "Update_Initial_Response": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@or(or(equals(variables('JobStatus'), 'SUCCESS'), equals(variables('JobStatus'), 'success')), or(equals(variables('JobStatus'), 'FAILED'), equals(variables('JobStatus'), 'failed')), greater(variables('PollCount'), variables('MaxPollAttempts')))", + "limit": { + "count": 10, + "timeout": "PT6M" + }, + "actions": { + "Increment_Poll_Count": { + "type": "IncrementVariable", + "inputs": { + "name": "PollCount", + "value": 1 + } + }, + "Check_Job_Status": { + "runAfter": { + "Increment_Poll_Count": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/job/status?source=@{variables('Source')}&job_id=@{variables('JobId')}&agent_id=@{variables('AgentId')}&system_id=@{variables('SystemId')}", + "headers": { + "Authorization": "Bearer @{body('Call_Auth_Playbook')?['access_token']}", + "accept": "application/json" + }, + "retryPolicy": { + "type": "exponential", + "count": 2, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT10S" + }, + "timeout": "PT30S" + } + }, + "Update_Job_Status": { + "runAfter": { + "Check_Job_Status": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobStatus", + "value": "@body('Check_Job_Status')?['status']" + } + }, + "Handle_Status_Check_Failure": { + "runAfter": { + "Check_Job_Status": [ + "Failed", + "TimedOut" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobStatus", + "value": "failed" + } + }, + "Update_Job_Response": { + "runAfter": { + "Update_Job_Status": [ + "Succeeded" + ], + "Handle_Status_Check_Failure": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobResponse", + "value": "@body('Check_Job_Status')" + } + }, + "Wait_If_Needed": { + "runAfter": { + "Update_Job_Response": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "not": { + "or": [ + { + "equals": [ + "@variables('JobStatus')", + "SUCCESS" + ] + }, + { + "equals": [ + "@variables('JobStatus')", + "success" + ] + } + ] + } + }, + "actions": { + "Wait_Before_Retry": { + "type": "Wait", + "inputs": { + "interval": { + "count": "@variables('PollIntervalSeconds')", + "unit": "Second" + } + } + } + } + } + } + }, + "Check_Final_Status": { + "runAfter": { + "Polling_Loop": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "or": [ + { + "equals": [ + "@variables('JobStatus')", + "SUCCESS" + ] + }, + { + "equals": [ + "@variables('JobStatus')", + "success" + ] + } + ] + } + ] + }, + "actions": { + "Success_Response": { + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": true, + "job_id": "@variables('JobId')", + "status": "@variables('JobStatus')", + "poll_count": "@variables('PollCount')", + "data": "@variables('JobResponse')", + "caller_info": "@triggerBody()?['caller_info']" + } + } + } + }, + "else": { + "actions": { + "Error_Response": { + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": 408, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": false, + "error": "Job polling timeout or failed", + "job_id": "@variables('JobId')", + "status": "@variables('JobStatus')", + "poll_count": "@variables('PollCount')", + "max_attempts": "@variables('MaxPollAttempts')", + "data": "@variables('JobResponse')", + "caller_info": "@triggerBody()?['caller_info']" + } + } + } + } + } + } + } + } + }, + "tags": { + "hidden-SentinelWorkspaceId": "[[variables('workspaceResourceId')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Playbook-', last(split(variables('playbookId3'),'/'))))]", + "properties": { + "parentId": "[variables('playbookId3')]", + "contentId": "[variables('_playbookContentId3')]", + "kind": "Playbook", + "version": "[variables('playbookVersion3')]", + "source": { + "kind": "Solution", + "name": "NetApp Ransomware Resilience", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "NetApp", + "email": "[variables('_email')]" + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + } + } + } + ], + "metadata": { + "title": "NetApp Ransomware Resilience Async Poll Playbook", + "description": "This playbook polls NetApp Ransomware Resilience job status asynchronously until completion or timeout using the updated job status API endpoint.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "3. Caller must provide job_id, agent_id, system_id, and source parameters" + ], + "postDeployment": [ + "1. Test the playbook by triggering it with a valid job_id, agent_id, system_id, and source", + "2. Monitor the playbook run history to verify successful polling and completion" + ], + "lastUpdateTime": "2025-09-17T00:00:00Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Async", + "Polling" + ], + "releaseNotes": { + "version": "1.0", + "title": "[variables('blanks')]", + "notes": [ + "Initial version" + ] + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId3')]", + "contentKind": "Playbook", + "displayName": "NetApp-RansomwareResilience-Async-Poll", + "contentProductId": "[variables('_playbookcontentProductId3')]", + "id": "[variables('_playbookcontentProductId3')]", + "version": "[variables('playbookVersion3')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('playbookTemplateSpecName4')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "NetApp-RansomwareResilience-Volume-Snapshot Playbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('playbookVersion4')]", + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Volume-Snapshot", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience async poll playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": { + "workspace-location-inline": "[concat('[resourceGroup().locatio', 'n]')]", + "workspace-name": "[parameters('workspace')]", + "workspaceResourceId": "[[resourceId('microsoft.OperationalInsights/Workspaces', variables('workspace-name'))]" + }, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[[parameters('PlaybookName')]", + "location": "[[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + }, + "asyncPollPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAsyncPollPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "volume_id": { + "type": "string" + }, + "agent_id": { + "type": "string" + }, + "system_id": { + "type": "string" + }, + "callbackData": { + "type": "object" + }, + "poll": { + "type": "boolean" + } + }, + "required": [ + "volume_id", + "agent_id", + "system_id" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "VolumeId", + "type": "string", + "value": "@triggerBody()?['volume_id']" + }, + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "DoPoll", + "type": "boolean", + "value": "@coalesce(triggerBody()?['poll'], true)" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "JobId", + "type": "string" + }, + { + "name": "JobAgentId", + "type": "string" + }, + { + "name": "PollResponse", + "type": "object", + "value": "[variables('TemplateEmptyObject')]" + }, + { + "name": "SubmissionStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Submit_Volume_Snapshot": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/storage/take-snapshot", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "Content-Type": "application/json", + "accept": "application/json" + }, + "body": { + "volume_id": "@variables('VolumeId')", + "agent_id": "@variables('AgentId')", + "system_id": "@variables('SystemId')" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Capture_Submission_Status_Code": { + "runAfter": { + "Submit_Volume_Snapshot": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "SubmissionStatusCode", + "value": "@outputs('Submit_Volume_Snapshot')['statusCode']" + } + }, + "Parse_Submission_Response": { + "runAfter": { + "Capture_Submission_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Submit_Volume_Snapshot')", + "schema": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "source": { + "type": "string" + }, + "agent_id": { + "type": "string" + } + } + } + } + }, + "Store_JobId": { + "runAfter": { + "Parse_Submission_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobId", + "value": "@body('Parse_Submission_Response')?['job_id']" + } + }, + "Store_Job_AgentId": { + "runAfter": { + "Store_JobId": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobAgentId", + "value": "@body('Parse_Submission_Response')?['agent_id']" + } + }, + "Check_If_Should_Poll": { + "runAfter": { + "Store_Job_AgentId": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@variables('DoPoll')", + true + ] + }, + { + "or": [ + { + "equals": [ + "@variables('SubmissionStatusCode')", + 200 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 201 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 202 + ] + } + ] + }, + { + "not": { + "equals": [ + "@variables('JobId')", + "" + ] + } + } + ] + }, + "actions": { + "Call_Async_Poll_Playbook": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAsyncPollPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "job_id": "@variables('JobId')", + "agent_id": "@variables('JobAgentId')", + "system_id": "@variables('SystemId')", + "source": "ontap", + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "original_volume_id": "@variables('VolumeId')" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + } + } + }, + "Parse_Poll_Response": { + "runAfter": { + "Check_If_Should_Poll": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Async_Poll_Playbook')", + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "jobId": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + } + } + }, + "Store_Poll_Response": { + "runAfter": { + "Parse_Poll_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "PollResponse", + "value": "@body('Parse_Poll_Response')" + } + }, + "Return_Response": { + "runAfter": { + "Store_Poll_Response": [ + "Succeeded", + "Skipped" + ], + "Check_If_Should_Poll": [ + "Succeeded" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Submit_Volume_Snapshot": [ + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), 500, if(or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)), 200, if(equals(variables('SubmissionStatusCode'), 0), 500, variables('SubmissionStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut'))), or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), 'Failed submitting volume snapshot request', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), 'submit', 'success'))", + "submission": { + "statusCode": "@variables('SubmissionStatusCode')", + "jobId": "@variables('JobId')", + "raw": "@body('Submit_Volume_Snapshot')", + "details": "@if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), body('Submit_Volume_Snapshot'), null)" + }, + "polling": { + "invoked": "@and(variables('DoPoll'), not(empty(variables('JobId'))))", + "playbook": "@parameters('asyncPollPlaybookName')", + "jobFinalStatus": "@coalesce(variables('PollResponse')?['status'], 'unknown')", + "jobResponse": "@variables('PollResponse')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + } + } + }, + "tags": { + "hidden-SentinelWorkspaceId": "[[variables('workspaceResourceId')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Playbook-', last(split(variables('playbookId4'),'/'))))]", + "properties": { + "parentId": "[variables('playbookId4')]", + "contentId": "[variables('_playbookContentId4')]", + "kind": "Playbook", + "version": "[variables('playbookVersion4')]", + "source": { + "kind": "Solution", + "name": "NetApp Ransomware Resilience", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "NetApp", + "email": "[variables('_email')]" + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + } + } + } + ], + "metadata": { + "title": "NetApp Ransomware Resilience Volume Snapshot Playbook", + "description": "This playbook creates a NetApp volume snapshot using the updated NetApp Ransomware Resilience take-snapshot API endpoint and optionally polls for completion.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. NetApp Ransomware Resilience Async Poll Playbook must be deployed first and ensure that it is functioning correctly", + "3. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "4. Caller must provide volume_id, agent_id, and system_id parameters" + ], + "postDeployment": [ + "1. Test the volume snapshot functionality with valid volume_id, agent_id, and system_id" + ], + "lastUpdateTime": "2025-09-22T00:00:00Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Volume", + "Snapshot" + ], + "releaseNotes": { + "version": "1.0", + "title": "[variables('blanks')]", + "notes": [ + "Initial version" + ] + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId4')]", + "contentKind": "Playbook", + "displayName": "NetApp-RansomwareResilience-Volume-Snapshot", + "contentProductId": "[variables('_playbookcontentProductId4')]", + "id": "[variables('_playbookcontentProductId4')]", + "version": "[variables('playbookVersion4')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('playbookTemplateSpecName5')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "NetApp-RansomwareResilience-Enrich-StorageVM Playbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('playbookVersion5')]", + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Enrich-StorageVM", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": { + "workspace-location-inline": "[concat('[resourceGroup().locatio', 'n]')]", + "workspace-name": "[parameters('workspace')]", + "workspaceResourceId": "[[resourceId('microsoft.OperationalInsights/Workspaces', variables('workspace-name'))]" + }, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[[parameters('PlaybookName')]", + "location": "[[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string" + }, + "system_id": { + "type": "string" + }, + "callbackData": { + "type": "object" + } + }, + "required": [ + "agent_id", + "system_id" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "StorageVMData", + "type": "array" + }, + { + "name": "EnrichmentStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Get_StorageVM_Data": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/enrich/storage?agent_id=@{variables('AgentId')}&system_id=@{variables('SystemId')}", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "accept": "application/json" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Capture_Enrichment_Status_Code": { + "runAfter": { + "Get_StorageVM_Data": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "EnrichmentStatusCode", + "value": "@outputs('Get_StorageVM_Data')['statusCode']" + } + }, + "Parse_StorageVM_Response": { + "runAfter": { + "Capture_Enrichment_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_StorageVM_Data')", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "volume_uuid": { + "type": "string" + }, + "volume_name": { + "type": "string" + }, + "svm_name": { + "type": "string" + } + } + } + } + } + }, + "Store_StorageVM_Data": { + "runAfter": { + "Parse_StorageVM_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "StorageVMData", + "value": "@body('Parse_StorageVM_Response')" + } + }, + "Finalize_Execution": { + "runAfter": { + "Store_StorageVM_Data": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Get_StorageVM_Data": [ + "Failed", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "execution_completed": true, + "timestamp": "@utcNow()" + } + }, + "Return_Response": { + "runAfter": { + "Finalize_Execution": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut')), 500, if(equals(variables('EnrichmentStatusCode'), 200), 200, if(equals(variables('EnrichmentStatusCode'), 0), 500, variables('EnrichmentStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut'))), equals(variables('EnrichmentStatusCode'), 200))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut')), 'Failed retrieving storage VM data', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut')), 'enrichment', 'success'))", + "account_id": "@variables('AccountId')", + "enrichment": { + "statusCode": "@variables('EnrichmentStatusCode')", + "storage_vms": "@variables('StorageVMData')", + "count": "@length(variables('StorageVMData'))", + "raw": "@body('Get_StorageVM_Data')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + } + } + }, + "tags": { + "hidden-SentinelWorkspaceId": "[[variables('workspaceResourceId')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Playbook-', last(split(variables('playbookId5'),'/'))))]", + "properties": { + "parentId": "[variables('playbookId5')]", + "contentId": "[variables('_playbookContentId5')]", + "kind": "Playbook", + "version": "[variables('playbookVersion5')]", + "source": { + "kind": "Solution", + "name": "NetApp Ransomware Resilience", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "NetApp", + "email": "[variables('_email')]" + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + } + } + } + ], + "metadata": { + "title": "NetApp Ransomware Resilience Enrich StorageVM Playbook", + "description": "This playbook enriches storage data by calling the updated NetApp Ransomware Resilience enrich storage API endpoint.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "3. Caller must provide agent_id and system_id parameters" + ], + "postDeployment": [ + "1. Test the storage enrichment functionality with valid agent_id and system_id" + ], + "lastUpdateTime": "2025-09-23T00:00:00Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "StorageVM", + "Enrich" + ], + "releaseNotes": { + "version": "1.0", + "title": "[variables('blanks')]", + "notes": [ + "Initial version" + ] + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId5')]", + "contentKind": "Playbook", + "displayName": "NetApp-RansomwareResilience-Enrich-StorageVM", + "contentProductId": "[variables('_playbookcontentProductId5')]", + "id": "[variables('_playbookcontentProductId5')]", + "version": "[variables('playbookVersion5')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('playbookTemplateSpecName6')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "NetApp-RansomwareResilience-Enrich-IP Playbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('playbookVersion6')]", + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Enrich-IP", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience async poll playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": { + "workspace-location-inline": "[concat('[resourceGroup().locatio', 'n]')]", + "workspace-name": "[parameters('workspace')]", + "workspaceResourceId": "[[resourceId('microsoft.OperationalInsights/Workspaces', variables('workspace-name'))]" + }, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[[parameters('PlaybookName')]", + "location": "[[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + }, + "asyncPollPlaybookName": { + "defaultValue": "[[parameters('NetAppRansomwareResilienceAsyncPollPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "ip_address": { + "type": "string" + }, + "callbackData": { + "type": "object" + } + }, + "required": [ + "ip_address" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "IpAddress", + "type": "string", + "value": "@triggerBody()?['ip_address']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "JobIds", + "type": "array" + }, + { + "name": "PollingResults", + "type": "array" + }, + { + "name": "EnrichmentStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + }, + "timeout": "PT30S" + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Get_IP_NetworkInterfaces": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/enrich/ip-address", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "accept": "application/json", + "Content-Type": "application/json" + }, + "body": { + "ip_address": "@{variables('IpAddress')}" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + }, + "timeout": "PT45S" + } + }, + "Capture_Enrichment_Status_Code": { + "runAfter": { + "Get_IP_NetworkInterfaces": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "EnrichmentStatusCode", + "value": "@outputs('Get_IP_NetworkInterfaces')['statusCode']" + } + }, + "Parse_JobIds_Response": { + "runAfter": { + "Capture_Enrichment_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_IP_NetworkInterfaces')", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "source": { + "type": "string" + }, + "agent_id": { + "type": "string" + } + } + } + } + } + }, + "Extract_Job_IDs": { + "runAfter": { + "Parse_JobIds_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobIds", + "value": "@body('Parse_JobIds_Response')" + } + }, + "Poll_All_Jobs": { + "runAfter": { + "Extract_Job_IDs": [ + "Succeeded" + ] + }, + "type": "Foreach", + "foreach": "@variables('JobIds')", + "actions": { + "Call_Async_Poll_Playbook": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAsyncPollPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "job_id": "@item()?['job_id']", + "agent_id": "@item()?['agent_id']", + "system_id": "default", + "source": "@item()?['source']", + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "original_ip": "@variables('IpAddress')" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + }, + "timeout": "PT90S" + } + }, + "Add_Poll_Result": { + "runAfter": { + "Call_Async_Poll_Playbook": [ + "Succeeded" + ] + }, + "type": "AppendToArrayVariable", + "inputs": { + "name": "PollingResults", + "value": { + "job_id": "@item()?['job_id']", + "poll_response": "@body('Call_Async_Poll_Playbook')", + "original_job_info": "@item()" + } + } + }, + "Add_Failed_Poll_Result": { + "runAfter": { + "Call_Async_Poll_Playbook": [ + "Failed", + "TimedOut" + ] + }, + "type": "AppendToArrayVariable", + "inputs": { + "name": "PollingResults", + "value": { + "job_id": "@item()?['job_id']", + "poll_response": { + "success": false, + "error": "Failed to poll job or timeout occurred" + }, + "original_job_info": "@item()" + } + } + } + }, + "runtimeConfiguration": { + "concurrency": { + "repetitions": 1 + } + } + }, + "Finalize_Execution": { + "runAfter": { + "Poll_All_Jobs": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Get_IP_NetworkInterfaces": [ + "Failed", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "execution_completed": true, + "timestamp": "@utcNow()" + } + }, + "Return_Response": { + "runAfter": { + "Finalize_Execution": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut')), 500, if(equals(variables('EnrichmentStatusCode'), 200), 200, if(equals(variables('EnrichmentStatusCode'), 0), 500, variables('EnrichmentStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut'))), equals(variables('EnrichmentStatusCode'), 200))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut')), 'Failed retrieving IP network interfaces data', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut')), 'enrichment', 'success'))", + "account_id": "@variables('AccountId')", + "ip_address": "@variables('IpAddress')", + "enrichment": { + "statusCode": "@variables('EnrichmentStatusCode')", + "job_ids_count": "@length(variables('JobIds'))", + "polling_results_count": "@length(variables('PollingResults'))", + "original_jobs": "@variables('JobIds')", + "polling_results": "@variables('PollingResults')", + "raw_initial_response": "@body('Get_IP_NetworkInterfaces')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + } + } + }, + "tags": { + "hidden-SentinelWorkspaceId": "[[variables('workspaceResourceId')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Playbook-', last(split(variables('playbookId6'),'/'))))]", + "properties": { + "parentId": "[variables('playbookId6')]", + "contentId": "[variables('_playbookContentId6')]", + "kind": "Playbook", + "version": "[variables('playbookVersion6')]", + "source": { + "kind": "Solution", + "name": "NetApp Ransomware Resilience", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "NetApp", + "email": "[variables('_email')]" + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + } + } + } + ], + "metadata": { + "title": "NetApp Ransomware Resilience Enrich IP Playbook", + "description": "This playbook enriches IP data by calling the updated NetApp Ransomware Resilience enrich IP address API endpoint and asynchronously polls multiple job results.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. NetApp Ransomware Resilience Async Poll Playbook must be deployed and ensure that it is functioning correctly", + "3. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "4. Caller must provide ip_address parameter" + ], + "postDeployment": [ + "1. Test the IP enrichment functionality with valid IP address" + ], + "lastUpdateTime": "2025-09-26T00:00:00Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "IP", + "Enrich", + "NetworkInterface" + ], + "releaseNotes": { + "version": "1.0", + "title": "[variables('blanks')]", + "notes": [ + "Initial version" + ] + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId6')]", + "contentKind": "Playbook", + "displayName": "NetApp-RansomwareResilience-Enrich-IP", + "contentProductId": "[variables('_playbookcontentProductId6')]", + "id": "[variables('_playbookcontentProductId6')]", + "version": "[variables('playbookVersion6')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "apiVersion": "2023-04-01-preview", + "location": "[parameters('workspace-location')]", + "properties": { + "version": "3.0.0", + "kind": "Solution", + "contentSchemaVersion": "3.0.0", + "displayName": "NetApp Ransomware Resilience", + "publisherDisplayName": "NetApp", + "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

• Review the solution Release Notes

\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

NetApp Ransomware Resilience - Comprehensive security solution for detecting and responding to ransomware threats across NetApp storage environments.

\n

Playbooks: 6

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "contentKind": "Solution", + "contentProductId": "[variables('_solutioncontentProductId')]", + "id": "[variables('_solutioncontentProductId')]", + "icon": "\"NetApp", + "contentId": "[variables('_solutionId')]", + "parentId": "[variables('_solutionId')]", + "source": { + "kind": "Solution", + "name": "NetApp Ransomware Resilience", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "NetApp", + "email": "[variables('_email')]" + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "kind": "Playbook", + "contentId": "[variables('_NetApp-RansomwareResilience-Auth-Playbook')]", + "version": "[variables('playbookVersion1')]" + }, + { + "kind": "Playbook", + "contentId": "[variables('_NetApp-RansomwareResilience_Volume_Offline_Playbook')]", + "version": "[variables('playbookVersion2')]" + }, + { + "kind": "Playbook", + "contentId": "[variables('_NetApp-RansomwareResilience_Async_Poll_Playbook')]", + "version": "[variables('playbookVersion3')]" + }, + { + "kind": "Playbook", + "contentId": "[variables('_NetApp-RansomwareResilience_Volume_Snapshot_Playbook')]", + "version": "[variables('playbookVersion4')]" + }, + { + "kind": "Playbook", + "contentId": "[variables('_NetApp-RansomwareResilience_Enrich_StorageVM_Playbook')]", + "version": "[variables('playbookVersion5')]" + }, + { + "kind": "Playbook", + "contentId": "[variables('_NetApp-RansomwareResilience_Enrich_IP_Playbook')]", + "version": "[variables('playbookVersion6')]" + } + ] + }, + "firstPublishDate": "2026-02-16", + "lastPublishDate": "2026-02-16", + "providers": [ + "NetApp" + ], + "categories": { + "domains": [ + "Security - Threat Protection", + "Storage" + ] + } + }, + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" + } + ], + "outputs": {} +} diff --git a/Solutions/NetApp Ransomware Resilience/Package/testParameters.json b/Solutions/NetApp Ransomware Resilience/Package/testParameters.json new file mode 100644 index 00000000000..e55ec41a9ac --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Package/testParameters.json @@ -0,0 +1,24 @@ +{ + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + } +} diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/README.md b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/README.md new file mode 100644 index 00000000000..23b521bab7f --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/README.md @@ -0,0 +1,42 @@ +# NetApp-RansomwareResilience-Auth + +## Overview +This playbook provides authentication services for all NetApp Ransomware Resilience playbooks. It creates and manages a shared Azure Key Vault that securely stores your NetApp API credentials and handles OAuth2 token generation. + +## Purpose +The Auth Playbook serves as the central authentication hub for the NetApp Ransomware Resilience solution. All other playbooks depend on this playbook to obtain valid authentication tokens when communicating with the NetApp Ransomware Resilience API. + +## Deployment Order +**This playbook MUST be deployed FIRST** before deploying any other NetApp Ransomware Resilience playbooks. + +## What It Does +- Creates a shared Azure Key Vault to store NetApp API credentials securely +- Stores your client ID, client secret, and account ID +- Provides authentication tokens to other playbooks on request +- Uses OAuth2 client credentials flow for secure API authentication + +## Prerequisites +Before deploying this playbook, you need: +1. Valid NetApp Ransomware Resilience API credentials: + - Client ID + - Client Secret + - Account ID +2. Appropriate permissions to create resources in your Azure subscription + +## Post-Deployment Configuration +After deploying this playbook: +1. Verify that the Key Vault was created successfully +2. Confirm that your credentials are stored correctly in the Key Vault +3. Test the Logic App to ensure it can retrieve authentication tokens +4. Grant access permissions to the Key Vault for other playbooks as needed + +## Security Notes +- All credentials are stored securely in Azure Key Vault +- The playbook uses system-assigned managed identity for secure operations +- Credentials are never exposed in plain text outside the Key Vault + +## Need Help? +If you encounter authentication issues, verify: +- Your NetApp API credentials are valid and active +- The Key Vault is accessible and contains all required secrets +- The Logic App has appropriate permissions to access the Key Vault diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/azuredeploy.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/azuredeploy.json new file mode 100644 index 00000000000..530cc8144c7 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/azuredeploy.json @@ -0,0 +1,342 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "NetApp Ransomware Resilience Authentication Playbook", + "description": "This playbook creates a shared Key Vault for NetApp Ransomware Resilience credentials and provides authentication services to all NetApp Ransomware Resilience playbooks in the solution.", + "prerequisites": [ + "1. Valid client_id, client_secret, and account_id for NetApp Ransomware Resilience API authentication", + "2. API endpoint that accepts OAuth2 client credentials flow" + ], + "postDeployment": [ + "1. Update the client_id, client_secret, and account_id values in the Key Vault", + "2. Test the Logic App to ensure proper token retrieval", + "3. The Key Vault created by this playbook will be shared across all NetApp Ransomware Resilience playbooks", + "4. Other playbooks will call this Auth playbook to obtain authentication tokens" + ], + "lastUpdateTime": "2025-09-17T00:00:00.000Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Authentication" + ], + "support": { + "tier": "Community" + }, + "author": { + "name": "NetApp" + } + }, + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the Logic App playbook to be created" + } + }, + "KeyVaultName": { + "type": "String", + "defaultValue": "[concat('RRSkv', uniqueString(resourceGroup().id))]", + "metadata": { + "description": "Name of the shared Key Vault to store NetApp Ransomware Resilience credentials" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + }, + "ClientId": { + "type": "securestring", + "metadata": { + "description": "Client ID for NetApp Ransomware Resilience API authentication" + } + }, + "ClientSecret": { + "type": "securestring", + "metadata": { + "description": "Client Secret for NetApp Ransomware Resilience API authentication" + } + }, + "AccountId": { + "type": "securestring", + "metadata": { + "description": "Account ID for NetApp Ransomware Resilience API authentication" + } + } + }, + "variables": { + "KeyVaultConnectionName": "[concat('keyvault-', parameters('PlaybookName'))]" + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('KeyVaultName')]", + "location": "[parameters('location')]", + "properties": { + "sku": { + "family": "A", + "name": "standard" + }, + "tenantId": "[subscription().tenantId]", + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": true, + "enableSoftDelete": true, + "softDeleteRetentionInDays": 90, + "enableRbacAuthorization": false, + "accessPolicies": [] + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2019-09-01", + "name": "[concat(parameters('KeyVaultName'), '/client-id')]", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "value": "[parameters('ClientId')]" + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2019-09-01", + "name": "[concat(parameters('KeyVaultName'), '/client-secret')]", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "value": "[parameters('ClientSecret')]" + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2019-09-01", + "name": "[concat(parameters('KeyVaultName'), '/account-id')]", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "value": "[parameters('AccountId')]" + } + }, + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[variables('KeyVaultConnectionName')]", + "location": "[parameters('location')]", + "kind": "V1", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "displayName": "[variables('KeyVaultConnectionName')]", + "parameterValueSet": { + "name": "oauthMI", + "values": { + "vaultName": { + "value": "[parameters('KeyVaultName')]" + } + } + }, + "api": { + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', parameters('location'), '/managedApis/keyvault')]" + } + } + }, + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[parameters('PlaybookName')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Web/connections', variables('KeyVaultConnectionName'))]", + "[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$connections": { + "defaultValue": {}, + "type": "Object" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "properties": { + "account_id": { + "type": "string" + }, + "api_hostname": { + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "actions": { + "Get_Client_ID": { + "runAfter": {}, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('client-id')}/value" + } + }, + "Get_Client_Secret": { + "runAfter": { + "Get_Client_ID": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('client-secret')}/value" + } + }, + "Get_Account_ID": { + "runAfter": { + "Get_Client_Secret": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('account-id')}/value" + } + }, + "HTTP_Token_Request": { + "runAfter": { + "Get_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://netapp-cloud-account.auth0.com/oauth/token", + "headers": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "body": "grant_type=client_credentials&client_id=@{body('Get_Client_ID')?['value']}&client_secret=@{body('Get_Client_Secret')?['value']}&audience=https://api.cloud.netapp.com" + } + }, + "Response": { + "runAfter": { + "HTTP_Token_Request": [ + "Succeeded" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "access_token": "@{body('HTTP_Token_Request')?['access_token']}", + "token_type": "@{body('HTTP_Token_Request')?['token_type']}", + "expires_in": "@{body('HTTP_Token_Request')?['expires_in']}", + "account_id": "@{body('Get_Account_ID')?['value']}" + } + } + } + }, + "outputs": {} + }, + "parameters": { + "$connections": { + "value": { + "keyvault": { + "connectionId": "[resourceId('Microsoft.Web/connections', variables('KeyVaultConnectionName'))]", + "connectionName": "[variables('KeyVaultConnectionName')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + }, + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', parameters('location'), '/managedApis/keyvault')]" + } + } + } + } + } + }, + { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2019-09-01", + "name": "[concat(parameters('KeyVaultName'), '/add')]", + "dependsOn": [ + "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]", + "[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + ], + "properties": { + "accessPolicies": [ + { + "tenantId": "[subscription().tenantId]", + "objectId": "[reference(resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')), '2019-05-01', 'Full').identity.principalId]", + "permissions": { + "secrets": [ + "get" + ] + } + } + ] + } + } + ], + "outputs": { + "PlaybookName": { + "type": "string", + "value": "[parameters('PlaybookName')]" + }, + "PlaybookResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + }, + "KeyVaultName": { + "type": "string", + "value": "[parameters('KeyVaultName')]" + }, + "KeyVaultResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('KeyVaultName'))]" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/azuredeploy.parameters.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/azuredeploy.parameters.json new file mode 100644 index 00000000000..605dc475a26 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Auth-Playbook/azuredeploy.parameters.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "PlaybookName": { + "value": "NetApp-RansomwareResilience-Auth" + }, + "KeyVaultName": { + "value": "[concat('rrskv', uniqueString(resourceGroup().id))]" + }, + "ClientId": { + "value": "YOUR_CLIENT_ID_HERE" + }, + "ClientSecret": { + "value": "YOUR_CLIENT_SECRET_HERE" + }, + "AccountId": { + "value": "YOUR_ACCOUNT_ID_HERE" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Manual-IP-to-Offline-Playbook/azuredeploy.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Manual-IP-to-Offline-Playbook/azuredeploy.json new file mode 100644 index 00000000000..f5ebed7bc79 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience-Manual-IP-to-Offline-Playbook/azuredeploy.json @@ -0,0 +1,418 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "NetApp RRS Manual IP to Volume Offline", + "description": "Manually trigger playbook to take a volume offline based on IP address enrichment", + "prerequisites": [ + "1. NetApp RRS Auth Playbook must be deployed", + "2. NetApp RRS Async Poll Playbook must be deployed", + "3. NetApp RRS Enrich IP Playbook must be deployed", + "4. NetApp RRS Enrich StorageVM Playbook must be deployed", + "5. NetApp RRS Volume Offline Playbook must be deployed" + ], + "postDeployment": [ + "1. Assign Managed Identity permissions if needed", + "2. Test with a valid IP address" + ], + "lastUpdateTime": "2026-02-09T00:00:00.000Z", + "entities": [], + "tags": ["NetApp", "Ransomware", "Volume", "Offline", "Manual"], + "support": { + "tier": "community" + }, + "author": { + "name": "NetApp" + } + }, + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Manual-IP-to-Offline-Playbook", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "IPEnrichmentPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Enrich-IP-Playbook", + "metadata": { + "description": "Name of the IP Enrichment Playbook" + } + }, + "StorageVMEnrichmentPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Enrich-StorageVM-Playbook", + "metadata": { + "description": "Name of the StorageVM Enrichment Playbook" + } + }, + "VolumeOfflinePlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Volume-Offline-Playbook", + "metadata": { + "description": "Name of the Volume Offline Playbook" + } + } + }, + "variables": { + "IPEnrichmentPlaybookId": "[resourceId('Microsoft.Logic/workflows', parameters('IPEnrichmentPlaybookName'))]", + "StorageVMEnrichmentPlaybookId": "[resourceId('Microsoft.Logic/workflows', parameters('StorageVMEnrichmentPlaybookName'))]", + "VolumeOfflinePlaybookId": "[resourceId('Microsoft.Logic/workflows', parameters('VolumeOfflinePlaybookName'))]" + }, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[parameters('PlaybookName')]", + "location": "[resourceGroup().location]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "ip_address": { + "type": "string", + "description": "IP address to enrich and take volume offline" + } + }, + "required": ["ip_address"] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "InputIP", + "type": "string", + "value": "@triggerBody()?['ip_address']" + }, + { + "name": "VolumeId", + "type": "string", + "value": "" + }, + { + "name": "AgentId", + "type": "string", + "value": "" + }, + { + "name": "SystemId", + "type": "string", + "value": "" + }, + { + "name": "IPEnrichmentStatus", + "type": "string", + "value": "" + }, + { + "name": "StorageEnrichmentStatus", + "type": "string", + "value": "" + }, + { + "name": "VolumeOfflineStatus", + "type": "string", + "value": "" + }, + { + "name": "ErrorMessage", + "type": "string", + "value": "" + } + ] + } + }, + "Step_1_Call_IP_Enrichment": { + "runAfter": { + "Initialize_Variables": ["Succeeded"] + }, + "type": "Workflow", + "inputs": { + "host": { + "triggerName": "manual", + "workflow": { + "id": "[variables('IPEnrichmentPlaybookId')]" + } + }, + "body": { + "ip_address": "@variables('InputIP')" + } + } + }, + "Parse_IP_Enrichment_Response": { + "runAfter": { + "Step_1_Call_IP_Enrichment": ["Succeeded", "Failed", "TimedOut"] + }, + "type": "Compose", + "inputs": "@if(equals(actions('Step_1_Call_IP_Enrichment').status, 'Succeeded'), body('Step_1_Call_IP_Enrichment'), json(concat('{\"success\": false, \"message\": \"IP Enrichment workflow failed or timed out: ', actions('Step_1_Call_IP_Enrichment').status, '\"}')))" + }, + "Check_IP_Enrichment_Success": { + "runAfter": { + "Parse_IP_Enrichment_Response": ["Succeeded"] + }, + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@outputs('Parse_IP_Enrichment_Response')?['success']", + true + ] + } + ] + }, + "actions": { + "Set_IP_Enrichment_Success": { + "type": "SetVariable", + "inputs": { + "name": "IPEnrichmentStatus", + "value": "Success" + } + }, + "Extract_Volume_Data": { + "runAfter": { + "Set_IP_Enrichment_Success": ["Succeeded"] + }, + "type": "Compose", + "inputs": "@first(first(outputs('Parse_IP_Enrichment_Response')?['enrichment']?['polling_results'])?['poll_response']?['data']?['records'])" + }, + "Set_Agent_ID": { + "runAfter": { + "Extract_Volume_Data": ["Succeeded"] + }, + "type": "SetVariable", + "inputs": { + "name": "AgentId", + "value": "@{outputs('Extract_Volume_Data')?['agent_id']}" + } + }, + "Set_System_ID": { + "runAfter": { + "Set_Agent_ID": ["Succeeded"] + }, + "type": "SetVariable", + "inputs": { + "name": "SystemId", + "value": "@{outputs('Extract_Volume_Data')?['system_id']}" + } + }, + "Step_2_Call_StorageVM_Enrichment": { + "runAfter": { + "Set_System_ID": ["Succeeded"] + }, + "type": "Workflow", + "inputs": { + "host": { + "triggerName": "manual", + "workflow": { + "id": "[variables('StorageVMEnrichmentPlaybookId')]" + } + }, + "body": { + "agent_id": "@variables('AgentId')", + "system_id": "@variables('SystemId')" + } + } + }, + "Parse_StorageVM_Response": { + "runAfter": { + "Step_2_Call_StorageVM_Enrichment": ["Succeeded", "Failed", "TimedOut"] + }, + "type": "Compose", + "inputs": "@if(equals(actions('Step_2_Call_StorageVM_Enrichment').status, 'Succeeded'), body('Step_2_Call_StorageVM_Enrichment'), json(concat('{\"success\": false, \"message\": \"StorageVM Enrichment workflow failed or timed out: ', actions('Step_2_Call_StorageVM_Enrichment').status, '\"}')))" + }, + "Check_StorageVM_Success": { + "runAfter": { + "Parse_StorageVM_Response": ["Succeeded"] + }, + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@outputs('Parse_StorageVM_Response')?['success']", + true + ] + } + ] + }, + "actions": { + "Set_StorageVM_Success": { + "type": "SetVariable", + "inputs": { + "name": "StorageEnrichmentStatus", + "value": "Success" + } + }, + "Extract_Volume_UUID": { + "runAfter": { + "Set_StorageVM_Success": ["Succeeded"] + }, + "type": "Compose", + "inputs": "@first(outputs('Parse_StorageVM_Response')?['enrichment']?['storage_vms'])?['volume_uuid']" + }, + "Update_Volume_ID": { + "runAfter": { + "Extract_Volume_UUID": ["Succeeded"] + }, + "type": "SetVariable", + "inputs": { + "name": "VolumeId", + "value": "@{outputs('Extract_Volume_UUID')}" + } + }, + "Step_3_Call_Volume_Offline": { + "runAfter": { + "Update_Volume_ID": ["Succeeded"] + }, + "type": "Workflow", + "inputs": { + "host": { + "triggerName": "manual", + "workflow": { + "id": "[variables('VolumeOfflinePlaybookId')]" + } + }, + "body": { + "volume_id": "@variables('VolumeId')", + "agent_id": "@variables('AgentId')", + "system_id": "@variables('SystemId')", + "poll": true + } + } + }, + "Parse_Volume_Offline_Response": { + "runAfter": { + "Step_3_Call_Volume_Offline": ["Succeeded", "Failed", "TimedOut"] + }, + "type": "Compose", + "inputs": "@if(equals(actions('Step_3_Call_Volume_Offline').status, 'Succeeded'), body('Step_3_Call_Volume_Offline'), json(concat('{\"success\": false, \"message\": \"Volume Offline workflow failed or timed out: ', actions('Step_3_Call_Volume_Offline').status, '\"}')))" + }, + "Set_Volume_Offline_Status": { + "runAfter": { + "Parse_Volume_Offline_Response": ["Succeeded"] + }, + "type": "SetVariable", + "inputs": { + "name": "VolumeOfflineStatus", + "value": "@{if(equals(outputs('Parse_Volume_Offline_Response')?['success'], true), 'Success - Volume Taken Offline', 'Failed - Volume NOT Offline')}" + } + } + }, + "else": { + "actions": { + "Set_StorageVM_Failed": { + "type": "SetVariable", + "inputs": { + "name": "StorageEnrichmentStatus", + "value": "Failed" + } + }, + "Set_StorageVM_Error": { + "runAfter": { + "Set_StorageVM_Failed": ["Succeeded"] + }, + "type": "SetVariable", + "inputs": { + "name": "ErrorMessage", + "value": "@{outputs('Parse_StorageVM_Response')?['message']}" + } + } + } + } + } + }, + "else": { + "actions": { + "Set_IP_Enrichment_Failed": { + "type": "SetVariable", + "inputs": { + "name": "IPEnrichmentStatus", + "value": "Failed" + } + }, + "Set_IP_Error": { + "runAfter": { + "Set_IP_Enrichment_Failed": ["Succeeded"] + }, + "type": "SetVariable", + "inputs": { + "name": "ErrorMessage", + "value": "@{outputs('Parse_IP_Enrichment_Response')?['message']}" + } + } + } + } + }, + "Response": { + "runAfter": { + "Check_IP_Enrichment_Success": ["Succeeded", "Failed"] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@{if(equals(variables('VolumeOfflineStatus'), 'Success - Volume Taken Offline'), 200, 400)}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@{equals(variables('VolumeOfflineStatus'), 'Success - Volume Taken Offline')}", + "input": { + "ip_address": "@{variables('InputIP')}" + }, + "enrichment_results": { + "ip_enrichment_status": "@{variables('IPEnrichmentStatus')}", + "storage_enrichment_status": "@{variables('StorageEnrichmentStatus')}", + "volume_offline_status": "@{variables('VolumeOfflineStatus')}" + }, + "volume_details": { + "volume_id": "@{variables('VolumeId')}", + "agent_id": "@{variables('AgentId')}", + "system_id": "@{variables('SystemId')}" + }, + "error_message": "@{variables('ErrorMessage')}", + "timestamp": "@{utcNow()}" + } + } + } + }, + "outputs": {} + }, + "parameters": {} + } + } + ], + "outputs": { + "PlaybookName": { + "type": "string", + "value": "[parameters('PlaybookName')]" + }, + "PlaybookResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + }, + "PlaybookUrl": { + "type": "string", + "value": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('PlaybookName'), 'manual'), '2019-05-01').value]" + } + } +} diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/README.md b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/README.md new file mode 100644 index 00000000000..4b539fef41e --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/README.md @@ -0,0 +1,50 @@ +# NetApp-RansomwareResilience-Async-Poll + +## Overview +This playbook monitors the status of asynchronous NetApp operations by polling job status until completion or timeout. It acts as a helper playbook for operations that take time to complete. + +## Purpose +When you perform actions like taking snapshots or taking volumes offline, these operations run asynchronously in the NetApp system. This playbook continuously checks the job status and notifies you when the operation completes, fails, or times out. + +## Deployment Order +**This playbook MUST be deployed SECOND**, after: +1. ✅ Auth Playbook (required) + +## What It Does +- Polls NetApp job status at regular intervals +- Monitors asynchronous operations until completion +- Handles timeout scenarios for long-running jobs +- Returns final job status to the calling playbook +- Provides operation results back to parent workflows + +## Prerequisites +Before deploying this playbook: +1. The Auth Playbook must be successfully deployed and functioning +2. Valid NetApp API credentials configured in the Auth Playbook + +## How It Works +1. Receives a job ID, agent ID, system ID, and source from a calling playbook +2. Requests an authentication token from the Auth Playbook +3. Polls the NetApp API for job status at regular intervals +4. Continues polling until the job completes, fails, or times out +5. Returns the final status to the calling playbook + +## When Is This Used? +This playbook is automatically called by other action playbooks such as: +- Volume Snapshot Playbook +- Volume Offline Playbook +- Any other playbook that initiates asynchronous NetApp operations + +You typically won't call this playbook directly—it's invoked automatically by other playbooks that need to wait for operation completion. + +## Post-Deployment Configuration +After deploying this playbook: +1. Test it by triggering with a valid job ID from a NetApp operation +2. Monitor the run history to verify successful polling behavior +3. Adjust timeout settings if needed based on your environment + +## Need Help? +If polling isn't working correctly, verify: +- The Auth Playbook is functioning and returning valid tokens +- The job ID being polled is valid and active +- Your NetApp API endpoint is accessible diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/azuredeploy.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/azuredeploy.json new file mode 100644 index 00000000000..897d1325d86 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/azuredeploy.json @@ -0,0 +1,481 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "NetApp Ransomware Resilience Async Poll Playbook", + "description": "This playbook polls NetApp Ransomware Resilience job status asynchronously until completion or timeout using the updated job status API endpoint.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "3. Caller must provide job_id, agent_id, system_id, and source parameters" + ], + "postDeployment": [ + "1. Test the playbook by triggering it with a valid job_id, agent_id, system_id, and source", + "2. Monitor the playbook run history to verify successful polling and completion" + ], + "lastUpdateTime": "2025-09-17T00:00:00.000Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Async", + "Polling" + ], + "support": { + "tier": "Community" + }, + "author": { + "name": "NetApp" + } + }, + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[parameters('PlaybookName')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "The NetApp Ransomware Resilience job ID to poll" + }, + "agent_id": { + "type": "string", + "description": "NetApp Ransomware Resilience agent ID" + }, + "system_id": { + "type": "string", + "description": "System ID for the working environment" + }, + "source": { + "type": "string", + "description": "Job source type" + }, + "caller_info": { + "type": "object", + "description": "Optional caller information for tracking" + } + }, + "required": [ + "job_id", + "agent_id", + "system_id", + "source" + ] + } + } + } + }, + "actions": { + "Initialize_variables": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "JobId", + "type": "string", + "value": "@triggerBody()?['job_id']" + }, + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "Source", + "type": "string", + "value": "@triggerBody()?['source']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "PollCount", + "type": "integer", + "value": 0 + }, + { + "name": "MaxPollAttempts", + "type": "integer", + "value": 10 + }, + { + "name": "PollIntervalSeconds", + "type": "integer", + "value": 30 + }, + { + "name": "JobStatus", + "type": "string", + "value": "pending" + }, + { + "name": "JobResponse", + "type": "object", + "value": {} + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + } + } + }, + "Set_Account_ID": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Call_Auth_Playbook')?['account_id']" + } + }, + "Initial_Status_Check": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/job/status?source=@{variables('Source')}&job_id=@{variables('JobId')}&agent_id=@{variables('AgentId')}&system_id=@{variables('SystemId')}", + "headers": { + "Authorization": "Bearer @{body('Call_Auth_Playbook')?['access_token']}", + "accept": "application/json" + }, + "retryPolicy": { + "type": "exponential", + "count": 2, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT10S" + }, + "timeout": "PT30S" + } + }, + "Update_Initial_Status": { + "runAfter": { + "Initial_Status_Check": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobStatus", + "value": "@body('Initial_Status_Check')?['status']" + } + }, + "Update_Initial_Response": { + "runAfter": { + "Update_Initial_Status": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobResponse", + "value": "@body('Initial_Status_Check')" + } + }, + "Polling_Loop": { + "runAfter": { + "Update_Initial_Response": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@or(or(equals(variables('JobStatus'), 'SUCCESS'), equals(variables('JobStatus'), 'success')), or(equals(variables('JobStatus'), 'FAILED'), equals(variables('JobStatus'), 'failed')), greater(variables('PollCount'), variables('MaxPollAttempts')))", + "limit": { + "count": 10, + "timeout": "PT6M" + }, + "actions": { + "Increment_Poll_Count": { + "runAfter": {}, + "type": "IncrementVariable", + "inputs": { + "name": "PollCount", + "value": 1 + } + }, + "Check_Job_Status": { + "runAfter": { + "Increment_Poll_Count": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/job/status?source=@{variables('Source')}&job_id=@{variables('JobId')}&agent_id=@{variables('AgentId')}&system_id=@{variables('SystemId')}", + "headers": { + "Authorization": "Bearer @{body('Call_Auth_Playbook')?['access_token']}", + "accept": "application/json" + }, + "retryPolicy": { + "type": "exponential", + "count": 2, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT10S" + }, + "timeout": "PT30S" + } + }, + "Update_Job_Status": { + "runAfter": { + "Check_Job_Status": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobStatus", + "value": "@body('Check_Job_Status')?['status']" + } + }, + "Handle_Status_Check_Failure": { + "runAfter": { + "Check_Job_Status": [ + "Failed", + "TimedOut" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobStatus", + "value": "failed" + } + }, + "Update_Job_Response": { + "runAfter": { + "Update_Job_Status": [ + "Succeeded" + ], + "Handle_Status_Check_Failure": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobResponse", + "value": "@body('Check_Job_Status')" + } + }, + "Wait_If_Needed": { + "runAfter": { + "Update_Job_Response": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "not": { + "or": [ + { + "equals": [ + "@variables('JobStatus')", + "SUCCESS" + ] + }, + { + "equals": [ + "@variables('JobStatus')", + "success" + ] + } + ] + } + }, + "actions": { + "Wait_Before_Retry": { + "runAfter": {}, + "type": "Wait", + "inputs": { + "interval": { + "count": "@variables('PollIntervalSeconds')", + "unit": "Second" + } + } + } + }, + "else": { + "actions": {} + } + } + } + }, + "Check_Final_Status": { + "runAfter": { + "Polling_Loop": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "or": [ + { + "equals": [ + "@variables('JobStatus')", + "SUCCESS" + ] + }, + { + "equals": [ + "@variables('JobStatus')", + "success" + ] + } + ] + } + ] + }, + "actions": { + "Success_Response": { + "runAfter": {}, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": true, + "job_id": "@variables('JobId')", + "status": "@variables('JobStatus')", + "poll_count": "@variables('PollCount')", + "data": "@variables('JobResponse')", + "caller_info": "@triggerBody()?['caller_info']" + } + } + } + }, + "else": { + "actions": { + "Error_Response": { + "runAfter": {}, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": 408, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": false, + "error": "Job polling timeout or failed", + "job_id": "@variables('JobId')", + "status": "@variables('JobStatus')", + "poll_count": "@variables('PollCount')", + "max_attempts": "@variables('MaxPollAttempts')", + "data": "@variables('JobResponse')", + "caller_info": "@triggerBody()?['caller_info']" + } + } + } + } + } + } + }, + "outputs": {} + }, + "parameters": {} + } + } + ], + "outputs": { + "PlaybookName": { + "type": "string", + "value": "[parameters('PlaybookName')]" + }, + "PlaybookResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + }, + "PlaybookUrl": { + "type": "string", + "value": "[concat('https://portal.azure.com/#resource', resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')))]" + }, + "TriggerUrl": { + "type": "string", + "value": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('PlaybookName'), 'manual'), '2019-05-01').value]" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/azuredeploy.parameters.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/azuredeploy.parameters.json new file mode 100644 index 00000000000..627245456a3 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Async_Poll_Playbook/azuredeploy.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "PlaybookName": { + "value": "NetApp-RansomwareResilience-Async-Poll" + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "value": "NetApp-RansomwareResilience-Auth" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/README.md b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/README.md new file mode 100644 index 00000000000..196e83f2181 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/README.md @@ -0,0 +1,62 @@ +# NetApp-RansomwareResilience-Enrich-IP + +## Overview +This playbook enriches IP address information by retrieving detailed network interface data from the NetApp Ransomware Resilience API. It helps you investigate network-related security incidents by providing context about storage network interfaces. + +## Purpose +When investigating a security incident involving a suspicious IP address, this playbook retrieves detailed information about the network interface from your NetApp storage systems, including associated volumes, storage VMs, and access patterns. + +## Deployment Order +**This playbook should be deployed THIRD**, after: +1. ✅ Auth Playbook (required) +2. ✅ Async Poll Playbook (required) + +## What It Does +- Accepts an IP address as input +- Retrieves authentication from the Auth Playbook +- Queries the NetApp API for network interface details associated with that IP +- Polls asynchronously for the enrichment results +- Returns detailed network interface information including: + - Network interface configuration + - Associated storage VMs + - Connected volumes + - Access policies and protocols + +## Prerequisites +Before deploying this playbook: +1. Auth Playbook must be deployed and functioning correctly +2. Async Poll Playbook must be deployed and functioning correctly +3. Valid NetApp API credentials configured + +## How to Use +This playbook can be: +- Called manually with an IP address to investigate +- Integrated into automation rules triggered by Microsoft Sentinel alerts +- Chained with other playbooks in your incident response workflow +- Combined with Volume Snapshot or Volume Offline playbooks for remediation + +**Input Required:** +- `ip_address`: The IP address you want to investigate + +## Use Case Example +When you receive an alert about suspicious activity from an IP address: +1. This playbook enriches the IP with NetApp storage context +2. You identify which storage VM and volumes are exposed +3. Based on the findings, you can take protective actions using other playbooks + +## Post-Deployment Configuration +After deploying this playbook: +1. Test with a known valid IP address from your NetApp environment +2. Verify the enrichment data is returned correctly +3. Consider integrating it into your incident response automation rules + +## Building Custom Workflows +This enrichment playbook is a building block. You can combine it with other playbooks to create complete incident response workflows. For example: +- Enrich IP → Identify volume → Take snapshot → Take volume offline + +## Need Help? +If enrichment isn't working, verify: +- The Auth Playbook is returning valid tokens +- The Async Poll Playbook is functioning correctly +- The IP address exists in your NetApp environment +- Your NetApp API endpoint is accessible diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/azuredeploy.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/azuredeploy.json new file mode 100644 index 00000000000..68e19f91438 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/azuredeploy.json @@ -0,0 +1,478 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "NetApp Ransomware Resilience Enrich IP Playbook", + "description": "This playbook enriches IP data by calling the updated NetApp Ransomware Resilience enrich IP address API endpoint and asynchronously polls multiple job results.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. NetApp Ransomware Resilience Async Poll Playbook must be deployed and ensure that it is functioning correctly", + "3. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "4. Caller must provide ip_address parameter" + ], + "postDeployment": [ + "1. Test the IP enrichment functionality with valid IP address" + ], + "lastUpdateTime": "2025-09-26T00:00:00.000Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "IP", + "Enrich", + "NetworkInterface" + ], + "support": { + "tier": "Community" + }, + "author": { + "name": "NetApp" + } + }, + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Enrich-IP", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience async poll playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[parameters('PlaybookName')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + }, + "asyncPollPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAsyncPollPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "ip_address": { + "type": "string" + }, + "callbackData": { + "type": "object" + } + }, + "required": [ + "ip_address" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "IpAddress", + "type": "string", + "value": "@triggerBody()?['ip_address']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "JobIds", + "type": "array" + }, + { + "name": "PollingResults", + "type": "array" + }, + { + "name": "EnrichmentStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + }, + "timeout": "PT30S" + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Get_IP_NetworkInterfaces": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/enrich/ip-address", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "accept": "application/json", + "Content-Type": "application/json" + }, + "body": { + "ip_address": "@{variables('IpAddress')}" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + }, + "timeout": "PT45S" + } + }, + "Capture_Enrichment_Status_Code": { + "runAfter": { + "Get_IP_NetworkInterfaces": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "EnrichmentStatusCode", + "value": "@outputs('Get_IP_NetworkInterfaces')['statusCode']" + } + }, + "Parse_JobIds_Response": { + "runAfter": { + "Capture_Enrichment_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_IP_NetworkInterfaces')", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "source": { + "type": "string" + }, + "agent_id": { + "type": "string" + } + } + } + } + } + }, + "Extract_Job_IDs": { + "runAfter": { + "Parse_JobIds_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobIds", + "value": "@body('Parse_JobIds_Response')" + } + }, + "Poll_All_Jobs": { + "runAfter": { + "Extract_Job_IDs": [ + "Succeeded" + ] + }, + "type": "Foreach", + "foreach": "@variables('JobIds')", + "actions": { + "Call_Async_Poll_Playbook": { + "runAfter": {}, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAsyncPollPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "job_id": "@item()?['job_id']", + "agent_id": "@item()?['agent_id']", + "system_id": "default", + "source": "@item()?['source']", + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "original_ip": "@variables('IpAddress')" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + }, + "timeout": "PT90S" + } + }, + "Add_Poll_Result": { + "runAfter": { + "Call_Async_Poll_Playbook": [ + "Succeeded" + ] + }, + "type": "AppendToArrayVariable", + "inputs": { + "name": "PollingResults", + "value": { + "job_id": "@item()?['job_id']", + "poll_response": "@body('Call_Async_Poll_Playbook')", + "original_job_info": "@item()" + } + } + }, + "Add_Failed_Poll_Result": { + "runAfter": { + "Call_Async_Poll_Playbook": [ + "Failed", + "TimedOut" + ] + }, + "type": "AppendToArrayVariable", + "inputs": { + "name": "PollingResults", + "value": { + "job_id": "@item()?['job_id']", + "poll_response": { + "success": false, + "error": "Failed to poll job or timeout occurred" + }, + "original_job_info": "@item()" + } + } + } + }, + "runtimeConfiguration": { + "concurrency": { + "repetitions": 1 + } + } + }, + "Finalize_Execution": { + "runAfter": { + "Poll_All_Jobs": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Get_IP_NetworkInterfaces": [ + "Failed", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "execution_completed": true, + "timestamp": "@utcNow()" + } + }, + "Return_Response": { + "runAfter": { + "Finalize_Execution": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut')), 500, if(equals(variables('EnrichmentStatusCode'), 200), 200, if(equals(variables('EnrichmentStatusCode'), 0), 500, variables('EnrichmentStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut'))), equals(variables('EnrichmentStatusCode'), 200))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut')), 'Failed retrieving IP network interfaces data', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Get_IP_NetworkInterfaces')['status'], 'Failed'), equals(actions('Get_IP_NetworkInterfaces')['status'], 'TimedOut')), 'enrichment', 'success'))", + "account_id": "@variables('AccountId')", + "ip_address": "@variables('IpAddress')", + "enrichment": { + "statusCode": "@variables('EnrichmentStatusCode')", + "job_ids_count": "@length(variables('JobIds'))", + "polling_results_count": "@length(variables('PollingResults'))", + "original_jobs": "@variables('JobIds')", + "polling_results": "@variables('PollingResults')", + "raw_initial_response": "@body('Get_IP_NetworkInterfaces')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + }, + "outputs": {} + }, + "parameters": {} + } + } + ], + "outputs": { + "PlaybookName": { + "type": "string", + "value": "[parameters('PlaybookName')]" + }, + "PlaybookResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + }, + "PlaybookUrl": { + "type": "string", + "value": "[concat('https://portal.azure.com/#resource', resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')))]" + }, + "TriggerUrl": { + "type": "string", + "value": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('PlaybookName'), 'manual'), '2019-05-01').value]" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/azuredeploy.parameters.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/azuredeploy.parameters.json new file mode 100644 index 00000000000..0ff758d73aa --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_IP_Playbook/azuredeploy.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "PlaybookName": { + "value": "NetApp-RansomwareResilience-Enrich-IP" + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "value": "NetApp-RansomwareResilience-Auth" + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "value": "NetApp-RansomwareResilience-Async-Poll" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/README.md b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/README.md new file mode 100644 index 00000000000..48f406844cd --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/README.md @@ -0,0 +1,65 @@ +# NetApp-RansomwareResilience-Enrich-StorageVM + +## Overview +This playbook enriches storage information by retrieving detailed configuration and status data for NetApp Storage Virtual Machines (SVMs). It provides valuable context when investigating incidents involving your NetApp storage infrastructure. + +## Purpose +When investigating a security incident, this playbook helps you understand the storage environment by retrieving detailed information about Storage VMs, including their volumes, security settings, and current operational status. + +## Deployment Order +**This playbook should be deployed FOURTH**, after: +1. ✅ Auth Playbook (required) +2. ✅ Async Poll Playbook (required) +3. ✅ Enrich IP Playbook (optional, recommended) + +## What It Does +- Accepts agent ID and system ID as input +- Retrieves authentication from the Auth Playbook +- Queries the NetApp API for Storage VM details +- Returns comprehensive storage configuration information including: + - Storage VM configuration and status + - Associated volumes and their states + - Security and protocol settings + - Network interface configurations + - Access policies + +## Prerequisites +Before deploying this playbook: +1. Auth Playbook must be deployed and functioning correctly +2. Valid NetApp API credentials configured + +## How to Use +This playbook can be: +- Called manually with agent ID and system ID to investigate storage configurations +- Triggered automatically as part of incident response workflows +- Combined with other playbooks to create comprehensive response actions +- Used to gather context before taking protective actions + +**Input Required:** +- `agent_id`: The NetApp agent identifier +- `system_id`: The NetApp system identifier + +## Use Case Example +When investigating a potential ransomware incident: +1. Use this playbook to retrieve Storage VM details +2. Identify all volumes and their current states +3. Assess which volumes may be at risk +4. Use Volume Snapshot playbook to protect critical data +5. Use Volume Offline playbook to isolate compromised volumes + +## Post-Deployment Configuration +After deploying this playbook: +1. Test with valid agent ID and system ID from your NetApp environment +2. Verify the storage enrichment data is returned correctly +3. Consider integrating it into your security automation workflows + +## Building Custom Workflows +This enrichment playbook is designed as a building block. Combine it with action playbooks to create complete incident response solutions: +- Enrich StorageVM → Identify critical volumes → Take snapshots → Take volumes offline if needed + +## Need Help? +If enrichment isn't working, verify: +- The Auth Playbook is returning valid tokens +- The agent ID and system ID are correct and active +- Your NetApp API endpoint is accessible +- The Storage VM exists in your NetApp environment diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/azuredeploy.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/azuredeploy.json new file mode 100644 index 00000000000..db2df9092ba --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/azuredeploy.json @@ -0,0 +1,376 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "NetApp Ransomware Resilience Enrich StorageVM Playbook", + "description": "This playbook enriches storage data by calling the updated NetApp Ransomware Resilience enrich storage API endpoint.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "3. Caller must provide agent_id and system_id parameters" + ], + "postDeployment": [ + "1. Test the storage enrichment functionality with valid agent_id and system_id" + ], + "lastUpdateTime": "2025-09-23T00:00:00.000Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "StorageVM", + "Enrich" + ], + "support": { + "tier": "Community" + }, + "author": { + "name": "NetApp" + } + }, + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Enrich-StorageVM", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[parameters('PlaybookName')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string" + }, + "system_id": { + "type": "string" + }, + "callbackData": { + "type": "object" + } + }, + "required": [ + "agent_id", + "system_id" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "StorageVMData", + "type": "array" + }, + { + "name": "EnrichmentStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Get_StorageVM_Data": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/enrich/storage?agent_id=@{variables('AgentId')}&system_id=@{variables('SystemId')}", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "accept": "application/json" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Capture_Enrichment_Status_Code": { + "runAfter": { + "Get_StorageVM_Data": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "EnrichmentStatusCode", + "value": "@outputs('Get_StorageVM_Data')['statusCode']" + } + }, + "Parse_StorageVM_Response": { + "runAfter": { + "Capture_Enrichment_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_StorageVM_Data')", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "volume_uuid": { + "type": "string" + }, + "volume_name": { + "type": "string" + }, + "svm_name": { + "type": "string" + } + } + } + } + } + }, + "Store_StorageVM_Data": { + "runAfter": { + "Parse_StorageVM_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "StorageVMData", + "value": "@body('Parse_StorageVM_Response')" + } + }, + "Finalize_Execution": { + "runAfter": { + "Store_StorageVM_Data": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Get_StorageVM_Data": [ + "Failed", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "execution_completed": true, + "timestamp": "@utcNow()" + } + }, + "Return_Response": { + "runAfter": { + "Finalize_Execution": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut')), 500, if(equals(variables('EnrichmentStatusCode'), 200), 200, if(equals(variables('EnrichmentStatusCode'), 0), 500, variables('EnrichmentStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut'))), equals(variables('EnrichmentStatusCode'), 200))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut')), 'Failed retrieving storage VM data', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Get_StorageVM_Data')['status'], 'Failed'), equals(actions('Get_StorageVM_Data')['status'], 'TimedOut')), 'enrichment', 'success'))", + "account_id": "@variables('AccountId')", + "enrichment": { + "statusCode": "@variables('EnrichmentStatusCode')", + "storage_vms": "@variables('StorageVMData')", + "count": "@length(variables('StorageVMData'))", + "raw": "@body('Get_StorageVM_Data')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + }, + "outputs": {} + }, + "parameters": {} + } + } + ], + "outputs": { + "PlaybookName": { + "type": "string", + "value": "[parameters('PlaybookName')]" + }, + "PlaybookResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + }, + "PlaybookUrl": { + "type": "string", + "value": "[concat('https://portal.azure.com/#resource', resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')))]" + }, + "TriggerUrl": { + "type": "string", + "value": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('PlaybookName'), 'manual'), '2019-05-01').value]" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/azuredeploy.parameters.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/azuredeploy.parameters.json new file mode 100644 index 00000000000..f59f902aa1e --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/azuredeploy.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "PlaybookName": { + "value": "NetApp-RansomwareResilience-Enrich-StorageVM" + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "value": "NetApp-RansomwareResilience-Auth" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/README.md b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/README.md new file mode 100644 index 00000000000..64a508265e6 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/README.md @@ -0,0 +1,85 @@ +# NetApp-RansomwareResilience-Volume-Offline + +## Overview +This playbook takes NetApp volumes offline to immediately stop access and prevent further damage during a security incident. Taking a volume offline is a protective action that isolates compromised or at-risk storage. + +## Purpose +When you identify a volume that is compromised by ransomware or under active attack, taking it offline immediately stops all access, preventing the spread of malware and protecting other parts of your infrastructure. + +## Deployment Order +**This playbook should be deployed SIXTH**, after: +1. ✅ Auth Playbook (required) +2. ✅ Async Poll Playbook (required) +3. ✅ Enrich IP Playbook (optional) +4. ✅ Enrich StorageVM Playbook (optional) +5. ✅ Volume Snapshot Playbook (optional, but **strongly recommended**) + +## What It Does +- Accepts volume ID, agent ID, and system ID as input +- Retrieves authentication from the Auth Playbook +- Initiates a volume offline operation via the NetApp API +- Uses the Async Poll Playbook to monitor operation completion +- Confirms when the volume is successfully taken offline +- Returns operation status + +## Prerequisites +Before deploying this playbook: +1. Auth Playbook must be deployed and functioning correctly +2. Async Poll Playbook must be deployed and functioning correctly +3. Valid NetApp API credentials configured + +## How to Use +This playbook can be: +- Called manually when you need to isolate a compromised volume +- Triggered automatically by Microsoft Sentinel automation rules during high-severity incidents +- Used as the final step in incident response workflows +- Combined with snapshot playbooks for data protection before isolation + +**Input Required:** +- `volume_id`: The ID of the volume to take offline +- `agent_id`: The NetApp agent identifier +- `system_id`: The NetApp system identifier + +## ⚠️ Critical Considerations +**Before taking a volume offline:** +- **Create a snapshot first** using the Volume Snapshot Playbook—this ensures you have a recovery point +- Understand the business impact—offline volumes are completely inaccessible +- Verify you're targeting the correct volume +- Have a recovery plan in place +- Document the reason for taking the volume offline + +## Use Case Example +**Ransomware Containment:** +1. Receive alert about active file encryption on a volume +2. Use Enrich IP or Enrich StorageVM playbooks to confirm the affected volume +3. **Use Volume Snapshot playbook to create a clean recovery point** +4. **Use this playbook to take the compromised volume offline** +5. Investigation and remediation can proceed safely +6. Restore from snapshot when ready + +## Post-Deployment Configuration +After deploying this playbook: +1. Test with a non-production volume using valid IDs +2. Verify the volume is taken offline successfully +3. Test bringing the volume back online to ensure recoverability +4. Configure automation rules with appropriate severity thresholds +5. Document your volume offline procedures and approval workflows + +## Building Custom Workflows +This playbook is typically the **final protective action** in an incident response workflow: +- Enrich IP → Identify volume → Take snapshot → **Take volume offline** +- Critical alert → Enrich StorageVM → Snapshot critical volumes → **Offline compromised volume** + +## Need Help? +If the volume offline operation isn't working, verify: +- The Auth Playbook is returning valid tokens +- The Async Poll Playbook is functioning correctly +- Volume ID, agent ID, and system ID are correct +- You have appropriate permissions to modify volume states +- The volume is currently online and accessible + +## Recovery +To bring a volume back online after remediation: +- Use NetApp management tools or APIs +- Verify the threat has been fully remediated before bringing volumes online +- Restore from snapshots if data was compromised diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/azuredeploy.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/azuredeploy.json new file mode 100644 index 00000000000..24fa0b277fb --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/azuredeploy.json @@ -0,0 +1,546 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "NetApp Ransomware Resilience Volume Offline Playbook", + "description": "This playbook takes a NetApp volume offline using the updated NetApp Ransomware Resilience take-volume-offline API endpoint and optionally polls for completion.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. NetApp Ransomware Resilience Async Poll Playbook must be deployed first and ensure that it is functioning correctly", + "3. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "4. Caller must provide volume_id, agent_id, and system_id parameters" + ], + "postDeployment": [ + "1. Test the volume offline functionality with valid volume_id, agent_id, and system_id" + ], + "lastUpdateTime": "2025-09-17T00:00:00.000Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Volume", + "Offline" + ], + "support": { + "tier": "Community" + }, + "author": { + "name": "NetApp" + } + }, + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Volume-Offline", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience async poll playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[parameters('PlaybookName')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + }, + "asyncPollPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAsyncPollPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "volume_id": { + "type": "string" + }, + "agent_id": { + "type": "string" + }, + "system_id": { + "type": "string" + }, + "callbackData": { + "type": "object" + }, + "poll": { + "type": "boolean" + } + }, + "required": [ + "volume_id", + "agent_id", + "system_id" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "VolumeId", + "type": "string", + "value": "@triggerBody()?['volume_id']" + }, + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "DoPoll", + "type": "boolean", + "value": "@coalesce(triggerBody()?['poll'], true)" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "JobId", + "type": "string" + }, + { + "name": "JobAgentId", + "type": "string" + }, + { + "name": "PollResponse", + "type": "object", + "value": {} + }, + { + "name": "SubmissionStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Submit_Volume_Offline": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/storage/take-volume-offline", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "Content-Type": "application/json", + "accept": "application/json" + }, + "body": { + "volume_id": "@variables('VolumeId')", + "agent_id": "@variables('AgentId')", + "system_id": "@variables('SystemId')" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Capture_Submission_Status_Code": { + "runAfter": { + "Submit_Volume_Offline": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "SubmissionStatusCode", + "value": "@outputs('Submit_Volume_Offline')['statusCode']" + } + }, + "Parse_Submission_Response": { + "runAfter": { + "Capture_Submission_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Submit_Volume_Offline')", + "schema": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "source": { + "type": "string" + }, + "agent_id": { + "type": "string" + } + } + } + } + }, + "Store_JobId": { + "runAfter": { + "Parse_Submission_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobId", + "value": "@body('Parse_Submission_Response')?['job_id']" + } + }, + "Store_Job_AgentId": { + "runAfter": { + "Store_JobId": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobAgentId", + "value": "@body('Parse_Submission_Response')?['agent_id']" + } + }, + "Check_If_Should_Poll": { + "runAfter": { + "Store_Job_AgentId": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@variables('DoPoll')", + true + ] + }, + { + "or": [ + { + "equals": [ + "@variables('SubmissionStatusCode')", + 200 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 201 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 202 + ] + } + ] + }, + { + "not": { + "equals": [ + "@variables('JobId')", + "" + ] + } + } + ] + }, + "actions": { + "Call_Async_Poll_Playbook": { + "runAfter": {}, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAsyncPollPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "job_id": "@variables('JobId')", + "agent_id": "@variables('JobAgentId')", + "system_id": "@variables('SystemId')", + "source": "ontap", + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "original_volume_id": "@variables('VolumeId')" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + } + }, + "else": { + "actions": {} + } + }, + "Parse_Poll_Response": { + "runAfter": { + "Check_If_Should_Poll": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@if(equals(actions('Call_Async_Poll_Playbook')['status'], 'Succeeded'), body('Call_Async_Poll_Playbook'), json('{\"success\": false, \"status\": \"FAILED\", \"error\": \"Poll playbook failed or timed out\"}'))" + }, + "Store_Poll_Response": { + "runAfter": { + "Parse_Poll_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "PollResponse", + "value": "@outputs('Parse_Poll_Response')" + } + }, + "Finalize_Execution": { + "runAfter": { + "Store_Poll_Response": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Check_If_Should_Poll": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Submit_Volume_Offline": [ + "Failed", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "execution_completed": true, + "timestamp": "@utcNow()" + } + }, + "Return_Response": { + "runAfter": { + "Finalize_Execution": [ + "Succeeded", + "Skipped", + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), 500, if(or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)), 200, if(equals(variables('SubmissionStatusCode'), 0), 500, variables('SubmissionStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut'))), or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), 'Failed submitting volume offline request', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), 'submit', 'success'))", + "submission": { + "statusCode": "@variables('SubmissionStatusCode')", + "jobId": "@variables('JobId')", + "raw": "@body('Submit_Volume_Offline')", + "details": "@if(or(equals(actions('Submit_Volume_Offline')['status'], 'Failed'), equals(actions('Submit_Volume_Offline')['status'], 'TimedOut')), body('Submit_Volume_Offline'), null)" + }, + "polling": { + "invoked": "@and(variables('DoPoll'), not(empty(variables('JobId'))))", + "playbook": "@parameters('asyncPollPlaybookName')", + "jobFinalStatus": "@coalesce(variables('PollResponse')?['status'], 'unknown')", + "jobResponse": "@variables('PollResponse')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + }, + "outputs": {} + }, + "parameters": {} + } + } + ], + "outputs": { + "PlaybookName": { + "type": "string", + "value": "[parameters('PlaybookName')]" + }, + "PlaybookResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + }, + "PlaybookUrl": { + "type": "string", + "value": "[concat('https://portal.azure.com/#resource', resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')))]" + }, + "TriggerUrl": { + "type": "string", + "value": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('PlaybookName'), 'manual'), '2019-05-01').value]" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/azuredeploy.parameters.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/azuredeploy.parameters.json new file mode 100644 index 00000000000..32b8f20bad9 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Offline_Playbook/azuredeploy.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "PlaybookName": { + "value": "NetApp-RansomwareResilience-Volume-Offline" + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "value": "NetApp-RansomwareResilience-Auth" + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "value": "NetApp-RansomwareResilience-Async-Poll" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/README.md b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/README.md new file mode 100644 index 00000000000..7a05b9774fd --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/README.md @@ -0,0 +1,75 @@ +# NetApp-RansomwareResilience-Volume-Snapshot + +## Overview +This playbook creates point-in-time snapshots of NetApp volumes to protect your data. When responding to a security incident, snapshots provide a clean recovery point and preserve evidence for investigation. + +## Purpose +During a ransomware or security incident, taking immediate snapshots of critical volumes ensures you have a clean copy of data before any potential corruption or encryption occurs. These snapshots can be used for recovery or forensic analysis. + +## Deployment Order +**This playbook should be deployed FIFTH**, after: +1. ✅ Auth Playbook (required) +2. ✅ Async Poll Playbook (required) +3. ✅ Enrich IP Playbook (optional) +4. ✅ Enrich StorageVM Playbook (optional) + +## What It Does +- Accepts volume ID, agent ID, and system ID as input +- Retrieves authentication from the Auth Playbook +- Initiates a snapshot creation operation via the NetApp API +- Uses the Async Poll Playbook to monitor snapshot completion +- Confirms when the snapshot is successfully created +- Returns snapshot details and status + +## Prerequisites +Before deploying this playbook: +1. Auth Playbook must be deployed and functioning correctly +2. Async Poll Playbook must be deployed and functioning correctly +3. Valid NetApp API credentials configured +4. Sufficient storage capacity for snapshots + +## How to Use +This playbook can be: +- Called manually when you identify a volume that needs protection +- Triggered automatically by Microsoft Sentinel automation rules +- Integrated into multi-step incident response workflows +- Combined with enrichment playbooks to identify which volumes to snapshot + +**Input Required:** +- `volume_id`: The ID of the volume to snapshot +- `agent_id`: The NetApp agent identifier +- `system_id`: The NetApp system identifier + +## Use Case Example +**Ransomware Incident Response:** +1. Receive alert about suspicious file encryption activity +2. Use Enrich IP or Enrich StorageVM playbooks to identify affected volumes +3. **Use this playbook to immediately snapshot clean volumes** +4. Use Volume Offline playbook to isolate compromised volumes +5. Restore from snapshots if needed + +## Important Notes +- Snapshots preserve the current state of the volume +- Snapshots consume storage space—monitor your capacity +- Take snapshots BEFORE taking volumes offline for maximum data protection +- Regular snapshots are recommended as part of your backup strategy + +## Post-Deployment Configuration +After deploying this playbook: +1. Test with a non-production volume using valid IDs +2. Verify the snapshot is created successfully +3. Configure automation rules to trigger snapshots during security incidents +4. Document your snapshot retention policies + +## Building Custom Workflows +Combine this playbook with others to create comprehensive incident response: +- Enrich IP → Identify volumes → **Take snapshots** → Take volumes offline +- Alert triggers → Enrich StorageVM → **Take snapshots of all critical volumes** + +## Need Help? +If snapshot creation isn't working, verify: +- The Auth Playbook is returning valid tokens +- The Async Poll Playbook is functioning correctly +- Volume ID, agent ID, and system ID are correct +- You have sufficient storage capacity for snapshots +- Your NetApp system supports snapshot operations diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/azuredeploy.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/azuredeploy.json new file mode 100644 index 00000000000..b19c8c7f3c4 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/azuredeploy.json @@ -0,0 +1,541 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "NetApp Ransomware Resilience Volume Snapshot Playbook", + "description": "This playbook creates a NetApp volume snapshot using the updated NetApp Ransomware Resilience take-snapshot API endpoint and optionally polls for completion.", + "prerequisites": [ + "1. NetApp Ransomware Resilience Auth Playbook must be deployed first and ensure that it is functioning correctly", + "2. NetApp Ransomware Resilience Async Poll Playbook must be deployed first and ensure that it is functioning correctly", + "3. Valid NetApp Ransomware Resilience API credentials configured in the Auth Playbook", + "4. Caller must provide volume_id, agent_id, and system_id parameters" + ], + "postDeployment": [ + "1. Test the volume snapshot functionality with valid volume_id, agent_id, and system_id" + ], + "lastUpdateTime": "2025-09-22T00:00:00.000Z", + "tags": [ + "NetApp", + "RansomwareResilience", + "Playbook", + "Volume", + "Snapshot" + ], + "support": { + "tier": "Community" + }, + "author": { + "name": "NetApp" + } + }, + "parameters": { + "PlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Volume-Snapshot", + "metadata": { + "description": "Name of the Logic App/Playbook" + } + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Auth", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience authentication playbook" + } + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "type": "String", + "defaultValue": "NetApp-RansomwareResilience-Async-Poll", + "metadata": { + "description": "Name of the NetApp Ransomware Resilience async poll playbook" + } + }, + "location": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Location for all resources" + } + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2019-05-01", + "name": "[parameters('PlaybookName')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "authPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAuthPlaybookName')]", + "type": "String" + }, + "asyncPollPlaybookName": { + "defaultValue": "[parameters('NetAppRansomwareResilienceAsyncPollPlaybookName')]", + "type": "String" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "volume_id": { + "type": "string" + }, + "agent_id": { + "type": "string" + }, + "system_id": { + "type": "string" + }, + "callbackData": { + "type": "object" + }, + "poll": { + "type": "boolean" + } + }, + "required": [ + "volume_id", + "agent_id", + "system_id" + ] + } + } + } + }, + "actions": { + "Initialize_Variables": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "VolumeId", + "type": "string", + "value": "@triggerBody()?['volume_id']" + }, + { + "name": "AgentId", + "type": "string", + "value": "@triggerBody()?['agent_id']" + }, + { + "name": "SystemId", + "type": "string", + "value": "@triggerBody()?['system_id']" + }, + { + "name": "CallbackData", + "type": "object", + "value": "@triggerBody()?['callbackData']" + }, + { + "name": "AccountId", + "type": "string" + }, + { + "name": "DoPoll", + "type": "boolean", + "value": "@coalesce(triggerBody()?['poll'], true)" + }, + { + "name": "Token", + "type": "string" + }, + { + "name": "JobId", + "type": "string" + }, + { + "name": "JobAgentId", + "type": "string" + }, + { + "name": "PollResponse", + "type": "object", + "value": {} + }, + { + "name": "SubmissionStatusCode", + "type": "integer", + "value": 0 + } + ] + } + }, + "Call_Auth_Playbook": { + "runAfter": { + "Initialize_Variables": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAuthPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "request_id": "@guid()" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Parse_Auth_Response": { + "runAfter": { + "Call_Auth_Playbook": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Auth_Playbook')", + "schema": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "account_id": { + "type": "string" + } + }, + "required": [ + "access_token", + "account_id" + ] + } + } + }, + "Set_Token": { + "runAfter": { + "Parse_Auth_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "Token", + "value": "@body('Parse_Auth_Response')?['access_token']" + } + }, + "Set_Account_ID": { + "runAfter": { + "Set_Token": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AccountId", + "value": "@body('Parse_Auth_Response')?['account_id']" + } + }, + "Submit_Volume_Snapshot": { + "runAfter": { + "Set_Account_ID": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://api.bluexp.netapp.com/v1/services/rps/v1/account/@{variables('AccountId')}/storage/take-snapshot", + "headers": { + "Authorization": "Bearer @{variables('Token')}", + "Content-Type": "application/json", + "accept": "application/json" + }, + "body": { + "volume_id": "@variables('VolumeId')", + "agent_id": "@variables('AgentId')", + "system_id": "@variables('SystemId')" + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + }, + "Capture_Submission_Status_Code": { + "runAfter": { + "Submit_Volume_Snapshot": [ + "Succeeded", + "Failed" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "SubmissionStatusCode", + "value": "@outputs('Submit_Volume_Snapshot')['statusCode']" + } + }, + "Parse_Submission_Response": { + "runAfter": { + "Capture_Submission_Status_Code": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Submit_Volume_Snapshot')", + "schema": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "source": { + "type": "string" + }, + "agent_id": { + "type": "string" + } + } + } + } + }, + "Store_JobId": { + "runAfter": { + "Parse_Submission_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobId", + "value": "@body('Parse_Submission_Response')?['job_id']" + } + }, + "Store_Job_AgentId": { + "runAfter": { + "Store_JobId": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "JobAgentId", + "value": "@body('Parse_Submission_Response')?['agent_id']" + } + }, + "Check_If_Should_Poll": { + "runAfter": { + "Store_Job_AgentId": [ + "Succeeded" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@variables('DoPoll')", + true + ] + }, + { + "or": [ + { + "equals": [ + "@variables('SubmissionStatusCode')", + 200 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 201 + ] + }, + { + "equals": [ + "@variables('SubmissionStatusCode')", + 202 + ] + } + ] + }, + { + "not": { + "equals": [ + "@variables('JobId')", + "" + ] + } + } + ] + }, + "actions": { + "Call_Async_Poll_Playbook": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('NetAppRansomwareResilienceAsyncPollPlaybookName'), 'manual'), '2019-05-01').value]", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "job_id": "@variables('JobId')", + "agent_id": "@variables('JobAgentId')", + "system_id": "@variables('SystemId')", + "source": "ontap", + "caller_info": { + "playbook_name": "@workflow().name", + "correlation_id": "@workflow().run.name", + "original_volume_id": "@variables('VolumeId')" + } + }, + "retryPolicy": { + "type": "exponential", + "count": 3, + "interval": "PT5S", + "minimumInterval": "PT5S", + "maximumInterval": "PT30S" + } + } + } + }, + "else": { + "actions": {} + } + }, + "Parse_Poll_Response": { + "runAfter": { + "Check_If_Should_Poll": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Call_Async_Poll_Playbook')", + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "jobId": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + } + } + }, + "Store_Poll_Response": { + "runAfter": { + "Parse_Poll_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "PollResponse", + "value": "@body('Parse_Poll_Response')" + } + }, + "Return_Response": { + "runAfter": { + "Store_Poll_Response": [ + "Succeeded", + "Skipped" + ], + "Check_If_Should_Poll": [ + "Succeeded" + ], + "Call_Auth_Playbook": [ + "Failed", + "TimedOut" + ], + "Submit_Volume_Snapshot": [ + "Failed", + "TimedOut" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 401, if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), 500, if(or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)), 200, if(equals(variables('SubmissionStatusCode'), 0), 500, variables('SubmissionStatusCode')))))", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "success": "@and(not(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut'))), not(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut'))), or(equals(variables('SubmissionStatusCode'), 200), equals(variables('SubmissionStatusCode'), 201), equals(variables('SubmissionStatusCode'), 202)))", + "error": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'Authentication failed in auth playbook', if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), 'Failed submitting volume snapshot request', null))", + "stage": "@if(or(equals(actions('Call_Auth_Playbook')['status'], 'Failed'), equals(actions('Call_Auth_Playbook')['status'], 'TimedOut')), 'auth', if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), 'submit', 'success'))", + "submission": { + "statusCode": "@variables('SubmissionStatusCode')", + "jobId": "@variables('JobId')", + "raw": "@body('Submit_Volume_Snapshot')", + "details": "@if(or(equals(actions('Submit_Volume_Snapshot')['status'], 'Failed'), equals(actions('Submit_Volume_Snapshot')['status'], 'TimedOut')), body('Submit_Volume_Snapshot'), null)" + }, + "polling": { + "invoked": "@and(variables('DoPoll'), not(empty(variables('JobId'))))", + "playbook": "@parameters('asyncPollPlaybookName')", + "jobFinalStatus": "@coalesce(variables('PollResponse')?['status'], 'unknown')", + "jobResponse": "@variables('PollResponse')" + }, + "callbackData": "@variables('CallbackData')", + "timestamp": "@utcnow()" + } + } + } + }, + "outputs": {} + }, + "parameters": {} + } + } + ], + "outputs": { + "PlaybookName": { + "type": "string", + "value": "[parameters('PlaybookName')]" + }, + "PlaybookResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Logic/workflows', parameters('PlaybookName'))]" + }, + "PlaybookUrl": { + "type": "string", + "value": "[concat('https://portal.azure.com/#resource', resourceId('Microsoft.Logic/workflows', parameters('PlaybookName')))]" + }, + "TriggerUrl": { + "type": "string", + "value": "[listCallbackUrl(resourceId('Microsoft.Logic/workflows/triggers', parameters('PlaybookName'), 'manual'), '2019-05-01').value]" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/azuredeploy.parameters.json b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/azuredeploy.parameters.json new file mode 100644 index 00000000000..92f2c12a165 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/NetApp-RansomwareResilience_Volume_Snapshot_Playbook/azuredeploy.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "PlaybookName": { + "value": "NetApp-RansomwareResilience-Volume-Snapshot" + }, + "NetAppRansomwareResilienceAuthPlaybookName": { + "value": "NetApp-RansomwareResilience-Auth" + }, + "NetAppRansomwareResilienceAsyncPollPlaybookName": { + "value": "NetApp-RansomwareResilience-Async-Poll" + } + } +} \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/Playbooks/README.md b/Solutions/NetApp Ransomware Resilience/Playbooks/README.md new file mode 100644 index 00000000000..be779537e87 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/Playbooks/README.md @@ -0,0 +1,171 @@ +# NetApp Ransomware Resilience Playbooks + +## Overview +The NetApp Ransomware Resilience solution provides a set of modular playbooks that work together to help you respond to ransomware and security incidents affecting your NetApp storage infrastructure. These playbooks integrate with Microsoft Sentinel to provide automated investigation and response capabilities. + +## Architecture +The playbooks are designed as **building blocks** that you can combine to create custom incident response workflows. Each playbook serves a specific purpose, and they work together to provide comprehensive ransomware protection. + +### Playbook Categories + +**Foundation Playbooks** (Required Infrastructure): +- **Authentication Playbook** - Manages credentials and provides authentication tokens +- **Async Poll Playbook** - Monitors asynchronous operations until completion + +**Investigation Playbooks** (Data Enrichment): +- **Enrich IP Playbook** - Retrieves network interface details for IP addresses +- **Enrich StorageVM Playbook** - Retrieves Storage VM configuration and volume information + +**Response Playbooks** (Protective Actions): +- **Volume Snapshot Playbook** - Creates point-in-time snapshots for data protection +- **Volume Offline Playbook** - Takes volumes offline to isolate compromised storage + +## 🚀 Deployment Order + +**You MUST deploy the playbooks in this specific order:** + +### 1. Authentication Playbook (FIRST) +Deploy the **NetApp-RansomwareResilience-Auth** first. This creates the shared Key Vault and provides authentication services that all other playbooks depend on. + +📖 [Auth Playbook Documentation](./NetApp-RansomwareResilience-Auth-Playbook/README.md) + +### 2. Async Poll Playbook (SECOND) +Deploy the **NetApp-RansomwareResilience-Async-Poll** next. This monitors asynchronous operations and is used by most other playbooks. + +📖 [Async Poll Playbook Documentation](./NetApp-RansomwareResilience_Async_Poll_Playbook/README.md) + +### 3. Enrich IP Playbook (THIRD) +Deploy the **NetApp-RansomwareResilience-Enrich-IP** to enable IP address investigation capabilities. + +📖 [Enrich IP Playbook Documentation](./NetApp-RansomwareResilience_Enrich_IP_Playbook/README.md) + +### 4. Enrich StorageVM Playbook (FOURTH) +Deploy the **NetApp-RansomwareResilience-Enrich-StorageVM** to enable storage configuration investigation. + +📖 [Enrich StorageVM Playbook Documentation](./NetApp-RansomwareResilience_Enrich_StorageVM_Playbook/README.md) + +### 5. Volume Snapshot Playbook (FIFTH) +Deploy the **NetApp-RansomwareResilience-Volume-Snapshot** to enable data protection through snapshots. + +📖 [Volume Snapshot Playbook Documentation](./NetApp-RansomwareResilience_Volume_Snapshot_Playbook/README.md) + +### 6. Volume Offline Playbook (SIXTH) +Deploy the **NetApp-RansomwareResilience-Volume-Offline** to enable volume isolation capabilities. + +📖 [Volume Offline Playbook Documentation](./NetApp-RansomwareResilience_Volume_Offline_Playbook/README.md) + +## Building Custom Incident Response Workflows + +These playbooks are designed to work together as building blocks. You can create powerful automated response workflows by combining them in different ways. + +### Example Workflow Pattern + +Here's a typical ransomware incident response pattern: + +``` +Incident Detected + ↓ +Investigate (Enrich IP or Enrich StorageVM) + ↓ +Identify Affected Volumes + ↓ +Protect Data (Volume Snapshot) + ↓ +Contain Threat (Volume Offline) +``` + +### Sample Use Cases + +**Use Case 1: Suspicious IP Investigation** +1. Alert triggers on suspicious IP activity +2. **Enrich IP Playbook** - Gather network interface details +3. **Enrich StorageVM Playbook** - Get full storage context +4. **Volume Snapshot Playbook** - Protect volumes before taking action +5. **Volume Offline Playbook** - Isolate if threat is confirmed + +**Use Case 2: Ransomware Alert Response** +1. Ransomware detection alert triggers +2. **Enrich StorageVM Playbook** - Identify all volumes on affected storage +3. **Volume Snapshot Playbook** - Immediately snapshot all critical volumes +4. **Volume Offline Playbook** - Take compromised volumes offline +5. Restore from snapshots after remediation + +**Use Case 3: Manual Investigation** +1. SOC analyst receives threat intelligence +2. Manually trigger **Enrich IP Playbook** with suspicious IP +3. Review enrichment data to assess risk +4. If needed, manually trigger **Volume Snapshot** and **Volume Offline** playbooks + +## Getting Started + +### Prerequisites +Before deploying any playbooks: +- Access to an Azure subscription with Microsoft Sentinel enabled +- Valid NetApp Ransomware Resilience API credentials (client ID, client secret, account ID) +- Appropriate permissions to create Azure resources +- Understanding of your NetApp storage infrastructure + +### Deployment Steps +1. **Deploy Foundation Playbooks** (Auth, then Async Poll) +2. **Configure Authentication** - Add your NetApp API credentials to the Key Vault +3. **Test Foundation Playbooks** - Verify authentication is working +4. **Deploy Investigation Playbooks** - Add enrichment capabilities as needed +5. **Deploy Response Playbooks** - Add protective action capabilities +6. **Test Each Playbook** - Verify functionality with non-production resources +7. **Create Automation Rules** - Connect playbooks to Microsoft Sentinel alerts +8. **Document Procedures** - Define when and how each playbook should be used + +### Testing Recommendations +- Always test with non-production volumes first +- Verify authentication before deploying action playbooks +- Create test automation rules before enabling production triggers +- Document volume offline/online procedures +- Establish snapshot retention policies + +## Best Practices for SOC Analysts + +### Investigation Phase +- Use **Enrich IP** and **Enrich StorageVM** playbooks to gather context +- Review enrichment data before taking protective actions +- Document findings for incident reports + +### Response Phase +- **Always take snapshots BEFORE taking volumes offline** +- Verify you're targeting the correct volumes +- Understand the business impact of taking volumes offline +- Follow your organization's approval process for disruptive actions + +### Recovery Phase +- Use snapshots to restore clean data +- Verify threats are fully remediated before bringing volumes online +- Update security controls to prevent recurrence + +## Security Considerations +- All NetApp API credentials are stored securely in Azure Key Vault +- Playbooks use managed identities for Azure resource access +- Review and approve automation rules before enabling auto-response +- Maintain audit logs of all playbook executions +- Regularly review and update API credentials + +## Support and Troubleshooting + +### Common Issues +- **Authentication failures**: Verify credentials in Key Vault and Auth Playbook functionality +- **Polling timeouts**: Check network connectivity to NetApp API endpoints +- **Invalid volume IDs**: Confirm IDs from enrichment playbooks before using action playbooks +- **Permission errors**: Verify playbook managed identities have required access + +### Getting Help +- Review individual playbook README files for specific troubleshooting +- Check Logic App run history for detailed error messages +- Verify prerequisites are met for each playbook +- Test with known-good values from your NetApp environment + +## Additional Resources +- Microsoft Sentinel Documentation: [https://docs.microsoft.com/azure/sentinel/](https://docs.microsoft.com/azure/sentinel/) +- Azure Logic Apps Documentation: [https://docs.microsoft.com/azure/logic-apps/](https://docs.microsoft.com/azure/logic-apps/) +- NetApp Ransomware Resilience Documentation: Contact NetApp Support + +--- + +**Remember:** These playbooks are powerful tools for incident response. Always test thoroughly, understand the impact of actions, and follow your organization's security procedures. diff --git a/Solutions/NetApp Ransomware Resilience/ReleaseNotes.md b/Solutions/NetApp Ransomware Resilience/ReleaseNotes.md new file mode 100644 index 00000000000..5bcd8bf4bf1 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/ReleaseNotes.md @@ -0,0 +1,3 @@ +**Version** | **Date Modified (DD-MM-YYYY)**| **ChangeHistory** | +|------------|-------------------------------|-------------------------------------------------------------------------------------------| +| 3.0.0 | 16-12-2025 | Added Initial changes for Playbooks, Data Connectors and Analytics Rules. | \ No newline at end of file diff --git a/Solutions/NetApp Ransomware Resilience/SolutionMetadata.json b/Solutions/NetApp Ransomware Resilience/SolutionMetadata.json new file mode 100644 index 00000000000..c07a7b46b90 --- /dev/null +++ b/Solutions/NetApp Ransomware Resilience/SolutionMetadata.json @@ -0,0 +1,27 @@ +{ + "id": "NetAppRansomwareResilience", + "title": "NetApp Ransomware Resilience", + "description": "NetApp Ransomware Resilience - Comprehensive security solution for detecting and responding to ransomware threats across NetApp storage environments.", + "publisher": "NetApp", + "publisherId": "netapp1234567890", + "offerId": "azure-sentinel-solution-netapprrs", + "firstPublishDate": "2026-02-16", + "lastPublishDate": "2026-02-16", + "providers": [ + "NetApp" + ], + "categories": { + "domains": [ + "Security - Threat Protection", + "Storage" + ], + "verticals": [] + }, + "support": { + "name": "NetApp", + "email": "support@netapp.com", + "tier": "Partner", + "link": "https://support.netapp.com" + }, + "version": "3.0.0" +} \ No newline at end of file