sigs.k8s.io/cluster-api-provider-azure@v1.14.3/api/v1beta1/azuremachine_validation.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1beta1
    18  
    19  import (
    20  	"encoding/base64"
    21  	"fmt"
    22  
    23  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
    24  	"github.com/google/uuid"
    25  	"golang.org/x/crypto/ssh"
    26  	"k8s.io/apimachinery/pkg/util/validation/field"
    27  	azureutil "sigs.k8s.io/cluster-api-provider-azure/util/azure"
    28  )
    29  
    30  // ValidateAzureMachineSpec checks an AzureMachineSpec and returns any validation errors.
    31  func ValidateAzureMachineSpec(spec AzureMachineSpec) field.ErrorList {
    32  	var allErrs field.ErrorList
    33  
    34  	if errs := ValidateImage(spec.Image, field.NewPath("image")); len(errs) > 0 {
    35  		allErrs = append(allErrs, errs...)
    36  	}
    37  
    38  	if errs := ValidateOSDisk(spec.OSDisk, field.NewPath("osDisk")); len(errs) > 0 {
    39  		allErrs = append(allErrs, errs...)
    40  	}
    41  
    42  	if errs := ValidateConfidentialCompute(spec.OSDisk.ManagedDisk, spec.SecurityProfile, field.NewPath("securityProfile")); len(errs) > 0 {
    43  		allErrs = append(allErrs, errs...)
    44  	}
    45  
    46  	if errs := ValidateSSHKey(spec.SSHPublicKey, field.NewPath("sshPublicKey")); len(errs) > 0 {
    47  		allErrs = append(allErrs, errs...)
    48  	}
    49  
    50  	if errs := ValidateUserAssignedIdentity(spec.Identity, spec.UserAssignedIdentities, field.NewPath("userAssignedIdentities")); len(errs) > 0 {
    51  		allErrs = append(allErrs, errs...)
    52  	}
    53  
    54  	if errs := ValidateDataDisks(spec.DataDisks, field.NewPath("dataDisks")); len(errs) > 0 {
    55  		allErrs = append(allErrs, errs...)
    56  	}
    57  
    58  	if errs := ValidateDiagnostics(spec.Diagnostics, field.NewPath("diagnostics")); len(errs) > 0 {
    59  		allErrs = append(allErrs, errs...)
    60  	}
    61  
    62  	if errs := ValidateNetwork(spec.SubnetName, spec.AcceleratedNetworking, spec.NetworkInterfaces, field.NewPath("networkInterfaces")); len(errs) > 0 {
    63  		allErrs = append(allErrs, errs...)
    64  	}
    65  
    66  	if errs := ValidateSystemAssignedIdentityRole(spec.Identity, spec.RoleAssignmentName, spec.SystemAssignedIdentityRole, field.NewPath("systemAssignedIdentityRole")); len(errs) > 0 {
    67  		allErrs = append(allErrs, errs...)
    68  	}
    69  
    70  	return allErrs
    71  }
    72  
    73  // ValidateNetwork validates the network configuration.
    74  func ValidateNetwork(subnetName string, acceleratedNetworking *bool, networkInterfaces []NetworkInterface, fldPath *field.Path) field.ErrorList {
    75  	if (networkInterfaces != nil) && len(networkInterfaces) > 0 && subnetName != "" {
    76  		return field.ErrorList{field.Invalid(fldPath, networkInterfaces, "cannot set both networkInterfaces and machine subnetName")}
    77  	}
    78  
    79  	if (networkInterfaces != nil) && len(networkInterfaces) > 0 && acceleratedNetworking != nil {
    80  		return field.ErrorList{field.Invalid(fldPath, networkInterfaces, "cannot set both networkInterfaces and machine acceleratedNetworking")}
    81  	}
    82  
    83  	for _, nic := range networkInterfaces {
    84  		if nic.PrivateIPConfigs < 1 {
    85  			return field.ErrorList{field.Invalid(fldPath, networkInterfaces, "number of privateIPConfigs per interface must be at least 1")}
    86  		}
    87  	}
    88  
    89  	return field.ErrorList{}
    90  }
    91  
    92  // ValidateSSHKey validates an SSHKey.
    93  func ValidateSSHKey(sshKey string, fldPath *field.Path) field.ErrorList {
    94  	allErrs := field.ErrorList{}
    95  
    96  	decoded, err := base64.StdEncoding.DecodeString(sshKey)
    97  	if err != nil {
    98  		allErrs = append(allErrs, field.Invalid(fldPath, sshKey, "the SSH public key is not properly base64 encoded"))
    99  		return allErrs
   100  	}
   101  
   102  	if _, _, _, _, err := ssh.ParseAuthorizedKey(decoded); err != nil {
   103  		allErrs = append(allErrs, field.Invalid(fldPath, sshKey, "the SSH public key is not valid"))
   104  		return allErrs
   105  	}
   106  
   107  	return allErrs
   108  }
   109  
   110  // ValidateSystemAssignedIdentity validates the system-assigned identities list.
   111  func ValidateSystemAssignedIdentity(identityType VMIdentity, oldIdentity, newIdentity string, fldPath *field.Path) field.ErrorList {
   112  	allErrs := field.ErrorList{}
   113  
   114  	if identityType == VMIdentitySystemAssigned {
   115  		if _, err := uuid.Parse(newIdentity); err != nil {
   116  			allErrs = append(allErrs, field.Invalid(fldPath, newIdentity, "Role assignment name must be a valid GUID. It is optional and will be auto-generated when not specified."))
   117  		}
   118  		if oldIdentity != "" && oldIdentity != newIdentity {
   119  			allErrs = append(allErrs, field.Invalid(fldPath, newIdentity, "Role assignment name should not be modified after AzureMachine creation."))
   120  		}
   121  	} else if newIdentity != "" {
   122  		allErrs = append(allErrs, field.Forbidden(fldPath, "Role assignment name should only be set when using system assigned identity."))
   123  	}
   124  
   125  	return allErrs
   126  }
   127  
   128  // ValidateUserAssignedIdentity validates the user-assigned identities list.
   129  func ValidateUserAssignedIdentity(identityType VMIdentity, userAssignedIdentities []UserAssignedIdentity, fldPath *field.Path) field.ErrorList {
   130  	allErrs := field.ErrorList{}
   131  
   132  	if identityType == VMIdentityUserAssigned {
   133  		if len(userAssignedIdentities) == 0 {
   134  			allErrs = append(allErrs, field.Required(fldPath, "must be specified for the 'UserAssigned' identity type"))
   135  		}
   136  		for _, identity := range userAssignedIdentities {
   137  			if identity.ProviderID != "" {
   138  				if _, err := azureutil.ParseResourceID(identity.ProviderID); err != nil {
   139  					allErrs = append(allErrs, field.Invalid(fldPath, identity.ProviderID, "must be a valid Azure resource ID"))
   140  				}
   141  			}
   142  		}
   143  	}
   144  
   145  	return allErrs
   146  }
   147  
   148  // ValidateSystemAssignedIdentityRole validates the system-assigned identity role.
   149  func ValidateSystemAssignedIdentityRole(identityType VMIdentity, roleAssignmentName string, role *SystemAssignedIdentityRole, fldPath *field.Path) field.ErrorList {
   150  	var allErrs field.ErrorList
   151  	if roleAssignmentName != "" && role != nil && role.Name != "" {
   152  		allErrs = append(allErrs, field.Invalid(fldPath, role.Name, "cannot set both roleAssignmentName and systemAssignedIdentityRole.name"))
   153  	}
   154  	if identityType == VMIdentitySystemAssigned {
   155  		if role.DefinitionID == "" {
   156  			allErrs = append(allErrs, field.Invalid(field.NewPath("Spec", "SystemAssignedIdentityRole", "DefinitionID"), role.DefinitionID, "the definitionID field cannot be empty"))
   157  		}
   158  		if role.Scope == "" {
   159  			allErrs = append(allErrs, field.Invalid(field.NewPath("Spec", "SystemAssignedIdentityRole", "Scope"), role.Scope, "the scope field cannot be empty"))
   160  		}
   161  	}
   162  	if identityType != VMIdentitySystemAssigned && role != nil {
   163  		allErrs = append(allErrs, field.Forbidden(field.NewPath("Spec", "Role"), "systemAssignedIdentityRole can only be set when identity is set to SystemAssigned"))
   164  	}
   165  	return allErrs
   166  }
   167  
   168  // ValidateDataDisks validates a list of data disks.
   169  func ValidateDataDisks(dataDisks []DataDisk, fieldPath *field.Path) field.ErrorList {
   170  	allErrs := field.ErrorList{}
   171  	lunSet := make(map[int32]struct{})
   172  	nameSet := make(map[string]struct{})
   173  	for _, disk := range dataDisks {
   174  		// validate that the disk size is between 4 and 32767.
   175  		if disk.DiskSizeGB < 4 || disk.DiskSizeGB > 32767 {
   176  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("DiskSizeGB"), "", "the disk size should be a value between 4 and 32767"))
   177  		}
   178  
   179  		// validate that all names are unique
   180  		if disk.NameSuffix == "" {
   181  			allErrs = append(allErrs, field.Required(fieldPath.Child("NameSuffix"), "the name suffix cannot be empty"))
   182  		}
   183  		if _, ok := nameSet[disk.NameSuffix]; ok {
   184  			allErrs = append(allErrs, field.Duplicate(fieldPath, disk.NameSuffix))
   185  		} else {
   186  			nameSet[disk.NameSuffix] = struct{}{}
   187  		}
   188  
   189  		// validate optional managed disk option
   190  		if disk.ManagedDisk != nil {
   191  			if errs := validateManagedDisk(disk.ManagedDisk, fieldPath.Child("managedDisk"), false); len(errs) > 0 {
   192  				allErrs = append(allErrs, errs...)
   193  			}
   194  		}
   195  
   196  		// validate that all LUNs are unique and between 0 and 63.
   197  		if disk.Lun == nil {
   198  			allErrs = append(allErrs, field.Required(fieldPath, "LUN should not be nil"))
   199  		} else if *disk.Lun < 0 || *disk.Lun > 63 {
   200  			allErrs = append(allErrs, field.Invalid(fieldPath, disk.Lun, "logical unit number must be between 0 and 63"))
   201  		} else if _, ok := lunSet[*disk.Lun]; ok {
   202  			allErrs = append(allErrs, field.Duplicate(fieldPath, disk.Lun))
   203  		} else {
   204  			lunSet[*disk.Lun] = struct{}{}
   205  		}
   206  
   207  		// validate cachingType
   208  		allErrs = append(allErrs, validateCachingType(disk.CachingType, fieldPath, disk.ManagedDisk)...)
   209  	}
   210  	return allErrs
   211  }
   212  
   213  // ValidateOSDisk validates the OSDisk spec.
   214  func ValidateOSDisk(osDisk OSDisk, fieldPath *field.Path) field.ErrorList {
   215  	allErrs := field.ErrorList{}
   216  
   217  	if osDisk.DiskSizeGB != nil {
   218  		if *osDisk.DiskSizeGB <= 0 || *osDisk.DiskSizeGB > 2048 {
   219  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("DiskSizeGB"), "", "the Disk size should be a value between 1 and 2048"))
   220  		}
   221  	}
   222  
   223  	if osDisk.OSType == "" {
   224  		allErrs = append(allErrs, field.Required(fieldPath.Child("OSType"), "the OS type cannot be empty"))
   225  	}
   226  
   227  	allErrs = append(allErrs, validateCachingType(osDisk.CachingType, fieldPath, osDisk.ManagedDisk)...)
   228  
   229  	if osDisk.ManagedDisk != nil {
   230  		if errs := validateManagedDisk(osDisk.ManagedDisk, fieldPath.Child("managedDisk"), true); len(errs) > 0 {
   231  			allErrs = append(allErrs, errs...)
   232  		}
   233  	}
   234  
   235  	if osDisk.DiffDiskSettings != nil && osDisk.ManagedDisk != nil && osDisk.ManagedDisk.DiskEncryptionSet != nil {
   236  		allErrs = append(allErrs, field.Invalid(
   237  			fieldPath.Child("managedDisks").Child("diskEncryptionSet"),
   238  			osDisk.ManagedDisk.DiskEncryptionSet.ID,
   239  			"diskEncryptionSet is not supported when diffDiskSettings.option is 'Local'",
   240  		))
   241  	}
   242  
   243  	return allErrs
   244  }
   245  
   246  // validateManagedDisk validates updates to the ManagedDiskParameters field.
   247  func validateManagedDisk(m *ManagedDiskParameters, fieldPath *field.Path, isOSDisk bool) field.ErrorList {
   248  	allErrs := field.ErrorList{}
   249  
   250  	if m != nil {
   251  		allErrs = append(allErrs, validateStorageAccountType(m.StorageAccountType, fieldPath.Child("StorageAccountType"), isOSDisk)...)
   252  
   253  		// DiskEncryptionSet can only be set when SecurityEncryptionType is set to DiskWithVMGuestState
   254  		// https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#securityencryptiontypes
   255  		if isOSDisk && m.SecurityProfile != nil && m.SecurityProfile.DiskEncryptionSet != nil {
   256  			if m.SecurityProfile.SecurityEncryptionType != SecurityEncryptionTypeDiskWithVMGuestState {
   257  				allErrs = append(allErrs, field.Invalid(
   258  					fieldPath.Child("securityProfile").Child("diskEncryptionSet"),
   259  					m.SecurityProfile.DiskEncryptionSet.ID,
   260  					"diskEncryptionSet is only supported when securityEncryptionType is set to DiskWithVMGuestState",
   261  				))
   262  			}
   263  		}
   264  	}
   265  
   266  	return allErrs
   267  }
   268  
   269  // ValidateDataDisksUpdate validates updates to Data disks.
   270  func ValidateDataDisksUpdate(oldDataDisks, newDataDisks []DataDisk, fieldPath *field.Path) field.ErrorList {
   271  	allErrs := field.ErrorList{}
   272  
   273  	diskErrMsg := "adding/removing data disks after machine creation is not allowed"
   274  	fieldErrMsg := "modifying data disk's fields after machine creation is not allowed"
   275  
   276  	if len(oldDataDisks) != len(newDataDisks) {
   277  		allErrs = append(allErrs, field.Invalid(fieldPath, newDataDisks, diskErrMsg))
   278  		return allErrs
   279  	}
   280  
   281  	oldDisks := make(map[string]DataDisk)
   282  
   283  	for _, disk := range oldDataDisks {
   284  		oldDisks[disk.NameSuffix] = disk
   285  	}
   286  
   287  	for i, newDisk := range newDataDisks {
   288  		if oldDisk, ok := oldDisks[newDisk.NameSuffix]; ok {
   289  			if newDisk.DiskSizeGB != oldDisk.DiskSizeGB {
   290  				allErrs = append(allErrs, field.Invalid(fieldPath.Index(i).Child("diskSizeGB"), newDataDisks, fieldErrMsg))
   291  			}
   292  
   293  			allErrs = append(allErrs, validateManagedDisksUpdate(oldDisk.ManagedDisk, newDisk.ManagedDisk, fieldPath.Index(i).Child("managedDisk"))...)
   294  
   295  			if (newDisk.Lun != nil && oldDisk.Lun != nil) && (*newDisk.Lun != *oldDisk.Lun) {
   296  				allErrs = append(allErrs, field.Invalid(fieldPath.Index(i).Child("lun"), newDataDisks, fieldErrMsg))
   297  			} else if (newDisk.Lun != nil && oldDisk.Lun == nil) || (newDisk.Lun == nil && oldDisk.Lun != nil) {
   298  				allErrs = append(allErrs, field.Invalid(fieldPath.Index(i).Child("lun"), newDataDisks, fieldErrMsg))
   299  			}
   300  
   301  			if newDisk.CachingType != oldDisk.CachingType {
   302  				allErrs = append(allErrs, field.Invalid(fieldPath.Index(i).Child("cachingType"), newDataDisks, fieldErrMsg))
   303  			}
   304  		} else {
   305  			allErrs = append(allErrs, field.Invalid(fieldPath.Index(i).Child("nameSuffix"), newDataDisks, diskErrMsg))
   306  		}
   307  	}
   308  
   309  	return allErrs
   310  }
   311  
   312  func validateManagedDisksUpdate(oldDiskParams, newDiskParams *ManagedDiskParameters, fieldPath *field.Path) field.ErrorList {
   313  	allErrs := field.ErrorList{}
   314  	fieldErrMsg := "changing managed disk options after machine creation is not allowed"
   315  
   316  	if newDiskParams != nil && oldDiskParams != nil {
   317  		if newDiskParams.StorageAccountType != oldDiskParams.StorageAccountType {
   318  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("storageAccountType"), newDiskParams, fieldErrMsg))
   319  		}
   320  		if newDiskParams.DiskEncryptionSet != nil && oldDiskParams.DiskEncryptionSet != nil {
   321  			if newDiskParams.DiskEncryptionSet.ID != oldDiskParams.DiskEncryptionSet.ID {
   322  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("diskEncryptionSet").Child("ID"), newDiskParams, fieldErrMsg))
   323  			}
   324  		} else if (newDiskParams.DiskEncryptionSet != nil && oldDiskParams.DiskEncryptionSet == nil) || (newDiskParams.DiskEncryptionSet == nil && oldDiskParams.DiskEncryptionSet != nil) {
   325  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("diskEncryptionSet"), newDiskParams, fieldErrMsg))
   326  		}
   327  	} else if (newDiskParams != nil && oldDiskParams == nil) || (newDiskParams == nil && oldDiskParams != nil) {
   328  		allErrs = append(allErrs, field.Invalid(fieldPath, newDiskParams, fieldErrMsg))
   329  	}
   330  
   331  	return allErrs
   332  }
   333  
   334  func validateStorageAccountType(storageAccountType string, fieldPath *field.Path, isOSDisk bool) field.ErrorList {
   335  	allErrs := field.ErrorList{}
   336  
   337  	if isOSDisk && storageAccountType == string(armcompute.StorageAccountTypesUltraSSDLRS) {
   338  		allErrs = append(allErrs, field.Invalid(fieldPath.Child("managedDisks").Child("storageAccountType"), storageAccountType, "UltraSSD_LRS can only be used with data disks, it cannot be used with OS Disks"))
   339  	}
   340  
   341  	if storageAccountType == "" {
   342  		allErrs = append(allErrs, field.Required(fieldPath, "the Storage Account Type for Managed Disk cannot be empty"))
   343  		return allErrs
   344  	}
   345  
   346  	for _, possibleStorageAccountType := range armcompute.PossibleDiskStorageAccountTypesValues() {
   347  		if string(possibleStorageAccountType) == storageAccountType {
   348  			return allErrs
   349  		}
   350  	}
   351  	allErrs = append(allErrs, field.Invalid(fieldPath, "", fmt.Sprintf("allowed values are %v", armcompute.PossibleDiskStorageAccountTypesValues())))
   352  	return allErrs
   353  }
   354  
   355  func validateCachingType(cachingType string, fieldPath *field.Path, managedDisk *ManagedDiskParameters) field.ErrorList {
   356  	allErrs := field.ErrorList{}
   357  	cachingTypeChildPath := fieldPath.Child("CachingType")
   358  
   359  	if managedDisk != nil && managedDisk.StorageAccountType == string(armcompute.StorageAccountTypesUltraSSDLRS) {
   360  		if cachingType != string(armcompute.CachingTypesNone) {
   361  			allErrs = append(allErrs, field.Invalid(cachingTypeChildPath, cachingType, fmt.Sprintf("cachingType '%s' is not supported when storageAccountType is '%s'. Allowed values are: '%s'", cachingType, armcompute.StorageAccountTypesUltraSSDLRS, armcompute.CachingTypesNone)))
   362  		}
   363  	}
   364  
   365  	for _, possibleCachingType := range armcompute.PossibleCachingTypesValues() {
   366  		if string(possibleCachingType) == cachingType {
   367  			return allErrs
   368  		}
   369  	}
   370  
   371  	allErrs = append(allErrs, field.Invalid(cachingTypeChildPath, cachingType, fmt.Sprintf("allowed values are %v", armcompute.PossibleCachingTypesValues())))
   372  	return allErrs
   373  }
   374  
   375  // ValidateDiagnostics validates the Diagnostic spec.
   376  func ValidateDiagnostics(diagnostics *Diagnostics, fieldPath *field.Path) field.ErrorList {
   377  	var allErrs field.ErrorList
   378  
   379  	if diagnostics != nil && diagnostics.Boot != nil {
   380  		switch diagnostics.Boot.StorageAccountType {
   381  		case UserManagedDiagnosticsStorage:
   382  			if diagnostics.Boot.UserManaged == nil {
   383  				allErrs = append(allErrs, field.Required(fieldPath.Child("UserManaged"),
   384  					fmt.Sprintf("userManaged must be specified when storageAccountType is '%s'", UserManagedDiagnosticsStorage)))
   385  			} else if diagnostics.Boot.UserManaged.StorageAccountURI == "" {
   386  				allErrs = append(allErrs, field.Required(fieldPath.Child("StorageAccountURI"),
   387  					fmt.Sprintf("StorageAccountURI cannot be empty when storageAccountType is '%s'", UserManagedDiagnosticsStorage)))
   388  			}
   389  		case ManagedDiagnosticsStorage:
   390  			if diagnostics.Boot.UserManaged != nil &&
   391  				diagnostics.Boot.UserManaged.StorageAccountURI != "" {
   392  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("StorageAccountURI"), diagnostics.Boot.UserManaged.StorageAccountURI,
   393  					fmt.Sprintf("StorageAccountURI cannot be set when storageAccountType is '%s'",
   394  						ManagedDiagnosticsStorage)))
   395  			}
   396  		case DisabledDiagnosticsStorage:
   397  			if diagnostics.Boot.UserManaged != nil &&
   398  				diagnostics.Boot.UserManaged.StorageAccountURI != "" {
   399  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("StorageAccountURI"), diagnostics.Boot.UserManaged.StorageAccountURI,
   400  					fmt.Sprintf("StorageAccountURI cannot be set when storageAccountType is '%s'",
   401  						ManagedDiagnosticsStorage)))
   402  			}
   403  		}
   404  	}
   405  
   406  	return allErrs
   407  }
   408  
   409  // ValidateConfidentialCompute validates the configuration options when the machine is a Confidential VM.
   410  // https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#vmdisksecurityprofile
   411  // https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#securityencryptiontypes
   412  // https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#uefisettings
   413  func ValidateConfidentialCompute(managedDisk *ManagedDiskParameters, profile *SecurityProfile, fieldPath *field.Path) field.ErrorList {
   414  	var allErrs field.ErrorList
   415  
   416  	var securityEncryptionType SecurityEncryptionType
   417  
   418  	if managedDisk != nil && managedDisk.SecurityProfile != nil {
   419  		securityEncryptionType = managedDisk.SecurityProfile.SecurityEncryptionType
   420  	}
   421  
   422  	if profile != nil && securityEncryptionType != "" {
   423  		// SecurityEncryptionType can only be set for Confindential VMs
   424  		if profile.SecurityType != SecurityTypesConfidentialVM {
   425  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("SecurityType"), profile.SecurityType,
   426  				fmt.Sprintf("SecurityType should be set to '%s' when securityEncryptionType is defined", SecurityTypesConfidentialVM)))
   427  		}
   428  
   429  		// Confidential VMs require vTPM to be enabled, irrespective of the SecurityEncryptionType used
   430  		if profile.UefiSettings == nil {
   431  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("UefiSettings"), profile.UefiSettings,
   432  				"UefiSettings should be set when securityEncryptionType is defined"))
   433  		}
   434  
   435  		if profile.UefiSettings != nil && (profile.UefiSettings.VTpmEnabled == nil || !*profile.UefiSettings.VTpmEnabled) {
   436  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("VTpmEnabled"), profile.UefiSettings.VTpmEnabled,
   437  				"VTpmEnabled should be set to true when securityEncryptionType is defined"))
   438  		}
   439  
   440  		if securityEncryptionType == SecurityEncryptionTypeDiskWithVMGuestState {
   441  			// DiskWithVMGuestState encryption type is not compatible with EncryptionAtHost
   442  			if profile.EncryptionAtHost != nil && *profile.EncryptionAtHost {
   443  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("EncryptionAtHost"), profile.EncryptionAtHost,
   444  					fmt.Sprintf("EncryptionAtHost cannot be set to 'true' when securityEncryptionType is set to '%s'", SecurityEncryptionTypeDiskWithVMGuestState)))
   445  			}
   446  
   447  			// DiskWithVMGuestState encryption type requires SecureBoot to be enabled
   448  			if profile.UefiSettings != nil && (profile.UefiSettings.SecureBootEnabled == nil || !*profile.UefiSettings.SecureBootEnabled) {
   449  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("SecureBootEnabled"), profile.UefiSettings.SecureBootEnabled,
   450  					fmt.Sprintf("SecureBootEnabled should be set to true when securityEncryptionType is set to '%s'", SecurityEncryptionTypeDiskWithVMGuestState)))
   451  			}
   452  		}
   453  	}
   454  
   455  	return allErrs
   456  }