akv-cli 0.10.1

The Azure Key Vault CLI (unofficial) can read secrets from Key Vault, securely pass secrets to other commands or inject them into configuration files, encrypt and decrypt secrets, and managed keys and secrets in Key Vault.
Documentation
// Copyright 2025 Heath Stewart.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

use azure_core::{base64, time::OffsetDateTime};
use azure_security_keyvault_certificates::models as sdk;
use azure_security_keyvault_certificates::models::{
    CertificatePolicyAction, CurveName, DeletionRecoveryLevel, KeyType, KeyUsageType,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct Certificate {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attributes: Option<CertificateAttributes>,
    #[serde(
        default,
        deserialize_with = "base64::option::deserialize",
        serialize_with = "base64::option::serialize",
        skip_serializing_if = "Option::is_none"
    )]
    pub cer: Option<Vec<u8>>,
    #[serde(rename = "contentType", skip_serializing_if = "Option::is_none")]
    pub content_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub kid: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub policy: Option<CertificatePolicy>,
    #[serde(rename = "preserveCertOrder", skip_serializing_if = "Option::is_none")]
    pub preserve_cert_order: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sid: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<HashMap<String, String>>,
    #[serde(
        default,
        deserialize_with = "base64::option::deserialize_url_safe",
        rename = "x5t",
        serialize_with = "base64::option::serialize_url_safe",
        skip_serializing_if = "Option::is_none"
    )]
    pub x509_thumbprint: Option<Vec<u8>>,
}

impl From<sdk::Certificate> for Certificate {
    fn from(value: sdk::Certificate) -> Self {
        Certificate {
            attributes: value.attributes.map(Into::into),
            cer: value.cer,
            content_type: value.content_type,
            id: value.id,
            kid: value.kid,
            policy: value.policy.map(Into::into),
            preserve_cert_order: value.preserve_cert_order,
            sid: value.sid,
            tags: value.tags,
            x509_thumbprint: value.x509_thumbprint,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct CertificateAttributes {
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        with = "azure_core::time::unix_time::option"
    )]
    pub created: Option<OffsetDateTime>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub enabled: Option<bool>,
    #[serde(
        default,
        rename = "exp",
        skip_serializing_if = "Option::is_none",
        with = "azure_core::time::unix_time::option"
    )]
    pub expires: Option<OffsetDateTime>,
    #[serde(
        default,
        rename = "nbf",
        skip_serializing_if = "Option::is_none",
        with = "azure_core::time::unix_time::option"
    )]
    pub not_before: Option<OffsetDateTime>,
    #[serde(rename = "recoverableDays", skip_serializing_if = "Option::is_none")]
    pub recoverable_days: Option<i32>,
    #[serde(rename = "recoveryLevel", skip_serializing_if = "Option::is_none")]
    pub recovery_level: Option<DeletionRecoveryLevel>,
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        with = "azure_core::time::unix_time::option"
    )]
    pub updated: Option<OffsetDateTime>,
}

