update-wmi.ps1

function Update-Wmi
{
    <#
    .Synopsis
        Stores data in WMI
    .Description
        Stores data in the WMI repository
    .Link
        Select-Wmi
    .Example
        Get-ChildItem |
            Select-Object Name, LastWriteTime, LastAccessTime, CreationTime |
            Update-Wmi
    #>

    [OutputType([Nullable])]
    param(
    # Any input object
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [PSObject]
    $InputObject,

    # The namespace the object will be stored in
    [string]
    $Namespace = "root\custom\data",

    # The name of the class. If not provided, the ClassName will be taken from the object. Illegal characters in WMI class names (like ., :, or /) will be converted into _dot_, _colon_, and _slash_ respectively.
    [string]
    $ClassName,

    # At least one property must be registered as a key
    [Parameter(Mandatory=$true,Position=0)]
    [string[]]
    $Key,

    # If set, will update existing instances. If not set, only new data will be added.
    [Switch]
    $Force
    )


    begin {
        #region Translate column types into CIM column types
        $cimColumnType = {
            if ($columnType -and $columnType[$prop.Name]) {
                $columnType[$prop.Name]
            } elseif ($prop.Value) {
                if ($prop.Value -is [String]) {
                    [Management.CimType]::String                            
                } elseif ($prop.Value -is [Byte]) {
                    [Management.CimType]::Char16
                } elseif ($prop.Value -is [Int16]) {
                    [Management.CimType]::SInt16
                } elseif ($prop.Value -is [Int]) {
                    [Management.CimType]::SInt32
                } elseif ($Prop.Value -is [Double]) {
                    [Management.CimType]::Real32
                } elseif ($prop.Value -is [Long]) {
                    [Management.CimType]::SInt64
                } elseif ($prop.Value -is [DateTime]) {
                    [Management.CimType]::DateTime
                } elseif ($prop.Value -is [Switch] -or $prop.Value -is [Bool]) {
                    [Management.CimType]::Boolean
                } else {
                    [Management.CimType]::String
                }

            } else {
                [Management.CimType]::String
            }
        }
        #endregion Translate column types into CIM column types

        #region Escape unsafe class names
        $getSafeClassName = {
        
            $cn = $ClassName.Replace(" ", "_space_").Replace("." ,"_dot_").Replace(":", "_colon_").Replace("/", "_slash_").Replace("#", "_pound_")            


            if ($(try { [uint32]::Parse($cn[0]) } catch {})) {
                "Number" + $cn
            } else {
                $cn
            }
        }
        #endregion Escape unsafe class names


        #region Create the namespace if it doesn't yet exist
        $namespacesToMake = New-Object Collections.Stack
        $originalNamespace = $Namespace
        do {
            $temprootNamespace = $Namespace | Split-Path 
            $leafName = $Namespace | Split-Path -Leaf

            if (-not $temprootNamespace) {
                $temprootNamespace = "root"
            }
        
            $namespaceExists = Get-WmiObject -Query "Select * from __namespace Where Name ='$leafName'" -Namespace $temprootNamespace  -ErrorAction SilentlyContinue
            if (-not $namespaceExists) {
                $null = $namespacesToMake.Push($leafName)
            } else {
                break
            }
            
            $Namespace = $temprootNamespace 
            $rootNamespace = $temprootNamespace

            if ($Namespace -eq 'root') {
                break
            }
        } while (-not $namespaceExists)
        

        $namespace= $originalNamespace


        foreach ($namespace in $namespacesToMake) {
            if (-not $Namespace) {continue}
            $mp = New-Object Management.ManagementPath
            $mp.NamespacePath = "$rootNamespace"  
            $mp.ClassName = "__namespace"
            $namespaceClass = New-Object Management.ManagementClass $mp
            $newNamespace = $namespaceClass.CreateInstance()
            $newNamespace.Name = $Namespace
            $null = $newNamespace.put()
            $rootNamespace = $rootNamespace + "\" + $Namespace
        }
        #endregion Create the namespace if it doesn't yet exist
    }
    


    process {
        
        if (-not $PSBoundParameters.Classname) {
            $ClassName = $InputObject.pstypenames[0]
        }

        

        $classPath = New-Object Management.ManagementPath
        $classPath.NamespacePath = $originalNamespace
        $classPath.ClassName = . $getSafeClassName


        $classDef = New-Object Management.ManagementClass $classPath

        $classDefExists = $(try { $classDef | Get-Member -ErrorAction SilentlyContinue } catch {}) -ne $null

        if (-not $classDefExists) {
            $classDef = New-Object Management.ManagementClass $originalNamespace, "", $null
            $classDef["__Class"] = . $getSafeClassName

            
            if ($InputObject -is [string]) {
                
            } else {
                
                foreach ($prop in $InputObject.PSObject.Properties) {
                    if ($prop.Name -like "__*") { continue }
                    $cimType = . $cimColumnType 
                    $classDef.Properties.Add($prop.Name, $cimType, $false)

                    if ($key -contains $prop.Name) {
                        $classDef.Properties[$prop.Name].Qualifiers.Add("Key", $true)
                    }
                }
                $null = $classDef.Put()
            }
        } else {
            $instances = $null
            foreach ($prop in $InputObject.PSObject.Properties ) {
                if ($prop.Name -like "__*") { continue }
                if ($prop.MemberType -eq 'AliasProperty') { continue } 
                $automaticPropertiesToIgnore = 'Scope',
                    'Path','Options',
                    'ClassPath', 'Properties','SystemProperties','Qualifiers','Site','Container'
                if ($InputObject -is [wmi] -and $automaticPropertiesToIgnore -contains $prop.Name) {
                    continue    
                }

                if ('PSComputerName', 'PSShowComputerName', 'RunspaceID' -contains $prop.Name) {
                    continue
                } 
                
                $cimType = . $cimColumnType 
                $propExists = $classDef.Properties[$prop.Name]

                if (-not $propExists) {
                    if (-not $instances) {
                        $instances = Get-WmiObject -Namespace $originalNamespace -Class (. $getSafeClassName)    
                    }
                    $classDef.Properties.Add($prop.Name, $cimType, $false)
                }

                if ($key -contains $prop.Name) {
                    if (-not $classDef.Properties[$prop.Name].Qualifiers["Key"]) {
                        $classDef.Properties[$prop.Name].Qualifiers.Add("Key", $true)
                    }
                }
                
            }
            
            if ($instances) {
                # Class definition changed. Rebuild objects. Ugh.
                $instanceProperties = $instances  | 
                    Get-Member -MemberType Property |                     
                    Where-Object { $_.Name -notlike "__*" }   

                $instances | Remove-WmiObject 
                $classPath = New-Object Management.ManagementPath
                $classPath.NamespacePath = $originalNamespace
                $classPath.ClassName = . $getSafeClassName


                $cdef = New-Object Management.ManagementClass $classPath
                $null = $cdef.Delete()
                foreach ($ip in $instanceProperties) {
                    if (-not $classDef.Properties[$ip.Name]) {
                        
                        
                        $classDef.Properties.Add($ip.Name, [Management.CimType]::String, $false)
                    }
                }

                
                $null = $classDef.Put();
                $instances | Update-Wmi -Namespace $originalNamespace -ClassName (. $getSafeClassName) -Key $key                  
            }
        }


        $where = @(foreach ($k in $key) {
            "$k = '$("$($inputObject.$k)".Replace("'","''"))'"
        }) -join ' AND '

        $instanceExists = Get-WmiObject -Class $classDef["__Class"] -Namespace $originalNamespace -Filter $where


        if ($instanceExists -and -not $force) {
            return
        }
        
        if ($force -and $instanceExists) {
            foreach ($prop in $InputObject.PSObject.Properties) {
                $instanceExists.($prop.Name) = 
                    if ($prop.Value -is [DateTime]) {
                        [Management.ManagementDateTimeConverter]::ToDmtfDateTime($prop.Value)
                    } else {
                        $prop.Value
                    }
                    
            }
            $null = $instanceExists.Put()
            
        } else {
            $classInstance = $classDef.CreateInstance()
            foreach ($prop in $InputObject.PSObject.Properties) {
                if ($prop.Name -like "__*") { continue }
                if ($prop.MemberType -eq 'AliasProperty') { continue } 
                $automaticPropertiesToIgnore = 'Scope',
                    'Path','Options',
                    'ClassPath', 'Properties','SystemProperties','Qualifiers','Site','Container'
                if ($InputObject -is [wmi] -and $automaticPropertiesToIgnore -contains $prop.Name) {
                    continue    
                }
                if ('PSComputerName', 'PSShowComputerName', 'RunspaceID' -contains $prop.Name) {
                    continue
                }
                $classInstance.($prop.Name) = 
                    if ($prop.Value -is [DateTime]) {
                        [Management.ManagementDateTimeConverter]::ToDmtfDateTime($prop.Value)
                    } else {
                        $prop.Value
                    }
            }
            $null = $classInstance.Put()
        } 

        
    }
}