MCSAssessment.psm1
|
function Invoke-MCSAssessment { Param( [Alias("Tenant","Tenants")] [string]$TenantID, [Alias("Subscription","Subscriptions","Subs")] [String[]]$SubscriptionID, [Alias("Workload","Workloads")] [String]$WorkloadName = "Default_Workload", [switch]$SkipCompress ) # WARA Files $RecommendationResourceTypesUri = 'https://azure.github.io/WARA-Build/objects/WARAinScopeResTypes.csv' $RecommendationDataUri = 'https://azure.github.io/WARA-Build/objects/recommendations.json' Write-Host "Setting Variables.." $workingDirectory = (Get-Location).Path Write-Host "Working Directory: $workingDirectory" if ($workingDirectory -eq "C:\") { Write-Host "Error: Working Directory cannot be the root of C:\" -ForegroundColor Red exit 1 } Write-Host "Validating Parameters..." if (-Not $TenantID) { Write-Host "Error: No TenantID" -ForegroundColor Red exit 1 } if (-Not $SubscriptionID) { Write-Host "Error: No SubscriptionID" -ForegroundColor Red exit 1 } <# Write-Host "Creating Folders..." $subjson = "$workingDirectory/JSON" $subcsv = "$workingDirectory/CSV" if (-Not (Test-Path $subjson)) { New-Item -ItemType Directory -Force -Path $subjson | Out-Null } if (-Not (Test-Path $subcsv)) { New-Item -ItemType Directory -Force -Path $subcsv | Out-Null } #> Write-Host "Starting ARI Data Collection for Workload: $WorkloadName" Invoke-ARI -ReportDir $workingDirectory -ReportName $WorkloadName -NoAutoUpdate -DiagramFullEnvironment -TenantId $TenantID -SubscriptionId $SubscriptionID -Debug -IncludeCosts -Lite Write-Host "Starting WARA Data Collection for Workload: $WorkloadName" Start-WARACollector -tenantid $TenantID -subscriptionid @($SubscriptionID) -Debug $WARAFile = Get-ChildItem -Path $workingDirectory -Filter "WARA-File*.json" $JSONResources = Get-Item -Path $WARAFile $JSONResources = $JSONResources.FullName $JSONContent = Get-Content -Path $JSONResources | ConvertFrom-Json $RootTypes = Invoke-RestMethod $RecommendationResourceTypesUri | ConvertFrom-Csv $RootTypes = $RootTypes | Where-Object { $_.InAprlAndOrAdvisor -eq 'yes' } $RecommendationObject = Invoke-RestMethod $RecommendationDataUri Write-Host "Count of WARA Recommendations: $($RecommendationObject.Count)" $ResourceRecommendations = $RecommendationObject | Where-Object { [string]::IsNullOrEmpty($_.tags) } $ResourceCollection = @() # First loop through the recommendations to get the impacted resources foreach ($Recom in $ResourceRecommendations) { $Resources = $JSONContent.ImpactedResources| Where-Object { ($_.recommendationId -eq $Recom.aprlGuid) } # If the recommendation is not a Custom Recommendation, we need to validate if the resources are not already in the tmp array (from a previous loop of a Custom Recommendation) if ([string]::IsNullOrEmpty($Resources) -and $Recom.aprlGuid -notin $tmp.Guid -and -not $Recom.checkName) { $Resources = $JSONContent.ImpactedResources | Where-Object { ($_.recommendationId -eq $Recom.aprlGuid) } } foreach ($Resource in $Resources) { $ResObj = [PSCustomObject]@{ 'Recommendation Guid' = $Recom.aprlGuid 'Recommendation Title' = $Recom.description 'Description' = $Recom.longDescription 'Priority' = $Recom.recommendationImpact 'Customer-facing annotation' = "" 'Internal-facing notes' = ($Recom.learnMoreLink.url -join " `n") 'Potential Benefit' = $Recommendation.'Potential Benefit' 'Resource Type' = $Resource.type 'Resource ID' = $Resource.id } $ResourceCollection += $ResObj } } Write-Host "Gethering Resource IDs" $ResourceList = @() $ResourceIDsQuery = "resources | project id" foreach ($Subscription in $SubscriptionID) { Write-Host "ResourceID loops: $Subscription" try { $QueryResult = Search-AzGraph -Query $ResourceIDsQuery -first 1000 -Subscription $Subscription -Debug:$false } catch { $QueryResult = Search-AzGraph -Query $ResourceIDsQuery -first 200 -Subscription $Subscription -Debug:$false } $ResourceList += $QueryResult while ($QueryResult.SkipToken) { try { $QueryResult = Search-AzGraph -Query $ResourceIDsQuery -SkipToken $QueryResult.SkipToken -Subscription $Subscription -first 1000 -Debug:$false } catch { $QueryResult = Search-AzGraph -Query $ResourceIDsQuery -SkipToken $QueryResult.SkipToken -Subscription $Subscription -first 200 -Debug:$false } $ResourceList += $QueryResult } } Write-Host "Starting Security Data Collection for Workload: $WorkloadName" $MDCQuery = Get-Content -Path ("$PSScriptRoot\MDC.kql") -Raw -Encoding UTF8 $MCSBQuery = Get-Content -Path ("$PSScriptRoot\MCSB.kql") -Raw -Encoding UTF8 $MDCLocalResults = @() foreach ($Subscription in $SubscriptionID) { Write-Host "Processing Security loops: $Subscription" try { $QueryResult = Search-AzGraph -Query $MDCQuery -first 1000 -Subscription $Subscription -Debug:$false } catch { $QueryResult = Search-AzGraph -Query $MDCQuery -first 200 -Subscription $Subscription -Debug:$false } $MDCLocalResults += $QueryResult while ($QueryResult.SkipToken) { try { $QueryResult = Search-AzGraph -Query $MDCQuery -SkipToken $QueryResult.SkipToken -Subscription $Subscription -first 1000 -Debug:$false } catch { $QueryResult = Search-AzGraph -Query $MDCQuery -SkipToken $QueryResult.SkipToken -Subscription $Subscription -first 200 -Debug:$false } $MDCLocalResults += $QueryResult } } Write-Host "Exporting Defender for Cloud Recommendations to CSV" $MDCLocalResults | Export-Csv -Path ($workingDirectory + "\DefenderForCloudRecommendations.csv") -NoTypeInformation -Encoding UTF8 Foreach ($MDC in $MDCLocalResults) { if ($MDC.state -eq 'Unhealthy') { $MDCResourceType = $MDC.resourceId.Split('/')[6]+ '/' + $MDC.resourceId.Split('/')[7] $Resourceid = $ResourceList | where-object { $_.id -eq $MDC.resourceId } if ([string]::IsNullOrEmpty($Resourceid.id) -eq $false) { $ResObj = [PSCustomObject]@{ 'Recommendation Guid' = $MDC.recommendationId 'Recommendation Title' = $MDC.recommendationDisplayName 'Description' = $MDC.description 'Priority' = $MDC.severity 'Customer-facing annotation' = "" 'Internal-facing notes' = $MDC.azurePortalRecommendationLink 'Potential Benefit' = "" 'Resource Type' = $MDCResourceType 'Resource ID' = $Resourceid.id } $ResourceCollection += $ResObj } } } $MCSBLocalResults = @() foreach ($Subscription in $SubscriptionID) { Write-Host "Processing Security loops: $Subscription" try { $QueryResult = Search-AzGraph -Query $MCSBQuery -first 1000 -Subscription $Subscription -Debug:$false } catch { $QueryResult = Search-AzGraph -Query $MCSBQuery -first 200 -Subscription $Subscription -Debug:$false } $MCSBLocalResults += $QueryResult while ($QueryResult.SkipToken) { try { $QueryResult = Search-AzGraph -Query $MCSBQuery -SkipToken $QueryResult.SkipToken -Subscription $Subscription -first 1000 -Debug:$false } catch { $QueryResult = Search-AzGraph -Query $MCSBQuery -SkipToken $QueryResult.SkipToken -Subscription $Subscription -first 200 -Debug:$false } $MCSBLocalResults += $QueryResult } } Write-Host "Exporting Security Benchmark Recommendations to CSV" $MCSBLocalResults | Export-Csv -Path ($workingDirectory + "\SecurityBenchmarkRecommendations.csv") -NoTypeInformation -Encoding UTF8 Foreach ($MCSB in $MCSBLocalResults) { if ($MCSB.state -eq 'Unhealthy' -and $MCSB.recommendationMetadataState -eq 'failed') { $MCSBResourceType = $MCSB.resourceId.Split('/')[6]+ '/' + $MCSB.resourceId.Split('/')[7] $Resourceid = $ResourceList | where-object { $_.id -eq $MCSB.resourceId } if ([string]::IsNullOrEmpty($Resourceid.id) -eq $false) { $ResObj = [PSCustomObject]@{ 'Recommendation Guid' = $MCSB.recommendationId 'Recommendation Title' = $MCSB.recommendationDisplayName 'Description' = $MCSB.description 'Priority' = $MCSB.severity 'Customer-facing annotation' = "" 'Internal-facing notes' = $MCSB.azurePortalRecommendationLink 'Potential Benefit' = "" 'Resource Type' = $MCSBResourceType 'Resource ID' = $Resourceid.id } $ResourceCollection += $ResObj } } } Write-Host "Total Recommendation Lines to Export: $($ResourceCollection.Count)" if ($ResourceCollection.Count -gt 500) { $Loop = $ResourceCollection.Count / 500 $Loop = [math]::ceiling($Loop) $Looper = 0 $Limit = 0 while ($Looper -lt $Loop) { $Looper ++ Write-Host "Exporting Partial CSV File: $Looper" $ResourceCollection[($Limit - 500)..($Limit - 1)] | Export-Csv -Path "$workingDirectory\Consolidated_Assessment_${WorkloadName}_Part${Looper}.csv" -NoTypeInformation -Encoding UTF8 $Limit += 500 } } else { Write-Host "Exporting Complete CSV File" $ResourceCollection | Export-Csv -Path "$workingDirectory\Consolidated_Assessment_$WorkloadName.csv" -NoTypeInformation -Encoding UTF8 } <# Write-Host "Starting PSRule Data Collection for Workload: $WorkloadName" foreach ($Subscription in $SubscriptionID) { # PSRule Export Write-Host " - Exporting rule data..." -ForegroundColor Yellow Export-AzRuleData -OutputPath "$subjson/$Subscription" -Subscription $Subscription Write-Host " - Generating CSV results..." -ForegroundColor Yellow invoke-psrule -inputPath "$subjson/$Subscription/$Subscription.json" -Outcome Fail -Module PSRule.Rules.Azure -OutputPath "$subcsv/$Subscription.csv" -OutputFormat Csv } #> if ($SkipCompress) { Write-Host "Skipping compression as per user request." -ForegroundColor Yellow } else { # Create a temporary folder to gather all contents $tempFolder = "$workingDirectory\TempForZip" New-Item -ItemType Directory -Path $tempFolder -Force | Out-Null $FilesToMove = Get-ChildItem -Path $workingDirectory -Include ("*.json","*.csv") -Recurse # Move all files into the temp folder foreach ($file in $FilesToMove) { Move-Item -Path $file.FullName -Destination $tempFolder } # Compress the temp folder into a ZIP file Compress-Archive -Path "$tempFolder\*" -DestinationPath ("$workingDirectory\Consolidated_Assessment.zip") -Force # Clean up the temporary folder Remove-Item -Path $tempFolder -Recurse -Force # Output path of the created ZIP file Write-Host "The Assessment Data Collection ZIP file is stored at the location : $zipFilePath" -foregroundColor Green # End of the script } # CleanUp <# Write-Host "Cleaning up WARA and ARI generated files..." if (Test-Path -PathType Leaf -Path $WARAFile) { Remove-Item $WARAFile -Force } #> if (Test-Path -PathType Container -Path "$workingDirectory\DiagramCache") { Remove-Item "$workingDirectory\DiagramCache" -Force -Recurse } if (Test-Path -PathType Container -Path "$workingDirectory\ReportCache") { Remove-Item "$workingDirectory\ReportCache" -Force -Recurse } if (Test-Path -PathType Leaf -Path "$workingDirectory\DiagramLogFile.log") { Remove-Item "$workingDirectory\DiagramLogFile.log" -Force } } |