Tools/Create-Azure-Sentinel-Solution/common/createCCPConnector.ps1 (921 lines of code) (raw):

[hashtable]$templateKindByCounter = @{ 1 = "ConnectorDefinition"; 2 = "Connections"; } [hashtable]$templateContentTypeByCounter = @{ 1 = "DataConnector"; 2 = "ResourcesDataConnector"; } #build the connection template parameters, according to the connector definition instructions function Get-ConnectionsTemplateParameters($activeResource, $ccpItem) { # this is for data connector definition only $title = $ccpItem.title; $paramTestForDefinition = [PSCustomObject]@{ defaultValue = $title; type = "securestring"; minLength = 1; } $workspaceParameter = [PSCustomObject]@{ defaultValue = "[parameters('workspace')]"; type = "securestring"; } $dcrConfigParameter = [PSCustomObject]@{ defaultValue = [PSCustomObject]@{ dataCollectionEndpoint = "data collection Endpoint"; dataCollectionRuleImmutableId = "data collection rule immutableId"; }; type = "object"; } $templateParameter = [PSCustomObject]@{ connectorDefinitionName = $paramTestForDefinition; workspace = $workspaceParameter; dcrConfig = $dcrConfigParameter; } $connectorDefinitionObject = $activeResource | where-object -Property "type" -eq 'Microsoft.OperationalInsights/workspaces/providers/dataConnectorDefinitions' foreach ($instructionSteps in $connectorDefinitionObject.properties.connectorUiConfig.instructionSteps) { New-ParametersForConnectorInstuctions $instructionSteps.instructions } return $templateParameter; } function New-ParametersForConnectorInstuctions($instructions) { foreach ($instruction in $instructions) { if ($instruction.type -eq "Textbox") { $newParameter = [PSCustomObject]@{ defaultValue = $instruction.parameters.name; type = "securestring"; minLength = 1; } $templateParameter | Add-Member -MemberType NoteProperty -Name $instruction.parameters.name -Value $newParameter } elseif ($instruction.type -eq "OAuthForm") { $newParameter = [PSCustomObject]@{ defaultValue = "-NA-"; type = "securestring"; minLength = 1; } if (![bool]($templateParameter.PSobject.Properties.name -match "ClientId")) { $templateParameter | Add-Member -MemberType NoteProperty -Name "ClientId" -Value $newParameter } if (![bool]($templateParameter.PSobject.Properties.name -match "ClientSecret")) { $templateParameter | Add-Member -MemberType NoteProperty -Name "ClientSecret" -Value $newParameter } if (![bool]($templateParameter.PSobject.Properties.name -match "AuthorizationCode")) { $templateParameter | Add-Member -MemberType NoteProperty -Name "AuthorizationCode" -Value $newParameter } } elseif ($instruction.type -eq "ContextPane") { New-ParametersForConnectorInstuctions $instruction.parameters.instructionSteps.instructions } elseif ($instruction.type -eq "Dropdown") { $newParameter = [PSCustomObject]@{ defaultValue = $instruction.parameters.name; type = "array"; minLength = 1; } $templateParameter | Add-Member -MemberType NoteProperty -Name $instruction.parameters.name -Value $newParameter } else { $instructionType = $instruction.type; Write-Host "Info: Specified Instruction type '$instructionType' is not from the instruction type list like Textbox, OAuthForm and ContextPane!" } } } function Get-MetaDataBaseResource($resourceName, $parentId, $contentId, $kind, $contentVersion, $dataFileMetadata, $solutionFileMetadata) { $author = $dataFileMetadata.Author.Split(" - "); $authorDetails = [PSCustomObject]@{ name = $author[0]; }; if ($null -ne $author[1]) { $authorDetails | Add-Member -NotePropertyName "email" -NotePropertyValue "[variables('_email')]" } $properties = [PSCustomObject]@{ parentId = $parentId; contentId = $contentId; kind = $kind; version = $contentVersion; source = [PSCustomObject]@{ sourceId = "[variables('_solutionId')]"; name = "[variables('_solutionName')]"; kind = "Solution"; }; author = $authorDetails; support = $solutionFileMetadata.support; } return [PSCustomObject]@{ name = "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',$resourceName)]"; apiVersion = "2022-01-01-preview" type = "Microsoft.OperationalInsights/workspaces/providers/metadata" properties = $properties; } } function Get-MetaDataResource($TemplateCounter, $dataFileMetadata, $solutionFileMetadata) { if ($templateContentTypeByCounter[$TemplateCounter] -eq "DataConnector") { $parentIdResourceName = "'Microsoft.SecurityInsights/dataConnectorDefinitions'" } else { $parentIdResourceName = "'Microsoft.SecurityInsights/dataConnectors'" } $parentId = "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), $parentIdResourceName, variables('_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)'))]" $metaDataResourceName = "concat('DataConnector-', variables('_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)'))" $metaDataContentId = "[variables('_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)')]" $metaDatsContentVersion = "[variables('dataConnectorCCPVersion')]" $metaDataResource = Get-MetaDataBaseResource $metaDataResourceName $parentId $metaDataContentId $templateContentTypeByCounter[$TemplateCounter] $metaDatsContentVersion $dataFileMetadata $solutionFileMetadata if ($templateContentTypeByCounter[$TemplateCounter] -eq "DataConnector") { $dependencies = [PSCustomObject]@{ "criteria" = @( [PSCustomObject]@{ "version" = "[variables('dataConnectorCCPVersion')]"; "contentId" = "[variables('_dataConnectorContentId$($templateKindByCounter[2])$($global:connectorCounter)')]"; "kind" = "ResourcesDataConnector" } ) } $metaDataResource.properties | Add-Member -NotePropertyName "dependencies" -NotePropertyValue $dependencies } return $metaDataResource; } function Get-ContentTemplateResource($contentResourceDetails, $TemplateCounter, $ccpItem) { $contentVersion = "variables('dataConnectorCCPVersion')"; $contentTemplateName = "variables('dataConnectorTemplateName$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)')"; $contentId = "variables('_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)')"; $resoureKind = $templateContentTypeByCounter[$TemplateCounter]; if ($resoureKind -eq "DataConnector") { $resoureKindTag = "dc"; } else { $resoureKindTag = "rdc"; } $title = $ccpItem.title; $displayName = $title; return [PSCustomObject]@{ type = "Microsoft.OperationalInsights/workspaces/providers/contentTemplates"; apiVersion = $contentResourceDetails.metadataApiVersion; # "2023-04-01-preview"; name = "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', $contentTemplateName, $contentVersion)]"; location = "[parameters('workspace-location')]"; dependsOn = @( "$($contentResourceDetails.dependsOn)" ); properties = [PSCustomObject]@{ contentId = "[$contentId]"; displayName = $displayName; contentKind = $templateContentTypeByCounter[$TemplateCounter]; mainTemplate = [PSCustomObject]@{ '$schema' = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"; contentVersion = "[$contentVersion]"; parameters = [PSCustomObject]@{}; variables = [PSCustomObject]@{}; resources = @( ) }; packageKind = "Solution"; packageVersion = "[variables('_solutionVersion')]"; packageName = "[variables('_solutionName')]"; contentProductId = "[concat(take(variables('_solutionId'), 50),'-','$resoureKindTag','-', uniqueString(concat(variables('_solutionId'),'-','$resoureKind','-',$contentId,'-', $contentVersion)))]"; packageId = "[variables('_solutionId')]"; contentSchemaVersion = $contentResourceDetails.contentSchemaVersion; version = "[variables('dataConnectorCCPVersion')]"; } } } function Get-ArmResource($name, $type, $kind, $properties) { [hashtable]$apiVersion = @{ "Microsoft.SecurityInsights/dataConnectors" = "2023-02-01-preview"; "Microsoft.SecurityInsights/dataConnectorDefinitions" = "2022-09-01-preview"; "Microsoft.OperationalInsights/workspaces/tables" = "2022-10-01"; "Microsoft.Insights/dataCollectionRules" = "2022-06-01"; } return [PSCustomObject]@{ name = $name; apiVersion = $apiVersion[$type] type = $type; location = "[parameters('workspace-location')]"; kind = $kind; properties = $properties; } } function addNewParameter($templateResourceObj, $parameterName, $isSecret = $false, $minLength = 1) { $hasParameter = [bool]($templateResourceObj.parameters.PSobject.Properties.name -match "$parameterName") if (!$hasParameter) { $templateResourceObj.parameters | Add-Member -NotePropertyName "$parameterName" -NotePropertyValue ([PSCustomObject] @{ defaultValue = $isSecret ? "-NA-" : "Enter $parameterName value"; type = "securestring"; minLength = $minLength; }) } return $templateResourceObj; } function addWorkspaceParameter($templateResourceObj, $parameterName) { $hasParameter = [bool]($templateResourceObj.parameters.PSobject.Properties.name -match "$parameterName") if (!$hasParameter) { $templateResourceObj.parameters | Add-Member -NotePropertyName "$parameterName" -NotePropertyValue ([PSCustomObject] @{ defaultValue = "[parameters('workspace')]"; type = "securestring"; }) } return $templateResourceObj; } function addGuidValueParameter($templateResourceObj) { $hasParameter = [bool]($templateResourceObj.parameters.PSobject.Properties.name -match "guidValue") if (!$hasParameter) { $templateResourceObj.parameters | Add-Member -NotePropertyName "guidValue" -NotePropertyValue ([PSCustomObject] @{ defaultValue = "[[newGuid()]"; type = "securestring"; }) } return $templateResourceObj; } function Add-NewObjectParameter { param ( [Parameter(Mandatory = $true)] [PSCustomObject] $TemplateResourceObj, [Parameter(Mandatory = $true)] [string] $ParameterName ) # Check if the parameter already exists $hasParameter = $TemplateResourceObj.parameters.PSObject.Properties.Name -contains $ParameterName if (-not $hasParameter) { # Add the new parameter with the desired structure $TemplateResourceObj.parameters | Add-Member -NotePropertyName $ParameterName -NotePropertyValue ([PSCustomObject] @{ type = "object" defaultValue = [PSCustomObject] @{} }) } return $TemplateResourceObj } # THIS IS THE STARTUP FUNCTION FOR CCP RESOURCE CREATOR function createCCPConnectorResources($contentResourceDetails, $dataFileMetadata, $solutionFileMetadata, $dcFolderName, $ccpDict, $solutionBasePath, $solutionName, $ccpTables, $ccpTablesCounter) { Write-Host "Inside of CCP Connector Code!" $solutionId = $solutionFileMetadata.publisherId + "." + $solutionFileMetadata.offerId $placeHolderPatternMatches = '\{{[a-zA-Z0-9]+\}}' if (!$global:baseMainTemplate.variables.workspaceResourceId) { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "workspaceResourceId" -NotePropertyValue "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]" } if (!$global:baseMainTemplate.variables._solutionName) { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "_solutionName" -NotePropertyValue $dataFileMetadata.Name } if (!$global:baseMainTemplate.variables._solutionVersion) { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "_solutionVersion" -NotePropertyValue $dataFileMetadata.Version } if (!$global:baseMainTemplate.variables._solutionId) { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "_solutionId" -NotePropertyValue "$solutionId" } if (!$global:baseMainTemplate.variables.dataConnectorCCPVersion) { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "dataConnectorCCPVersion" -NotePropertyValue "1.0.0" } try { foreach ($ccpItem in $ccpDict) { $activeResource = @() $tableCounter = 1; $templateName = $ccpItem.DCDefinitionId; For ($TemplateCounter = 1; $TemplateCounter -lt 3; $TemplateCounter++) { if (!$global:baseMainTemplate.variables."_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)") { if ($TemplateCounter -eq 1) { $dataConnectorContentIdName = $templateName; } else { $dataConnectorContentIdName = $templateName + $templateKindByCounter[$TemplateCounter]; } $global:baseMainTemplate.variables | Add-Member -NotePropertyName "_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)" -NotePropertyValue "$dataConnectorContentIdName" } if (!$global:baseMainTemplate.variables."dataConnectorTemplateName$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)") { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "dataConnectorTemplateName$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)" -NotePropertyValue "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)')))]" } $templateContent = Get-ContentTemplateResource $contentResourceDetails $TemplateCounter $ccpItem; if ($TemplateCounter -eq 1) { #========start:dc definition resource=========== $dcDefinitionFilteredPath = $ccpItem.DCDefinitionFilePath.Replace($solutionName + "/", "").Replace($dcFolderName + "/", "") $ccpDataDefinitionFilePath = $solutionBasePath + "/" + $solutionName + "/" + $dcFolderName + "/" + $dcDefinitionFilteredPath $ccpDataDefinitionFilePath = $ccpDataDefinitionFilePath.Replace("//", "/") Write-Host "CCP DataDefinition File Path : $ccpDataDefinitionFilePath" #$fileContent = Get-Content -Raw "$ccpDataDefinitionFilePath" | Out-String | ConvertFrom-Json $fileContent = ReadFileContent -filePath $ccpDataDefinitionFilePath if ($null -eq $fileContent) { exit 1; } if ($fileContent.type -eq "Microsoft.SecurityInsights/dataConnectorDefinitions") { Write-Host "Processing for CCP DataDefinition file path: $dcDefinitionFilteredPath" $resourceName = "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentId$($templateKindByCounter[1])$($global:connectorCounter)'))]" $armResource = Get-ArmResource $resourceName $fileContent.type $fileContent.kind $fileContent.properties $armResource.type = "Microsoft.OperationalInsights/workspaces/providers/dataConnectorDefinitions" ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource -propertyName 'location' -isInnerObject $false -innerObjectName $null -kindType $null -isSecret $true -isRequired $false -fileType 'dataConnectorDefinitions' -minLength 1 $templateContentConnectorDefinition = $templateContent; $templateContentConnectorDefinition.properties.mainTemplate.resources += $armResource $activeResource += $armResource $activeResource += Get-MetaDataResource $TemplateCounter $dataFileMetadata $solutionFileMetadata } #========end:dc definition resource=========== } $templateContent.properties.mainTemplate.resources += Get-MetaDataResource $TemplateCounter $dataFileMetadata $solutionFileMetadata if ($TemplateCounter -eq 2) { $templateContent.properties.mainTemplate.variables | Add-Member -NotePropertyName "_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)" -NotePropertyValue "[variables('_dataConnectorContentId$($templateKindByCounter[2])$($global:connectorCounter)')]" $templateContentConnections = $templateContent $global:DependencyCriteria += [PSCustomObject]@{ kind = "DataConnector"; contentId = "[variables('_dataConnectorContentId$($templateKindByCounter[$TemplateCounter])$($global:connectorCounter)')]"; version = if ($dataFileMetadata.TemplateSpec) { "[variables('dataConnectorCCPVersion')]" }else { $dataFileMetadata.Version }; }; } else { $templateContentConnectorDefinition = $templateContent } } #========start:dc definition resource=========== $ccpPollerFilePath = $ccpItem.DCPollerFilePath Write-Host "CCP Poller File Path : $ccpPollerFilePath" $ccpPollerFilePath = $ccpPollerFilePath.Replace("//", "/") $fileContent = ReadFileContent -filePath $ccpPollerFilePath if ($null -eq $fileContent) { exit 1; } function GetDataConnectorPollerResourceName ($dataConnectorName) { $splitNamesBySlash = $dataConnectorName -split '/' $concatenateParts = @() $outputString = '' $guidValue = "parameters('guidValue')" foreach ($currentName in $splitNamesBySlash) { if ($currentName.Contains('{{')) { $placeHolderFieldName = $currentName -replace '{{', '' -replace '}}', '' $placeHolderMatched = [regex]::Matches($currentName, $placeHolderPatternMatches) if ($placeHolderMatched.Length -eq $currentName.Length) { if ($placeHolderFieldName -eq 'workspace') { $concatenateParts += "parameters('innerWorkspace')" $templateContentConnections.properties.mainTemplate = addWorkspaceParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName $($placeHolderFieldName) -isSecret $true } else { $concatenateParts += "parameters('$($placeHolderFieldName)')" $templateContentConnections.properties.mainTemplate = addNewParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName $($placeHolderFieldName) -isSecret $true } } else { $text = $currentName -replace $placeHolderMatched.Value, '' $concatenateParts += "'/$($text)'" $parameterNameValue = $placeHolderMatched.Value -replace '{{', '' -replace '}}', '' $concatenateParts += "parameters('$($parameterNameValue)')" $templateContentConnections.properties.mainTemplate = addNewParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName $($parameterNameValue) -isSecret $true } } else { if ($currentName.Count -eq 1 -and $currentName -like '{{') { $concatenateParts = "[concat(parameters('innerWorkspace'),'/Microsoft.SecurityInsights/', '$($currentName)', $guidValue)]" } else { if ($concatenateParts.Count -ge 1) { # if we have multiple parts in name {{innerWorkspace}}/Microsoft.SecurityInsights/OktaDCV1_{{domainname}} $concatenateParts += "'/$($currentName)'" } else { # if name is abcwork $concatenateParts += "$($currentName)" } } } } if ($concatenateParts.Count -gt 1 -and $concatenateParts -notmatch 'concat') { $outputString = "[[concat($($concatenateParts -join ', '), $guidValue)]" } elseif ($concatenateParts.Count -eq 1 -and $concatenateParts[0] -match 'parameters') { # if we just have parameters('abcwork') $outputString = "[[concat(parameters('innerWorkspace'),'/Microsoft.SecurityInsights/', $($concatenateParts[0]), $guidValue)]" } else { # if we just have 'abcwork' $outputString = "[[concat(parameters('innerWorkspace'),'/Microsoft.SecurityInsights/', '$($concatenateParts[0])', $guidValue)]" } return $outputString } function CCPDataConnectorsResource($fileContent) { if ($fileContent.type -eq "Microsoft.SecurityInsights/dataConnectors") { # add parameter of guidValue if not present $templateContentConnections.properties.mainTemplate = addGuidValueParameter -templateResourceObj $templateContentConnections.properties.mainTemplate # add parameter of innerWorkspace if not present $templateContentConnections.properties.mainTemplate = addWorkspaceParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName 'innerWorkspace' -isSecret $true Write-Host "Processing for CCP Poller file path: $ccpPollerFilePath" $resourceName = GetDataConnectorPollerResourceName -dataConnectorName $fileContent.name $armResource = Get-ArmResource $resourceName $fileContent.type $fileContent.kind $fileContent.properties $armResource.type = "Microsoft.OperationalInsights/workspaces/providers/dataConnectors" $armResource.kind = $ccpItem.PollerKind; # dataCollectionEndpoint : this is optional field for users to add. $hasDataCollectionEndpoint = [bool](($armResource.properties.dcrConfig).PSobject.Properties.name -match "dataCollectionEndpoint") if ($hasDataCollectionEndpoint) { $dataCollectionEndpointProperty = $armResource.properties.dcrConfig.dataCollectionEndpoint $placeHoldersMatched = $dataCollectionEndpointProperty | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { $armResource.properties.dcrConfig.dataCollectionEndpoint = "[[parameters('dcrConfig').dataCollectionEndpoint]" } } else { # if dataCollectionEndpoint property not present then add it $armResource.properties.dcrConfig | Add-Member -MemberType NoteProperty -Name "dataCollectionEndpoint" -Value "[[parameters('dcrConfig').dataCollectionEndpoint]" } # dataCollectionRuleImmutableId $hasDataCollectionRuleImmutableId = [bool](($armResource.properties.dcrConfig).PSobject.Properties.name -match "dataCollectionRuleImmutableId") if ($hasDataCollectionRuleImmutableId) { $dataCollectionRuleImmutableIdProperty = $armResource.properties.dcrConfig.dataCollectionRuleImmutableId $placeHoldersMatched = $dataCollectionRuleImmutableIdProperty | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { $armResource.properties.dcrConfig.dataCollectionRuleImmutableId = "[[parameters('dcrConfig').dataCollectionRuleImmutableId]" } } else { # if dataCollectionRuleImmutableId property not present then add it $armResource.properties.dcrConfig | Add-Member -MemberType NoteProperty -Name "dataCollectionRuleImmutableId" -Value "[[parameters('dcrConfig').dataCollectionRuleImmutableId]" } $fileType = 'data Connector poller' if ($armResource.kind.ToLower() -eq 'gcp') { CreateGCPResourceProperties -armResource $armResource -templateContentConnections $templateContentConnections -fileType $fileType } elseif ($armResource.kind.ToLower() -eq 'restapipoller') { CreateRestApiPollerResourceProperties -armResource $armResource -templateContentConnections $templateContentConnections -fileType $fileType } elseif ($armResource.kind.ToLower() -eq 'push' ) { $templateContentConnections.properties.mainTemplate = Add-NewObjectParameter ` -TemplateResourceObj $templateContentConnections.properties.mainTemplate ` -ParameterName 'auth' # Add properties to the 'defaultValue' object within 'auth' if ($templateContentConnections.properties.mainTemplate.parameters.auth -is [PSCustomObject]) { $templateContentConnections.properties.mainTemplate.parameters.auth.defaultValue | Add-Member -MemberType NoteProperty -Name "appId" -Value "[[parameters('auth').appId]]" $templateContentConnections.properties.mainTemplate.parameters.auth.defaultValue | Add-Member -MemberType NoteProperty -Name "servicePrincipalId" -Value "[[parameters('auth').servicePrincipalId]]" } else { Write-Error "Failed to create or update 'auth' parameter." } $hasAppId = [bool](($armResource.properties.auth).PSobject.Properties.name -match "appId") if ($hasAppId) { $appIdProperty = $armResource.properties.auth.appId $placeHoldersMatched = $appIdProperty | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { $armResource.properties.auth.appId = "[[parameters('auth').appId]" } } else { # if dataCollectionEndpoint property not present then add it $armResource.properties.auth | Add-Member -MemberType NoteProperty -Name "appId" -Value "[[parameters('auth').appId]" } $hasServicePrincipalId = [bool](($armResource.properties.auth).PSobject.Properties.name -match "servicePrincipalId") if ($hasServicePrincipalId) { $servicePrincipalIdProperty = $armResource.properties.auth.servicePrincipalId $placeHoldersMatched = $servicePrincipalIdProperty | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { $armResource.properties.auth.servicePrincipalId = "[[parameters('auth').servicePrincipalId]" } } else { # if dataCollectionEndpoint property not present then add it $armResource.properties.auth | Add-Member -MemberType NoteProperty -Name "servicePrincipalId" -Value "[[parameters('auth').servicePrincipalId]" } } elseif ($armResource.kind.ToLower() -eq 'amazonwebservicess3') { CreateAwsResourceProperties -armResource $armResource -templateContentConnections $templateContentConnections -fileType $fileType } else { Write-Host "Error: Data Connector Poller file should have 'kind' attribute with value either 'RestApiPoller', 'GCP', 'AmazonWebServicesS3' or 'Push'." -BackgroundColor Red exit 1; } $templateContentConnections.properties.mainTemplate.resources += $armResource } } if ($fileContent -is [System.Object[]]) { foreach ($content in $fileContent) { CCPDataConnectorsResource -fileContent $content; } } else { CCPDataConnectorsResource -fileContent $fileContent; } #========end:dc definition resource=========== #========start: dcr resource=========== $ccpDCRFilePath = $ccpItem.DCRFilePath $ccpDCRFilePath = $ccpDCRFilePath.Replace("//", "/") Write-Host "CCP DCR File Path : $ccpDCRFilePath" #$fileContent = Get-Content -Raw "$ccpDCRFilePath" | Out-String | ConvertFrom-Json $fileContent = ReadFileContent -filePath $ccpDCRFilePath if ($null -eq $fileContent) { exit 1; } if ($fileContent.type -eq "Microsoft.Insights/dataCollectionRules") { Write-Host "Processing for CCP DCR file path: $ccpDCRFilePath" if (-not $fileContent.properties.destinations.PSObject.Properties.Name -contains "logAnalytics") { # if logAnalytics array is not specified $logAnalyticsObject = @{ "name" = "clv2ws1" "workspaceResourceId" = "[variables('workspaceResourceId')]" } $fileContent.properties.destinations | Add-Member -MemberType NoteProperty -Name "logAnalytics" -Value $logAnalyticsObject } else { if (-not $fileContent.properties.destinations.logAnalytics[0].PSObject.Properties.Name -contains "name") { $fileContent.properties.destinations.logAnalytics[0] | Add-Member -MemberType NoteProperty -Name "name" -Value "clv2ws1" } if ($fileContent.properties.destinations.logAnalytics[0].PSObject.Properties.Name -contains "workspaceResourceId") { $fileContent.properties.destinations.logAnalytics[0].workspaceResourceId = "[variables('workspaceResourceId')]" } else { $fileContent.properties.destinations.logAnalytics[0] | Add-Member -MemberType NoteProperty -Name "workspaceResourceId" -Value "[variables('workspaceResourceId')]" } } $dcrPlaceHolderMatched = $fileContent.name | Select-String $placeHolderPatternMatches -AllMatches if ($dcrPlaceHolderMatched.Matches.Value.Count -gt 0) { $startIndexOfOpenBraces = $fileContent.name.indexOf('{{') $nameWithoutPlaceHolder = $fileContent.name.substring(0, $startIndexOfOpenBraces) $fileContent.name = "[concat('" + $nameWithoutPlaceHolder + "', parameters('workspace'))]" } $armResource = Get-ArmResource $fileContent.name $fileContent.type $fileContent.kind $fileContent.properties # location ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource -propertyName 'location' -isInnerObject $false -innerObjectName $null -kindType $null -isSecret $true -isRequired $false -fileType 'dataCollectionRules' -minLength 1 # dataCollectionEndpointId $hasDataCollectionEndpointIdProperty = [bool](($armResource.properties).PSobject.Properties.name -match "dataCollectionEndpointId") if ($hasDataCollectionEndpointIdProperty) { $dataCollectionEndpointIdProperty = $armResource.properties.dataCollectionEndpointId $placeHoldersMatched = $dataCollectionEndpointIdProperty | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { $placeHolderName = $placeHoldersMatched.Matches.Value.replace("{{", "").replace("}}", "") $dataCollectionEndpointIdPropertyName = $placeHolderName + $global:connectorCounter $armResource.properties.dataCollectionEndpointId = "[variables('$($dataCollectionEndpointIdPropertyName)')]" if (!$global:baseMainTemplate.variables."$dataCollectionEndpointIdPropertyName") { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "$dataCollectionEndpointIdPropertyName" -NotePropertyValue "[concat('/subscriptions/',parameters('subscription'),'/resourceGroups/',parameters('resourceGroupName'),'/providers/Microsoft.Insights/dataCollectionEndpoints/',parameters('workspace'))]" } } } else { # if dataCollectionEndpointId property not present then add it $armResource.properties | Add-Member -MemberType NoteProperty -Name "dataCollectionEndpointId" -Value "[variables('dataCollectionEndpointId')]" if (!$global:baseMainTemplate.variables.dataCollectionEndpointId) { $global:baseMainTemplate.variables | Add-Member -NotePropertyName "dataCollectionEndpointId" -NotePropertyValue "[concat('/subscriptions/',parameters('subscription'),'/resourceGroups/',parameters('resourceGroupName'),'/providers/Microsoft.Insights/dataCollectionEndpoints/',parameters('workspace'))]" } } if (!$global:baseMainTemplate.parameters.resourceGroupName) { $resourceGroupNameParameter = [PSCustomObject] @{ type = "string"; defaultValue = "[resourceGroup().name]"; metadata = [PSCustomObject] @{ description = "resource group name where Microsoft Sentinel is setup" }; } $global:baseMainTemplate.parameters | Add-Member -MemberType NoteProperty -Name "resourceGroupName" -Value $resourceGroupNameParameter } if (!$global:baseMainTemplate.parameters.subscription) { $subscriptionParameter = [PSCustomObject] @{ type = "string"; defaultValue = "[last(split(subscription().id, '/'))]"; metadata = [PSCustomObject] @{ description = "subscription id where Microsoft Sentinel is setup" }; } $global:baseMainTemplate.parameters | Add-Member -MemberType NoteProperty -Name "subscription" -Value $subscriptionParameter } # workspaceResourceId $hasWorkspaceResourceIdProperty = [bool](($armResource.properties.destinations.logAnalytics[0]).PSobject.Properties.name -match "workspaceResourceId") if ($hasWorkspaceResourceIdProperty) { ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.destinations.logAnalytics[0] -propertyName 'workspaceResourceId' -isInnerObject $true -innerObjectName $logAnalytics -kindType $null -isSecret $true -isRequired $false -fileType 'dataCollectionRules' -minLength 1 } else { # if workspaceResourceId property not present then add it $armResource.properties.destinations.logAnalytics | Add-Member -MemberType NoteProperty -Name "workspaceResourceId" -Value "[[parameters('workspaceResourceId')]" $templateContentConnections.properties.mainTemplate = addNewParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName 'workspaceResourceId' -isSecret $true } $armResource = $(removePropertiesRecursively $armResource $false) $templateContentConnectorDefinition.properties.mainTemplate.resources += $armResource } #========end: dcr resource=========== #========start: tables resource=========== $ccpTablesFilePath = $ccpItem.TableFilePath Write-Host "CCP Table File Path : $ccpTablesFilePath" if ($null -ne $ccpTablesFilePath -and $ccpTablesFilePath -ne '') { $fileContent = ReadFileContent -filePath $ccpTablesFilePath if ($null -eq $fileContent) { exit 1; } foreach ($tableContent in $fileContent) { if($tableContent.type -eq "Microsoft.OperationalInsights/workspaces/tables") { $resourceName = $tableContent.name $armResource = Get-ArmResource $resourceName $tableContent.type $tableContent.kind $tableContent.properties ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $true -propertyObject $armResource -propertyName 'location' -isInnerObject $false -innerObjectName $null -kindType $null -isSecret $true -isRequired $false -fileType 'tables' -minLength 1 $templateContentConnectorDefinition.properties.mainTemplate.resources += $armResource $tableCounter ++; } } } if ($null -ne $ccpTables -and $ccpTables.count -gt 0 -and $ccpTablesCounter -eq 1) { # add additional tables if any and run this code only once foreach ($tableFilePath in $ccpTables) { #$fileContent = Get-Content -Raw "$tableFilePath" | Out-String | ConvertFrom-Json $fileContent = ReadFileContent -filePath $tableFilePath if ($null -eq $fileContent) { exit 1; } if ($fileContent.type -eq "Microsoft.OperationalInsights/workspaces/tables") { $resourceName = $fileContent.name $armResource = Get-ArmResource $resourceName $fileContent.type $fileContent.kind $fileContent.properties $hasLocationProperty = [bool]($armResource.PSobject.Properties.name -match "location") if ($hasLocationProperty) { $locationProperty = $armResource.location $placeHoldersMatched = $locationProperty | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { $placeHolderName = $placeHoldersMatched.Matches.Value.replace("{{", "").replace("}}", "") $templateContentConnections.properties.mainTemplate = addNewParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName $placeHolderName -isSecret $true } } $templateContentConnectorDefinition.properties.mainTemplate.resources += $armResource } } $ccpTablesCounter += 1 } #========end: tables resource=========== ## Build the full package resources $paramItems = Get-ConnectionsTemplateParameters $activeResource $ccpItem; $finalParameters = $templateContentConnections.properties.mainTemplate.parameters; foreach ($prop1 in $paramItems.psobject.Properties) { $hasProperty = $false; foreach ($prop2 in $finalParameters.psobject.Properties) { if ($prop1.Name -eq $prop2.Name) { $hasProperty = $true break; } } if (!$hasProperty) { $finalParameters | Add-Member -MemberType NoteProperty -Name $prop1.Name -Value $prop1.Value } } $global:baseMainTemplate.resources += $templateContentConnectorDefinition $global:baseMainTemplate.resources += $activeResource $global:baseMainTemplate.resources += $templateContentConnections if ($global:connectorCounter -eq 1) { $baseDataConnectorStep = [PSCustomObject] @{ name = "dataconnectors"; label = "Data Connectors"; bladeTitle = "Data Connectors"; elements = @(); } $global:baseCreateUiDefinition.parameters.steps += $baseDataConnectorStep } $connectorDescriptionText = "This Solution installs the data connector for $solutionName. You can get $solutionName data in your Microsoft Sentinel workspace. After installing the solution, configure and enable this data connector by following guidance in Manage solution view." $baseDataConnectorTextElement = [PSCustomObject] @{ name = "dataconnectors$global:connectorCounter-text"; type = "Microsoft.Common.TextBlock"; options = [PSCustomObject] @{ text = $connectorDescriptionText; } } $currentStepNum = $global:baseCreateUiDefinition.parameters.steps.Count - 1 $global:baseCreateUiDefinition.parameters.steps[$currentStepNum].elements += $baseDataConnectorTextElement $connectDataSourcesLink = [PSCustomObject] @{ name = "dataconnectors-link$($global:connectorCounter)"; type = "Microsoft.Common.TextBlock"; options = [PSCustomObject] @{ link = [PSCustomObject] @{ label = "Learn more about connecting data sources"; uri = "https://docs.microsoft.com/azure/sentinel/connect-data-sources"; } } } $global:baseCreateUiDefinition.parameters.steps[$currentStepNum].elements += $connectDataSourcesLink $global:connectorCounter += 1 } } catch { Write-Host "Error occurred in createCCPConnector file. Error Details $_" } } function ProcessPropertyPlaceholders($armResource, $templateContentConnections, $isOnlyObjectCheck, # check if object is present or not example: auth: {'seviceaccountemail'...}. Here check if auth is provided. $propertyObject, # $armResource.properties $propertyName, # sqsUrls $isInnerObject, # false. This propertyName is not inside of any other object or array other than properties. if yes then specify that name below $innerObjectName, # $null $kindType, # AWS. This can we GCP or AWS or RestApiPoller or $null for location $isSecret, # false $isRequired, # true, if true then it checks if the field is present or not else give error and exit $fileType, #either dataConnectorDefinitions or dataConnectors poller or dataCollectionRules or tables $minLength = 1, # default 1 $isCreateArray = $false # create if true then create array else string ) { $placeHolderPatternMatches = '\{{[a-zA-Z0-9]+\}}' $hasProperty = [bool]($propertyObject.PSobject.Properties.name -match "$($propertyName)") if ($hasProperty) { if (!$isOnlyObjectCheck) { $placeHoldersMatched = $propertyObject.$($propertyName) | Select-String $placeHolderPatternMatches -AllMatches if ($null -ne $placeHoldersMatched -and $placeHoldersMatched.Matches.Value.Count -gt 0) { $placeHolderName = $placeHoldersMatched.Matches.Value.replace("{{", "").replace("}}", "") if ($isCreateArray) { # create array $propertyObject.$($propertyName) = @("[[parameters('$($placeHolderName)')]") } else { # normal string field $hasMoreText = $placeHoldersMatched.Line.Replace("{{$($placeHolderName)}}", '') if ($hasMoreText.Length -gt 0) { $propertyObject.$($propertyName) = "[[concat(parameters('$($placeHolderName)'), '$($hasMoreText)')]" } else { $propertyObject.$($propertyName) = "[[parameters('$($placeHolderName)')]" } } $templateContentConnections.properties.mainTemplate = addNewParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName $placeHolderName -isSecret $isSecret -minLength $minLength } } } else { if ($isRequired -and $isInnerObject) { Write-Host "Error: Attribute '$($propertyName)' missing from '$($innerObjectName)' Object from $($kindType) $($fileType) 'properties' section." -BackgroundColor Red exit 1; } elseif ($isRequired -and $isInnerObject -eq $false) { Write-Host "Error: Attribute '$($propertyName)' missing from '$($kindType)' $($fileType) 'properties' section." -BackgroundColor Red exit 1; } else { Write-Host "Warning: Attribute '$($propertyName)' missing from $($fileType)." } } } function CreateRestApiPollerResourceProperties($armResource, $templateContentConnections, $fileType) { $kindType = 'RestApiPoller' if($armResource.properties.auth.type.ToLower() -eq 'oauth2') { # clientid ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'ClientId' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 4 -isCreateArray $false # client secret ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'ClientSecret' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 4 -isCreateArray $false # authorization code if($armResource.properties.auth.grantType -eq 'authorization_code') { ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'AuthorizationCode' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $false -fileType $fileType -minLength 4 -isCreateArray $false } # AuthorizationEndpoint placeholder ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'AuthorizationEndpoint' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $false -fileType $fileType -minLength 4 -isCreateArray $false # TokenEndpoint placeholder ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'TokenEndpoint' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $false -fileType $fileType -minLength 4 -isCreateArray $false } elseif ($armResource.properties.auth.type.ToLower() -eq 'basic') { # username ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'username' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 4 -isCreateArray $false # password ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'password' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 4 -isCreateArray $false } elseif ($armResource.properties.auth.type.ToLower() -eq 'apikey') { # ApiKey ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'apikey' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 4 -isCreateArray $false } else { Write-Host "Error: For kind RestApiPoller, Data Connector Poller file should have 'auth' object with 'type' attribute having value either 'Basic', 'OAuth2' or 'APIKey'." -BackgroundColor Red exit 1; } if ($armResource.properties.request.PSObject.Properties["apiEndPoint"] -and $armResource.properties.request.apiEndPoint.contains("{{")) { # identify any placeholders in apiEndpoint $endPointUrl = $armResource.properties.request.apiEndPoint $placeHoldersMatched = $endPointUrl | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { # has some placeholders $finalizedEndpointUrl = "[[concat(" $closureBrackets = ")]" foreach ($currentPlaceHolder in $placeHoldersMatched.Matches.Value) { $placeHolderName = $currentPlaceHolder.replace("{{", "").replace("}}", "") $splitEndpoint = $endPointUrl -split "($currentPlaceHolder)" foreach($splitItem in $splitEndpoint) { if ($splitItem -eq '') { continue } if ($splitItem -eq $currentPlaceHolder) { $finalizedEndpointUrl += "parameters('" + $placeHolderName + "')," $templateContentConnections.properties.mainTemplate = addNewParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName "$placeHolderName" -isSecret $true } else { $finalizedEndpointUrl += "'" + $splitItem + "'," } } } $armResource.properties.request.apiEndPoint = $finalizedEndpointUrl.Substring(0, $finalizedEndpointUrl.Length - 1) + $closureBrackets } } # headers placeholder $hasHeaders = [bool]($armResource.properties.request.PSobject.Properties.name -match "headers") if ($hasHeaders) { foreach ($headerProps in $armResource.properties.request.headers.PsObject.Properties) { $headerPropName = $headerProps.Name $headerPropValue = $headerProps.Value if ($headerPropValue.ToString().contains("{{")) { $placeHoldersMatched = $headerPropValue | Select-String $placeHolderPatternMatches -AllMatches if ($placeHoldersMatched.Matches.Value.Count -gt 0) { $placeHolderName = $placeHoldersMatched.Matches.Value.replace("{{", "").replace("}}", "") $armResource.properties.request.headers."$headerPropName" = "[[parameters('$($placeHolderName)')]" $templateContentConnections.properties.mainTemplate = addNewParameter -templateResourceObj $templateContentConnections.properties.mainTemplate -parameterName "$placeHolderName" -isSecret $true } } } } # paging placeholder Paging($armResource) # stepInfo placeholder $hasStepInfo = [bool]($armResource.properties.PSobject.Properties.name -match "stepInfo") $stepIds = @() if ($hasStepInfo) { $hasNextSteps = [bool]($armResource.properties.stepInfo.PSobject.Properties.name -match "nextSteps") if ($hasNextSteps) { foreach ($step in $armResource.properties.stepInfo.nextSteps) { $stepIds += $step.stepId } } else { Write-Host "Warning: 'stepInfo' object is missing 'nextSteps' array." } if ($stepIds.Count -gt 0) { $stepIdsString = $stepIds -join ', ' Write-Host "List of identified 'stepId' in 'stepInfo' are: $stepIdsString" } } # stepCollectorConfigs placeholder $hasStepCollectorConfigs = [bool]($armResource.properties.PSobject.Properties.name -match "stepCollectorConfigs") if ($hasStepCollectorConfigs) { foreach ($stepId in $stepIds) { # Check if the stepId exists in the stepCollectorConfigs if ($armResource.properties.stepCollectorConfigs.PSObject.Properties.Match($stepId)) { ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.stepCollectorConfigs.$stepId.request -propertyName 'apiEndpoint' -isInnerObject $true -innerObjectName 'request' -kindType $kindType -isSecret $true -isRequired $false -fileType $fileType -minLength 4 -isCreateArray $false } else { Write-Host "Warning: Step ID $stepId not found in stepCollectorConfigs." } } } QueryParameters($armResource) } function Paging($armResource) { $hasPaging = [bool]($armResource.properties.PSobject.Properties.name -match "paging") if ($hasPaging) { $pagingProperties = $armResource.properties.paging.PSobject.Properties.name # Iterate over each property of the paging object foreach ($propertyName in $pagingProperties) { ProcessPropertyPlaceholders -armResource $armResource ` -templateContentConnections $templateContentConnections ` -isOnlyObjectCheck $false ` -propertyObject $armResource.properties.paging ` -propertyName $propertyName ` -isInnerObject $true ` -innerObjectName 'paging' ` -kindType $kindType ` -isSecret $true ` -isRequired $false ` -fileType $fileType ` -minLength 4 ` -isCreateArray $false } } } function QueryParameters($armResource) { $hasQueryParameters = [bool]($armResource.properties.request.PSobject.Properties.name -match "queryParameters") if ($hasQueryParameters) { $queryParameterProperties = $armResource.properties.request.queryParameters.PSobject.Properties.name $objectPattern = '{{.*?}}\[\d*\]' # check pattern {{queryType}}[0] foreach ($propertyName in $queryParameterProperties) { $propValue = $armResource.properties.request.queryParameters.$propertyName if ($propValue -match $objectPattern) { $armResource.properties.request.queryParameters.$propertyName = $propValue -replace '{{(.*?)}}(\[\d*\])', '[[parameters(''$1'')$2]' } else { ProcessPropertyPlaceholders -armResource $armResource ` -templateContentConnections $templateContentConnections ` -isOnlyObjectCheck $false ` -propertyObject $armResource.properties.request.queryParameters ` -propertyName $propertyName ` -isInnerObject $true ` -innerObjectName 'queryParameters' ` -kindType $kindType ` -isSecret $true ` -isRequired $false ` -fileType $fileType ` -minLength 4 ` -isCreateArray $false } } } } function CreateGCPResourceProperties($armResource, $templateContentConnections, $fileType) { $kindType = 'GCP' ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties -propertyName 'connectorDefinitionName' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties -propertyName 'dataType' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $true -propertyObject $armResource.properties -propertyName 'dcrConfig' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.dcrConfig -propertyName 'streamName' -isInnerObject $true -innerObjectName 'dcrConfig' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $true -propertyObject $armResource.properties -propertyName 'auth' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'serviceAccountEmail' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 4 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'projectNumber' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 1 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.auth -propertyName 'workloadIdentityProviderId' -isInnerObject $true -innerObjectName 'auth' -kindType $kindType -isSecret $true -isRequired $true -minLength 4 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $true -propertyObject $armResource.properties -propertyName 'request' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.request -propertyName 'projectId' -isInnerObject $true -innerObjectName 'request' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 4 -isCreateArray $false # Request section subscriptionNames property ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.request -propertyName 'subscriptionNames' -isInnerObject $true -innerObjectName 'request' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $true } $awsSolutions = @('VMware Carbon Black Cloud') function CreateAwsResourceProperties($armResource, $templateContentConnections, $fileType) { $kindType = 'AmazonWebServicesS3' ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $true -propertyObject $armResource.properties -propertyName 'dataTypes' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $true -propertyObject $armResource.properties.dataTypes -propertyName 'logs' -isInnerObject $true -innerObjectName 'dataTypes' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.dataTypes.logs -propertyName 'state' -isInnerObject $true -innerObjectName 'logs' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $true -propertyObject $armResource.properties -propertyName 'dcrConfig' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false if ($awsSolutions.Contains($solutionName)) { # Handle properties destinationTable and streamName in dc poller file for this solutions as a special case $armResource.properties.dcrConfig.streamName = "[[parameters('streamName')[0]]" $armResource.properties.destinationTable = "[[concat(parameters('streamName')[0],'_CL')]" $templateContentConnections.properties.mainTemplate.parameters | Add-Member -NotePropertyName "streamName" -NotePropertyValue ([PSCustomObject] @{ type = "array" }) } else { ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties.dcrConfig -propertyName 'streamName' -isInnerObject $true -innerObjectName 'dcrConfig' -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties -propertyName 'destinationTable' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false } ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties -propertyName 'roleArn' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $false ProcessPropertyPlaceholders -armResource $armResource -templateContentConnections $templateContentConnections -isOnlyObjectCheck $false -propertyObject $armResource.properties -propertyName 'sqsUrls' -isInnerObject $false -innerObjectName $null -kindType $kindType -isSecret $true -isRequired $true -fileType $fileType -minLength 3 -isCreateArray $true }