Framework/Managers/ControlBaselineManager.ps1
Set-StrictMode -Version Latest class ControlBaselineManager { hidden [PSObject] $AzSKResourceGroup = $null; hidden [PSObject] $AzSKStorageAccount = $null; hidden [PSObject] $AzSKStorageResourceBaselineContainer = $null; hidden [int] $HasWritePermissions = -1; hidden [string] $BaselineResourceBlobName = "BaselineResourceBlob.json" hidden [string] $CAScanProgressSnapshotsContainerName = [Constants]::CAScanProgressSnapshotsContainerName hidden [BaselineResourceMap] $BaselineControlObj = $null [PSObject] $ControlSettings; hidden [ActiveStatus] $ActiveStatus = [ActiveStatus]::NotStarted; hidden static [ControlBaselineManager] $Instance = $null; static [ControlBaselineManager] GetInstance() { if ( $null -eq [ControlBaselineManager]::Instance) { [ControlBaselineManager]::Instance = [ControlBaselineManager]::new(); } return [ControlBaselineManager]::Instance } ControlBaselineManager() { $this.ControlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json"); #$this.GetAzSKControlBaselineContainer() } hidden [bool] HasControlBaselineStatusWritePermissions() { $hasPermissions = $false; return $hasPermissions; } hidden [void] GetAzSKControlBaselineContainer() { if($null -eq $this.AzSKStorageAccount) { $this.GetAzSKStorageAccount() } if($null -eq $this.AzSKStorageAccount) { return; } try { #Able to read the container then read permissions are good $containerObject = Get-AzureStorageContainer -Context $this.AzSKStorageAccount.Context -Name $this.CAScanProgressSnapshotsContainerName -ErrorAction Stop $this.AzSKStorageResourceBaselineContainer = $containerObject; } catch { try { New-AzureStorageContainer -Context $this.AzSKStorageAccount.Context -Name $this.CAScanProgressSnapshotsContainerName -ErrorAction SilentlyContinue $containerObject = Get-AzureStorageContainer -Context $this.AzSKStorageAccount.Context -Name $this.CAScanProgressSnapshotsContainerName -ErrorAction SilentlyContinue $this.AzSKStorageResourceBaselineContainer = $containerObject; } catch { #Do nothing } } } hidden [void] GetAzSKStorageAccount() { if($null -eq $this.AzSKResourceGroup) { $this.GetAzSKRG(); } if($null -ne $this.AzSKResourceGroup) { $StorageAccount = Get-AzureRmStorageAccount -ResourceGroupName $this.AzSKResourceGroup.ResourceGroupName | Where-Object {$_.StorageAccountName -like 'azsk*'} -ErrorAction SilentlyContinue #if no storage account found then it assumes that there is no control state feature is not used and if there are more than one storage account found it assumes the same $this.AzSKStorageAccount = $StorageAccount; } } hidden [PSObject] GetAzSKRG() { $azSKConfigData = [ConfigurationManager]::GetAzSKConfigData() $resourceGroup = Get-AzureRmResourceGroup -Name $azSKConfigData.AzSKRGName -ErrorAction SilentlyContinue $this.AzSKResourceGroup = $resourceGroup return $resourceGroup; } [void] UpdateResourceStatus([string] $resourceId, [ScanState] $state) { $resourceValues = @(); $this.GetBaselineControlObject(); if($this.IsListAvailableAndActive()) { #$idHash = [Helpers]::ComputeHash($resourceId) $resourceValue = $this.BaselineControlObj.ResourceMapTable | Where-Object { $_.Id -eq $resourceId}; if($null -ne $resourceValue) { $resourceValue.ModifiedDate = [DateTime]::UtcNow; $resourceValue.State = $state; #$this.BaselineControlObj.ResourceMapTable[$idHash] = $resourceValue; } else { $resourceValue = [BaselineResource]@{ Id = $resourceId; State = $state; CreatedDate = [DateTime]::UtcNow; ModifiedDate = [DateTime]::UtcNow; } $this.BaselineControlObj.ResourceMapTable +=$resourceValue; } $this.PersistStorageBlob(); } } [void] RemoveControlBaseline() { if($null -ne $this.BaselineControlObj) { $AzSKTemp = [Constants]::AzSKAppFolderPath + "\TempState"; if(-not (Test-Path "$AzSKTemp\ControlsBaseline")) { mkdir -Path "$AzSKTemp\ControlsBaseline" -ErrorAction Stop | Out-Null } $masterFilePath = "$AzSKTemp\ControlsBaseline\$($this.BaselineResourceBlobName)" $controlStateBlob = Get-AzureStorageBlob -Container $this.CAScanProgressSnapshotsContainerName -Context $this.AzSKStorageAccount.Context -Blob "$($this.BaselineResourceBlobName)" -ErrorAction SilentlyContinue if($null -ne $controlStateBlob) { Get-AzureStorageBlobContent -CloudBlob $controlStateBlob.ICloudBlob -Context $this.AzSKStorageAccount.Context -Destination $masterFilePath -Force $baselineResources = Get-ChildItem -Path $masterFilePath -Force | Get-Content | ConvertFrom-Json if($baselineResources -ne $null -and ($baselineResources.ResourceMapTable | Measure-Object).Count -gt 0 -and ($baselineResources.ResourceMapTable | Where-Object { $_.State -ne "COMP" } | Measure-Object).Count -eq 0) { $this.ArchiveBlob("_End_"); Remove-AzureStorageBlob -CloudBlob $controlStateBlob.ICloudBlob -Force -Context $this.AzSKStorageAccount.Context } } $this.BaselineControlObj = $null } } [void] CreateResourceMasterList([PSObject] $resourceIds) { if(($resourceIds | Measure-Object).Count -gt 0) { $resourceIdMap = @(); $resourceIds | ForEach-Object { $resourceId = $_; #$hashId = [Helpers]::ComputeHash($resourceId); $resourceValue = [BaselineResource]@{ Id = $resourceId; State = [ScanState]::INIT CreatedDate = [DateTime]::UtcNow; ModifiedDate = [DateTime]::UtcNow; } #$resourceIdMap.Add($hashId,$resourceValue); $resourceIdMap +=$resourceValue } $masterControlBlob = [BaselineResourceMap]@{ Id = [DateTime]::UtcNow.ToString("yyyyMMdd_HHmmss"); CreatedDate = [DateTime]::UtcNow; ResourceMapTable = $resourceIdMap; } $this.BaselineControlObj = $masterControlBlob; $this.PersistStorageBlob(); $this.ActiveStatus = [ActiveStatus]::Yes; } } [void] PersistStorageBlob() { $this.GetBaselineControlObject(); if($null -ne $this.BaselineControlObj) { $AzSKTemp = [Constants]::AzSKAppFolderPath + "\TempState"; if(-not (Test-Path "$AzSKTemp\ControlsBaseline")) { mkdir -Path "$AzSKTemp\ControlsBaseline" -ErrorAction Stop | Out-Null } $masterFilePath = "$AzSKTemp\ControlsBaseline\$($this.BaselineResourceBlobName)" [Helpers]::ConvertToJsonCustom($this.BaselineControlObj) | Out-File $masterFilePath -Force Set-AzureStorageBlobContent -File $masterFilePath -Container $this.CAScanProgressSnapshotsContainerName -BlobType Block -Context $this.AzSKStorageAccount.Context -Force } } hidden [void] ArchiveBlob() { $this.ArchiveBlob("_"); } hidden [void] ArchiveBlob([string] $token) { try { $AzSKTemp = [Constants]::AzSKAppFolderPath + "\TempState"; if(-not (Test-Path "$AzSKTemp\ControlsBaseline")) { mkdir -Path "$AzSKTemp\ControlsBaseline" -ErrorAction Stop | Out-Null } $archiveName = $this.CAScanProgressSnapshotsContainerName + $token + (Get-Date).ToUniversalTime().ToString("yyyyMMddHHmmss") + ".json"; $masterFilePath = "$AzSKTemp\ControlsBaseline\$archiveName" $controlStateBlob = Get-AzureStorageBlob -Container $this.CAScanProgressSnapshotsContainerName -Context $this.AzSKStorageAccount.Context -Blob "$($this.BaselineResourceBlobName)" -ErrorAction SilentlyContinue if($null -ne $controlStateBlob) { Get-AzureStorageBlobContent -CloudBlob $controlStateBlob.ICloudBlob -Context $this.AzSKStorageAccount.Context -Destination $masterFilePath -Force Set-AzureStorageBlobContent -File $masterFilePath -Container $this.CAScanProgressSnapshotsContainerName -Blob "Archive/$archiveName" -BlobType Block -Context $this.AzSKStorageAccount.Context -Force } } catch { #eat exception as archive should not impact actual flow } } hidden [void] GetBaselineControlObject() { if($null -eq $this.BaselineControlObj) { # region Migration # AzSK TBR #[MigrationHelper]::TryMigration($this.SubscriptionContext, $this.InvocationContext); #endregion $AzSKTemp = [Constants]::AzSKAppFolderPath + "\TempState"; if(-not (Test-Path "$AzSKTemp\ControlsBaseline")) { mkdir -Path "$AzSKTemp\ControlsBaseline" -ErrorAction Stop | Out-Null } $masterFilePath = "$AzSKTemp\ControlsBaseline\$($this.BaselineResourceBlobName)" $controlStateBlob = Get-AzureStorageBlob -Container $this.CAScanProgressSnapshotsContainerName -Context $this.AzSKStorageAccount.Context -Blob "$($this.BaselineResourceBlobName)" -ErrorAction SilentlyContinue if($null -ne $controlStateBlob) { Get-AzureStorageBlobContent -CloudBlob $controlStateBlob.ICloudBlob -Context $this.AzSKStorageAccount.Context -Destination $masterFilePath -Force $this.BaselineControlObj = Get-ChildItem -Path $masterFilePath -Force | Get-Content | ConvertFrom-Json } } } [ActiveStatus] IsMasterListActive() { if($null -eq $this.AzSKStorageAccount -or $null -eq $this.AzSKStorageResourceBaselineContainer ) { $this.GetAzSKControlBaselineContainer(); } if($null -ne $this.ControlSettings.BaselineControls) { $this.GetBaselineControlObject(); $expiryInDays = [Int32]::Parse($this.ControlSettings.BaselineControls.ExpiryInDays); if($null -eq $this.BaselineControlObj) { return $this.ActiveStatus = [ActiveStatus]::No; } if($this.BaselineControlObj.CreatedDate.AddDays($expiryInDays) -lt [DateTime]::UtcNow) { $this.RemoveControlBaseline(); return $this.ActiveStatus = [ActiveStatus]::No; } return $this.ActiveStatus = [ActiveStatus]::Yes } else { return $this.ActiveStatus = [ActiveStatus]::No; } } [PSObject] GetResourceStatus([string] $resourceId) { $resourceValues = @(); $this.GetBaselineControlObject(); if($this.IsListAvailableAndActive()) { $idHash = [Helpers]::ComputeHash($resourceId) $resourceValue = $this.BaselineControlObj.ResourceMapTable[$idHash]; $resourceValues += $resourceValue; return $resourceValues; } return $null; } [PSObject] GetNonScannedResources() { $nonScannedResources = @(); $this.GetBaselineControlObject(); if($this.IsListAvailableAndActive()) { $nonScannedResources +=[BaselineResource[]] $this.BaselineControlObj.ResourceMapTable | Where-Object {$_.State -eq [ScanState]::INIT} return $nonScannedResources; } return $null; } [PSObject] GetAllListedResources() { $nonScannedResources = @(); $this.ArchiveBlob() $this.GetBaselineControlObject(); if($this.IsListAvailableAndActive()) { $nonScannedResources += $this.BaselineControlObj.ResourceMapTable return $nonScannedResources; } return $null; } [Bool] IsListAvailableAndActive() { if($null -ne $this.BaselineControlObj -and $this.ActiveStatus -eq [ActiveStatus]::Yes -and $null -ne $this.BaselineControlObj.ResourceMapTable) { return $true } else { return $false } } [PSObject] GetBaselineControlDetails() { return $this.ControlSettings.BaselineControls } } |