Framework/Core/AzSKInfo/PersistedStateInfo.ps1
using namespace System.Management.Automation Set-StrictMode -Version Latest class PersistedStateInfo: AzCommandBase { hidden [PSObject] $AzSKRG = $null hidden [String] $AzSKRGName = "" PersistedStateInfo([string] $subscriptionId, [InvocationInfo] $invocationContext): Base($subscriptionId, $invocationContext) { #$this.DoNotOpenOutputFolder = $true; $this.AzSKRGName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName; $this.AzSKRG = Get-AzResourceGroup -Name $this.AzSKRGName -ErrorAction SilentlyContinue } [MessageData[]] UpdatePersistedState([string] $filePath) { [string] $errorMessages=""; $customErrors=@(); [MessageData[]] $messages = @(); try { $azskConfig = [ConfigurationManager]::GetAzSKConfigData(); $successCount = 0; $totalCount = 0; $settingStoreComplianceSummaryInUserSubscriptions = [ConfigurationManager]::GetAzSKSettings().StoreComplianceSummaryInUserSubscriptions; #return if feature is turned off at server config if(-not $azskConfig.StoreComplianceSummaryInUserSubscriptions -and -not $settingStoreComplianceSummaryInUserSubscriptions) { $this.PublishCustomMessage("NOTE: This feature is currently disabled in your environment. Please contact the cloud security team for your org.", [MessageType]::Warning); return $messages; } #Check for file path exist if(-not (Test-Path -path $filePath)) { $this.PublishCustomMessage("Could not find file: [$filePath]. `nPlease rerun the command with correct path.", [MessageType]::Error); return $messages; } # Read Local CSV file [CsvOutputItem[]] $controlResultSet =@(); $controlResultSet = Get-ChildItem -Path $filePath -Filter '*.csv' -Force | Get-Content | Convertfrom-csv #pick only those controls whose usercomments has been updated $controlResultSet = $controlResultSet | Where-Object {$_.FeatureName -ne "AzSKCfg" -and -not [string]::IsNullOrWhiteSpace($_.UserComments)} $erroredControls = @(); $totalCount = ($controlResultSet | Measure-Object).Count; $invalidUserComments = $controlResultSet | Where-Object { $_.UserComments.length -gt 255} if(($invalidUserComments | Measure-Object).Count -eq 0) { if(($invalidUserComments | Measure-Object).Count -eq $totalCount ) { $this.PublishCustomMessage("Could not find any control in file with usercomments: [$filePath].",[MessageType]::Error); return $messages; } else { $scannedResources = $controlResultSet | Group-Object -Property ResourceId # Read file from Storage $complianceReportHelper = [ComplianceReportHelper]::new($this.SubscriptionContext, $this.GetCurrentModuleVersion()); $PersistedScanControls =$null; # Check for write access if($complianceReportHelper.HaveRequiredPermissions() -eq 1) { [ComplianceStateTableEntity[]] $PersistedScanControls = $complianceReportHelper.GetSubscriptionComplianceReport(); } else { $this.PublishCustomMessage("You don't have the required permissions to update user comments. If you'd like to update user comments, please request your subscription owner to grant you 'Contributor' access to the DevOps Kit resource group.", [MessageType]::Warning); return $messages; } [ComplianceStateTableEntity[]] $UpdatedPersistedControls = @(); $scannedResources | ForEach-Object { $scannedResource = $_ $scannedResource.Group | ForEach-Object { try { $control = $_; $partsToHash = $scannedResource.Name; $partitionKey = [Helpers]::ComputeHash($partsToHash.ToLower()); $filteredPersistedControl = $PersistedScanControls | Where-Object { $_.PartitionKey -eq $partitionKey -and $_.ControlID -eq $control.ControlID} if(($filteredPersistedControl | Measure-Object).Count -gt 0) { $encoder = [System.Text.Encoding]::UTF8 $encUserComments= $encoder.GetBytes($control.UserComments) $encUserCommentsString= $encoder.GetString($encUserComments) $filteredPersistedControl.UserComments = $encUserCommentsString $UpdatedPersistedControls += $filteredPersistedControl; $successCount += 1 } else { $erroredControls += $this.CreateCustomErrorObject($control,"Could not find previous persisted state."); } } catch { $this.PublishException($_); $erroredControls+=$this.CreateCustomErrorObject($currentItem,"Could not find previous persisted state.") } } } if(($UpdatedPersistedControls | Measure-Object).Count -gt 0) { $complianceReportHelper.SetLocalSubscriptionScanReport($UpdatedPersistedControls); } } } else { $invalidUserComments | ForEach-Object { $erroredControls+=$this.CreateCustomErrorObject($_,"User Comment's length should not exceed 255 characters.") } } # If updation failed for any control, genearte error file if(($erroredControls | Measure-Object).Count -gt 0) { $nonNullProps=@(); [CsvOutputItem].GetMembers() | Where-Object { $_.MemberType -eq [System.Reflection.MemberTypes]::Property } | ForEach-Object { $propName = $_.Name; if(($erroredControls | Where-object { -not [string]::IsNullOrWhiteSpace($_.$propName) } | Measure-object).Count -ne 0) { $nonNullProps += $propName; } }; $nonNullProps += "ErrorDetails" $csvItems = $erroredControls | Select-Object -Property $nonNullProps $controlCSV = New-Object -TypeName WriteCSVData $controlCSV.FileName = "Controls_NotUpdated_" + $this.RunIdentifier $controlCSV.FileExtension = 'csv' $controlCSV.FolderPath = '' $controlCSV.MessageData = $csvItems $this.PublishAzSKRootEvent([AzSKRootEvent]::WriteCSV, $controlCSV); $this.PublishCustomMessage("[$successCount/$totalCount] user comments have been updated successfully.", [MessageType]::Update); $this.PublishCustomMessage("[$(($erroredControls | Measure-Object).Count)/$totalCount] user comments could not be updated due to an error. See the log file for details.", [MessageType]::Warning); } else { $this.PublishCustomMessage("All User Comments have been updated successfully.", [MessageType]::Update); } } catch { $this.PublishEvent([AzSKGenericEvent]::Exception, "Unable to update user comments. Could not find previous persisted state in DevOps Kit storage."); $this.PublishException($_); } return $messages; } hidden [PSObject] CreateCustomErrorObject($currentItem,$reason) { $currentItem | Add-Member -NotePropertyName ErrorDetails -NotePropertyValue $reason return $currentItem; } } |