Framework/Core/SVT/Services/KeyVault.ps1
using namespace Microsoft.Azure.Commands.KeyVault.Models Set-StrictMode -Version Latest class KeyVault: SVTBase { hidden [PSVault] $ResourceObject; KeyVault([string] $subscriptionId, [string] $resourceGroupName, [string] $resourceName): Base($subscriptionId, $resourceGroupName, $resourceName) { $this.GetResourceObject(); } KeyVault([string] $subscriptionId, [SVTResource] $svtResource): Base($subscriptionId, $svtResource) { $this.GetResourceObject(); } hidden [PSVault] GetResourceObject() { if (-not $this.ResourceObject) { $this.ResourceObject = Get-AzureRmKeyVault -VaultName $this.ResourceContext.ResourceName ` -ResourceGroupName $this.ResourceContext.ResourceGroupName if(-not $this.ResourceObject) { throw ("Resource '{0}' not found under Resource Group '{1}'" -f ($this.ResourceContext.ResourceName), ($this.ResourceContext.ResourceGroupName)) } } return $this.ResourceObject; } hidden [ControlResult] CheckAdvancedAccessPolicies([ControlResult] $controlResult) { $accessPolicies = @{}; $accessPolicies.Add("Enable access to Azure Virtual Machines for deployment", $this.ResourceObject.EnabledForDeployment); $accessPolicies.Add("Enable access to Azure Resource Manager for template deployment", $this.ResourceObject.EnabledForTemplateDeployment); $accessPolicies.Add("Enable access to Azure Disk Encryption for volume encryption", $this.ResourceObject.EnabledForDiskEncryption); if($this.ResourceObject.EnabledForDeployment -and $this.ResourceObject.EnabledForDiskEncryption -and $this.ResourceObject.EnabledForTemplateDeployment) { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("All Advanced Access Policies are enabled - ["+ $this.ResourceContext.ResourceName +"]" , $accessPolicies)); } elseif($this.ResourceObject.EnabledForDeployment -or $this.ResourceObject.EnabledForDiskEncryption -or $this.ResourceObject.EnabledForTemplateDeployment) { $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new("Validate Advanced Access Policies - ["+ $this.ResourceContext.ResourceName +"]" , $accessPolicies)); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("All Advanced Access Policies are disabled - ["+ $this.ResourceContext.ResourceName +"]", $accessPolicies)); } return $controlResult; } hidden [ControlResult] CheckAccessPolicies([ControlResult] $controlResult) { $accessPolicies = $this.ResourceObject.AccessPolicies $allPermisionUsers = $accessPolicies | Where-Object { $_.PermissionsToKeys -eq "All" -and $_.PermissionsToSecrets -eq "All" -and $_.PermissionsToCertificates -eq "All" } if(($allPermisionUsers | Measure-Object).count -gt 0) { $controlResult.VerificationResult = [VerificationResult]::Failed; $controlResult.AddMessage([MessageData]::new("Applications/Users having full control to Key/Secret/Certificate - ["+ $this.ResourceContext.ResourceName +"]" , $allPermisionUsers)); } else { $controlResult.VerificationResult = [VerificationResult]::Verify; } $controlResult.AddMessage([MessageData]::new("Validate access policies and their assigned permissions to Key/Secret/Certificate - ["+ $this.ResourceContext.ResourceName +"]" , $accessPolicies)); return $controlResult; } hidden [ControlResult] CheckKeyHSMProtected([ControlResult] $controlResult) { try { $allKeys = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allKeys | Measure-Object).Count -gt 0) { $nonHsmKeys = @(); $allKeys | ForEach-Object { Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $nonHsmKeys += Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version | Where-Object { $_.Attributes.KeyType -ne $this.ControlSettings.KeyVault.KeyType }; } } if(($nonHsmKeys | Measure-Object).Count -eq 0) { $controlResult.AddMessage( [VerificationResult]::Passed, [MessageData]::new("All Keys, including previous versions, are protected by HSM for Key Vault - ["+ $this.ResourceContext.ResourceName +"]")); } else { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Following Keys, including previous versions, are not protected by HSM. Delete the key versions in order to comply." , ($nonHsmKeys | Select-Object Name, Version -ExpandProperty Attributes ))); } } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No Keys found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } return $controlResult; } hidden [ControlResult] CheckKeyMinimumOperations([ControlResult] $controlResult) { try { $allKeys = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allKeys| Measure-Object).Count -gt 0) { $keyDetails = @(); $allKeys | ForEach-Object { Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $key = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version #| Where-Object {$null -eq $_.Attributes.Expires } $keyDetails += $key #rename variable } } $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new("Verify the operations permitted using Key on - ["+ $this.ResourceContext.ResourceName +"]", ($keyDetails | Select-Object Name, Version, @{Label="Key Operations"; Expression={[system.string]::Join(", ",$_.Key.KeyOps)}} ) ) ); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No Keys found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } return $controlResult; } hidden [ControlResult] CheckAppAuthenticationCertificate([ControlResult] $controlResult) { try{ $outputList = @(); $appList = $this.GetAzureRmKeyVaultApplications() $appList | ForEach-Object { $credentials = Get-AzureRmADAppCredential -ApplicationId $_.ApplicationId $compliance = if (($credentials| Where-Object { $_.Type -eq $this.ControlSettings.KeyVault.ADAppCredentialTypePwd } | Measure-Object).Count -eq 0 ) { "Yes" } else { "No" } ; $output = New-Object System.Object $output | Add-Member -type NoteProperty -name AzureADAppName -Value $_.DisplayName $output | Add-Member -type NoteProperty -name ApplicationId -Value $_.ApplicationId $output | Add-Member -type NoteProperty -name CertificateCredentialCount -Value ($credentials | Where-Object { $_.Type -eq $this.ControlSettings.KeyVault.ADAppCredentialTypeCrt } | Measure-Object ).Count $output | Add-Member -type NoteProperty -name PasswordCredentialCount -Value ($credentials | Where-Object { $_.Type -eq $this.ControlSettings.KeyVault.ADAppCredentialTypePwd } | Measure-Object).Count $output | Add-Member -type NoteProperty -name Compliance -Value $compliance $outputList += $output; } if(($outputList| Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Compliance details of Azure Active Directory applications:", $outputList)); if (($outputList | Where-Object { ($_.Compliance -eq "No") } | Measure-Object ).Count -gt 0) { $controlResult.AddMessage([VerificationResult]::Failed , [MessageData]::new("Remove the password credentials from Azure AD Applications which are non-compliant.") ); } else { $controlResult.VerificationResult = [VerificationResult]::Passed } } else { $controlResult.AddMessage([VerificationResult]::Passed , [MessageData]::new("No Azure AD Applications have access to Key Vault.") ); } } catch { if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } return $controlResult; } hidden [ControlResult] CheckAppsSharingKayVault([ControlResult] $controlResult) { $appList = $this.GetAzureRmKeyVaultApplications() if( ($appList | Measure-Object ).Count -gt 1) { $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new("Validate that Azure AD Applications requires access to Key Vault. Total:" + ($appList | Measure-Object ).Count , $appList)); } elseif( ($appList | Measure-Object ).Count -eq 1) { $controlResult.AddMessage([VerificationResult]::Passed, "Only 1 Azure AD Application has access to Key Vault.", $appList); } else { $controlResult.AddMessage([VerificationResult]::Passed, "No Azure AD Applications have access to Key Vault."); } return $controlResult; } hidden [ControlResult] CheckKeyExpirationDate([ControlResult] $controlResult) { $IsKeysCompliant = $True $IsAccessDenied = $False try { $allKeys = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allKeys| Measure-Object).Count -gt 0) { $keysWithoutExpiry = @(); $keysWithExpiry = @(); $longDurationActiveKeys = @(); $allKeys | ForEach-Object { Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $key = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version #| Where-Object { $_.Attributes.Expires -eq $null } if($null -eq $key.Attributes.Expires) { $keysWithoutExpiry += $key } else { $keysWithExpiry += $key } } } if(($keysWithoutExpiry | Measure-Object ).Count -gt 0) { $IsKeysCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Keys, including previous versions, does not have expiry date. Set the expiry date in order to comply.", ($keysWithoutExpiry | Select-Object -Property Name, Version -ExpandProperty Attributes) )); } if(($keysWithExpiry | Measure-Object ).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Keys, including previous versions, have expiry date for Key Vault - ["+$this.ResourceContext.ResourceName+"]", ($keysWithExpiry | Select-Object -Property Name, Version -ExpandProperty Attributes ) )); $keysWithExpiry | ForEach-Object { $startDate = [DateTime] $_.Attributes.Created if($null -ne $_.Attributes.NotBefore) { $startDate = [DateTime] $_.Attributes.NotBefore; } if ((((([DateTime] $_.Attributes.Expires) - $startDate).TotalDays)/($this.ControlSettings.KeyVault.KeyRotationDuration_Days)) -gt 1) { $longDurationActiveKeys += $_ } } if (($longDurationActiveKeys| Measure-Object ).Count -gt 0) { $IsKeysCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Keys, including previous versions, are active for more than $($this.ControlSettings.KeyVault.KeyRotationDuration_Days) Days. Please delete/rotate the keys in order to comply.", ($longDurationActiveKeys | Select-Object -Property Name, Version -ExpandProperty Attributes ) )); } } } else { $controlResult.AddMessage( [MessageData]::new("No Keys found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { $IsKeysCompliant = $False $IsAccessDenied = $True if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } $IsSecretCompliant = $True try { $allSecrets = Get-AzureKeyVaultSecret -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allSecrets| Measure-Object).Count -gt 0) { $secretsWithoutExpiry = @(); $secretsWithExpiry = @(); $longDurationActiveSecrets = @(); $allSecrets | ForEach-Object { Get-AzureKeyVaultSecret -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $secret = Get-AzureKeyVaultSecret -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version #| Where-Object { $_.Attributes.Expires -eq $null } if($null -eq $secret.Attributes.Expires) { $secretsWithoutExpiry += $secret } else { $secretsWithExpiry += $secret } } } if(($secretsWithoutExpiry| Measure-Object ).Count -gt 0) { $IsSecretCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Secrets, including previous versions, does not have expiry date. Set the expiry date in order to comply.", ($secretsWithoutExpiry | Select-Object Name, Version -ExpandProperty Attributes ) )); } if(($secretsWithExpiry| Measure-Object ).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Secrets, including previous versions, have expiry date for Key Vault - ["+ $this.ResourceContext.ResourceName +"]", ( $secretsWithExpiry | Select-Object Name, Version -ExpandProperty Attributes ) )); $secretsWithExpiry | ForEach-Object { $startDate = [DateTime] $_.Attributes.Created if($null -ne $_.Attributes.NotBefore) { $startDate = [DateTime] $_.Attributes.NotBefore; } if ((((([DateTime] $_.Attributes.Expires) - $startDate).TotalDays)/($this.ControlSettings.KeyVault.SecretRotationDuration_Days)) -gt 1) { $longDurationActiveSecrets += $_ } } if (($longDurationActiveSecrets| Measure-Object ).Count -gt 0) { $IsSecretCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Secrets, including previous versions, are active for more than $($this.ControlSettings.KeyVault.SecretRotationDuration_Days) Days. Please delete/renew the secrets in order to comply.", ( $longDurationActiveSecrets | Select-Object Name, Version -ExpandProperty Attributes ) )); } } } else { $controlResult.AddMessage( [MessageData]::new("No Secrets found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { $IsSecretCompliant = $False $IsAccessDenied = $True if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Secrets.")); } else { throw $_ } } #Got exception becuase of acess denied, Keeping status Verify. if(-not $IsAccessDenied) { if($IsKeysCompliant -and $IsSecretCompliant) { $controlResult.VerificationResult = [VerificationResult]::Passed } else { $controlResult.VerificationResult = [VerificationResult]::Failed } } return $controlResult; } hidden [PSObject] GetAzureRmKeyVaultApplications () { $applicationList = @(); $this.ResourceObject.AccessPolicies | ForEach-Object { $svcPrincipal= Get-AzureRmADServicePrincipal -ObjectId $_.ObjectId if($svcPrincipal){ $application = Get-AzureRmADApplication -ApplicationId $svcPrincipal.ApplicationId if($application){ $applicationList += $application } } } return $applicationList; } } |