Framework/Core/SubscriptionSecurity/SecurityCenter.ps1
using namespace System.Management.Automation Set-StrictMode -Version Latest class SecurityCenter: AzSKRoot { [PSObject] $Policy = $null; [PSObject[]] $UniquePolicies = $null; [string] $Off = "Off"; [string] $On = "On"; [string] $ContactPhoneNumber; [string] $ContactEmail; [bool] $IsValidVersion; [bool] $IsLatestVersion; [string] $CurrentVersion; [string] $LatestVersion; SecurityCenter([string] $subscriptionId): Base($subscriptionId) { $this.LoadPolicies(); } hidden [void] LoadPolicies() { $this.Policy = $this.LoadServerConfigFile("SecurityCenter.json"); $azskRGName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName; $this.CurrentVersion = [Helpers]::GetResourceGroupTag($azskRGName, [Constants]::SecurityCenterConfigVersionTagName) if([string]::IsNullOrWhiteSpace($this.CurrentVersion)) { $this.CurrentVersion = "0.0.0" } $minSupportedVersion = [ConfigurationManager]::GetAzSKConfigData().AzSKASCMinReqdVersion $this.IsLatestVersion = $this.IsLatestVersionConfiguredOnSub($this.Policy.Version, [Constants]::SecurityCenterConfigVersionTagName); $this.IsValidVersion = $this.IsLatestVersionConfiguredOnSub($this.Policy.Version, [Constants]::SecurityCenterConfigVersionTagName) -or [System.Version]$minSupportedVersion -le [System.Version]$this.CurrentVersion ; $this.LatestVersion = $this.Policy.Version; } hidden [PSObject[]] GetUniquePolicies() { if(-not $this.UniquePolicies) { $this.UniquePolicies = @(); $allPolicies = @(); $allPolicies += [SecurityCenterHelper]::InvokeGetSecurityCenterRequest($this.SubscriptionContext.SubscriptionId, [SecurityCenterHelper]::PoliciesApi) if($allPolicies.Count -ne 0) { #Query to select only subscription level polices and other polices which are modified explicitly $this.UniquePolicies += $allPolicies | Where-Object { [Helpers]::CheckMember($_, "properties.policyLevel") -and ($_.properties.policyLevel -eq "Subscription" -or $_.properties.unique -eq $this.On ) }; if($this.UniquePolicies.Count -eq 0) { #no relevant policies found message here $this.PublishCustomMessage("No Subscription level or uniquely configured policies found in the Security Center", [MessageType]::Warning); } } else { #Error message here $this.PublishCustomMessage("Not able to get the Security Center policies", [MessageType]::Error); } } return $this.UniquePolicies; } [PSObject[]] GetMisconfiguredPolicies() { $policies = @(); $misConfiguredPolicies = @(); $policies += $this.GetUniquePolicies(); if($policies.Count -ne 0) { #If recommendations object is kept blank in Policy json, consider to check all properties to be 'On' #Check with get-member here #if(($this.Policy.properties.recommendations | Get-Member -MemberType Properties | Measure-Object).Count -eq 0) #{ #Pick first object and add all recommendation property to policy json object $samplePolicy = $policies | Select-Object -First 1 if([Helpers]::CheckMember($samplePolicy, "properties.recommendations")) { $samplePolicy.properties.recommendations | Get-Member -MemberType Properties | ForEach-Object { $property = $_.Name; $value = $this.On; #retain the value from the configured policy on subscription if([Helpers]::CheckMember($samplePolicy, "properties.recommendations.$property")) { $value = $($samplePolicy.properties.recommendations.$property); } #override the value as per the expected policy from server if([Helpers]::CheckMember($this.Policy, "properties.recommendations.$property")) { $value = $($this.Policy.properties.recommendations.$property); } Add-Member -InputObject $this.Policy.properties.recommendations -MemberType NoteProperty -Name $_.Name -Value $value -Force } } #} $policies | ForEach-Object { $isMisconfigured = $true; if([Helpers]::CompareObject($this.Policy.properties, $_.properties)) { # Check for email address and phone number props if([Helpers]::CheckMember($_, "properties.securityContactConfiguration.securityContactEmails") -and -not [string]::IsNullOrEmpty($_.properties.securityContactConfiguration.securityContactEmails) -and [Helpers]::CheckMember($_, "properties.securityContactConfiguration.securityContactPhone") -and -not [string]::IsNullOrEmpty($_.properties.securityContactConfiguration.securityContactPhone)) { #Capture the contact phone number and emailid. This infomration is being captured as part of the metadata for the subscription $this.ContactPhoneNumber = $_.properties.securityContactConfiguration.securityContactPhone $this.ContactEmail = $_.properties.securityContactConfiguration.securityContactEmails $isMisconfigured = $false } } if($isMisconfigured) { $misConfiguredPolicies += $_; } }; } return $misConfiguredPolicies; } [MessageData[]] SetPolicies() { return $this.SetPolicies($null,$null); } [MessageData[]] SetPolicies([string] $securityContactEmails, [string] $securityPhoneNumber) { [MessageData[]] $messages = @(); #setting the tag at AzSKRG $azskRGName = [ConfigurationManager]::GetAzSKConfigData().AzSKRGName; $policiesToProcess = @(); $misConfiguredPolicies = $this.GetMisconfiguredPolicies(); if($misConfiguredPolicies.Count -ne 0) { $messages += [MessageData]::new("Security center policies must be configured with settings mentioned below:", $this.Policy.properties); $messageText = "Found Security Center policies which are not correctly configured. Total misconfigured policies: $($misConfiguredPolicies.Count)"; $messages += [MessageData]::new($messageText); $this.PublishCustomMessage($messageText); # Check if subscription level policies are misconfigured if(($misConfiguredPolicies | Where-Object { $_.properties.policyLevel -eq "Subscription" } | Measure-Object).Count -eq 0) { $policiesToProcess += $this.GetUniquePolicies() | Where-Object { $_.properties.policyLevel -eq "Subscription" }; } $policiesToProcess += $misConfiguredPolicies; #$messages += $this.ModifyPolicies($misConfiguredPolicies, $securityContactEmails, $securityPhoneNumber) } else { $this.PublishCustomMessage("All Security Center policies are correctly configured. "); $policiesToProcess += $this.GetUniquePolicies(); } $messages += $this.ModifyPolicies($policiesToProcess, $securityContactEmails, $securityPhoneNumber) [Helpers]::SetResourceGroupTags($azskRGName,@{[Constants]::SecurityCenterConfigVersionTagName=$this.Policy.Version}, $false) return $messages; } [MessageData[]] ModifyPolicies([PSObject[]] $policies, [string] $securityContactEmails, [string] $securityPhoneNumber) { [MessageData[]] $messages = @(); if($policies.Count -ne 0) { #Keeping a copy of policy email addresses. The original policy object is going to update while merging email addresses $policyEmails = @(); if([Helpers]::CheckMember($this.Policy, "properties.securityContactConfiguration.securityContactEmails")) { $policyEmails += $this.Policy.properties.securityContactConfiguration.securityContactEmails; } $updateObject = $this.Policy | Select-Object -Property properties $policies | Where-Object { $_.properties.policyLevel -eq "Subscription" } | ForEach-Object { #Merge email addresses $allEmails = @(); if(-not [string]::IsNullOrWhiteSpace($securityContactEmails)) { # User provided email addresses $allEmails += $this.ConvertToStringArray($securityContactEmails); } # Add email addresses from policy files $allEmails += $policyEmails; # Ignore existing email addresses if user has provided any email addresses if($allEmails.Count -eq 0 -and [Helpers]::CheckMember($_, "properties.securityContactConfiguration.securityContactEmails") -and $_.properties.securityContactConfiguration.securityContactEmails.Count -ne 0) { $allEmails += $_.properties.securityContactConfiguration.securityContactEmails | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }; } $updateObject.properties.securityContactConfiguration.securityContactEmails = [array] ($allEmails | Select-Object -Unique) $policyName = ""; if([Helpers]::CheckMember($_, "name")) { $policyName = "[$($_.name)]"; } $exceptionMessage = ""; # Check if securityContactEmails is still null, then set it to blank array if(-not $updateObject.properties.securityContactConfiguration.securityContactEmails) { $exceptionMessage += "'SecurityContactEmails' is required to configure ASC. Please set up Security Center policy with cmdlet Set-AzSKAzureSecurityCenterPolicies. Run 'Get-Help Set-AzSKAzureSecurityCenterPolicies -full' for more help.`r`n"; $updateObject.properties.securityContactConfiguration.securityContactEmails = @(""); } $isPhoneRequired = $true; $existingPhoneNumber = ""; if([Helpers]::CheckMember($_, "properties.securityContactConfiguration.securityContactPhone")) { if(-not [string]::IsNullOrWhiteSpace($_.properties.securityContactConfiguration.securityContactPhone)) { $isPhoneRequired = $false; $existingPhoneNumber = $_.properties.securityContactConfiguration.securityContactPhone; } } if($isPhoneRequired -and [string]::IsNullOrWhiteSpace($securityPhoneNumber)) { $exceptionMessage += "'SecurityPhoneNumber' is required to configure ASC. Please set up Security Center policy with cmdlet Set-AzSKAzureSecurityCenterPolicies. Run 'Get-Help Set-AzSKAzureSecurityCenterPolicies -full' for more help.`r`n"; } if(-not [string]::IsNullOrWhiteSpace($exceptionMessage)) { throw ([SuppressedException]::new($exceptionMessage, [SuppressedExceptionType]::Generic)) } # Set phone number if(-not [string]::IsNullOrWhiteSpace($securityPhoneNumber)) { if(-not (Get-Member -InputObject $updateObject.properties.securityContactConfiguration -Name "securityContactPhone")) { Add-Member -InputObject $updateObject.properties.securityContactConfiguration -MemberType NoteProperty -Name "securityContactPhone" -Value $securityPhoneNumber } } elseif(-not [string]::IsNullOrWhiteSpace($existingPhoneNumber)) { if(-not (Get-Member -InputObject $updateObject.properties.securityContactConfiguration -Name "securityContactPhone")) { Add-Member -InputObject $updateObject.properties.securityContactConfiguration -MemberType NoteProperty -Name "securityContactPhone" -Value $existingPhoneNumber } else { $updateObject.properties.securityContactConfiguration.securityContactPhone = $existingPhoneNumber; } } $messages += [MessageData]::new("Updating [$($_.properties.policyLevel)] level Security Center policy $policyName...", $_); $response = [SecurityCenterHelper]::InvokePutSecurityCenterRequest($_.id, $updateObject); [MessageData] $resultMessage = $null if(($response | Measure-Object).Count -ne 0) { $resultMessage = [MessageData]::new("Successfully updated [$($_.properties.policyLevel)] level Security Center policy $policyName", [MessageType]::Update); } else { $resultMessage = [MessageData]::new("Not able to update [$($_.properties.policyLevel)] level Security Center policy $policyName", [MessageType]::Error); } $messages += $resultMessage; $this.PublishCustomMessage($resultMessage); } # Setting up/Load the original values $this.Policy.properties.securityContactConfiguration.securityContactEmails = $policyEmails; if((Get-Member -InputObject $this.Policy.properties.securityContactConfiguration -Name "securityContactPhone")) { $this.Policy.properties.securityContactConfiguration.securityContactPhone = ""; } $nonDefaultPolicies = @(); $nonDefaultPolicies += $policies | Where-Object { $_.properties.unique -eq $this.On } | Select-Object -Property id, name if($nonDefaultPolicies.Count -ne 0) { $messageText = " `r`nFound policies at resource group level in overridden state. These policies have to be manually corrected. Total: $($nonDefaultPolicies.Count)"; $messages += [MessageData]::new($messageText + "`r`nBelow are the policies that have to be manually corrected: ", $nonDefaultPolicies); $this.PublishCustomMessage($messageText); } } return $messages; } } |