impl From<sdk::CertificateAttributes> for CertificateAttributes {
    fn from(value: sdk::CertificateAttributes) -> Self {
        CertificateAttributes {
            created: value.created,
            enabled: value.enabled,
            expires: value.expires,
            not_before: value.not_before,
            recoverable_days: value.recoverable_days,
            recovery_level: value.recovery_level,
            updated: value.updated,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct CertificatePolicy {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attributes: Option<CertificateAttributes>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    #[serde(rename = "issuer", skip_serializing_if = "Option::is_none")]
    pub issuer_parameters: Option<IssuerParameters>,
    #[serde(rename = "key_props", skip_serializing_if = "Option::is_none")]
    pub key_properties: Option<KeyProperties>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lifetime_actions: Option<Vec<LifetimeAction>>,
    #[serde(rename = "secret_props", skip_serializing_if = "Option::is_none")]
    pub secret_properties: Option<SecretProperties>,
    #[serde(rename = "x509_props", skip_serializing_if = "Option::is_none")]
    pub x509_certificate_properties: Option<X509CertificateProperties>,
}

impl From<sdk::CertificatePolicy> for CertificatePolicy {
    fn from(value: sdk::CertificatePolicy) -> Self {
        CertificatePolicy {
            attributes: value.attributes.map(Into::into),
            id: value.id,
            issuer_parameters: value.issuer_parameters.map(Into::into),
            key_properties: value.key_properties.map(Into::into),
            lifetime_actions: value
                .lifetime_actions
                .map(|v| v.into_iter().map(Into::into).collect()),
            secret_properties: value.secret_properties.map(Into::into),
            x509_certificate_properties: value.x509_certificate_properties.map(Into::into),
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct IssuerParameters {
    #[serde(rename = "cert_transparency", skip_serializing_if = "Option::is_none")]
    pub certificate_transparency: Option<bool>,
    #[serde(rename = "cty", skip_serializing_if = "Option::is_none")]
    pub certificate_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

impl From<sdk::IssuerParameters> for IssuerParameters {
    fn from(value: sdk::IssuerParameters) -> Self {
        IssuerParameters {
            certificate_transparency: value.certificate_transparency,
            certificate_type: value.certificate_type,
            name: value.name,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct KeyProperties {
    #[serde(rename = "crv", skip_serializing_if = "Option::is_none")]
    pub curve: Option<CurveName>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub exportable: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_size: Option<i32>,
    #[serde(rename = "kty", skip_serializing_if = "Option::is_none")]
    pub key_type: Option<KeyType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reuse_key: Option<bool>,
}

impl From<sdk::KeyProperties> for KeyProperties {
    fn from(value: sdk::KeyProperties) -> Self {
        KeyProperties {
            curve: value.curve,
            exportable: value.exportable,
            key_size: value.key_size,
            key_type: value.key_type,
            reuse_key: value.reuse_key,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct LifetimeAction {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub action: Option<LifetimeActionType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trigger: Option<LifetimeActionTrigger>,
}

impl From<sdk::LifetimeAction> for LifetimeAction {
    fn from(value: sdk::LifetimeAction) -> Self {
        LifetimeAction {
            action: value.action.map(Into::into),
            trigger: value.trigger.map(Into::into),
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct LifetimeActionTrigger {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub days_before_expiry: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lifetime_percentage: Option<i32>,
}

impl From<sdk::LifetimeActionTrigger> for LifetimeActionTrigger {
    fn from(value: sdk::LifetimeActionTrigger) -> Self {
        LifetimeActionTrigger {
            days_before_expiry: value.days_before_expiry,
            lifetime_percentage: value.lifetime_percentage,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct LifetimeActionType {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub action_type: Option<CertificatePolicyAction>,
}

impl From<sdk::LifetimeActionType> for LifetimeActionType {
    fn from(value: sdk::LifetimeActionType) -> Self {
        LifetimeActionType {
            action_type: value.action_type,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct SecretProperties {
    #[serde(rename = "contentType", skip_serializing_if = "Option::is_none")]
    pub content_type: Option<String>,
}

impl From<sdk::SecretProperties> for SecretProperties {
    fn from(value: sdk::SecretProperties) -> Self {
        SecretProperties {
            content_type: value.content_type,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct SubjectAlternativeNames {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dns_names: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub emails: Option<Vec<String>>,
    #[serde(rename = "ipAddresses", skip_serializing_if = "Option::is_none")]
    pub ip_addresses: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uris: Option<Vec<String>>,
    // cspell:ignore "upns"
    #[serde(rename = "upns", skip_serializing_if = "Option::is_none")]
    pub user_principal_names: Option<Vec<String>>,
}

impl From<sdk::SubjectAlternativeNames> for SubjectAlternativeNames {
    fn from(value: sdk::SubjectAlternativeNames) -> Self {
        SubjectAlternativeNames {
            dns_names: value.dns_names,
            emails: value.emails,
            ip_addresses: value.ip_addresses,
            uris: value.uris,
            user_principal_names: value.user_principal_names,
        }
    }
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct X509CertificateProperties {
    // cspell:ignore ekus
    #[serde(rename = "ekus", skip_serializing_if = "Option::is_none")]
    pub enhanced_key_usage: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_usage: Option<Vec<KeyUsageType>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subject: Option<String>,
    #[serde(rename = "sans", skip_serializing_if = "Option::is_none")]
    pub subject_alternative_names: Option<SubjectAlternativeNames>,
    #[serde(rename = "validity_months", skip_serializing_if = "Option::is_none")]
    pub validity_in_months: Option<i32>,
}

impl From<sdk::X509CertificateProperties> for X509CertificateProperties {
    fn from(value: sdk::X509CertificateProperties) -> Self {
        X509CertificateProperties {
            enhanced_key_usage: value.enhanced_key_usage,
            key_usage: value.key_usage,
            subject: value.subject,
            subject_alternative_names: value.subject_alternative_names.map(Into::into),
            validity_in_months: value.validity_in_months,
        }
    }
}