Blog Post

MIF Busters

,

Management Information Format (MIF) files are formatted text files containing additional information about hardware and software components. The MIF format originated out of the Desktop Management Interface Standards in 1996, but has since been displaced by newer technologies including CIM. As an aside Microsoft's implementation of CIM and WBEM standards is WMI.
 
Why am I explaining MIF files? Even though the DMI standard was end of life in 2005, Microsoft System Management Server (SMS)/System Center Configuration Manager (SCCM) use MIF files to extend the information collected on managed devices. If you use SMS/SCCM, MIF files are still relevant.
 
The format of a MIF files looks something like this:
 
Start Component
        Name = "Acme Server Location"
        Start Group
                Name    = "Acme Server Location"
                ID      = 1
                Class   = "Acme Server Location"
                Start Attribute
                        Name    = "Admin Contact"
                        ID      = 4
                        Type    = String(50)
                        Value   = "Chad Miller"
                End Attribute
                Start Attribute
                        Name    = "Admin Phone"
                        ID      = 5
                        Type    = String(40)
                        Value   = "55500"
                End Attribute
        End Group
End Component
 
The above MIF file is used to assign contact information for a server and may contain additional information including location, asset tag, or server type. If SMS/SCCM has been configured to collect MIF files as part of its inventorying process, this additional information will be added to SMS/SCCM. Depending on your organization's use of MIF files there could be alot of information contained in these files. So, I thought I'd write a PowerShell script to parse MIF files.
 
One of the things that struck me about the MIF file format is its resemblance to XML. Instead of closing and ending tags, there are "Start" and "End" sections. Its almost as if a MIF file is an XML file stuck in a text file body.  Converting a MIF into XML seems like a logically approach in extracting the data in a useable format. The script below uses a series of replace and regex to transform a MIF file into a XML document. Once we have an XML document, we can select the properties and obtain the parent group and component attributes:
 
param ($fileName, $computerName=$env:ComputerName)

#######################
function ConvertTo-MIFXml
{
    param ($mifFile)

    $mifText = gc $mifFile |
    #Remove illegal XML characters
    % { $_ -replace "&", "&" } |
    % { $_ -replace"'", "'" } |
    % { $_ -replace "<", "&lt;" } |
    % { $_ -replace ">", "&gt;" } |
    #Create Component attribute
    % { $_ -replace 'Start Component','<Component' } |
    #Create Group attribute
    % { $_ -replace 'Start Group','><Group' } |
    #Create Attribute attribute
    % { $_ -replace 'Start Attribute','><Attribute' } |
    #Create closing tags
    % { $_ -replace 'End Attribute','></Attribute>' } |
    % { $_ -replace 'End Group','</Group>' } |
    % { $_ -replace 'End Component','</Component>'} |
    #Remove all quotes
    % { $_ -replace '"' } |
    #Remove MIF comments. MIF Comments start with //
    % { $_ -replace "(\s*//\s*.*)" } |
    #Extract name/value and quote value
    % { $_ -replace "\s*([^\s]+)\s*=\s*(.+$)",'$1="$2"' } |
    #Replace tabs with spaces
    % { $_ -replace "\t", " " } |
    #Replace 2 spaces with 1
    % { $_ -replace "\s{2,}", " " }

    #Join the array, cleanup some spacing and extra > signs
    [xml]$mifXml = [string]::Join(" ", $mifText) -replace ">\s*>",">" -replace "\s+>",">"

    return $mifXml

} #ConvertTo-MIFXml

#######################
ConvertTo-MIFXml $fileName | foreach {$_.component} | foreach {$_.Group} | foreach {$_.Attribute} | select @{n='SystemName';e={$computerName}}, `
@{n='Component';e={$($_.psbase.ParentNode).psbase.ParentNode.name}}, @{n='Group';e={$_.psbase.ParentNode.name}}, `
@{n='FileName';e={[System.IO.Path]::GetFileName($FileName)}}, ID, Name, Value

Running the above script named mifparser.ps1 on the Location.mif file shown earlier produces the following output:
 
 
 
That's nice, but what if you wanted to collect the MIF files from every server you administer. The default location for MIF files on SMS/SCCM managed computers is C:\WINDOWS\System32\CCM\Inventory\noidmifs on x86 machines or C:\WINDOWS\SysWOW64\CCM\Inventory\noidmifs on x64 machines. If you query the SMS/SCCM database you can produce a CSV file listing the server name and MIF file path. Run the following SQL query from SQL Server Management Studio connected to your SMS/SCCM database and save the output as a CSV file:
 
SELECT 
DISTINCT SMS_R_System.Name0,
CASE
WHEN SMS_G_System_OS.Name0 LIKE '%x64%' THEN '\\' + SMS_R_System.Name0 + '\c$\WINDOWS\SysWOW64\CCM\Inventory\noidmifs\*.mif'
ELSE '\\' + SMS_R_System.Name0 + '\c$\WINDOWS\System32\CCM\Inventory\noidmifs\*.mif'
END AS OS
FROM SMS_Acme.dbo.System_DISC AS SMS_R_System

JOIN SMS_Acme.dbo.Operating_System_DATA AS SMS_G_System_OS
ON SMS_G_System_OS.MachineID = SMS_R_System.ItemKey

You can then use the CSV file as input to the mifparser.ps1 script:
 
import-csv C:\bin\servers.csv | %{$server = $_.server; get-childitem $_.path | select fullname, @{n='server'; e={$server}}} | %{c:\bin\mifparser.ps1 $_.fullname $_.server} | export-csv ./mif.csv -noTypeInformat
 
Now that's better, I now have a consolidated CSV file with all the MIF file information. I've parsed a few hundred MIF files without issues. I can then load the CSV file into a SQL table for further analysis.
 
CREATE TABLE [dbo].[mif](
        [SystemName] [varchar](1000) NULL,
        [Component] [varchar](1000) NULL,
        [Group] [varchar](1000) NULL,
        [FileName] [varchar](1000) NULL,
        [ID] [varchar](1000) NULL,
        [Name] [varchar](1000) NULL,
        [Value] [varchar](1000) NULL
);
 
BULK INSERT dbautility.dbo.mif
   FROM 'C:\Users\u00\Desktop\mif.csv'
   WITH ( FIELDTERMINATOR =',',
         ROWTERMINATOR ='\n')
 
 

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating