Repository: ratify-project/ratify
Pull request #1874 (2024-10-17 19:01:54)

feat: support enabled status for kmp keys/certs
# Description

## What this PR does / why we need it:

This PR adds an `Enabled` field to the KMPMapyKey struct which allows the provider to store the status of certificates and keys being pulled from the provider into Ratify's KMP. Which is needed to support multiple versions of certificates and keys being stored in the KMP. See discussions and the design doc, [here](https://github.com/ratify-project/ratify/pull/1831), for more details.

## Which issue(s) this PR fixes *(optional, using `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when the PR gets merged)*:

Fixes:
- #1751 
- #1831 

## Type of change

Please delete options that are not relevant.

- [x] New feature (non-breaking change which adds functionality)

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Please also list any relevant details for your test configuration

- [x] Deployed to AKS for Manual testing

```bash
> k describe keymanagementprovider akv
Name:         akv
Namespace:    
Labels:       <none>
Annotations:  <none>
API Version:  config.ratify.deislabs.io/v1beta1
Kind:         KeyManagementProvider
Metadata:
  Creation Timestamp:  2024-10-17T14:56:38Z
  Generation:          1
  Resource Version:    5614657
  UID:                 dbb53be3-1dab-4ad5-be79-f565c3269697
Spec:
  Parameters:
    Certificates:
      Name:     ratify
      Version:  5fe70628ce34425ca1396e88c509d355
      Name:     ratify
      Version:  0ff373a9259c4578a247cfd7861a8805
    Client ID:  38f733d4-05f9-41b3-b8e1-896376ba52d6
    Keys:
      Name:          issue1751
    Tenant ID:       b4c72be8-cae1-4584-be77-62b1e94ad0dc
    Vault URI:       https://example.vault.azure.net/
  Refresh Interval:  1m
  Type:              azurekeyvault
Status:
  Issuccess:        true
  Lastfetchedtime:  2024-10-17T18:56:50Z
  Properties:
    Certificates:
      Enabled:         false
      Last Refreshed:  2024-10-17T18:56:50Z
      Name:            ratify
      Version:         5fe70628ce34425ca1396e88c509d355
      Enabled:         true
      Last Refreshed:  2024-10-17T18:56:50Z
      Name:            ratify
      Version:         0ff373a9259c4578a247cfd7861a8805
    Keys:
      Enabled:         false
      Last Refreshed:  2024-10-17T18:56:50Z
      Name:            issue1751
      Version:         
Events:                <none>
```
```
> k logs deployment/ratify -n gatekeeper-system
time=2024-10-17T18:56:50.402944925Z level=info msg=reconciling cluster key management provider 'akv'
time=2024-10-17T18:56:50.403102625Z level=debug msg=vaultURI https://example.vault.azure.net/ component-type=keyManagementProvider go.version=go1.22.8
time=2024-10-17T18:56:50.495968077Z level=debug msg=fetching secret from key vault, certName ratify,  keyvault https://example.vault.azure.net/ component-type=keyManagementProvider go.version=go1.22.8
time=2024-10-17T18:56:50.525906097Z level=debug msg=fetching secret from key vault, certName ratify,  keyvault https://example.vault.azure.net/ component-type=keyManagementProvider go.version=go1.22.8
time=2024-10-17T18:56:50.578216358Z level=debug msg=azurekeyvault certprovider getCertsFromSecretBundle: 1 certificates parsed, Certificate 'ratify', version '0ff373a9259c4578a247cfd7861a8805' component-type=keyManagementProvider go.version=go1.22.8
time=2024-10-17T18:56:50.578267858Z level=debug msg=fetching key from key vault, keyName issue1751,  keyvault https://example.vault.azure.net/ component-type=keyManagementProvider go.version=go1.22.8
time=2024-10-17T18:56:50.595643612Z level=info msg=2 certificate(s) & 1 key(s) fetched for key management provider akv
time=2024-10-17T18:56:50.595670912Z level=info msg=Reconciled KeyManagementProviderintervalDuration1m0s
```

Unit test currently do not fully test the `GetCertificate` and `GetKeys` methods of KMP providers, but can be added if desired to the PR or a follow up PR. Also waiting to see if the `azidentiy` work will be done before adding unit tests because that will likely change the client that will need to be mocked to fully tests the methods.

# Checklist:

- [ ] Does the affected code have corresponding tests?
- [ ] Are the changes documented, not just with inline documentation, but also with conceptual documentation such as an overview of a new feature, or task-based documentation like a tutorial? Consider if this change should be announced on your project blog.
- [ ] Does this introduce breaking changes that would require an announcement or bumping the major version?
- [x] Do all new files have appropriate license header?

# Post Merge Requirements
- [ ] MAINTAINERS: manually trigger the "Publish Package" workflow after merging any PR that indicates `Helm Chart Change`


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/ratify-project/ratify/pull/1874?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project) Report
Attention: Patch coverage is `13.33333%` with `39 lines` in your changes missing coverage. Please review.

| [Files with missing lines](https://app.codecov.io/gh/ratify-project/ratify/pull/1874?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project) | Patch % | Lines |
|---|---|---|
| [...kg/keymanagementprovider/azurekeyvault/provider.go](https://app.codecov.io/gh/ratify-project/ratify/pull/1874?src=pr&el=tree&filepath=pkg%2Fkeymanagementprovider%2Fazurekeyvault%2Fprovider.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project#diff-cGtnL2tleW1hbmFnZW1lbnRwcm92aWRlci9henVyZWtleXZhdWx0L3Byb3ZpZGVyLmdv) | 13.33% | [39 Missing :warning: ](https://app.codecov.io/gh/ratify-project/ratify/pull/1874?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project) |

| [Files with missing lines](https://app.codecov.io/gh/ratify-project/ratify/pull/1874?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project) | Coverage Δ | |
|---|---|---|
| [pkg/keymanagementprovider/keymanagementprovider.go](https://app.codecov.io/gh/ratify-project/ratify/pull/1874?src=pr&el=tree&filepath=pkg%2Fkeymanagementprovider%2Fkeymanagementprovider.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project#diff-cGtnL2tleW1hbmFnZW1lbnRwcm92aWRlci9rZXltYW5hZ2VtZW50cHJvdmlkZXIuZ28=) | `92.94% <ø> (-1.27%)` | :arrow_down: |
| [...kg/keymanagementprovider/azurekeyvault/provider.go](https://app.codecov.io/gh/ratify-project/ratify/pull/1874?src=pr&el=tree&filepath=pkg%2Fkeymanagementprovider%2Fazurekeyvault%2Fprovider.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project#diff-cGtnL2tleW1hbmFnZW1lbnRwcm92aWRlci9henVyZWtleXZhdWx0L3Byb3ZpZGVyLmdv) | `66.40% <13.33%> (-8.60%)` | :arrow_down: |

... and [131 files with indirect coverage changes](https://app.codecov.io/gh/ratify-project/ratify/pull/1874/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ratify-project)

duffney: @akashsinghal @binbin-li @junczhu did you notice the changes I pushed that adds an interface to the azkv provider to improve test coverage? Lmk if you've got any suggestion on those changes. 
binbin-li: > @akashsinghal @binbin-li @junczhu did you notice the changes I pushed that adds an interface to the azkv provider to improve test coverage? Lmk if you've got any suggestion on those changes.

I like the refactoring, it definitely makes the unit test easier.
binbin-li: lgtm once all nitpick comments resolved.
duffney: > lgtm once all nitpick comments resolved.

@binbin-li, I think I got all the nits implemented. Please lmk if I missed any.

Patch:
diff --git a/pkg/controllers/clusterresource/keymanagementprovider_controller.go b/pkg/controllers/clusterresource/keymanagementprovider_controller.go
index afb53a9..df454b3 100644
--- a/pkg/controllers/clusterresource/keymanagementprovider_controller.go
+++ b/pkg/controllers/clusterresource/keymanagementprovider_controller.go
@@ -80,7 +80,7 @@ func (r *KeyManagementProviderReconciler) ReconcileWithType(ctx context.Context,
 		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
 	}
 
-	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
+	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type, resource)
 	if err != nil {
 		kmpErr := re.ErrorCodeKeyManagementProviderFailure.WithError(err).WithDetail("Failed to create key management provider from CR")
 
diff --git a/pkg/controllers/namespaceresource/keymanagementprovider_controller.go b/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
index 2bbdd0b..dcba257 100644
--- a/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
+++ b/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
@@ -79,7 +79,7 @@ func (r *KeyManagementProviderReconciler) ReconcileWithType(ctx context.Context,
 		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
 	}
 
-	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
+	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type, resource)
 	if err != nil {
 		kmpErr := re.ErrorCodeKeyManagementProviderFailure.WithError(err).WithDetail("Failed to create key management provider from CR")
 
diff --git a/pkg/controllers/utils/kmp.go b/pkg/controllers/utils/kmp.go
index a2085cf..8bda6a6 100644
--- a/pkg/controllers/utils/kmp.go
+++ b/pkg/controllers/utils/kmp.go
@@ -26,8 +26,8 @@ import (
 )
 
 // SpecToKeyManagementProvider creates KeyManagementProvider from  KeyManagementProviderSpec config
-func SpecToKeyManagementProvider(raw []byte, keyManagamentSystemName string) (kmp.KeyManagementProvider, error) {
-	kmProviderConfig, err := rawToKeyManagementProviderConfig(raw, keyManagamentSystemName)
+func SpecToKeyManagementProvider(raw []byte, keyManagamentSystemName, resource string) (kmp.KeyManagementProvider, error) {
+	kmProviderConfig, err := rawToKeyManagementProviderConfig(raw, keyManagamentSystemName, resource)
 	if err != nil {
 		return nil, err
 	}
@@ -42,7 +42,7 @@ func SpecToKeyManagementProvider(raw []byte, keyManagamentSystemName string) (km
 }
 
 // rawToKeyManagementProviderConfig converts raw json to KeyManagementProviderConfig
-func rawToKeyManagementProviderConfig(raw []byte, keyManagamentSystemName string) (config.KeyManagementProviderConfig, error) {
+func rawToKeyManagementProviderConfig(raw []byte, keyManagamentSystemName, resource string) (config.KeyManagementProviderConfig, error) {
 	pluginConfig := config.KeyManagementProviderConfig{}
 
 	if string(raw) == "" {
@@ -53,6 +53,7 @@ func rawToKeyManagementProviderConfig(raw []byte, keyManagamentSystemName string
 	}
 
 	pluginConfig[types.Type] = keyManagamentSystemName
+	pluginConfig[types.Resource] = resource
 
 	return pluginConfig, nil
 }
diff --git a/pkg/keymanagementprovider/azurekeyvault/provider.go b/pkg/keymanagementprovider/azurekeyvault/provider.go
index da885af..22c3fba 100644
--- a/pkg/keymanagementprovider/azurekeyvault/provider.go
+++ b/pkg/keymanagementprovider/azurekeyvault/provider.go
@@ -24,7 +24,9 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"encoding/pem"
+	"errors"
 	"fmt"
+	"strconv"
 	"strings"
 	"time"
 
@@ -40,6 +42,7 @@ import (
 	"golang.org/x/crypto/pkcs12"
 
 	kv "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
+	"github.com/Azure/go-autorest/autorest"
 	"github.com/Azure/go-autorest/autorest/azure"
 )
 
@@ -59,6 +62,7 @@ type AKVKeyManagementProviderConfig struct {
 	TenantID     string                `json:"tenantID"`
 	ClientID     string                `json:"clientID"`
 	CloudName    string                `json:"cloudName,omitempty"`
+	Resource     string                `json:"resource,omitempty"`
 	Certificates []types.KeyVaultValue `json:"certificates,omitempty"`
 	Keys         []types.KeyVaultValue `json:"keys,omitempty"`
 }
@@ -69,13 +73,44 @@ type akvKMProvider struct {
 	tenantID     string
 	clientID     string
 	cloudName    string
+	resource     string
 	certificates []types.KeyVaultValue
 	keys         []types.KeyVaultValue
 	cloudEnv     *azure.Environment
-	kvClient     *kv.BaseClient
+	kvClient     kvClient
 }
+
 type akvKMProviderFactory struct{}
 
+// kvClient is an interface to interact with the keyvault client used for mocking purposes
+type kvClient interface {
+	// GetCertificate retrieves a certificate from the keyvault
+	GetCertificate(ctx context.Context, vaultBaseURL string, certificateName string, certificateVersion string) (kv.CertificateBundle, error)
+	// GetKey retrieves a key from the keyvault
+	GetKey(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string) (kv.KeyBundle, error)
+	// GetSecret retrieves a secret from the keyvault
+	GetSecret(ctx context.Context, vaultBaseURL string, secretName string, secretVersion string) (kv.SecretBundle, error)
+}
+
+type kvClientImpl struct {
+	kv.BaseClient
+}
+
+// GetCertificate retrieves a certificate from the keyvault
+func (c *kvClientImpl) GetCertificate(ctx context.Context, vaultBaseURL string, certificateName string, certificateVersion string) (kv.CertificateBundle, error) {
+	return c.BaseClient.GetCertificate(ctx, vaultBaseURL, certificateName, certificateVersion)
+}
+
+// GetKey retrieves a key from the keyvault
+func (c *kvClientImpl) GetKey(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string) (kv.KeyBundle, error) {
+	return c.BaseClient.GetKey(ctx, vaultBaseURL, keyName, keyVersion)
+}
+
+// GetSecret retrieves a secret from the keyvault
+func (c *kvClientImpl) GetSecret(ctx context.Context, vaultBaseURL string, secretName string, secretVersion string) (kv.SecretBundle, error) {
+	return c.BaseClient.GetSecret(ctx, vaultBaseURL, secretName, secretVersion)
+}
+
 // initKVClient is a function to initialize the keyvault client
 // used for mocking purposes
 var initKVClient = initializeKvClient
@@ -116,6 +151,7 @@ func (f *akvKMProviderFactory) Create(_ string, keyManagementProviderConfig conf
 		certificates: conf.Certificates,
 		keys:         conf.Keys,
 		cloudEnv:     azureCloudEnv,
+		resource:     conf.Resource,
 	}
 	if err := provider.validate(); err != nil {
 		return nil, err
@@ -127,7 +163,7 @@ func (f *akvKMProviderFactory) Create(_ string, keyManagementProviderConfig conf
 	if err != nil {
 		return nil, re.ErrorCodePluginInitFailure.NewError(re.KeyManagementProvider, ProviderName, re.AKVLink, err, "failed to create keyvault client", re.HideStackTrace)
 	}
-	provider.kvClient = kvClient
+	provider.kvClient = &kvClientImpl{*kvClient}
 
 	return provider, nil
 }
@@ -140,25 +176,42 @@ func (s *akvKMProvider) GetCertificates(ctx context.Context) (map[keymanagementp
 	for _, keyVaultCert := range s.certificates {
 		logger.GetLogger(ctx, logOpt).Debugf("fetching secret from key vault, certName %v,  keyvault %v", keyVaultCert.Name, s.vaultURI)
 
-		// fetch the object from Key Vault
-		// GetSecret is required so we can fetch the entire cert chain. See issue https://github.com/ratify-project/ratify/issues/695 for details
 		startTime := time.Now()
+
+		// GetSecret is required so we can fetch the entire cert chain. See issue https://github.com/ratify-project/ratify/issues/695 for details
 		secretBundle, err := s.kvClient.GetSecret(ctx, s.vaultURI, keyVaultCert.Name, keyVaultCert.Version)
 		if err != nil {
+			if isSecretDisabledError(err) {
+				// if secret is disabled, get the version of the certificate for status
+				certBundle, err := s.kvClient.GetCertificate(ctx, s.vaultURI, keyVaultCert.Name, keyVaultCert.Version)
+				if err != nil {
+					return nil, nil, fmt.Errorf("failed to get certificate objectName:%s, objectVersion:%s, error: %w", keyVaultCert.Name, keyVaultCert.Version, err)
+				}
+				keyVaultCert.Version = getObjectVersion(*certBundle.Kid)
+				isEnabled := *certBundle.Attributes.Enabled
+				lastRefreshed := startTime.Format(time.RFC3339)
+				certProperty := getStatusProperty(keyVaultCert.Name, keyVaultCert.Version, lastRefreshed, isEnabled)
+				certsStatus = append(certsStatus, certProperty)
+				mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: keyVaultCert.Version, Enabled: isEnabled}
+				keymanagementprovider.DeleteCertificateFromMap(s.resource, mapKey)
+				continue
+			}
+
 			return nil, nil, fmt.Errorf("failed to get secret objectName:%s, objectVersion:%s, error: %w", keyVaultCert.Name, keyVaultCert.Version, err)
 		}
 
-		certResult, certProperty, err := getCertsFromSecretBundle(ctx, secretBundle, keyVaultCert.Name)
+		isEnabled := *secretBundle.Attributes.Enabled
+
+		certResult, certProperty, err := getCertsFromSecretBundle(ctx, secretBundle, keyVaultCert.Name, isEnabled)
 		if err != nil {
 			return nil, nil, fmt.Errorf("failed to get certificates from secret bundle:%w", err)
 		}
 
 		metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultCert.Name)
 		certsStatus = append(certsStatus, certProperty...)
-		certMapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: keyVaultCert.Version}
+		certMapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: keyVaultCert.Version, Enabled: isEnabled}
 		certsMap[certMapKey] = certResult
 	}
-
 	return certsMap, getStatusMap(certsStatus, types.CertificatesStatus), nil
 }
 
@@ -177,17 +230,27 @@ func (s *akvKMProvider) GetKeys(ctx context.Context) (map[keymanagementprovider.
 			return nil, nil, fmt.Errorf("failed to get key objectName:%s, objectVersion:%s, error: %w", keyVaultKey.Name, keyVaultKey.Version, err)
 		}
 
-		if keyBundle.Attributes != nil && keyBundle.Attributes.Enabled != nil && !*keyBundle.Attributes.Enabled {
-			return nil, nil, fmt.Errorf("key %s version %s is disabled. please re-enable in azure key vault or remove reference to this key", keyVaultKey.Name, keyVaultKey.Version)
+		isEnabled := *keyBundle.Attributes.Enabled
+		// if version is set as "" in the config, use the version from the key bundle
+		keyVaultKey.Version = getObjectVersion(*keyBundle.Key.Kid)
+
+		if !isEnabled {
+			startTime := time.Now()
+			lastRefreshed := startTime.Format(time.RFC3339)
+			properties := getStatusProperty(keyVaultKey.Name, keyVaultKey.Version, lastRefreshed, isEnabled)
+			keysStatus = append(keysStatus, properties)
+			mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: keyVaultKey.Version, Enabled: isEnabled}
+			keymanagementprovider.DeleteKeyFromMap(s.resource, mapKey)
+			continue
 		}
 
 		publicKey, err := getKeyFromKeyBundle(keyBundle)
 		if err != nil {
 			return nil, nil, fmt.Errorf("failed to get key from key bundle:%w", err)
 		}
-		keysMap[keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: keyVaultKey.Version}] = publicKey
+		keysMap[keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: keyVaultKey.Version, Enabled: isEnabled}] = publicKey
 		metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultKey.Name)
-		properties := getStatusProperty(keyVaultKey.Name, keyVaultKey.Version, time.Now().Format(time.RFC3339))
+		properties := getStatusProperty(keyVaultKey.Name, keyVaultKey.Version, time.Now().Format(time.RFC3339), isEnabled)
 		keysStatus = append(keysStatus, properties)
 	}
 
@@ -205,11 +268,12 @@ func getStatusMap(statusMap []map[string]string, contentType string) keymanageme
 	return status
 }
 
-// return a status object that consist of the cert/key name, version and last refreshed time
-func getStatusProperty(name, version, lastRefreshed string) map[string]string {
+// return a status object that consist of the cert/key name, version, enabled and last refreshed time
+func getStatusProperty(name, version, lastRefreshed string, enabled bool) map[string]string {
 	properties := map[string]string{}
 	properties[types.StatusName] = name
 	properties[types.StatusVersion] = version
+	properties[types.StatusEnabled] = strconv.FormatBool(enabled)
 	properties[types.StatusLastRefreshed] = lastRefreshed
 	return properties
 }
@@ -230,7 +294,7 @@ func initializeKvClient(ctx context.Context, keyVaultEndpoint, tenantID, clientI
 	kvClient := kv.New()
 	kvEndpoint := strings.TrimSuffix(keyVaultEndpoint, "/")
 
-	err := kvClient.AddToUserAgent(userAgent)
+	err := kvClient.Client.AddToUserAgent(userAgent)
 	if err != nil {
 		return nil, re.ErrorCodeConfigInvalid.WithDetail("Failed to add user agent to keyvault client.").WithRemediation(re.AKVLink).WithError(err)
 	}
@@ -244,7 +308,7 @@ func initializeKvClient(ctx context.Context, keyVaultEndpoint, tenantID, clientI
 
 // Parse the secret bundle and return an array of certificates
 // In a certificate chain scenario, all certificates from root to leaf will be returned
-func getCertsFromSecretBundle(ctx context.Context, secretBundle kv.SecretBundle, certName string) ([]*x509.Certificate, []map[string]string, error) {
+func getCertsFromSecretBundle(ctx context.Context, secretBundle kv.SecretBundle, certName string, enabled bool) ([]*x509.Certificate, []map[string]string, error) {
 	if secretBundle.ContentType == nil || secretBundle.Value == nil || secretBundle.ID == nil {
 		return nil, nil, re.ErrorCodeCertInvalid.NewError(re.KeyManagementProvider, ProviderName, re.EmptyLink, nil, "found invalid secret bundle for certificate  %s, contentType, value, and id must not be nil", re.HideStackTrace)
 	}
@@ -297,7 +361,7 @@ func getCertsFromSecretBundle(ctx context.Context, secretBundle kv.SecretBundle,
 			}
 			for _, cert := range decodedCerts {
 				results = append(results, cert)
-				certProperty := getStatusProperty(certName, version, lastRefreshed)
+				certProperty := getStatusProperty(certName, version, lastRefreshed, enabled)
 				certsStatus = append(certsStatus, certProperty)
 			}
 		default:
@@ -352,6 +416,19 @@ func getObjectVersion(id string) string {
 	return splitID[len(splitID)-1]
 }
 
+func isSecretDisabledError(err error) bool {
+	var de autorest.DetailedError
+	if errors.As(err, &de) {
+		var re *azure.RequestError
+		if errors.As(de.Original, &re) {
+			if re.ServiceError.Code == "SecretDisabled" {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 // validate checks vaultURI, tenantID, clientID are set and all certificates/keys have a name
 func (s *akvKMProvider) validate() error {
 	if s.vaultURI == "" {
diff --git a/pkg/keymanagementprovider/azurekeyvault/types/types.go b/pkg/keymanagementprovider/azurekeyvault/types/types.go
index 5cde595..cae8607 100644
--- a/pkg/keymanagementprovider/azurekeyvault/types/types.go
+++ b/pkg/keymanagementprovider/azurekeyvault/types/types.go
@@ -25,6 +25,8 @@ const (
 	StatusName = "Name"
 	// Certificate version string for the certificate status property
 	StatusVersion = "Version"
+	// Enabled string for the certificate status property
+	StatusEnabled = "True"
 	// Last refreshed string for the certificate status property
 	StatusLastRefreshed = "LastRefreshed"
 )
diff --git a/pkg/keymanagementprovider/keymanagementprovider.go b/pkg/keymanagementprovider/keymanagementprovider.go
index 584f508..8a656b6 100644
--- a/pkg/keymanagementprovider/keymanagementprovider.go
+++ b/pkg/keymanagementprovider/keymanagementprovider.go
@@ -41,6 +41,7 @@ type KeyManagementProviderStatus map[string]interface{}
 type KMPMapKey struct {
 	Name    string
 	Version string
+	Enabled bool
 }
 
 type PublicKey struct {
@@ -156,6 +157,30 @@ func DeleteResourceFromMap(resource string) {
 	keyErrMap.Delete(resource)
 }
 
+// DeleteKeyFromMap deletes the keys from the map
+func DeleteCertificateFromMap(resource string, certKey KMPMapKey) {
+	if certs, ok := certificatesMap.Load(resource); ok {
+		for k := range certs.(map[KMPMapKey][]*x509.Certificate) {
+			if k.Name == certKey.Name && k.Version == certKey.Version {
+				delete(certs.(map[KMPMapKey][]*x509.Certificate), k)
+				continue
+			}
+		}
+	}
+}
+
+// DeleteKeyFromMap deletes the keys from the map
+func DeleteKeyFromMap(resource string, key KMPMapKey) {
+	if keys, ok := keyMap.Load(resource); ok {
+		for k := range keys.(map[KMPMapKey]PublicKey) {
+			if k.Name == key.Name && k.Version == key.Version {
+				delete(keys.(map[KMPMapKey]PublicKey), k)
+				continue
+			}
+		}
+	}
+}
+
 // FlattenKMPMap flattens the map of certificates fetched for a single key management provider resource and returns a single array
 func FlattenKMPMap(certMap map[KMPMapKey][]*x509.Certificate) []*x509.Certificate {
 	var items []*x509.Certificate
diff --git a/pkg/keymanagementprovider/types/types.go b/pkg/keymanagementprovider/types/types.go
index c15354d..2e67b69 100644
--- a/pkg/keymanagementprovider/types/types.go
+++ b/pkg/keymanagementprovider/types/types.go
@@ -19,5 +19,6 @@ const (
 	SpecVersion string = "0.1.0"
 	Version     string = "version"
 	Type        string = "type"
+	Resource    string = "resource"
 	Source      string = "source"
 )


Test patch:
diff --git a/pkg/controllers/utils/kmp_test.go b/pkg/controllers/utils/kmp_test.go
index e3223e4..bc3790f 100644
--- a/pkg/controllers/utils/kmp_test.go
+++ b/pkg/controllers/utils/kmp_test.go
@@ -26,6 +26,7 @@ func TestSpecToKeyManagementProviderProvider(t *testing.T) {
 		name      string
 		raw       []byte
 		kmpType   string
+		resource  string
 		expectErr bool
 	}{
 		{
@@ -36,19 +37,21 @@ func TestSpecToKeyManagementProviderProvider(t *testing.T) {
 			name:      "missing inline provider required fields",
 			raw:       []byte("{\"type\": \"inline\"}"),
 			kmpType:   "inline",
+			resource:  "test",
 			expectErr: true,
 		},
 		{
 			name:      "valid spec",
 			raw:       []byte(`{"type": "inline", "contentType": "certificate", "value": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIQXy2VqtlhSkiZKAGhsnkjbDANBgkqhkiG9w0BAQsFADBvMRswGQYDVQQD\nExJyYXRpZnkuZXhhbXBsZS5jb20xDzANBgNVBAsTBk15IE9yZzETMBEGA1UEChMKTXkgQ29tcGFu\neTEQMA4GA1UEBxMHUmVkbW9uZDELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMB4XDTIzMDIwMTIy\nNDUwMFoXDTI0MDIwMTIyNTUwMFowbzEbMBkGA1UEAxMScmF0aWZ5LmV4YW1wbGUuY29tMQ8wDQYD\nVQQLEwZNeSBPcmcxEzARBgNVBAoTCk15IENvbXBhbnkxEDAOBgNVBAcTB1JlZG1vbmQxCzAJBgNV\nBAgTAldBMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL10bM81\npPAyuraORABsOGS8M76Bi7Guwa3JlM1g2D8CuzSfSTaaT6apy9GsccxUvXd5cmiP1ffna5z+EFmc\nizFQh2aq9kWKWXDvKFXzpQuhyqD1HeVlRlF+V0AfZPvGt3VwUUjNycoUU44ctCWmcUQP/KShZev3\n6SOsJ9q7KLjxxQLsUc4mg55eZUThu8mGB8jugtjsnLUYvIWfHhyjVpGrGVrdkDMoMn+u33scOmrt\nsBljvq9WVo4T/VrTDuiOYlAJFMUae2Ptvo0go8XTN3OjLblKeiK4C+jMn9Dk33oGIT9pmX0vrDJV\nX56w/2SejC1AxCPchHaMuhlwMpftBGkCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQC\nMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU0eaKkZj+MS9jCp9Dg1zdv3v/aKww\nHQYDVR0OBBYEFNHmipGY/jEvYwqfQ4Nc3b97/2isMA0GCSqGSIb3DQEBCwUAA4IBAQBNDcmSBizF\nmpJlD8EgNcUCy5tz7W3+AAhEbA3vsHP4D/UyV3UgcESx+L+Nye5uDYtTVm3lQejs3erN2BjW+ds+\nXFnpU/pVimd0aYv6mJfOieRILBF4XFomjhrJOLI55oVwLN/AgX6kuC3CJY2NMyJKlTao9oZgpHhs\nLlxB/r0n9JnUoN0Gq93oc1+OLFjPI7gNuPXYOP1N46oKgEmAEmNkP1etFrEjFRgsdIFHksrmlOlD\nIed9RcQ087VLjmuymLgqMTFX34Q3j7XgN2ENwBSnkHotE9CcuGRW+NuiOeJalL8DBmFXXWwHTKLQ\nPp5g6m1yZXylLJaFLKz7tdMmO355\n-----END CERTIFICATE-----\n"}`),
 			kmpType:   "inline",
+			resource:  "test",
 			expectErr: false,
 		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			_, err := SpecToKeyManagementProvider(tc.raw, tc.kmpType)
+			_, err := SpecToKeyManagementProvider(tc.raw, tc.kmpType, tc.resource)
 			if tc.expectErr != (err != nil) {
 				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
 			}
@@ -60,6 +63,7 @@ func TestSpecToKeyManagementProviderProvider(t *testing.T) {
 func TestRawToKeyManagementProviderConfig(t *testing.T) {
 	testCases := []struct {
 		name         string
+		resource     string
 		raw          []byte
 		expectErr    bool
 		expectConfig config.KeyManagementProviderConfig
@@ -72,23 +76,26 @@ func TestRawToKeyManagementProviderConfig(t *testing.T) {
 		},
 		{
 			name:         "unmarshal failure",
+			resource:     "test",
 			raw:          []byte("invalid"),
 			expectErr:    true,
 			expectConfig: config.KeyManagementProviderConfig{},
 		},
 		{
 			name:      "valid Raw",
+			resource:  "test",
 			raw:       []byte("{\"type\": \"inline\"}"),
 			expectErr: false,
 			expectConfig: config.KeyManagementProviderConfig{
-				"type": "inline",
+				"type":     "inline",
+				"resource": "test",
 			},
 		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			config, err := rawToKeyManagementProviderConfig(tc.raw, "inline")
+			config, err := rawToKeyManagementProviderConfig(tc.raw, "inline", "test")
 
 			if tc.expectErr != (err != nil) {
 				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
diff --git a/pkg/keymanagementprovider/azurekeyvault/provider_test.go b/pkg/keymanagementprovider/azurekeyvault/provider_test.go
index 676a438..98effa5 100644
--- a/pkg/keymanagementprovider/azurekeyvault/provider_test.go
+++ b/pkg/keymanagementprovider/azurekeyvault/provider_test.go
@@ -20,12 +20,16 @@ package azurekeyvault
 import (
 	"context"
 	"crypto"
+	"encoding/base64"
+	"errors"
 	"strings"
 	"testing"
 	"time"
 
 	kv "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
+	"github.com/Azure/go-autorest/autorest"
 	"github.com/Azure/go-autorest/autorest/azure"
+	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/ratify-project/ratify/internal/version"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider/azurekeyvault/types"
 	"github.com/ratify-project/ratify/pkg/keymanagementprovider/config"
@@ -185,58 +189,238 @@ func TestCreate(t *testing.T) {
 	}
 }
 
+type MockKvClient struct {
+	GetCertificateFunc func(ctx context.Context, certificateName string, certificateVersion string, arg string) (kv.CertificateBundle, error)
+	GetSecretFunc      func(ctx context.Context, secretName string, secretVersion string, arg string) (kv.SecretBundle, error)
+	GetKeyFunc         func(ctx context.Context, keyName string, keyVersion string, arg string) (kv.KeyBundle, error)
+}
+
+func (m *MockKvClient) GetCertificate(ctx context.Context, certificateName string, certificateVersion string, arg string) (kv.CertificateBundle, error) {
+	if m.GetCertificateFunc != nil {
+		return m.GetCertificateFunc(ctx, certificateName, certificateVersion, arg)
+	}
+	return kv.CertificateBundle{}, nil
+}
+func (m *MockKvClient) GetSecret(ctx context.Context, secretName string, secretVersion string, arg string) (kv.SecretBundle, error) {
+	if m.GetSecretFunc != nil {
+		return m.GetSecretFunc(ctx, secretName, secretVersion, arg)
+	}
+	return kv.SecretBundle{}, nil
+}
+func (m *MockKvClient) GetKey(ctx context.Context, keyName string, keyVersion string, arg string) (kv.KeyBundle, error) {
+	if m.GetKeyFunc != nil {
+		return m.GetKeyFunc(ctx, keyName, keyVersion, arg)
+	}
+	return kv.KeyBundle{}, nil
+}
+
 // TestGetCertificates tests the GetCertificates function
 func TestGetCertificates(t *testing.T) {
-	factory := &akvKMProviderFactory{}
-	config := config.KeyManagementProviderConfig{
-		"vaultUri": "https://testkv.vault.azure.net/",
-		"tenantID": "tid",
-		"clientID": "clientid",
-		"certificates": []map[string]interface{}{
-			{
-				"name":    "cert1",
-				"version": "",
+	testCases := []struct {
+		name         string
+		mockKvClient *MockKvClient
+		expectedErr  bool
+	}{
+		{
+			name: "GetSecret error",
+			mockKvClient: &MockKvClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string, _ string) (kv.SecretBundle, error) {
+					return kv.SecretBundle{}, errors.New("error")
+				},
 			},
+			expectedErr: true,
+		},
+		{
+			name: "Certificate disabled",
+			mockKvClient: &MockKvClient{
+				GetCertificateFunc: func(_ context.Context, _ string, _ string, _ string) (kv.CertificateBundle, error) {
+					return kv.CertificateBundle{
+						ID:  to.StringPtr("https://testkv.vault.azure.net/certificates/cert1"),
+						Kid: to.StringPtr("https://testkv.vault.azure.net/keys/key1"),
+						Attributes: &kv.CertificateAttributes{
+							Enabled: to.BoolPtr(false),
+						},
+					}, nil
+				},
+				GetSecretFunc: func(_ context.Context, _ string, _ string, _ string) (kv.SecretBundle, error) {
+					err := autorest.DetailedError{
+						Original: &azure.RequestError{
+							ServiceError: &azure.ServiceError{Code: "SecretDisabled"},
+						},
+					}
+					return kv.SecretBundle{}, err
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name: "Certificate disabled error",
+			mockKvClient: &MockKvClient{
+				GetCertificateFunc: func(_ context.Context, _ string, _ string, _ string) (kv.CertificateBundle, error) {
+					return kv.CertificateBundle{}, errors.New("error")
+				},
+				GetSecretFunc: func(_ context.Context, _ string, _ string, _ string) (kv.SecretBundle, error) {
+					err := autorest.DetailedError{
+						Original: &azure.RequestError{
+							ServiceError: &azure.ServiceError{Code: "SecretDisabled"},
+						},
+					}
+					return kv.SecretBundle{}, err
+				},
+			},
+			expectedErr: true,
+		},
+		{
+			name: "Certificate enabled",
+			mockKvClient: &MockKvClient{
+				GetCertificateFunc: func(_ context.Context, _ string, _ string, _ string) (kv.CertificateBundle, error) {
+					return kv.CertificateBundle{
+						ID:  to.StringPtr("https://testkv.vault.azure.net/certificates/cert1"),
+						Kid: to.StringPtr("https://testkv.vault.azure.net/keys/key1"),
+						Attributes: &kv.CertificateAttributes{
+							Enabled: to.BoolPtr(true),
+						},
+					}, nil
+				},
+				GetSecretFunc: func(_ context.Context, _ string, _ string, _ string) (kv.SecretBundle, error) {
+					return kv.SecretBundle{
+						ID:          to.StringPtr("https://testkv.vault.azure.net/secrets/secret1"),
+						Kid:         to.StringPtr("https://testkv.vault.azure.net/keys/key1"),
+						ContentType: to.StringPtr("application/x-pem-file"),
+						Attributes: &kv.SecretAttributes{
+							Enabled: to.BoolPtr(true),
+						},
+						Value: to.StringPtr("-----BEGIN CERTIFICATE-----\nMIIC8TCCAdmgAwIBAgIUaNrwbhs/I1ecqUYdzD2xuAVNdmowDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzdaFw0yNDA2MjAwMTIyMzdaMBkxFzAVBgNVBAMMDnJhdGlm\neS5kZWZhdWx0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtskG1BUt\n4Fw2lbm53KbwZb1hnLmWdwRotZyznhhk/yrUDcq3uF6klwpk/E2IKfUKIo6doHSk\nXaEZXR68UtXygvA4wdg7xZ6kKpXy0gu+RxGE6CGtDHTyDDzITu+NBjo21ZSsyGpQ\nJeIKftUCHdwdygKf0CdJx8A29GBRpHGCmJadmt7tTzOnYjmbuPVLeqJo/Ex9qXcG\nZbxoxnxr5NCocFeKx+EbLo+k/KjdFB2PKnhgzxAaMMMP6eXPr8l5AlzkC83EmPvN\ntveuaBbamdlFkD+53TZeZlxt3GIdq93Iw/UpbQ/pvhbrztMT+UVEkm15sShfX8Xn\nL2st5A4n0V+66QIDAQABoyAwHjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIH\ngDANBgkqhkiG9w0BAQsFAAOCAQEAGpOqozyfDSBjoTepsRroxxcZ4sq65gw45Bme\nm36BS6FG0WHIg3cMy6KIIBefTDSKrPkKNTtuF25AeGn9jM+26cnfDM78ZH0+Lnn7\n7hs0MA64WMPQaWs9/+89aM9NADV9vp2zdG4xMi6B7DruvKWyhJaNoRqK/qP6LdSQ\nw8M+21sAHvXgrRkQtJlVOzVhgwt36NOb1hzRlQiZB+nhv2Wbw7fbtAaADk3JAumf\nvM+YdPS1KfAFaYefm4yFd+9/C0KOkHico3LTbELO5hG0Mo/EYvtjM+Fljb42EweF\n3nAx1GSPe5Tn8p3h6RyJW5HIKozEKyfDuLS0ccB/nqT3oNjcTw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDRTCCAi2gAwIBAgIUcC33VfaMhOnsl7avNTRVQozoVtUwDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzZaFw0yMzA2MjIwMTIyMzZaMCoxDzANBgNVBAoMBlJhdGlm\neTEXMBUGA1UEAwwOUmF0aWZ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDDFhDnyPrVDZaeRu6Tbg1a/iTwus+IuX+h8aKhKS1yHz4EF/Lz\nxCy7lNSQ9srGMMVumWuNom/ydIphff6PejZM1jFKPU6OQR/0JX5epcVIjbKa562T\nDguUxJ+h5V3EIyM4RqOWQ2g/xZo86x5TzyNJXiVdHHRvmDvUNwPpMeDjr/EHVAni\n5YQObxkJRiiZ7XOa5zz3YztVm8sSZAwPWroY1HIfvtP+KHpiNDIKSymmuJkH4SEr\nJn++iqN8na18a9DFBPTTrLPe3CxATGrMfosCMZ6LP3iFLLc/FaSpwcnugWdewsUK\nYs+sUY7jFWR7x7/1nyFWyRrQviM4f4TY+K7NAgMBAAGjYzBhMB0GA1UdDgQWBBQH\nYePW7QPP2p1utr3r6gqzEkKs+DAfBgNVHSMEGDAWgBQHYePW7QPP2p1utr3r6gqz\nEkKs+DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDANBgkqhkiG9w0B\nAQsFAAOCAQEAjKp4vx3bFaKVhAbQeTsDjWJgmXLK2vLgt74MiUwSF6t0wehlfszE\nIcJagGJsvs5wKFf91bnwiqwPjmpse/thPNBAxh1uEoh81tOklv0BN790vsVpq3t+\ncnUvWPiCZdRlAiGGFtRmKk3Keq4sM6UdiUki9s+wnxypHVb4wIpVxu5R271Lnp5I\n+rb2EQ48iblt4XZPczf/5QJdTgbItjBNbuO8WVPOqUIhCiFuAQziLtNUq3p81dHO\nQ2BPgmaitCpIUYHVYighLauBGCH8xOFzj4a4KbOxKdxyJTd0La/vRCKaUtJX67Lc\nfQYVR9HXQZ0YlmwPcmIG5v7wBfcW34NUvA==\n-----END CERTIFICATE-----\n"),
+					}, nil
+				},
+			},
+		},
+		{
+			name: "getCertsFromSecretBundle error",
+			mockKvClient: &MockKvClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string, _ string) (kv.SecretBundle, error) {
+					return kv.SecretBundle{
+						ContentType: to.StringPtr("test"),
+						ID:          to.StringPtr("https://testkv.vault.azure.net/secrets/secret1"),
+						Kid:         to.StringPtr("https://testkv.vault.azure.net/keys/key1"),
+						Attributes: &kv.SecretAttributes{
+							Enabled: to.BoolPtr(true),
+						},
+						Value: to.StringPtr("-----BEGIN CERTIFICATE-----\nMIIC8TCCAdmgAwIBAgIUaNrwbhs/I1ecqUYdzD2xuAVNdmowDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzdaFw0yNDA2MjAwMTIyMzdaMBkxFzAVBgNVBAMMDnJhdGlm\neS5kZWZhdWx0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtskG1BUt\n4Fw2lbm53KbwZb1hnLmWdwRotZyznhhk/yrUDcq3uF6klwpk/E2IKfUKIo6doHSk\nXaEZXR68UtXygvA4wdg7xZ6kKpXy0gu+RxGE6CGtDHTyDDzITu+NBjo21ZSsyGpQ\nJeIKftUCHdwdygKf0CdJx8A29GBRpHGCmJadmt7tTzOnYjmbuPVLeqJo/Ex9qXcG\nZbxoxnxr5NCocFeKx+EbLo+k/KjdFB2PKnhgzxAaMMMP6eXPr8l5AlzkC83EmPvN\ntveuaBbamdlFkD+53TZeZlxt3GIdq93Iw/UpbQ/pvhbrztMT+UVEkm15sShfX8Xn\nL2st5A4n0V+66QIDAQABoyAwHjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIH\ngDANBgkqhkiG9w0BAQsFAAOCAQEAGpOqozyfDSBjoTepsRroxxcZ4sq65gw45Bme\nm36BS6FG0WHIg3cMy6KIIBefTDSKrPkKNTtuF25AeGn9jM+26cnfDM78ZH0+Lnn7\n7hs0MA64WMPQaWs9/+89aM9NADV9vp2zdG4xMi6B7DruvKWyhJaNoRqK/qP6LdSQ\nw8M+21sAHvXgrRkQtJlVOzVhgwt36NOb1hzRlQiZB+nhv2Wbw7fbtAaADk3JAumf\nvM+YdPS1KfAFaYefm4yFd+9/C0KOkHico3LTbELO5hG0Mo/EYvtjM+Fljb42EweF\n3nAx1GSPe5Tn8p3h6RyJW5HIKozEKyfDuLS0ccB/nqT3oNjcTw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDRTCCAi2gAwIBAgIUcC33VfaMhOnsl7avNTRVQozoVtUwDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzZaFw0yMzA2MjIwMTIyMzZaMCoxDzANBgNVBAoMBlJhdGlm\neTEXMBUGA1UEAwwOUmF0aWZ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDDFhDnyPrVDZaeRu6Tbg1a/iTwus+IuX+h8aKhKS1yHz4EF/Lz\nxCy7lNSQ9srGMMVumWuNom/ydIphff6PejZM1jFKPU6OQR/0JX5epcVIjbKa562T\nDguUxJ+h5V3EIyM4RqOWQ2g/xZo86x5TzyNJXiVdHHRvmDvUNwPpMeDjr/EHVAni\n5YQObxkJRiiZ7XOa5zz3YztVm8sSZAwPWroY1HIfvtP+KHpiNDIKSymmuJkH4SEr\nJn++iqN8na18a9DFBPTTrLPe3CxATGrMfosCMZ6LP3iFLLc/FaSpwcnugWdewsUK\nYs+sUY7jFWR7x7/1nyFWyRrQviM4f4TY+K7NAgMBAAGjYzBhMB0GA1UdDgQWBBQH\nYePW7QPP2p1utr3r6gqzEkKs+DAfBgNVHSMEGDAWgBQHYePW7QPP2p1utr3r6gqz\nEkKs+DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDANBgkqhkiG9w0B\nAQsFAAOCAQEAjKp4vx3bFaKVhAbQeTsDjWJgmXLK2vLgt74MiUwSF6t0wehlfszE\nIcJagGJsvs5wKFf91bnwiqwPjmpse/thPNBAxh1uEoh81tOklv0BN790vsVpq3t+\ncnUvWPiCZdRlAiGGFtRmKk3Keq4sM6UdiUki9s+wnxypHVb4wIpVxu5R271Lnp5I\n+rb2EQ48iblt4XZPczf/5QJdTgbItjBNbuO8WVPOqUIhCiFuAQziLtNUq3p81dHO\nQ2BPgmaitCpIUYHVYighLauBGCH8xOFzj4a4KbOxKdxyJTd0La/vRCKaUtJX67Lc\nfQYVR9HXQZ0YlmwPcmIG5v7wBfcW34NUvA==\n-----END CERTIFICATE-----\n"),
+					}, nil
+				},
+			},
+			expectedErr: true,
 		},
 	}
 
-	provider, err := factory.Create("v1", config, "")
-	if err != nil {
-		t.Fatalf("expected no err but got error = %v", err)
-	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			provider := &akvKMProvider{
+				certificates: []types.KeyVaultValue{
+					{
+						Name:    "cert1",
+						Version: "c1f03df1113d460491d970737dfdc35d",
+					},
+				},
+				kvClient: tc.mockKvClient,
+			}
 
-	certs, certStatus, err := provider.GetCertificates(context.Background())
-	assert.NotNil(t, err)
-	assert.Nil(t, certs)
-	assert.Nil(t, certStatus)
+			_, _, err := provider.GetCertificates(context.Background())
+			if tc.expectedErr != (err != nil) {
+				t.Fatalf("error = %v, expectedErr = %v", err, tc.expectedErr)
+			}
+		})
+	}
 }
 
 // TestGetKeys tests the GetKeys function
 func TestGetKeys(t *testing.T) {
-	factory := &akvKMProviderFactory{}
-	config := config.KeyManagementProviderConfig{
-		"vaultUri": "https://testkv.vault.azure.net/",
-		"tenantID": "tid",
-		"clientID": "clientid",
-		"keys": []map[string]interface{}{
-			{
-				"name": "key1",
+	testCases := []struct {
+		name         string
+		mockKvClient *MockKvClient
+		expectedErr  bool
+	}{
+		{
+			name: "GetKey error",
+			mockKvClient: &MockKvClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string, _ string) (kv.KeyBundle, error) {
+					return kv.KeyBundle{}, errors.New("error")
+				},
 			},
+			expectedErr: true,
+		},
+		{
+			name: "Key disabled",
+			mockKvClient: &MockKvClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string, _ string) (kv.KeyBundle, error) {
+					return kv.KeyBundle{
+						Key: &kv.JSONWebKey{
+							Kid: to.StringPtr("https://testkv.vault.azure.net/keys/key1"),
+						},
+						Attributes: &kv.KeyAttributes{
+							Enabled: to.BoolPtr(false),
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name: "getKeyFromKeyBundle error",
+			mockKvClient: &MockKvClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string, _ string) (kv.KeyBundle, error) {
+					return kv.KeyBundle{
+						Key: &kv.JSONWebKey{
+							Kid: to.StringPtr("https://testkv.vault.azure.net/keys/key1"),
+						},
+						Attributes: &kv.KeyAttributes{
+							Enabled: to.BoolPtr(true),
+						},
+					}, nil
+				},
+			},
+			expectedErr: true,
+		},
+		{
+			name: "Key enabled",
+			mockKvClient: &MockKvClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string, _ string) (kv.KeyBundle, error) {
+					return kv.KeyBundle{
+						Key: &kv.JSONWebKey{
+							Kid: to.StringPtr("https://testkv.vault.azure.net/keys/key1"),
+							Kty: kv.RSA,
+							N:   to.StringPtr(base64.StdEncoding.EncodeToString([]byte("n"))),
+							E:   to.StringPtr(base64.StdEncoding.EncodeToString([]byte("e"))),
+						},
+						Attributes: &kv.KeyAttributes{
+							Enabled: to.BoolPtr(true),
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
 		},
 	}
 
-	initKVClient = func(_ context.Context, _, _, _, _ string) (*kv.BaseClient, error) {
-		return &kv.BaseClient{}, nil
-	}
-	provider, err := factory.Create("v1", config, "")
-	if err != nil {
-		t.Fatalf("expected no err but got error = %v", err)
-	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			provider := &akvKMProvider{
+				keys: []types.KeyVaultValue{
+					{
+						Name:    "key1",
+						Version: "c1f03df1113d460491d970737dfdc35d",
+					},
+				},
+				kvClient: tc.mockKvClient,
+			}
 
-	keys, keyStatus, err := provider.GetKeys(context.Background())
-	assert.NotNil(t, err)
-	assert.Nil(t, keys)
-	assert.Nil(t, keyStatus)
+			_, _, err := provider.GetKeys(context.Background())
+			if tc.expectedErr != (err != nil) {
+				t.Fatalf("error = %v, expectedErr = %v", err, tc.expectedErr)
+			}
+		})
+	}
 }
 
 func TestIsRefreshable(t *testing.T) {
@@ -288,8 +472,9 @@ func TestGetStatusProperty(t *testing.T) {
 	timeNow := time.Now().String()
 	certName := "certName"
 	certVersion := "versionABC"
+	isEnabled := true
 
-	status := getStatusProperty(certName, certVersion, timeNow)
+	status := getStatusProperty(certName, certVersion, timeNow, isEnabled)
 	assert.Equal(t, certName, status[types.StatusName])
 	assert.Equal(t, timeNow, status[types.StatusLastRefreshed])
 	assert.Equal(t, certVersion, status[types.StatusVersion])
@@ -349,7 +534,7 @@ func TestGetCertsFromSecretBundle(t *testing.T) {
 				ContentType: &cases[i].contentType,
 			}
 
-			certs, status, err := getCertsFromSecretBundle(context.Background(), testdata, "certName")
+			certs, status, err := getCertsFromSecretBundle(context.Background(), testdata, "certName", true)
 			if tc.expectedErr {
 				assert.NotNil(t, err)
 				assert.Nil(t, certs)
diff --git a/pkg/keymanagementprovider/keymanagementprovider_test.go b/pkg/keymanagementprovider/keymanagementprovider_test.go
index 0441562..57a2828 100644
--- a/pkg/keymanagementprovider/keymanagementprovider_test.go
+++ b/pkg/keymanagementprovider/keymanagementprovider_test.go
@@ -176,11 +176,19 @@ func TestGetCertificatesFromMap_ErrorFromReconcile(t *testing.T) {
 
 // TestDeleteCertificatesFromMap checks if certificates are deleted from the map
 func TestDeleteCertificatesFromMap(t *testing.T) {
-	certificatesMap.Delete("test")
-	setCertificatesInMap("test", map[KMPMapKey][]*x509.Certificate{{}: {{Raw: []byte("testcert")}}})
-	DeleteResourceFromMap("test")
-	if _, ok := certificatesMap.Load("test"); ok {
-		t.Fatalf("certificatesMap should have been deleted for key")
+	resource := "test"
+	disabledCert := KMPMapKey{Name: "test", Version: "0", Enabled: false}
+	enabledCert := KMPMapKey{Name: "test", Version: "1", Enabled: true}
+	certsMap := map[KMPMapKey][]*x509.Certificate{}
+	certsMap[disabledCert] = []*x509.Certificate{{Raw: []byte("testcert")}}
+	certsMap[enabledCert] = []*x509.Certificate{{Raw: []byte("testcert")}}
+	setCertificatesInMap(resource, certsMap)
+	DeleteCertificateFromMap(resource, disabledCert)
+
+	certs, _ := GetCertificatesFromMap(context.Background(), resource)
+
+	if len(certs) != 1 {
+		t.Fatalf("certificates should have been deleted from the map")
 	}
 }
 
@@ -252,11 +260,17 @@ func TestGetKeysFromMap_AccessDifferentNamespace_ReturnsFalse(t *testing.T) {
 
 // TestDeleteKeysFromMap checks if key map entry is deleted from the map
 func TestDeleteKeysFromMap(t *testing.T) {
-	keyMap.Delete("test")
-	setKeysInMap("test", "", map[KMPMapKey]crypto.PublicKey{{}: &rsa.PublicKey{}})
-	DeleteResourceFromMap("test")
-	if _, ok := keyMap.Load("test"); ok {
-		t.Fatalf("keysMap should have been deleted for key")
+	resource := "test"
+	keysMap := map[KMPMapKey]crypto.PublicKey{}
+	keyMapKey0 := KMPMapKey{Name: "test", Version: "0", Enabled: false}
+	keyMapKey1 := KMPMapKey{Name: "test", Version: "1", Enabled: true}
+	keysMap[keyMapKey0] = crypto.PublicKey(&rsa.PublicKey{})
+	keysMap[keyMapKey1] = crypto.PublicKey(&rsa.PublicKey{})
+	setKeysInMap(resource, "", keysMap)
+	DeleteKeyFromMap(resource, keyMapKey1)
+	keys, _ := GetKeysFromMap(context.Background(), resource)
+	if len(keys) != 1 {
+		t.Fatalf("keys should have been deleted from the map")
 	}
 }