internal override async Task InitializeChildrenAsync(AzureContext azureContext) { _VirtualNetwork = azureContext.AzureSubscription.GetAzureARMVirtualNetwork(azureContext.AzureSubscription, this.SubnetId); if (_VirtualNetwork != null) { foreach (Subnet subnet in _VirtualNetwork.Subnets) { if (subnet.Name == this.SubnetName) { _Subnet = subnet; break; } } } if (this.PublicIpAddressId != String.Empty) { _PublicIP = await azureContext.AzureSubscription.GetAzureARMPublicIP(this.PublicIpAddressId); } }
public async Task ValidateAzureResources() { Alerts.Clear(); if (this.TargetSubscription == null) { this.AddAlert(AlertType.Error, "Target Azure Subscription must be provided for template generation.", this.ResourceGroup); } else { if (this.TargetSubscription.Locations == null || this.TargetSubscription.Locations.Count() == 0) { this.AddAlert(AlertType.Error, "Target Azure Subscription must have one or more Locations instantiated.", this.ResourceGroup); } } if (this.ResourceGroup == null) { this.AddAlert(AlertType.Error, "Target Resource Group must be provided for template generation.", this.ResourceGroup); } else { if (this.ResourceGroup.TargetLocation == null) { this.AddAlert(AlertType.Error, "Target Resource Group Location must be provided for template generation.", this.ResourceGroup); } else { // It is possible that the Target Location is no longer in the Target Subscription // Sample case, user first connected to Azure Commercial as source (and set as initial target) // but then logged into a different account for the target and target is now USGov. if (this.TargetSubscription != null && this.TargetSubscription.Locations != null) { if (!this.TargetSubscription.Locations.Contains(this.ResourceGroup.TargetLocation)) { this.AddAlert(AlertType.Error, "Target Resource Group Location '" + this.ResourceGroup.TargetLocation.ToString() + "' is not available in Subscription '" + this.TargetSubscription.ToString() + "'. Select a new Target Location.", this.ResourceGroup); } } } } foreach (MigrationTarget.StorageAccount targetStorageAccount in this.StorageAccounts) { ValidateTargetApiVersion(targetStorageAccount); await targetStorageAccount.CheckNameAvailability(this.TargetSubscription); if (!targetStorageAccount.IsNameAvailable) { this.AddAlert(AlertType.Error, "Target Name for Storage Account '" + targetStorageAccount.ToString() + "' already exists within Azure Environment " + this.TargetSubscription.AzureEnvironment.ToString() + ". A new (available) target name must be specified.", targetStorageAccount); } if (targetStorageAccount.BlobStorageNamespace == null || targetStorageAccount.BlobStorageNamespace.Trim().Length <= 0) { this.AddAlert(AlertType.Error, "Blob Storage Namespace for Target Storage Account '" + targetStorageAccount.ToString() + "' must be specified.", targetStorageAccount); } if (!this.IsStorageAccountVmDiskTarget(targetStorageAccount)) { this.AddAlert(AlertType.Warning, "Target Storage Account '" + targetStorageAccount.ToString() + "' is not utilized within this Resource Group Deployment as a Virtual Machine Disk Target. Consider removing to avoid creation of a non-utilized Storage Account.", targetStorageAccount); } } foreach (MigrationTarget.VirtualNetwork targetVirtualNetwork in this.VirtualNetworks) { ValidateTargetApiVersion(targetVirtualNetwork); foreach (MigrationTarget.Subnet targetSubnet in targetVirtualNetwork.TargetSubnets) { if (targetSubnet.NetworkSecurityGroup != null) { MigrationTarget.NetworkSecurityGroup networkSecurityGroupInMigration = this.SeekNetworkSecurityGroup(targetSubnet.NetworkSecurityGroup.ToString()); if (networkSecurityGroupInMigration == null) { this.AddAlert(AlertType.Error, "Virtual Network '" + targetVirtualNetwork.ToString() + "' Subnet '" + targetSubnet.ToString() + "' utilizes Network Security Group (NSG) '" + targetSubnet.NetworkSecurityGroup.ToString() + "', but the NSG resource is not added into the migration template.", targetSubnet); } } } } foreach (MigrationTarget.NetworkSecurityGroup targetNetworkSecurityGroup in this.NetworkSecurityGroups) { ValidateTargetApiVersion(targetNetworkSecurityGroup); if (targetNetworkSecurityGroup.TargetName == string.Empty) { this.AddAlert(AlertType.Error, "Target Name for Network Security Group must be specified.", targetNetworkSecurityGroup); } } foreach (MigrationTarget.LoadBalancer targetLoadBalancer in this.LoadBalancers) { ValidateTargetApiVersion(targetLoadBalancer); if (targetLoadBalancer.TargetName == string.Empty) { this.AddAlert(AlertType.Error, "Target Name for Load Balancer must be specified.", targetLoadBalancer); } if (targetLoadBalancer.FrontEndIpConfigurations.Count == 0) { this.AddAlert(AlertType.Error, "Load Balancer must have a FrontEndIpConfiguration.", targetLoadBalancer); } else { if (targetLoadBalancer.LoadBalancerType == MigrationTarget.LoadBalancerType.Internal) { if (targetLoadBalancer.FrontEndIpConfigurations.Count > 0) { if (targetLoadBalancer.FrontEndIpConfigurations[0].TargetSubnet == null) { this.AddAlert(AlertType.Error, "Internal Load Balancer must have an internal Subnet association.", targetLoadBalancer); } else { // russell if (targetLoadBalancer.FrontEndIpConfigurations[0].TargetPrivateIPAllocationMethod == IPAllocationMethodEnum.Static) { if (!IPv4CIDR.IsIpAddressInAddressPrefix(targetLoadBalancer.FrontEndIpConfigurations[0].TargetSubnet.AddressPrefix, targetLoadBalancer.FrontEndIpConfigurations[0].TargetPrivateIpAddress)) { this.AddAlert(AlertType.Error, "Load Balancer '" + targetLoadBalancer.ToString() + "' IP Address '" + targetLoadBalancer.FrontEndIpConfigurations[0].TargetPrivateIpAddress + "' is not valid within Subnet '" + targetLoadBalancer.FrontEndIpConfigurations[0].TargetSubnet.ToString() + "' Address Prefix '" + targetLoadBalancer.FrontEndIpConfigurations[0].TargetSubnet.AddressPrefix + "'.", targetLoadBalancer); } else { if (targetLoadBalancer.FrontEndIpConfigurations[0].TargetVirtualNetwork != null && targetLoadBalancer.FrontEndIpConfigurations[0].TargetVirtualNetwork.GetType() == typeof(Azure.Arm.VirtualNetwork) && IPv4CIDR.IsValidIpAddress(targetLoadBalancer.FrontEndIpConfigurations[0].TargetPrivateIpAddress) // Only worth passing to Azure for Availability validation if the IP address is valid. ) { Arm.VirtualNetwork armVirtualNetwork = (Arm.VirtualNetwork)targetLoadBalancer.FrontEndIpConfigurations[0].TargetVirtualNetwork; (bool isAvailable, List <String> availableIps) = await armVirtualNetwork.IsIpAddressAvailable(targetLoadBalancer.FrontEndIpConfigurations[0].TargetPrivateIpAddress); if (!isAvailable) { this.AddAlert(AlertType.Error, "Load Balancer '" + targetLoadBalancer.ToString() + "' IP Address '" + targetLoadBalancer.FrontEndIpConfigurations[0].TargetPrivateIpAddress + "' is not available within Virtual Network '" + targetLoadBalancer.FrontEndIpConfigurations[0].TargetVirtualNetwork.ToString() + "'.", targetLoadBalancer); } } } } } } } else { if (targetLoadBalancer.FrontEndIpConfigurations[0].PublicIp == null) { this.AddAlert(AlertType.Error, "Public Load Balancer must have a Public IP association.", targetLoadBalancer); } else { if (targetLoadBalancer.FrontEndIpConfigurations[0].PublicIp.GetType() == typeof(MigrationTarget.PublicIp)) { MigrationTarget.PublicIp migrationTargetPublicIp = (MigrationTarget.PublicIp)targetLoadBalancer.FrontEndIpConfigurations[0].PublicIp; // Ensure the selected Public IP Address is "in the migration" as a target new Public IP Object bool publicIpExistsInMigration = false; foreach (Azure.MigrationTarget.PublicIp publicIp in this.PublicIPs) { if (publicIp.TargetName == migrationTargetPublicIp.TargetName) { publicIpExistsInMigration = true; break; } } if (!publicIpExistsInMigration) { this.AddAlert(AlertType.Error, "Load Balancer Public IP '" + migrationTargetPublicIp.TargetName + "' specified '" + targetLoadBalancer.ToString() + "' is not included in the migration template.", targetLoadBalancer); } } else if (targetLoadBalancer.FrontEndIpConfigurations[0].PublicIp.GetType() == typeof(Arm.PublicIP)) { Arm.PublicIP armPublicIp = (Arm.PublicIP)targetLoadBalancer.FrontEndIpConfigurations[0].PublicIp; if (armPublicIp.IpConfigurationId != String.Empty) { this.AddAlert(AlertType.Error, "Load Balancer referenced ARM Public IP '" + armPublicIp.Name + "' is already in use by ARM Resource '" + armPublicIp.IpConfigurationId + "'.", targetLoadBalancer); } } } } } } foreach (Azure.MigrationTarget.AvailabilitySet availablitySet in this.AvailablitySets) { ValidateTargetApiVersion(availablitySet); if (availablitySet.TargetVirtualMachines.Count == 0) { this.AddAlert(AlertType.Error, "Availability Set '" + availablitySet.ToString() + "' does not contain any Virtual Machines. Remove the Availability Set from the Target Resources for export or associate Virtual Machines to the Availability Set.", availablitySet); } else if (availablitySet.TargetVirtualMachines.Count == 1) { this.AddAlert(AlertType.Warning, "Availability Set '" + availablitySet.ToString() + "' only contains a single VM. Only utilize an Availability Set if additional VMs will be added; otherwise, a single VM instance should not reside within an Availability Set.", availablitySet); } if (!availablitySet.IsManagedDisks && !availablitySet.IsUnmanagedDisks) { this.AddAlert(AlertType.Error, "All OS and Data Disks for Virtual Machines contained within Availablity Set '" + availablitySet.ToString() + "' should be either Unmanaged Disks or Managed Disks for consistent deployment.", availablitySet); } } foreach (Azure.MigrationTarget.VirtualMachine virtualMachine in this.VirtualMachines) { ValidateTargetApiVersion(virtualMachine); if (virtualMachine.TargetName == string.Empty) { this.AddAlert(AlertType.Error, "Target Name for Virtual Machine '" + virtualMachine.ToString() + "' must be specified.", virtualMachine); } if (virtualMachine.OSVirtualHardDisk == null) { this.AddAlert(AlertType.Error, "Virtual Machine '" + virtualMachine.ToString() + "' does not have an OS Disk.", virtualMachine); } if (virtualMachine.TargetAvailabilitySet == null) { if (virtualMachine.OSVirtualHardDisk != null && virtualMachine.OSVirtualHardDisk.TargetStorage != null && virtualMachine.OSVirtualHardDisk.TargetStorage.StorageAccountType != StorageAccountType.Premium_LRS) { this.AddAlert(AlertType.Warning, "Virtual Machine '" + virtualMachine.ToString() + "' is not part of an Availability Set. OS Disk should be migrated to Azure Premium Storage to receive an Azure SLA for single server deployments. Existing configuration will receive no (0%) Service Level Agreement (SLA).", virtualMachine.OSVirtualHardDisk); } foreach (Azure.MigrationTarget.Disk dataDisk in virtualMachine.DataDisks) { if (dataDisk.TargetStorage != null && dataDisk.TargetStorage.StorageAccountType != StorageAccountType.Premium_LRS) { this.AddAlert(AlertType.Warning, "Virtual Machine '" + virtualMachine.ToString() + "' is not part of an Availability Set. Data Disk '" + dataDisk.ToString() + "' should be migrated to Azure Premium Storage to receive an Azure SLA for single server deployments. Existing configuration will receive no (0%) Service Level Agreement (SLA).", dataDisk); } } } else { bool virtualMachineAvailabitySetExists = false; foreach (MigrationTarget.AvailabilitySet targetAvailabilitySet in this.AvailablitySets) { if (targetAvailabilitySet.ToString() == virtualMachine.TargetAvailabilitySet.ToString()) { virtualMachineAvailabitySetExists = true; } } if (!virtualMachineAvailabitySetExists) { this.AddAlert(AlertType.Error, "Virtual Machine '" + virtualMachine.ToString() + "' utilizes Availability Set '" + virtualMachine.TargetAvailabilitySet.ToString() + "'; however, the Availability Set is not included in the Export.", virtualMachine); } } if (virtualMachine.TargetSize == null) { this.AddAlert(AlertType.Error, "Target Size for Virtual Machine '" + virtualMachine.ToString() + "' must be specified.", virtualMachine); } else { // Ensure that the selected target size is available in the target Azure Location if (this.ResourceGroup != null && this.ResourceGroup.TargetLocation != null) { if (this.ResourceGroup.TargetLocation.VMSizes == null || this.ResourceGroup.TargetLocation.VMSizes.Count == 0) { this.AddAlert(AlertType.Error, "No ARM VM Sizes are available for Azure Location '" + this.ResourceGroup.TargetLocation.DisplayName + "'.", virtualMachine); } else { // Ensure selected target VM Size is available in the Target Azure Location Arm.VMSize matchedVmSize = this.ResourceGroup.TargetLocation.SeekVmSize(virtualMachine.TargetSize.Name); if (matchedVmSize == null) { this.AddAlert(AlertType.Error, "Specified VM Size '" + virtualMachine.TargetSize.Name + "' for Virtual Machine '" + virtualMachine.ToString() + "' is invalid as it is not available in Azure Location '" + this.ResourceGroup.TargetLocation.DisplayName + "'.", virtualMachine); } } } if (virtualMachine.OSVirtualHardDisk != null && virtualMachine.OSVirtualHardDisk.TargetStorage != null && virtualMachine.OSVirtualHardDisk.TargetStorage.StorageAccountType == StorageAccountType.Premium_LRS && !virtualMachine.TargetSize.IsStorageTypeSupported(virtualMachine.OSVirtualHardDisk.StorageAccountType)) { this.AddAlert(AlertType.Error, "Premium Disk based Virtual Machines must be of VM Series 'B', 'DS', 'DS v2', 'DS v3', 'GS', 'GS v2', 'Ls' or 'Fs'.", virtualMachine); } } foreach (Azure.MigrationTarget.NetworkInterface networkInterface in virtualMachine.NetworkInterfaces) { // Seek the inclusion of the Network Interface in the export object bool networkInterfaceExistsInExport = false; foreach (Azure.MigrationTarget.NetworkInterface targetNetworkInterface in this.NetworkInterfaces) { if (String.Compare(networkInterface.SourceName, targetNetworkInterface.SourceName, true) == 0) { networkInterfaceExistsInExport = true; break; } } if (!networkInterfaceExistsInExport) { this.AddAlert(AlertType.Error, "Network Interface Card (NIC) '" + networkInterface.ToString() + "' is used by Virtual Machine '" + virtualMachine.ToString() + "', but is not included in the exported resources.", virtualMachine); } if (virtualMachine.TargetSize != null) { if (networkInterface.EnableAcceleratedNetworking && !virtualMachine.TargetSize.IsAcceleratedNetworkingSupported) { this.AddAlert(AlertType.Error, "Network Interface Card (NIC) '" + networkInterface.ToString() + "' has Accelerated Networking enabled, but the Virtual Machine must be of VM Series 'D', 'DSv2', 'DSv3', 'E', 'ESv3', 'F', 'FS', 'FSv2', 'Ms' or 'Mms' to support Accelerated Networking.", networkInterface); } else if (!networkInterface.EnableAcceleratedNetworking && virtualMachine.TargetSize.IsAcceleratedNetworkingSupported) { this.AddAlert(AlertType.Recommendation, "Network Interface Card (NIC) '" + networkInterface.ToString() + "' has Accelerated Networking disabled and the Virtual Machine Size '" + virtualMachine.TargetSize.ToString() + "' can support Accelerated Networking. Consider enabling Accelerated Networking.", networkInterface); } } if (networkInterface.NetworkSecurityGroup != null) { MigrationTarget.NetworkSecurityGroup networkSecurityGroupInMigration = this.SeekNetworkSecurityGroup(networkInterface.NetworkSecurityGroup.ToString()); if (networkSecurityGroupInMigration == null) { this.AddAlert(AlertType.Error, "Network Interface Card (NIC) '" + networkInterface.ToString() + "' utilizes Network Security Group (NSG) '" + networkInterface.NetworkSecurityGroup.ToString() + "', but the NSG resource is not added into the migration template.", networkInterface); } } foreach (Azure.MigrationTarget.NetworkInterfaceIpConfiguration ipConfiguration in networkInterface.TargetNetworkInterfaceIpConfigurations) { if (ipConfiguration.TargetVirtualNetwork == null) { this.AddAlert(AlertType.Error, "Target Virtual Network for Virtual Machine '" + virtualMachine.ToString() + "' Network Interface '" + networkInterface.ToString() + "' must be specified.", networkInterface); } else { if (ipConfiguration.TargetVirtualNetwork.GetType() == typeof(MigrationTarget.VirtualNetwork)) { MigrationTarget.VirtualNetwork virtualMachineTargetVirtualNetwork = (MigrationTarget.VirtualNetwork)ipConfiguration.TargetVirtualNetwork; bool targetVNetExists = false; foreach (MigrationTarget.VirtualNetwork targetVirtualNetwork in this.VirtualNetworks) { if (targetVirtualNetwork.TargetName == virtualMachineTargetVirtualNetwork.TargetName) { targetVNetExists = true; break; } } if (!targetVNetExists) { this.AddAlert(AlertType.Error, "Target Virtual Network '" + virtualMachineTargetVirtualNetwork.ToString() + "' for Virtual Machine '" + virtualMachine.ToString() + "' Network Interface '" + networkInterface.ToString() + "' is invalid, as it is not included in the migration / template. Either include the source Virtual Network in the Migration Template (if this is the first time migration needing a new ARM Virtual Network), or select an existing ARM Virtual Network and Subnet to migrate the Virtual Machine into.", networkInterface); } } } if (ipConfiguration.TargetSubnet == null) { this.AddAlert(AlertType.Error, "Target Subnet for Virtual Machine '" + virtualMachine.ToString() + "' Network Interface '" + networkInterface.ToString() + "' must be specified.", networkInterface); } else { if (!IPv4CIDR.IsValidCIDR(ipConfiguration.TargetSubnet.AddressPrefix)) { this.AddAlert(AlertType.Error, "Target Subnet '" + ipConfiguration.TargetSubnet.ToString() + "' used by Virtual Machine '" + virtualMachine.ToString() + "' has an invalid IPv4 Address Prefix: " + ipConfiguration.TargetSubnet.AddressPrefix, ipConfiguration.TargetSubnet); } else { if (ipConfiguration.TargetPrivateIPAllocationMethod == IPAllocationMethodEnum.Static) { if (!IPv4CIDR.IsIpAddressInAddressPrefix(ipConfiguration.TargetSubnet.AddressPrefix, ipConfiguration.TargetPrivateIpAddress)) { this.AddAlert(AlertType.Error, "Target IP Address '" + ipConfiguration.TargetPrivateIpAddress + "' is not valid in Subnet '" + ipConfiguration.TargetSubnet.ToString() + "' Address Prefix '" + ipConfiguration.TargetSubnet.AddressPrefix + "'.", networkInterface); } } } } if (ipConfiguration.TargetPublicIp != null) { if (ipConfiguration.TargetPublicIp.GetType().UnderlyingSystemType == typeof(MigrationTarget.PublicIp)) { MigrationTarget.PublicIp publicIpInMigration = this.SeekPublicIp(ipConfiguration.TargetPublicIp.ToString()); if (publicIpInMigration == null) { this.AddAlert(AlertType.Error, "Network Interface Card (NIC) '" + networkInterface.ToString() + "' IP Configuration '" + ipConfiguration.ToString() + "' utilizes Public IP '" + ipConfiguration.TargetPublicIp.ToString() + "', but the Public IP resource is not added into the migration template.", networkInterface); } } else if (ipConfiguration.TargetPublicIp.GetType().UnderlyingSystemType == typeof(Arm.PublicIP)) { Arm.PublicIP existingArmPublicIp = (Arm.PublicIP)ipConfiguration.TargetPublicIp; if (existingArmPublicIp.IpConfigurationId != String.Empty) { this.AddAlert(AlertType.Error, "Network Interface Card (NIC) '" + networkInterface.ToString() + "' IP Configuration '" + ipConfiguration.ToString() + "' utilizes existing Public IP '" + ipConfiguration.TargetPublicIp.ToString() + "', but the Public IP resource is already used by existing ARM Resource '" + existingArmPublicIp.IpConfigurationId + "'.", networkInterface); } } } } } if (virtualMachine.OSVirtualHardDisk != null) { ValidateVMDisk(virtualMachine.OSVirtualHardDisk); } foreach (MigrationTarget.Disk dataDisk in virtualMachine.DataDisks) { ValidateVMDisk(dataDisk); if (!dataDisk.Lun.HasValue || dataDisk.Lun.Value == -1) { this.AddAlert(AlertType.Error, "Data Disk '" + dataDisk.ToString() + "' must have a valid LUN Index assigned.", dataDisk); } else { if (virtualMachine.TargetSize != null) { if (dataDisk.Lun > virtualMachine.TargetSize.maxDataDiskCount - 1) { this.AddAlert(AlertType.Error, "Data Disk '" + dataDisk.ToString() + "' LUN index " + dataDisk.Lun.Value.ToString() + " exceeds the maximum LUN of " + (virtualMachine.TargetSize.maxDataDiskCount - 1).ToString() + " allowed by VM Size '" + virtualMachine.TargetSize.ToString() + "'.", dataDisk); } } int lunCount = virtualMachine.DataDisks.Where(a => a.Lun == dataDisk.Lun).Count(); if (lunCount > 1) { this.AddAlert(AlertType.Error, "Multiple data disks are assigned to LUN " + dataDisk.Lun.ToString() + " on Virtual Machine '" + virtualMachine.ToString() + "'. Data Disk LUNs must be unique.", dataDisk); } } } if (!virtualMachine.IsManagedDisks && !virtualMachine.IsUnmanagedDisks) { this.AddAlert(AlertType.Error, "All OS and Data Disks for Virtual Machine '" + virtualMachine.ToString() + "' should be either Unmanaged Disks or Managed Disks for consistent deployment.", virtualMachine); } } foreach (Azure.MigrationTarget.Disk targetDisk in this.Disks) { ValidateTargetApiVersion(targetDisk); ValidateDiskStandards(targetDisk); } // todo now asap - Add test for NSGs being present in Migration //MigrationTarget.NetworkSecurityGroup targetNetworkSecurityGroup = (MigrationTarget.NetworkSecurityGroup)this.SeekNetworkSecurityGroup(targetSubnet.NetworkSecurityGroup.ToString()); //if (targetNetworkSecurityGroup == null) //{ // this.AddAlert(AlertType.Error, "Subnet '" + subnet.name + "' utilized ASM Network Security Group (NSG) '" + targetSubnet.NetworkSecurityGroup.ToString() + "', which has not been added to the ARM Subnet as the NSG was not included in the ARM Template (was not selected as an included resources for export).", targetNetworkSecurityGroup); //} // todo add error if existing target disk storage is not in the same data center / region as vm. if (AfterResourceValidation != null) { await AfterResourceValidation.Invoke(); } }