Example #1
0
        /// <summary>
        /// Utility to parse the xml metadata output of a ASM classic deployment (cloud service). This utility will take in to params, the virtual network name
        /// containing VMs, and the path + filename of the metadata output file containing deployments.  This utility parses the output of the MetadataExtract powershell
        /// script, and dumps out the metadata to a CSV file.  It will generate two files, one containing the csv output, and one containing any problem areas that neeed
        /// attention before the vnet can be migrated to ARM. The 3 types of problems it looks for are:
        ///     1. Cloud Services that contain non-migratable Availability Sets (1 AS needs to be set for the whole cloud service, or no AS should be set)
        ///     2. Cloud Services that contain a mix of single NIC and multi NIC VMs (yes, this was allowed at one point and can happen)
        ///     3. Web/Worker role cloud services (this cannot be migrated)
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            try
            {
                if (args.Length != 2)
                {
                    Console.WriteLine("Missing parameters. AsmMetadataParser.exe vnetName metadataXmlFile");
                    return;
                }

                string vnetName    = args[0];
                string metadataXml = args[1];

                var fileOutput = System.IO.File.CreateText(vnetName + "-vmresults.csv");

                /*
                 * vmname -- name of the VM
                 * csname -- name of the cloud service
                 * cscleanup -- there is two or more defined availability sets in the cloud service
                 * availset -- the name of the availabilityset
                 * mixedmodeas -- there are both vm's in an availability set and vm's not in an AS in the cloud service
                 * lbendpointname -- load balancer endpoint name
                 * lbport -- load balancer port
                 * lbvip -- load balancer IP
                 * lbtype -- type of load balancer -- options are "external" or "internal"
                 * size -- vm size
                 * agent -- is an azure agent configured
                 * running -- is the vm running or stopped.  Options are "started" or "stopped"
                 * status -- status of the instance -- readyrole, stoppeddeallocated, rolestateunknown
                 * osdisktype -- type of osdisk: standard or premium
                 * datadisks -- the number of datadisks on the vm
                 * datadiskstype -- the type of the data disks -- standard or premium
                 * os -- either windows or linux
                 * ip -- ip address of the primary nic
                 * subnet -- subnet of the primary nic
                 * secondarynics -- secondary nics IP addresses, separated by a '|' char
                 * secondarysubnet -- secondary nics subnet -- all must be the same
                 * mixedmodenics -- true of there are both single nic and multinic VMs in the same cloud service -- which isn't allow in ARM
                 * extensions -- vm extension state -- ignore -- this is wrong and need to change
                 * osdiskname -- the registered os disk name
                 * datadisknames -- the registered data disk names separated by a '|' char
                 * osdiskstorageaccount -- the storage account that holds the OS disk
                 * newstorageaccount -- used only for the optional SA balancing script. The new V2 SA.
                 * newsaresourcegroup -- used only for the optional SA balancing script. The new V2 SA resource group.
                 * osdiskvhd -- url to the os disk vhd
                 * datadiskvhds -- url to the data disk vhds separated by a '|' char, in the same order as datadisknames
                 * endpoints -- contains all the configured endpoint data for the vm
                 * reservedip -- does the deployment/cloudservice have a reserved ip
                 */

                fileOutput.WriteLine("vmname,csname,reservedip,cscleanup,availset,mixedmodeas,lbendpointname,lbport,lbvip,lbtype,size,agent,running,status,osdisktype,datadisks,datadiskstype,os,ip,subnet,secondarynics,secondarysubnet,mixedmodenics,extensions,osdiskname,datadisknames,osdiskstorageaccount,newstorageaccount,newsaresourcegroup,osdiskvhd,datadiskvhds,endpoints");
                SortedList <string, List <VMMetadata> > sortedlist = new SortedList <string, List <VMMetadata> >();
                Dictionary <string, int> asRemediationList         = new Dictionary <string, int>(); // list of VMs and that need availability set cleanup before vnet migration to ARM
                List <string>            wrRemediationList         = new List <string>();            // list of web/worker roles that need removal from the vnet before migration to ARM

                XmlDocument xml = new XmlDocument();
                xml.Load(metadataXml);

                var nsmgr = new XmlNamespaceManager(xml.NameTable);
                nsmgr.AddNamespace("n", "http://schemas.microsoft.com/windowsazure");

                // walk through all deployments/cloudservices
                foreach (XmlNode deployment in xml.DocumentElement.ChildNodes)
                {
                    XmlNode vnet = deployment.SelectSingleNode("n:VirtualNetworkName", nsmgr);
                    if (vnet != null && vnet.InnerText.ToLower() == vnetName.ToLower())
                    {
                        ;  // VM in the correct vnet
                    }
                    else
                    {
                        continue;
                    }

                    string reservedip               = "";
                    bool   singleNicDeployment      = false;
                    bool   multiNicDeployment       = false;
                    int    vmInCloudServiceCount    = 0;
                    string firstAvailabilitySetInCS = "";
                    string previousAS               = "";
                    string previousAS2              = "";

                    string csname = deployment.SelectSingleNode("n:Url", nsmgr).InnerText;
                    csname = csname.Substring(7);
                    csname = csname.Split('.')[0];

                    // does the deployment have a reserved ip?
                    XmlNode roleInstanceList = deployment.SelectSingleNode("n:RoleInstanceList", nsmgr);

                    // read deployment specific data
                    XmlNode virtualIpNode = deployment.SelectSingleNode("n:VirtualIPs/n:VirtualIP", nsmgr);
                    if (virtualIpNode != null)
                    {
                        XmlNode isReservedNode = virtualIpNode.SelectSingleNode("n:IsReserved", nsmgr);
                        if (isReservedNode != null)
                        {
                            reservedip = virtualIpNode.SelectSingleNode("n:Address", nsmgr).InnerText;
                        }
                    }

                    // walk through the VMs inside of the deployment -- roleInstance element
                    foreach (XmlNode roleInstance in roleInstanceList.ChildNodes)
                    {
                        string vmname = roleInstance.SelectSingleNode("n:RoleName", nsmgr).InnerText;

                        // select the role node
                        XmlNode role = deployment.SelectSingleNode("n:RoleList/n:Role[n:RoleName = \"" + vmname + "\"]", nsmgr);
                        if (role.SelectSingleNode("n:RoleSize", nsmgr) == null)  // indicator that this is a web/worker role, not an IaaS VM
                        {
                            // PaaS web/worker role instance running in the vnet
                            string  cssubnet     = "";
                            XmlNode cssubnetNode = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:SubnetNames", nsmgr);
                            if (cssubnetNode.ChildNodes.Count > 0)
                            {
                                cssubnet = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:SubnetNames/n:SubnetName", nsmgr).InnerText.ToLower();
                            }

                            wrRemediationList.Add(csname + "     " + cssubnet);
                            continue;
                        }

                        vmInCloudServiceCount++;
                        string  size           = role.SelectSingleNode("n:RoleSize", nsmgr).InnerText;
                        string  readyrole      = roleInstance.SelectSingleNode("n:PowerState", nsmgr).InnerText;
                        string  instancestatus = roleInstance.SelectSingleNode("n:InstanceStatus", nsmgr).InnerText;
                        XmlNode agentNode      = roleInstance.SelectSingleNode("n:GuestAgentStatus/n:Status", nsmgr);
                        string  agent          = "";
                        if (agentNode != null)
                        {
                            agent = agentNode.InnerText;
                        }

                        XmlNode availsetNode = role.SelectSingleNode("n:AvailabilitySetName", nsmgr);
                        string  availset     = (availsetNode != null) ? availsetNode.InnerText : "";
                        if (!string.IsNullOrEmpty(previousAS2) && !string.IsNullOrEmpty(availset) && previousAS2 != availset)
                        {
                            previousAS = "true";
                        }
                        if (previousAS != availset)
                        {
                            previousAS2 = availset;
                        }

                        // cloud service has availability set incompatability with ARM and will take some AS cleanup
                        bool flagCloudServiceForCleanup = false;
                        if (vmInCloudServiceCount == 1) // first VM
                        {
                            firstAvailabilitySetInCS = availset;
                        }
                        if (firstAvailabilitySetInCS != availset)
                        {
                            flagCloudServiceForCleanup = true;
                        }

                        if (flagCloudServiceForCleanup)
                        {
                            if (asRemediationList.ContainsKey(csname))
                            {
                                asRemediationList[csname] = vmInCloudServiceCount;
                            }
                            else
                            {
                                asRemediationList.Add(csname, vmInCloudServiceCount);
                            }
                        }

                        // search for LoadBalancedEndpointSetName elements
                        string      loadbalancedEndpointSetName = "";
                        string      loadbalancedPort            = "";
                        string      loadbalancedVip             = "";
                        string      loadbalancedType            = "";
                        XmlNodeList endpoints = role.SelectNodes("n:ConfigurationSets/n:ConfigurationSet/n:InputEndpoints/n:InputEndpoint[n:LoadBalancedEndpointSetName]", nsmgr);
                        if (endpoints.Count == 1)
                        {
                            loadbalancedEndpointSetName = endpoints[0].SelectSingleNode("n:LoadBalancedEndpointSetName", nsmgr).InnerText;
                            loadbalancedPort            = endpoints[0].SelectSingleNode("n:LocalPort", nsmgr).InnerText;
                            XmlNode vipNode  = endpoints[0].SelectSingleNode("n:Vip", nsmgr);
                            XmlNode nameNode = endpoints[0].SelectSingleNode("n:LoadBalancerName", nsmgr);

                            if (vipNode != null)
                            {
                                loadbalancedVip = vipNode.InnerText;
                            }
                            if (nameNode != null)
                            {
                                loadbalancedType = "internal";
                            }
                            else
                            {
                                loadbalancedType = "external";
                            }
                        }
                        else if (endpoints.Count > 1)
                        {
                            loadbalancedType = "unexpected";

                            loadbalancedEndpointSetName = endpoints[0].SelectSingleNode("n:LoadBalancedEndpointSetName", nsmgr).InnerText;
                            loadbalancedPort            = endpoints[0].SelectSingleNode("n:LocalPort", nsmgr).InnerText;
                            XmlNode vipNode  = endpoints[0].SelectSingleNode("n:Vip", nsmgr);
                            XmlNode nameNode = endpoints[0].SelectSingleNode("n:LoadBalancerName", nsmgr);

                            if (vipNode != null)
                            {
                                loadbalancedVip = vipNode.InnerText;
                            }
                            if (nameNode != null)
                            {
                                loadbalancedType = "internal";
                            }
                            else
                            {
                                loadbalancedType = "external";
                            }
                        }

                        string allEndpoints = "";
                        // Add together all endpoints into this format
                        // setname|privateport|publicport|endpointname|protocol|vip|directreturn|lbname|lbtype;setname|privateport|publicport|endpointname|protocol|vip|directreturn|lbname|lbtype
                        XmlNode endpoints2 = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:InputEndpoints", nsmgr);
                        if (endpoints2 != null)
                        {
                            foreach (XmlNode endpoint2 in endpoints2.ChildNodes)
                            {
                                string setname      = "";
                                string privateport  = "";
                                string publicport   = "";
                                string endpointname = "";
                                string protocol     = "";
                                string vip          = "";
                                string directreturn = "";
                                string lbname       = "";
                                string lbtype       = "";

                                XmlNode setNode = endpoint2.SelectSingleNode("n:LoadBalancedEndpointSetName", nsmgr);
                                if (setNode != null)
                                {
                                    setname = endpoint2.SelectSingleNode("n:LoadBalancedEndpointSetName", nsmgr).InnerText;
                                }

                                privateport  = endpoint2.SelectSingleNode("n:LocalPort", nsmgr).InnerText;
                                publicport   = endpoint2.SelectSingleNode("n:Port", nsmgr).InnerText;
                                endpointname = endpoint2.SelectSingleNode("n:Name", nsmgr).InnerText;
                                protocol     = endpoint2.SelectSingleNode("n:Protocol", nsmgr).InnerText;
                                directreturn = endpoint2.SelectSingleNode("n:EnableDirectServerReturn", nsmgr).InnerText;

                                XmlNode vipNode2 = endpoint2.SelectSingleNode("n:Vip", nsmgr);
                                if (vipNode2 != null)
                                {
                                    vip = vipNode2.InnerText;
                                }

                                XmlNode nameNode2 = endpoint2.SelectSingleNode("n:LoadBalancerName", nsmgr);
                                if (nameNode2 != null)
                                {
                                    lbtype = "internal";
                                    lbname = nameNode2.InnerText;
                                }
                                else
                                {
                                    lbtype = "external";
                                }

                                if (allEndpoints == "")
                                {
                                    allEndpoints = setname + "|" + privateport + "|" + publicport + "|" + endpointname + "|" + protocol + "|" + vip + "|" + directreturn + "|" + lbname + "|" + lbtype;
                                }
                                else
                                {
                                    allEndpoints = allEndpoints + ";" + setname + "|" + privateport + "|" + publicport + "|" + endpointname + "|" + protocol + "|" + vip + "|" + directreturn + "|" + lbname + "|" + lbtype;
                                }
                            }
                        }

                        string  os                   = role.SelectSingleNode("n:OSVirtualHardDisk/n:OS", nsmgr).InnerText;
                        XmlNode osDiskTypeNode       = role.SelectSingleNode("n:OSVirtualHardDisk/n:IOType", nsmgr);
                        string  osDiskType           = (osDiskTypeNode != null) ? osDiskType = osDiskTypeNode.InnerText : osDiskType = "Standard";
                        string  osDiskName           = role.SelectSingleNode("n:OSVirtualHardDisk/n:DiskName", nsmgr).InnerText;
                        string  osDiskVhd            = role.SelectSingleNode("n:OSVirtualHardDisk/n:MediaLink", nsmgr).InnerText;
                        string  osDiskStorageAccount = role.SelectSingleNode("n:OSVirtualHardDisk/n:MediaLink", nsmgr).InnerText;
                        osDiskStorageAccount = osDiskStorageAccount.Substring(8);
                        int charIndex = osDiskStorageAccount.IndexOf('.');
                        osDiskStorageAccount = osDiskStorageAccount.Substring(0, charIndex);

                        string  ip;
                        XmlNode ipNode       = roleInstance.SelectSingleNode("n:IpAddress", nsmgr);
                        XmlNode staticIpNode = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:StaticVirtualNetworkIPAddress", nsmgr);
                        if (ipNode != null)
                        {
                            ip = ipNode.InnerText;
                        }
                        else if (staticIpNode != null)
                        {
                            ip = staticIpNode.InnerText;
                        }
                        else
                        {
                            ip = "";
                        }

                        XmlNode disksNode     = role.SelectSingleNode("n:DataVirtualHardDisks", nsmgr);
                        int     disks         = 0;
                        string  disksType     = "";
                        string  dataDiskNames = "";
                        string  dataDiskVhds  = "";
                        if (disksNode != null)
                        {
                            disks = disksNode.ChildNodes.Count;
                            if (disks > 0)
                            {
                                XmlNode diskTypeNode = disksNode.ChildNodes[0].SelectSingleNode("n:IOType", nsmgr);
                                disksType = (diskTypeNode != null) ? diskTypeNode.InnerText : "Standard";

                                foreach (XmlNode diskNode in disksNode.ChildNodes)
                                {
                                    if (dataDiskNames != "")
                                    {
                                        dataDiskNames = dataDiskNames + "|";
                                    }
                                    dataDiskNames = dataDiskNames + diskNode.SelectSingleNode("n:DiskName", nsmgr).InnerText;

                                    if (dataDiskVhds != "")
                                    {
                                        dataDiskVhds = dataDiskVhds + "|";
                                    }
                                    dataDiskVhds = dataDiskVhds + diskNode.SelectSingleNode("n:MediaLink", nsmgr).InnerText;
                                }
                            }
                        }

                        string  subnet     = "";
                        XmlNode subnetNode = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:SubnetNames", nsmgr);
                        if (subnetNode.ChildNodes.Count > 0)
                        {
                            subnet = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:SubnetNames/n:SubnetName", nsmgr).InnerText.ToLower();
                        }

                        string  extensions        = "";
                        XmlNode resourceExtension = role.SelectSingleNode("n:ResourceExtensionReferences", nsmgr);
                        if (resourceExtension != null)
                        {
                            foreach (XmlNode extension in resourceExtension.ChildNodes)
                            {
                                string ex = extension.SelectSingleNode("n:ReferenceName", nsmgr).InnerText;
                                if (ex == "BGInfo")
                                {
                                    continue;
                                }

                                if (extensions == "")
                                {
                                    extensions = ex;
                                }
                                else
                                {
                                    extensions = extensions + "|" + ex;
                                }
                            }
                        }

                        string  secondaryIPs    = "";
                        string  secondarysubnet = "";
                        XmlNode nicNodes        = roleInstance.SelectSingleNode("n:NetworkInterfaces", nsmgr);
                        if (nicNodes != null)
                        {
                            foreach (XmlNode nicnode in nicNodes.ChildNodes)
                            {
                                if (secondaryIPs != "")
                                {
                                    secondaryIPs = secondaryIPs + "|";
                                }
                                secondaryIPs       = secondaryIPs + nicnode.SelectSingleNode("n:IPConfigurations/n:IPConfiguration/n:Address", nsmgr).InnerText;
                                secondarysubnet    = nicnode.SelectSingleNode("n:IPConfigurations/n:IPConfiguration/n:SubnetName", nsmgr).InnerText.ToLower();
                                multiNicDeployment = true;
                            }

                            if (nicNodes.ChildNodes.Count == 0)
                            {
                                singleNicDeployment = true;
                            }
                        }
                        else
                        {
                            singleNicDeployment = true;
                        }

                        // Collect metadata in a structure to be added to a sorted list, so we can group VMs by cloud service
                        // This is needed so when we hydrate the VMs with a PS script, we can launch multiple instances.
                        // Each instance will be isolated by cloud service so we don't run into any concurrency issues / locking.
                        VMMetadata md = new VMMetadata();
                        md.m_vmname               = vmname;
                        md.m_csname               = csname;
                        md.m_size                 = size;
                        md.m_agent                = agent;
                        md.m_readyrole            = readyrole;
                        md.m_osDiskType           = osDiskType;
                        md.m_os                   = os;
                        md.m_ip                   = ip;
                        md.m_instancestatus       = instancestatus;
                        md.m_disks                = disks;
                        md.m_disksType            = disksType;
                        md.m_subnet               = subnet;
                        md.m_secondaryIPs         = secondaryIPs;
                        md.m_secondarysubnet      = secondarysubnet;
                        md.m_availset             = availset;
                        md.m_mixedmodenics        = (singleNicDeployment && multiNicDeployment && (instancestatus != "StoppedDeallocated")) ? "true" : "";
                        md.m_mixedmodeas          = previousAS; // (vmsInAvailabilitySet && vmsNotInAvailabilitySet) ? "true" : "";
                        md.m_lbendpointname       = loadbalancedEndpointSetName;
                        md.m_lbendpointport       = loadbalancedPort;
                        md.m_lbtype               = loadbalancedType;
                        md.m_lbvip                = loadbalancedVip;
                        md.m_flagCsForCleanup     = (flagCloudServiceForCleanup) ? csname : "";
                        md.m_extensions           = extensions;
                        md.m_osDiskName           = osDiskName;
                        md.m_dataDiskNames        = dataDiskNames;
                        md.m_osDiskVhd            = osDiskVhd;
                        md.m_dataDiskVhds         = dataDiskVhds;
                        md.m_osDiskStorageAccount = osDiskStorageAccount;
                        md.m_endpoints            = allEndpoints;
                        md.m_reservedip           = reservedip;

                        if (!sortedlist.Keys.Contains((string)csname))
                        {
                            List <VMMetadata> l = new List <VMMetadata>();
                            l.Add(md);
                            sortedlist.Add((string)csname, l);
                        }
                        else
                        {
                            List <VMMetadata> l = sortedlist[(string)csname];
                            l.Add(md);
                        }
                    }
                }

                foreach (List <VMMetadata> l_item in sortedlist.Values)
                {
                    foreach (VMMetadata item in l_item)
                    {
                        fileOutput.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29},{30},{31}",
                                             item.m_vmname, item.m_csname, item.m_reservedip, item.m_flagCsForCleanup, item.m_availset, item.m_mixedmodeas, item.m_lbendpointname, item.m_lbendpointport, item.m_lbvip, item.m_lbtype,
                                             item.m_size, item.m_agent,
                                             item.m_readyrole, item.m_instancestatus, item.m_osDiskType, item.m_disks, item.m_disksType, item.m_os,
                                             item.m_ip, item.m_subnet, item.m_secondaryIPs, item.m_secondarysubnet, item.m_mixedmodenics, item.m_extensions,
                                             item.m_osDiskName, item.m_dataDiskNames, item.m_osDiskStorageAccount, "", "", item.m_osDiskVhd, item.m_dataDiskVhds, item.m_endpoints);
                    }
                }

                fileOutput.Close();

                // Build a report that shows the VMs that need remediation/cleanup for AS compatability
                var fileRemediation  = System.IO.File.CreateText(vnetName + "-RemediationList.txt");
                int remediationCount = 0;
                fileRemediation.WriteLine("Availability Set Cleanup");
                fileRemediation.WriteLine("Cloud Service     Number of VMs");
                fileRemediation.WriteLine();
                foreach (string key in asRemediationList.Keys)
                {
                    fileRemediation.WriteLine("{0}    {1}", key, asRemediationList[key]);
                    remediationCount = remediationCount + asRemediationList[key];
                }
                fileRemediation.WriteLine();
                fileRemediation.WriteLine("AS Cleanup Totals");
                fileRemediation.WriteLine();
                fileRemediation.WriteLine("CloudServices: {0}   VMs: {1}", asRemediationList.Count, remediationCount);
                fileRemediation.WriteLine("\n\n");

                fileRemediation.WriteLine("Web/Worker Role Cleanup");
                fileRemediation.WriteLine("web/worker role cloud service list");
                fileRemediation.WriteLine();
                if (wrRemediationList.Count == 0)
                {
                    fileRemediation.WriteLine("no web/worker roles found");
                }
                foreach (string key in wrRemediationList)
                {
                    fileRemediation.WriteLine("-- {0}", key);
                }
                fileRemediation.WriteLine();
                fileRemediation.WriteLine("For Cloud Services that have single and multi NIC VMs in the same service, look at the csv file -- mixmodenics column = true");

                fileRemediation.Close();
                Console.WriteLine("Success. CSV created.");
            }
            catch (Exception e)
            {
                Console.WriteLine("Unexpected problem occured: " + e.Message + "\n" + e.StackTrace);
            }
        }
Example #2
0
        /// <summary>
        /// Utility to parse the xml metadata output of a ASM classic deployment (cloud service). This utility will take in to params, the virtual network name
        /// containing VMs, and the path + filename of the metadata output file containing deployments.  This utility parses the output of the MetadataExtract powershell
        /// script, and dumps out the metadata to a CSV file.  It will generate two files, one containing the csv output, and one containing any problem areas that neeed
        /// attention before the vnet can be migrated to ARM. The 3 types of problems it looks for are:
        ///     1. Cloud Services that contain non-migratable Availability Sets (1 AS needs to be set for the whole cloud service, or no AS should be set)
        ///     2. Cloud Services that contain a mix of single NIC and multi NIC VMs (yes, this was allowed at one point and can happen)
        ///     3. Web/Worker role cloud services (this cannot be migrated)
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            try
            {
                if (args.Length != 2)
                {
                    Console.WriteLine("Missing parameters. AsmMetadataParser.exe vnetName metadataXmlFile");
                    return;
                }

                string vnetName    = args[0];
                string metadataXml = args[1];

                var fileOutput = System.IO.File.CreateText(vnetName + "-vmresults.csv");
                fileOutput.WriteLine("vmname,csname,cscleanup,availset,mixedmodeas,lbendpointname,lbport,lbvip,lbtype,size,agent,running,osdisktype,datadisks,datadiskstype,os,ip,subnet,secondarynics,secondarysubnet,mixedmodenics");
                SortedList <string, List <VMMetadata> > sortedlist = new SortedList <string, List <VMMetadata> >();
                Dictionary <string, int> asRemediationList         = new Dictionary <string, int>(); // list of VMs and that need availability set cleanup before vnet migration to ARM
                List <string>            wrRemediationList         = new List <string>();            // list of web/worker roles that need removal from the vnet before migration to ARM

                XmlDocument xml = new XmlDocument();
                xml.Load(metadataXml);

                var nsmgr = new XmlNamespaceManager(xml.NameTable);
                nsmgr.AddNamespace("n", "http://schemas.microsoft.com/windowsazure");

                // walk through all deployments/cloudservices
                foreach (XmlNode deployment in xml.DocumentElement.ChildNodes)
                {
                    XmlNode vnet = deployment.SelectSingleNode("n:VirtualNetworkName", nsmgr);
                    if (vnet != null && vnet.InnerText.ToLower() == vnetName.ToLower())
                    {
                        ;  // VM in the correct vnet
                    }
                    else
                    {
                        continue;
                    }

                    bool   singleNicDeployment      = false;
                    bool   multiNicDeployment       = false;
                    int    vmInCloudServiceCount    = 0;
                    string firstAvailabilitySetInCS = "";
                    string previousAS  = "";
                    string previousAS2 = "";

                    string csname = deployment.SelectSingleNode("n:Url", nsmgr).InnerText;
                    csname = csname.Substring(7);
                    csname = csname.Split('.')[0];

                    // read deployment specific data
                    XmlNode roleInstanceList = deployment.SelectSingleNode("n:RoleInstanceList", nsmgr);

                    // walk through the VMs inside of the deployment -- roleInstance element
                    foreach (XmlNode roleInstance in roleInstanceList.ChildNodes)
                    {
                        string vmname = roleInstance.SelectSingleNode("n:RoleName", nsmgr).InnerText;

                        // select the role node
                        XmlNode role = deployment.SelectSingleNode("n:RoleList/n:Role[n:RoleName = \"" + vmname + "\"]", nsmgr);
                        if (role.SelectSingleNode("n:RoleSize", nsmgr) == null)  // indicator that this is a web/worker role, not an IaaS VM
                        {
                            // PaaS web/worker role instance running in the vnet
                            wrRemediationList.Add(csname);
                            continue;
                        }

                        vmInCloudServiceCount++;
                        string  size      = role.SelectSingleNode("n:RoleSize", nsmgr).InnerText;
                        string  readyrole = roleInstance.SelectSingleNode("n:PowerState", nsmgr).InnerText;
                        XmlNode agentNode = roleInstance.SelectSingleNode("n:GuestAgentStatus/n:Status", nsmgr);
                        string  agent     = (agentNode != null) ? "TRUE" : "FALSE";

                        XmlNode availsetNode = role.SelectSingleNode("n:AvailabilitySetName", nsmgr);
                        string  availset     = (availsetNode != null) ? availsetNode.InnerText : "";
                        if (!string.IsNullOrEmpty(previousAS2) && !string.IsNullOrEmpty(availset) && previousAS2 != availset)
                        {
                            previousAS = "true";
                        }
                        if (previousAS != availset)
                        {
                            previousAS2 = availset;
                        }

                        // cloud service has availability set incompatability with ARM and will take some AS cleanup
                        bool flagCloudServiceForCleanup = false;
                        if (vmInCloudServiceCount == 1) // first VM
                        {
                            firstAvailabilitySetInCS = availset;
                        }
                        if (firstAvailabilitySetInCS != availset)
                        {
                            flagCloudServiceForCleanup = true;
                        }

                        if (flagCloudServiceForCleanup)
                        {
                            if (asRemediationList.ContainsKey(csname))
                            {
                                asRemediationList[csname] = vmInCloudServiceCount;
                            }
                            else
                            {
                                asRemediationList.Add(csname, vmInCloudServiceCount);
                            }
                        }

                        // search for LoadBalancedEndpointSetName elements
                        string      loadbalancedEndpointSetName = "";
                        string      loadbalancedPort            = "";
                        string      loadbalancedVip             = "";
                        string      loadbalancedType            = "";
                        XmlNodeList endpoints = role.SelectNodes("n:ConfigurationSets/n:ConfigurationSet/n:InputEndpoints/n:InputEndpoint[n:LoadBalancedEndpointSetName]", nsmgr);
                        if (endpoints.Count == 1)
                        {
                            loadbalancedEndpointSetName = endpoints[0].SelectSingleNode("n:LoadBalancedEndpointSetName", nsmgr).InnerText;
                            loadbalancedPort            = endpoints[0].SelectSingleNode("n:LocalPort", nsmgr).InnerText;
                            XmlNode vipNode  = endpoints[0].SelectSingleNode("n:Vip", nsmgr);
                            XmlNode nameNode = endpoints[0].SelectSingleNode("n:LoadBalancerName", nsmgr);

                            if (vipNode != null)
                            {
                                loadbalancedVip = vipNode.InnerText;
                            }
                            if (nameNode != null)
                            {
                                loadbalancedType = "internal";
                            }
                            else
                            {
                                loadbalancedType = "external";
                            }
                        }
                        else if (endpoints.Count > 1)
                        {
                            loadbalancedType = "unexpected";

                            loadbalancedEndpointSetName = endpoints[0].SelectSingleNode("n:LoadBalancedEndpointSetName", nsmgr).InnerText;
                            loadbalancedPort            = endpoints[0].SelectSingleNode("n:LocalPort", nsmgr).InnerText;
                            XmlNode vipNode  = endpoints[0].SelectSingleNode("n:Vip", nsmgr);
                            XmlNode nameNode = endpoints[0].SelectSingleNode("n:LoadBalancerName", nsmgr);

                            if (vipNode != null)
                            {
                                loadbalancedVip = vipNode.InnerText;
                            }
                            if (nameNode != null)
                            {
                                loadbalancedType = "internal";
                            }
                            else
                            {
                                loadbalancedType = "external";
                            }
                        }

                        string  os             = role.SelectSingleNode("n:OSVirtualHardDisk/n:OS", nsmgr).InnerText;
                        XmlNode osDiskTypeNode = role.SelectSingleNode("n:OSVirtualHardDisk/n:IOType", nsmgr);
                        string  osDiskType     = (osDiskTypeNode != null) ? osDiskType = osDiskTypeNode.InnerText : osDiskType = "Standard";
                        string  ip;
                        XmlNode ipNode       = roleInstance.SelectSingleNode("n:IpAddress", nsmgr);
                        XmlNode staticIpNode = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:StaticVirtualNetworkIPAddress", nsmgr);
                        if (ipNode != null)
                        {
                            ip = ipNode.InnerText;
                        }
                        else if (staticIpNode != null)
                        {
                            ip = staticIpNode.InnerText;
                        }
                        else
                        {
                            ip = "";
                        }

                        XmlNode disksNode = role.SelectSingleNode("n:DataVirtualHardDisks", nsmgr);
                        int     disks     = 0;
                        string  disksType = "";
                        if (disksNode != null)
                        {
                            disks = disksNode.ChildNodes.Count;
                            if (disks > 0)
                            {
                                XmlNode diskTypeNode = disksNode.ChildNodes[0].SelectSingleNode("n:IOType", nsmgr);
                                disksType = (diskTypeNode != null) ? diskTypeNode.InnerText : "Standard";
                            }
                        }

                        string  subnet     = "";
                        XmlNode subnetNode = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:SubnetNames", nsmgr);
                        if (subnetNode.ChildNodes.Count > 0)
                        {
                            subnet = role.SelectSingleNode("n:ConfigurationSets/n:ConfigurationSet/n:SubnetNames/n:SubnetName", nsmgr).InnerText.ToLower();
                        }

                        string  secondaryIPs    = "";
                        string  secondarysubnet = "";
                        XmlNode nicNodes        = roleInstance.SelectSingleNode("n:NetworkInterfaces", nsmgr);
                        if (nicNodes != null)
                        {
                            foreach (XmlNode nicnode in nicNodes.ChildNodes)
                            {
                                if (secondaryIPs != "")
                                {
                                    secondaryIPs = secondaryIPs + "|";
                                }
                                secondaryIPs       = secondaryIPs + nicnode.SelectSingleNode("n:IPConfigurations/n:IPConfiguration/n:Address", nsmgr).InnerText;
                                secondarysubnet    = nicnode.SelectSingleNode("n:IPConfigurations/n:IPConfiguration/n:SubnetName", nsmgr).InnerText.ToLower();
                                multiNicDeployment = true;
                            }

                            if (nicNodes.ChildNodes.Count == 0)
                            {
                                singleNicDeployment = true;
                            }
                        }
                        else
                        {
                            singleNicDeployment = true;
                        }

                        // Collect metadata in a structure to be added to a sorted list, so we can group VMs by cloud service
                        // This is needed so when we hydrate the VMs with a PS script, we can launch multiple instances.
                        // Each instance will be isolated by cloud service so we don't run into any concurrency issues / locking.
                        VMMetadata md = new VMMetadata();
                        md.m_vmname           = vmname;
                        md.m_csname           = csname;
                        md.m_size             = size;
                        md.m_agent            = agent;
                        md.m_readyrole        = readyrole;
                        md.m_osDiskType       = osDiskType;
                        md.m_os               = os;
                        md.m_ip               = ip;
                        md.m_disks            = disks;
                        md.m_disksType        = disksType;
                        md.m_subnet           = subnet;
                        md.m_secondaryIPs     = secondaryIPs;
                        md.m_secondarysubnet  = secondarysubnet;
                        md.m_availset         = availset;
                        md.m_mixedmodenics    = (singleNicDeployment && multiNicDeployment) ? "true" : "";
                        md.m_mixedmodeas      = previousAS; // (vmsInAvailabilitySet && vmsNotInAvailabilitySet) ? "true" : "";
                        md.m_lbendpointname   = loadbalancedEndpointSetName;
                        md.m_lbendpointport   = loadbalancedPort;
                        md.m_lbtype           = loadbalancedType;
                        md.m_lbvip            = loadbalancedVip;
                        md.m_flagCsForCleanup = (flagCloudServiceForCleanup) ? csname : "";

                        if (!sortedlist.Keys.Contains((string)csname))
                        {
                            List <VMMetadata> l = new List <VMMetadata>();
                            l.Add(md);
                            sortedlist.Add((string)csname, l);
                        }
                        else
                        {
                            List <VMMetadata> l = sortedlist[(string)csname];
                            l.Add(md);
                        }
                    }
                }

                //fileOutput.WriteLine("vmname,csname,cscleanup,availset,mixedmodeas,lbendpointname,lbport,lbvip,lbtype,size,agent,running,osdisktype,datadisks,datadiskstype,os,ip,subnet,secondarynics,secondarysubnet,mixedmodenics");

                foreach (List <VMMetadata> l_item in sortedlist.Values)
                {
                    foreach (VMMetadata item in l_item)
                    {
                        fileOutput.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20}",
                                             item.m_vmname, item.m_csname, item.m_flagCsForCleanup, item.m_availset, item.m_mixedmodeas, item.m_lbendpointname, item.m_lbendpointport, item.m_lbvip, item.m_lbtype,
                                             item.m_size, item.m_agent,
                                             item.m_readyrole, item.m_osDiskType, item.m_disks, item.m_disksType, item.m_os,
                                             item.m_ip, item.m_subnet, item.m_secondaryIPs, item.m_secondarysubnet, item.m_mixedmodenics);
                    }
                }

                fileOutput.Close();

                // Build a report that shows the VMs that need remediation/cleanup for AS compatability
                var fileRemediation  = System.IO.File.CreateText(vnetName + "-RemediationList.txt");
                int remediationCount = 0;
                fileRemediation.WriteLine("Availability Set Cleanup");
                fileRemediation.WriteLine("Cloud Service     Number of VMs");
                fileRemediation.WriteLine();
                foreach (string key in asRemediationList.Keys)
                {
                    fileRemediation.WriteLine("{0}    {1}", key, asRemediationList[key]);
                    remediationCount = remediationCount + asRemediationList[key];
                }
                fileRemediation.WriteLine();
                fileRemediation.WriteLine("AS Cleanup Totals");
                fileRemediation.WriteLine();
                fileRemediation.WriteLine("CloudServices: {0}   VMs: {1}", asRemediationList.Count, remediationCount);
                fileRemediation.WriteLine("\n\n");

                fileRemediation.WriteLine("Web/Worker Role Cleanup");
                fileRemediation.WriteLine("web/worker role cloud service list");
                fileRemediation.WriteLine();
                if (wrRemediationList.Count == 0)
                {
                    fileRemediation.WriteLine("no web/worker roles found");
                }
                foreach (string key in wrRemediationList)
                {
                    fileRemediation.WriteLine("-- {0}", key);
                }
                fileRemediation.WriteLine();
                fileRemediation.WriteLine("For Cloud Services that have single and multi NIC VMs in the same service, look at the csv file -- mixmodenics column = true");

                fileRemediation.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine("Unexpected problem occured: " + e.Message + "\n" + e.StackTrace);
            }
        }