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