Thursday, April 26, 2007

How to walk the IIS metabase using WMI and System.Management

Here's a little addition to my last post where I described how to set a property (HttpRedirect) in the IIS 6.0 metabase. I got curious about how one could output the entire metabase using WMI by walking the hierarchy. The trick is the ManagementObject.GetRelated() method which gets all the objects related to the current object. You have to be carefull because that includes the parent and my first attempt at recursively walking the hierarchy got stuck in a loop. I just use a hashtable to keep track of objects that I've already found. Some properties can be arrays and sometimes they are arrays of ManagementObjects that themselves have properties so WritePropertiesAsArray calls back to WriteProperties. The whole thing is written to a file by the Output static class. You'll need to set your own file path to the outputPath constant.
using System;
using System.Collections;
using System.Management;

namespace WMITest.MetabaseWalker
{
 /// 
 /// Summary description for Program.
 /// 
 public class Program
 {
        const string serverName = "192.168.0.166";
        const string userName = "Administrator";
        const string password = "mike";
        const string wmiPathToDefaultWebsite = "IIsComputer.Name=\"LM\"";

        Hashtable outputObjects = new Hashtable();

        [STAThread]
        public static void Main()
        {
            Program program = new Program();
            program.WalkMetabase();
        }

        private void WalkMetabase()
        {
            ConnectionOptions options = new ConnectionOptions();
            options.Username = userName;
            options.Password = password;
            options.Authentication = AuthenticationLevel.PacketPrivacy;

            ManagementPath path = new ManagementPath();
            path.Server = serverName;
            path.NamespacePath = "root/MicrosoftIISv2";

            ManagementScope scope = new ManagementScope(path, options);

            using(ManagementObject obj = new ManagementObject(
                      scope,
                      new ManagementPath(wmiPathToDefaultWebsite), null))
            {
                OutputObject(obj);
            }
        }

        private void OutputObject(ManagementObject obj)
        {
            outputObjects.Add(obj.Path.RelativePath, new object());

            Output.WriteLine();
            Output.WriteLine("{0}", true, obj.Path.RelativePath);
            Output.WriteLine();

            WriteProperties(obj.Properties);
            WriteProperties(obj.SystemProperties);

            foreach(ManagementObject relatedObject in obj.GetRelated())
            {
                if(!outputObjects.ContainsKey(relatedObject.Path.RelativePath))
                {
                    Output.TabIn();
                    OutputObject(relatedObject);
                    Output.TabOut();
                }
            }
        }

        private void WriteProperties(PropertyDataCollection properties)
        {
            Output.TabIn();
            foreach(PropertyData property in properties)
            {
                Output.WriteLine("{0}:\t{1}, \t{2}",
                    property.Name,
                    (property.Value == null) ? "null" : property.Value.ToString(),
                    property.Type.ToString());

                WritePropertyAsArray(property);
            }
            Output.TabOut();
        }

        private void WritePropertyAsArray(PropertyData property)
        {
            if(property.IsArray && property.Value != null)
            {
                ICollection propertyArray = property.Value as ICollection;
                if(propertyArray == null)
                    throw new ApplicationException("can't cast property.Value as ICollection");
                Output.TabIn();
                if(propertyArray.Count == 0)
                {
                    Output.WriteLine("No Items");
                }
                int counter = 0;
                foreach(object item in propertyArray)
                {
                    ManagementBaseObject managementObject = item as ManagementBaseObject;
                    if(managementObject != null)
                    {
                        Output.WriteLine("{0}[{1}]", property.Name, counter.ToString());
                        WriteProperties(managementObject.Properties);
                        counter++;
                    }
                    else
                    {
                        Output.WriteLine("{0}", item.ToString());
                    }
                }
                Output.TabOut();
            }
        }
 }
}
And here's the output class
using System;
using System.IO;

namespace WMITest.MetabaseWalker
{
    /// 
    /// Summary description for Output.
    /// 
    public class Output
    {
        const string outputPath = @"C:\VisualStudio\WMITest\WMITest.MetabaseWalker\Output.txt";
        static int tabs = 0;

        static Output()
        {
            using(File.Create(outputPath)){}
        }

        public static void WriteLine()
        {
            WriteLine("");
        }

        public static void WriteLine(string format, params object[] args)
        {
            WriteLine(format, false, args);
        }

        public static void WriteLine(string format, bool writeToConsole, params object[] args)
        {
            if(writeToConsole)
            {
                Console.WriteLine(new string('\t', tabs) + format, args);
            }
            using(StreamWriter writer = File.AppendText(outputPath))
            {
                writer.WriteLine(new string('\t', tabs) + format, args);
            }
        }

        public static void TabIn()
        {
            tabs++;
        }

        public static void TabOut()
        {
            tabs--;
        }
    }
}

No comments: