Пример #1
0
        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);
            }
        }
Пример #2
0
        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();
            }
        }