Framework/Helpers/ComplianceReportHelper.ps1
Set-StrictMode -Version Latest class ComplianceReportHelper { hidden [StorageHelper] $azskStorageInstance; hidden [int] $retryCount = 3; hidden [string] $subscriptionId; ComplianceReportHelper([string] $subId) { $this.subscriptionId = $subId; $this.CreateComplianceReportContainer(); } hidden [void] CreateComplianceReportContainer() { try { $azskRGName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName $azskStorageAccount = Find-AzureRmResource -ResourceNameContains $([Constants]::StorageAccountPreName) -ResourceGroupNameEquals $azskRGName -ResourceType 'Microsoft.Storage/storageAccounts' if($azskStorageAccount) { $this.azskStorageInstance = [StorageHelper]::new($this.subscriptionId, $azskRGName,$azskStorageAccount.Location, $azskStorageAccount.Name); $this.azskStorageInstance.CreateStorageContainerIfNotExists([Constants]::ComplianceReportContainerName); } } catch { #exception will be thrown if it fails to access or create the snapshot container } } hidden [LocalSubscriptionReport] GetLocalSubscriptionScanReport() { [LocalSubscriptionReport] $storageReport = $null; try { if($this.azskStorageInstance.HaveWritePermissions -eq 0) { return $null; } $complianceReportBlobName = [Constants]::ComplianceReportBlobName + ".zip" $ContainerName = [Constants]::ComplianceReportContainerName $AzSKTemp = [Constants]::AzSKAppFolderPath + [Constants]::ComplianceReportPath; if(-not (Test-Path -Path $AzSKTemp)) { mkdir -Path $AzSKTemp -Force } $this.azskStorageInstance.DownloadFilesFromBlob($ContainerName, $complianceReportBlobName, $AzSKTemp, $true); $fileName = $AzSKTemp+"\"+$this.subscriptionId +".json"; $StorageReportJson = $null; try { # extract file from zip $compressedFileName = $AzSKTemp+"\"+[Constants]::ComplianceReportBlobName +".zip" if((Test-Path -Path $compressedFileName -PathType Leaf)) { Expand-Archive -Path $compressedFileName -DestinationPath $AzSKTemp -Force if((Test-Path -Path $fileName -PathType Leaf)) { $StorageReportJson = (Get-ChildItem -Path $fileName -Force | Get-Content | ConvertFrom-Json) } } } catch { #unable to find zip file. return empty object return $null; } if($null -ne $StorageReportJson) { $storageReport = [LocalSubscriptionReport] $StorageReportJson; } return $storageReport; } finally{ [Helpers]::CleanupLocalFolder([Constants]::AzSKAppFolderPath + [Constants]::ComplianceReportPath); } } hidden [LSRSubscription] GetLocalSubscriptionScanReport([string] $subId) { if($this.azskStorageInstance.HaveWritePermissions -eq 0) { return $null; } $fullScanResult = $this.GetLocalSubscriptionScanReport(); if($null -ne $fullScanResult -and ($fullScanResult.Subscriptions | Measure-Object ).Count -gt 0) { return $fullScanResult.Subscriptions | Where-Object { $_.SubscriptionId -eq $subId } } else { return $null; } } hidden [void] SetLocalSubscriptionScanReport([LocalSubscriptionReport] $scanResultForStorage) { try { if($this.azskStorageInstance.HaveWritePermissions -eq 0) { return; } $AzSKTemp = [Constants]::AzSKAppFolderPath + [Constants]::ComplianceReportPath; if(-not (Test-Path "$AzSKTemp")) { mkdir -Path "$AzSKTemp" -ErrorAction Stop | Out-Null } else { Remove-Item -Path "$AzSKTemp\*" -Force -Recurse } $fileName = "$AzSKTemp\" + $this.subscriptionId +".json" $compressedFileName = "$AzSKTemp\" + [Constants]::ComplianceReportBlobName +".zip" $ContainerName = [Constants]::ComplianceReportContainerName; [Helpers]::ConvertToJsonCustomCompressed($scanResultForStorage) | Out-File $fileName -Force #compress file before store to storage Compress-Archive -Path $fileName -CompressionLevel Optimal -DestinationPath $compressedFileName -Update $fileInfos = @(); $fileInfos += [System.IO.FileInfo]::new($compressedFileName); $this.azskStorageInstance.UploadFilesToBlob($ContainerName, "", $fileInfos, $true); } finally { [Helpers]::CleanupLocalFolder([Constants]::AzSKAppFolderPath + [Constants]::ComplianceReportPath); } } hidden [LocalSubscriptionReport] MergeSVTScanResult($currentScanResults, $resourceInventory, $scanSource, $scannerVersion, $scanKind) { if($currentScanResults.Count -lt 1) { return $null} $SVTEventContextFirst = $currentScanResults[0] $complianceReport = $this.GetLocalSubscriptionScanReport(); $subscription = [LSRSubscription]::new() [LSRResources[]] $resources = @() if($null -ne $complianceReport -and (($complianceReport.Subscriptions | Where-Object { $_.SubscriptionId -eq $this.subscriptionId }) | Measure-Object).Count -gt 0) { $subscription = $complianceReport.Subscriptions | Where-Object { $_.SubscriptionId -eq $this.subscriptionId } } else { $subscription.SubscriptionId = $this.subscriptionId $subscription.SubscriptionName = $SVTEventContextFirst.SubscriptionContext.SubscriptionName } if($null -ne $subscription.ScanDetails) { $resources = $subscription.ScanDetails.Resources } else { $subscription.ScanDetails = [LSRScanDetails]::new() } $currentScanResults | ForEach-Object { $currentScanResult = $_ try { if($currentScanResult.FeatureName -eq "SubscriptionCore") { if(($subscription.ScanDetails.SubscriptionScanResult | Measure-Object).Count -gt 0) { $matchedControlResults = $subscription.ScanDetails.SubscriptionScanResult | Where-Object { $currentScanResult.ControlItem.Id -eq $_.ControlIntId } if((($matchedControlResults) | Measure-Object).Count -gt0) { $_complianceSubResult = $matchedControlResults $svtResults = $this.ConvertScanResultToSnapshotResult($currentScanResult, $scanSource, $scannerVersion, $scanKind, $_complianceSubResult, $true) $subscription.ScanDetails.SubscriptionScanResult = $subscription.ScanDetails.SubscriptionScanResult | Where-Object {$_.ControlIntId -ne $currentScanResult.ControlItem.Id } $subscription.ScanDetails.SubscriptionScanResult += $svtResults } else { $subscription.ScanDetails.SubscriptionScanResult += $this.ConvertScanResultToSnapshotResult($currentScanResult, $scanSource, $scannerVersion, $scanKind, $null, $true) } } else { $subscription.ScanDetails.SubscriptionScanResult += $this.ConvertScanResultToSnapshotResult($currentScanResult, $scanSource, $scannerVersion, $scanKind, $null, $true) } } elseif($currentScanResult.FeatureName -ne "AzSKCfg") { $filteredResource = $resources | Where-Object {$_.ResourceId -eq $currentScanResult.ResourceContext.ResourceId } if(($filteredResource | Measure-Object).Count -gt 0) { $resource = $filteredResource $resource.LastEventOn = [DateTime]::UtcNow $matchedControlResults = $resource.ResourceScanResult | Where-Object { $_.ControlIntId -eq $currentScanResult.ControlItem.Id } if((($matchedControlResults) | Measure-Object).Count -gt 0) { $_complianceResResult = $matchedControlResults $svtResults = $this.ConvertScanResultToSnapshotResult($currentScanResult, $scanSource, $scannerVersion, $scanKind, $_complianceResResult, $false) $resource.ResourceScanResult = $resource.ResourceScanResult | Where-Object { $_.ControlIntId -ne $_complianceResResult[0].ControlIntId } $resource.ResourceScanResult += $svtResults } else { $resource.ResourceScanResult += $this.ConvertScanResultToSnapshotResult($currentScanResult, $scanSource, $scannerVersion, $scanKind, $null, $false) } $tmpResources = $resources | Where-Object {$_.ResourceId -ne $resource.ResourceId } $resources = @() $resources += $tmpResources $resources += $resource } else { $resource = [LSRResources]::New() $resource.HashId = [Helpers]::ComputeHash($currentScanResult.ResourceContext.ResourceId) $resource.ResourceId = $currentScanResult.ResourceContext.ResourceId $resource.LastEventOn = [DateTime]::UtcNow $resource.FirstScannedOn = [DateTime]::UtcNow $resource.ResourceGroupName = $currentScanResult.ResourceContext.ResourceGroupName $resource.ResourceName = $currentScanResult.ResourceContext.ResourceName # ToDo: Need to confirm # $resource.ResourceMetadata = [Helpers]::ConvertToJsonCustomCompressed($currentScanResult.ResourceContext.ResourceMetadata) $resource.FeatureName = $currentScanResult.FeatureName $resource.ResourceScanResult += $this.ConvertScanResultToSnapshotResult($currentScanResult, $scanSource, $scannerVersion, $scanKind, $null, $false) $resources += $resource } } } catch { [EventBase]::PublishGenericException($_); } } if($null -ne $resourceInventory) { if($resources.Count -gt 0) { $deletedResoures = @() $resources | ForEach-Object { $resource = $_ if(($resourceInventory | Where-Object { $_.ResourceId -eq $resource.ResourceId } | Measure-Object).Count -eq 0) { $deletedResoures += $resource.ResourceId } } $resources = $resources | Where-Object { $deletedResoures -notcontains $_.ResourceId } } $resourceInventory | ForEach-Object { $resource = $_ try { if([Helpers]::CheckMember($resource, "ResourceId")) { if((($resources | Where-Object { $_.ResourceId -eq $resource.ResourceId }) | Measure-Object).Count -eq 0) { $newResource = [LSRResources]::new() $newResource.HashId = [Helpers]::ComputeHash($resource.ResourceId) $newResource.ResourceId = $resource.ResourceId $newResource.FeatureName = $supportedResourceTypes[$resource.ResourceType.ToLower()] $newResource.ResourceGroupName = $resource.ResourceGroupName $newResource.ResourceName = $resource.Name $resources += $newResource } } } catch { [EventBase]::PublishGenericException($_); } } } # Remove updated objects from existing compliance data $resources | ForEach-Object { $resource = $_ if($null -ne $subscription.ScanDetails.Resources -and $subscription.ScanDetails.Resources.Count -gt 0) { $subscription.ScanDetails.Resources = $subscription.ScanDetails.Resources | Where-Object { $_.ResourceId -ne $resource.ResourceId } } } # append new updated objects $subscription.ScanDetails.Resources += $resources if($null -ne $complianceReport) { $complianceReport.Subscriptions = $complianceReport.Subscriptions | Where-Object { $_.SubscriptionId -ne $subscription.SubscriptionId } } else { $complianceReport = [LocalSubscriptionReport]::new() } $complianceReport.Subscriptions += $subscription; return $complianceReport } hidden [LSRControlResultBase[]] ConvertScanResultToSnapshotResult($svtResult, $scanSource, $scannerVersion, $scanKind, $oldResult, $isSubscriptionScan) { [LSRControlResultBase[]] $scanResults = @(); $svtResult.ControlResults | ForEach-Object { $currentResult = $_ $isLegitimateResult = ($currentResult.CurrentSessionContext.IsLatestPSModule -and $currentResult.CurrentSessionContext.Permissions.HasRequiredAccess -and $currentResult.CurrentSessionContext.Permissions.HasAttestationReadPermissions) if($isLegitimateResult) { $resourceScanResult = [LSRControlResultBase]::new() if($isSubscriptionScan) { if($null -ne $oldResult) { $resourceScanResult = $oldResult } else { $resourceScanResult = [LSRSubscriptionControlResult]::new() } } else { if($null -ne $oldResult) { $resourceScanResult = $oldResult | Where-Object { $_.ChildResourceName -eq $currentResult.ChildResourceName } } else { $resourceScanResult = [LSRResourceScanResult]::new() } } if($resourceScanResult.VerificationResult -ne $currentResult.VerificationResult) { $resourceScanResult.LastResultTransitionOn = [System.DateTime]::UtcNow } if($resourceScanResult.FirstScannedOn -eq [Constants]::AzSKDefaultDateTime) { $resourceScanResult.FirstScannedOn = [System.DateTime]::UtcNow } if($resourceScanResult.FirstFailedOn -eq [Constants]::AzSKDefaultDateTime -and $currentResult.ActualVerificationResult -ne [VerificationResult]::Passed) { $resourceScanResult.FirstFailedOn = [System.DateTime]::UtcNow } $resourceScanResult.ScannedBy = [Helpers]::GetCurrentRMContext().Account $resourceScanResult.ScanSource = $scanSource $resourceScanResult.ScannerVersion = $scannerVersion $resourceScanResult.ControlVersion = $scannerVersion if(-not $isSubscriptionScan) { $resourceScanResult.ChildResourceName = $currentResult.ChildResourceName } $resourceScanResult.ControlId = $svtResult.ControlItem.ControlId $resourceScanResult.ControlIntId = $svtResult.ControlItem.Id $resourceScanResult.ControlSeverity = $svtResult.ControlItem.ControlSeverity $resourceScanResult.ActualVerificationResult = $currentResult.ActualVerificationResult $resourceScanResult.AttestationStatus = $currentResult.AttestationStatus if($resourceScanResult.AttestationStatus -ne [AttestationStatus]::None -and $null -ne $currentResult.StateManagement -and $null -ne $currentResult.StateManagement.AttestedStateData) { if($resourceScanResult.FirstAttestedOn -eq [Constants]::AzSKDefaultDateTime) { $resourceScanResult.FirstAttestedOn = $currentResult.StateManagement.AttestedStateData.AttestedDate } if($currentResult.StateManagement.AttestedStateData.AttestedDate -gt $resourceScanResult.AttestedDate) { $resourceScanResult.AttestationCounter = $resourceScanResult.AttestationCounter + 1 } $resourceScanResult.AttestedBy = $currentResult.StateManagement.AttestedStateData.AttestedBy $resourceScanResult.AttestedDate = $currentResult.StateManagement.AttestedStateData.AttestedDate $resourceScanResult.Justification = $currentResult.StateManagement.AttestedStateData.Justification # $resourceScanResult.AttestationData = [Helpers]::ConvertToJsonCustomCompressed($currentResult.StateManagement.AttestedStateData.DataObject) } else { $resourceScanResult.AttestedBy = "" $resourceScanResult.AttestedDate = [Constants]::AzSKDefaultDateTime $resourceScanResult.Justification = "" $resourceScanResult.AttestationData = "" } $resourceScanResult.VerificationResult = $currentResult.VerificationResult $resourceScanResult.ScanKind = $scanKind $resourceScanResult.ScannerModuleName = [Constants]::AzSKModuleName $resourceScanResult.IsLatestPSModule = $currentResult.CurrentSessionContext.IsLatestPSModule $resourceScanResult.HasRequiredPermissions = $currentResult.CurrentSessionContext.Permissions.HasRequiredAccess $resourceScanResult.HasAttestationWritePermissions = $currentResult.CurrentSessionContext.Permissions.HasAttestationWritePermissions $resourceScanResult.HasAttestationReadPermissions = $currentResult.CurrentSessionContext.Permissions.HasAttestationReadPermissions $resourceScanResult.UserComments = $currentResult.UserComments $resourceScanResult.IsBaselineControl = $svtResult.ControlItem.IsBaselineControl if($svtResult.ControlItem.Tags.Contains("OwnerAccess") -or $svtResult.ControlItem.Tags.Contains("GraphRead")) { $resourceScanResult.HasOwnerAccessTag = $true } $resourceScanResult.LastScannedOn = [DateTime]::UtcNow # ToDo: Need to confirm #$resourceScanResult.Metadata = $scanResult.Metadata $scanResults += $resourceScanResult } } return $scanResults } } |