Overblog Tous les blogs Top blogs Technologie & Science Tous les blogs Technologie & Science
Editer l'article Suivre ce blog Administration + Créer mon blog
MENU

[SCCM] import task sequence

Publié le par damcuvelier

#Requires -Version 3.0
<#
    .SYNOPSIS
        Imports a Task Sequence from a XML file
    .DESCRIPTION
        Imports a Task Sequence from a XML file as used by ConfigMgr 2007 and older.
        
        ConfigMgr 2012 and above use a different format for the default export via the ConfigMgr console
        which consists of several files and folders compressed into an archive which can also contain copies
        of the referenced packages.
    .EXAMPLE
        .\Import-TaskSequence.ps1 -Path "C:\Backup\TST00001_001.xml" -ID "TST00001"
        Import and replace an existing Task Sequence.
    .EXAMPLE
        .\Import-TaskSequence.ps1 -Path "C:\Backup\TST00001_001.xml" -ID "TST00001" -ShowProgress -PassThru
        Import and replace an existing Task Sequence while showing the progress of the operation and returning the updated Task Sequence package object.
    .EXAMPLE
        .\Import-TaskSequence.ps1 -Path "C:\Backup\TST00001_001.xml" -Create -Name "Save the World"
        Import Task Sequence and create a new Task Sequence Package.
    .EXAMPLE
        .\Import-TaskSequence.ps1 -Path "C:\Backup\TST00001_001.xml" -Create -Name "Save the World" -ProviderServer "CM01" -SiteCode "TST" -Credential (Get-Credential)
        Import Task Sequence and create a new Task Sequence Package using specific ConfigMgr connection.
    .LINK
        http://maikkoster.com/
        https://github.com/MaikKoster/ConfigMgr/blob/master/TaskSequence/Import-TaskSequence.ps1
    .NOTES
        Copyright (c) 2016 Maik Koster
        Author:  Maik Koster
        Version: 1.0
        Date:    29.03.2016
        Version History:
            1.0 - 29.03.2016 - Published script
            
#>
[CmdLetBinding(SupportsShouldProcess,DefaultParameterSetName="ID")]
PARAM (
    # Specifies the name and path of the Task Sequence xml file.
    [Parameter(Mandatory, ParameterSetName="ID")]
    [Parameter(Mandatory, ParameterSetName="Create")]
    [Parameter(Mandatory, ParameterSetName="Name")]
    [ValidateScript({Test-Path $_ -PathType 'Leaf'})]
    [Alias("FilePath")]
    [string]$Path,
    # Specifies the Task Sequence ID (PackageID).
    # Use either ID or Name to select the Task Sequence.
    [Parameter(Mandatory, ParameterSetName="ID")]
    [ValidateNotNullOrEmpty()]
    [Alias("PackageID")]
    [string]$ID,
    # Specifies if a new Task Sequence Package shall be created.
    # Use the Name parameter to specify the name of the new Task Sequence.
    [Parameter(Mandatory,ParameterSetName="Create")]
    [switch]$Create,
    # Specifies the name of an existing or the new Task Sequence
    [Parameter(Mandatory, ParameterSetName="Name")]
    [Parameter(ParameterSetName="Create")]
    [ValidateNotNullOrEmpty()]
    [string]$Name,
    # Specifies the description of the new Task Sequence
    [Parameter(ParameterSetName="Create")]
    [string]$Description,
    # Enable the Progress output.
    [switch]$ShowProgress,
    # Specifies if the script should pass through the updated or created task sequence package
    [switch]$PassThru,
    # Specifies the ConfigMgr Provider Server name.
    # If no value is specified, the script assumes to be executed on the Site Server.
    [Alias("SiteServer", "ServerName")]
    [string]$ProviderServer = $env:COMPUTERNAME,
    # Specifies the ConfigMgr Provider Site Code.
    # If no value is specified, the script will evaluate it from the Site Server.
    [string]$SiteCode,
    # Specifies the credentials to connect to the ConfigMgr Provider Server.
    [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
)
Process {
    ###############################################################################
    # Start Script
    ###############################################################################
    
    # Ensure this isn't processed when dot sourced by e.g. Pester Test trun
    if ($MyInvocation.InvocationName -ne '.') {
        # Path has been validated already
        # TODO: Add Replace logic
        Set-Progress -ShowProgress ($ShowProgress.IsPresent) -Activity "Import Task Sequence '$Path'" -Status "Get content from '$Path'" -TotalSteps 4 -Step 1
        $TaskSequencePackageXML = [xml](Get-Content -Path $Path)
        
        if ($TaskSequencePackageXML -ne $null) {
            # Create a connection to the ConfigMgr Provider Server
            $ConnParams = @{
                ServerName = $ProviderServer;
                SiteCode = $SiteCode;
            }
            if ($PSBoundParameters["Credential"]) {$connParams.Credential = $Credential}
            $ProcessParams = @{TaskSequencePackageXML = $TaskSequencePackageXML}
            if ($ShowProgress.IsPresent) {$ProcessParams.ShowProgress = $true}
            if ($PassThru.IsPresent) {$ProcessParams.PassThru = $true}
        
            New-CMConnection @ConnParams
        
            switch ($PSCmdLet.ParameterSetName) {
                "Create" {Start-Import @ProcessParams -Create -Name $Name -Description $Description}
                "ID" {Start-Import @ProcessParams -ID $ID}
                "Name" {Start-Import @ProcessParams -Name $Name}
            }
        } else {
            Write-Error "File '$Path' is empty or not a valid xml file."
        }
    }
}
Begin {
Function New-CMWMIInstance {
    [CmdletBinding(SupportsShouldProcess)]
    PARAM (
        # Specifies the  ConfigMgr WMI provider Class Name
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ClassName
    )
    if ([string]::IsNullOrWhiteSpace($ClassName)) { throw "Class is not specified" }
    # Ensure ConfigMgr Provider information is available
    if (Test-CMConnection) {
        Write-Verbose "Create new WMI $ClassName instance."
        if ($global:CMCredential -ne [System.Management.Automation.PSCredential]::Empty) {
            if ($PSCmdlet.ShouldProcess("Class: $ClassName", "New-CMWMIInstance")) {
                $CMClass = Get-WmiObject -List -Class $ClassName -ComputerName $CMProviderServer -Namespace $CMNamespace -Credential $global:CMCredential
            }
        } else {
            if ($PSCmdlet.ShouldProcess("Class: $ClassName", "New-CMWMIInstance")) {
                $CMClass = Get-WmiObject -List -Class $ClassName -ComputerName $CMProviderServer -Namespace $CMNamespace
            }
        }
        if ($CMClass -ne $null) {
            $CMinstance = $CMClass.CreateInstance()
            Return $CMinstance
        }
    }
}
Function Test-CMConnection {
    if ( ([string]::IsNullOrWhiteSpace($global:CMProviderServer)) -or
            ([string]::IsNullOrWhiteSpace($global:CMSiteCode)) -or
            ([string]::IsNullOrWhiteSpace($global:CMNamespace)) -or
            ($global:CMSession -eq $null)) {
        New-CMConnection
        $true
    } else {
        $true
    }
}
Function Get-CMSession {
    [CmdLetBinding()]
    PARAM (
        # Specifies the ComputerName to connect to.
        [Parameter(Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName = $env:COMPUTERNAME,
            
        # Specifies the credentials to connect to the Provider Server.
        [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
        $Opt = New-CimSessionOption -Protocol Dcom
        $SessionParams = @{
            ErrorAction = 'Stop'
        }
        if ($PSBoundParameters['Credential']) {
            $SessionParams.Credential = $Credential
        }
    }
    Process {
        # Check if there is an already existing session to the specified computer
        $Session = Get-CimSession | Where-Object { $_.ComputerName -eq $ComputerName} | Select-Object -First 1
        if ($Session -eq $null) {
            
            $SessionParams.ComputerName = $ComputerName
            $WSMan = Test-WSMan -ComputerName $ComputerName -ErrorAction SilentlyContinue
            if (($WSMan -ne $null) -and ($WSMan.ProductVersion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+')) {
                try {
                    Write-Verbose -Message "Attempt to connect to $ComputerName using the WSMAN protocol."
                    $Session = New-CimSession @SessionParams
                } catch {
                    Write-Verbose "Unable to connect to $ComputerName using the WSMAN protocol. Test DCOM ..."
                        
                }
            }
            if ($Session -eq $null) {
                $SessionParams.SessionOption = $Opt
 
                try {
                    Write-Verbose -Message "Attempt to connect to $ComputerName using the DCOM protocol."
                    $Session = New-CimSession @SessionParams
                } catch {
                    Write-Error -Message "Unable to connect to $ComputerName using the WSMAN or DCOM protocol. Verify $ComputerName is online or credentials and try again."
                }
            }
                
            If ($Session -eq $null) {
                $Session = Get-CimSession | Where-Object { $_.ComputerName -eq $ComputerName} | Select-Object -First 1
            }
        }
        Return $Session
    }
}
Function New-CMInstance {
    [CmdletBinding(SupportsShouldProcess)]
    PARAM (
        # Specifies the ConfigMgr WMI provider Class Name
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ClassName,
        # Specifies the properties to be supplied to the new object
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [hashtable]$Property,
        # Set EnforceWMI to enforce the deprecated WMI cmdlets
        # still required for e.g. embedded classes without key as they need to be handled differently
        [switch]$EnforceWMI,
        # Specifies if the new instance shall be created on the client only.
        # Will be used for embedded classes without key property
        [switch]$ClientOnly
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
    Process {
        if ([string]::IsNullOrWhiteSpace($ClassName)) { throw "Class is not specified" }
        # Ensure ConfigMgr Provider information is available
        if (Test-CMConnection) {
            $PropertyString = ($Property.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join "; "
            Write-Debug "Create new $ClassName object. Properties: $PropertyString"
            if ($EnforceWMI) {
                $NewCMObject = New-CMWMIInstance -ClassName $ClassName
                if ($NewCMObject -ne $null) {
                    #try to update supplied arguments
                    try {
                        Write-Debug "Add Properties to WMI class"
                        $Property.GetEnumerator() | ForEach-Object {
                            $Key = $_.Key
                            $Value = $_.Value
                            Write-Debug "$Key : $Value"
                            $NewCMObject[$Key] = $Value
                        }
                    } catch {
                        Write-Error "Unable to update Properties on WMI class $ClassName"
                    }
                }
            } else {
                if ($ClientOnly.IsPresent) {
                    if ($PSCmdlet.ShouldProcess("Class: $ClassName", "Call New-CimInstance")) {
                        $NewCMObject = Invoke-CimCommand {New-Object CimInstance $global:CMSession.GetClass($global:CMNamespace, $ClassName) -ErrorAction Stop}
                        if ($NewCMObject -ne $null) {
                            #try to update supplied arguments
                            try {
                                Write-Debug "Add Properties to WMI class"
                                $Property.GetEnumerator() | ForEach-Object {
                                    $Key = $_.Key
                                    $Value = $_.Value
                                    Write-Debug "$Key : $Value"
                                    $NewCMObject[$Key] = $Value
                                }
                            } catch {
                                Write-Error "Unable to update Properties on WMI class $ClassName"
                            }
                        }
                    }
                }
                if ($PSCmdlet.ShouldProcess("Class: $ClassName", "Call New-CimInstance")) {
                    $NewCMObject = Invoke-CimCommand {New-CimInstance -CimSession $global:CMSession -Namespace $global:CMNamespace -ClassName $ClassName -Property $Property -ErrorAction Stop}
                    if ($NewCMObject -ne $null) {
                        # Ensure that generated properties are udpated
                        $hack = $NewCMObject.PSBase | Select-Object * | Out-Null
                    }
                }
            }
            Return $NewCMObject
        }
    }
}
Function Invoke-CimCommand {
    PARAM(
        # Specifies the Cim based Command that shall be executed
        [Parameter(Mandatory)]
        [scriptblock]$Command
    )
    $RetryCount = 0
    Do {
        $Retry = $false
        Try {
            &amp; $Command
        } Catch {
            if ($_.Exception -ne $null) {
                if (($_.Exception.HResult -eq -2147023169 ) -or ($_.Exception.ErrorData.error_Code -eq 2147944127)) {
                    if ($RetryCount -ge 3) {
                        $Retry = $false
                    } else {
                        $RetryCount += 1
                        $Retry = $true
                        Write-Verbose "CIM/WMI command failed with Error 2147944127 (HRESULT 0x800706bf)."
                        Write-Verbose "Common RPC error, retry on default. Current retry count $RetryCount"
                    }
                } else {
                    throw $_.Exception
                }
            } else {
                throw
            }
        }
    } While ($Retry)
}

 


Function Get-CMInstance {
    [CmdletBinding()]
    PARAM (
        # Specifies the ConfigMgr WMI provider Class Name
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ClassName,
        # Specifies the Where clause to filter the specified ConfigMgr WMI provider class.
        # If no filter is supplied, all objects will be returned.
        [string]$Filter,
        # Indicates that the requested class contains lazy properties that shall be returned.
        # As the CIM CmdLets don't support lazy properties, the objects will be queried using
        # the deprecated WMI CmdLets.
        [switch]$ContainsLazy
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
    Process {
        if ([string]::IsNullOrWhiteSpace($ClassName)) { throw "Class is not specified" }
        # Ensure ConfigMgr Provider information is available
        if (Test-CMConnection) {
            if (($Filter.Contains(" JOIN ")) -or ($ContainsLazy.IsPresent)) {
                Write-Verbose "Fall back to WMI cmdlets"                
                $WMIParams = @{
                    ComputerName = $global:CMProviderServer;
                    Namespace = $CMNamespace;
                    Class = $ClassName;
                    Filter = $Filter
                }
                if ($global:CMCredential -ne [System.Management.Automation.PSCredential]::Empty) {
                    $WMIParams.Credential = $CMCredential
                }
                Invoke-CimCommand {Get-WmiObject @WMIParams -ErrorAction Stop}
            } else {
                $InstanceParams = @{
                    CimSession = $global:CMSession
                    Namespace = $global:CMNamespace
                    ClassName = $ClassName
                }
                if ($Filter -ne "") {
                    $InstanceParams.Filter = $Filter
                }
                Invoke-CimCommand {Get-CimInstance @InstanceParams -ErrorAction Stop}
            }
        }
    }
}
Function Get-CallerPreference {
    <#
    .Synopsis
        Fetches "Preference" variable values from the caller's scope.
    .DESCRIPTION
        Script module functions do not automatically inherit their caller's variables, but they can be
        obtained through the $PSCmdlet variable in Advanced Functions.  This function is a helper function
        for any script module Advanced Function; by passing in the values of $ExecutionContext.SessionState
        and $PSCmdlet, Get-CallerPreference will set the caller's preference variables locally.
    .PARAMETER Cmdlet
        The $PSCmdlet object from a script module Advanced Function.
    .PARAMETER SessionState
        The $ExecutionContext.SessionState object from a script module Advanced Function.  This is how the
        Get-CallerPreference function sets variables in its callers' scope, even if that caller is in a different
        script module.
    .PARAMETER Name
        Optional array of parameter names to retrieve from the caller's scope.  Default is to retrieve all
        Preference variables as defined in the about_Preference_Variables help file (as of PowerShell 4.0)
        This parameter may also specify names of variables that are not in the about_Preference_Variables
        help file, and the function will retrieve and set those as well.
    .EXAMPLE
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
        Imports the default PowerShell preference variables from the caller into the local scope.
    .EXAMPLE
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -Name 'ErrorActionPreference','SomeOtherVariable'
        Imports only the ErrorActionPreference and SomeOtherVariable variables into the local scope.
    .EXAMPLE
        'ErrorActionPreference','SomeOtherVariable' | Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
        Same as Example 2, but sends variable names to the Name parameter via pipeline input.
    .INPUTS
        String
    .OUTPUTS
        None.  This function does not produce pipeline output.
    .LINK
        about_Preference_Variables
    #>
    [CmdletBinding(DefaultParameterSetName = 'AllVariables')]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript({ $_.GetType().FullName -eq 'System.Management.Automation.PSScriptCmdlet' })]
        $Cmdlet,
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.SessionState]
        $SessionState,
        [Parameter(ParameterSetName = 'Filtered', ValueFromPipeline = $true)]
        [string[]]
        $Name
    )
    Begin {
        $filterHash = @{}
    }
    
    Process {
        if ($null -ne $Name) {
            foreach ($string in $Name) {
                $filterHash[$string] = $true
            }
        }
    }
    End {
        # List of preference variables taken from the about_Preference_Variables help file in PowerShell version 4.0
        $vars = @{
            'ErrorView' = $null
            'FormatEnumerationLimit' = $null
            'LogCommandHealthEvent' = $null
            'LogCommandLifecycleEvent' = $null
            'LogEngineHealthEvent' = $null
            'LogEngineLifecycleEvent' = $null
            'LogProviderHealthEvent' = $null
            'LogProviderLifecycleEvent' = $null
            'MaximumAliasCount' = $null
            'MaximumDriveCount' = $null
            'MaximumErrorCount' = $null
            'MaximumFunctionCount' = $null
            'MaximumHistoryCount' = $null
            'MaximumVariableCount' = $null
            'OFS' = $null
            'OutputEncoding' = $null
            'ProgressPreference' = $null
            'PSDefaultParameterValues' = $null
            'PSEmailServer' = $null
            'PSModuleAutoLoadingPreference' = $null
            'PSSessionApplicationName' = $null
            'PSSessionConfigurationName' = $null
            'PSSessionOption' = $null
            'ErrorActionPreference' = 'ErrorAction'
            'DebugPreference' = 'Debug'
            'ConfirmPreference' = 'Confirm'
            'WhatIfPreference' = 'WhatIf'
            'VerbosePreference' = 'Verbose'
            'WarningPreference' = 'WarningAction'
        }
        foreach ($entry in $vars.GetEnumerator()) {
            if (([string]::IsNullOrEmpty($entry.Value) -or -not $Cmdlet.MyInvocation.BoundParameters.ContainsKey($entry.Value)) -and
                ($PSCmdlet.ParameterSetName -eq 'AllVariables' -or $filterHash.ContainsKey($entry.Name))) {
                $variable = $Cmdlet.SessionState.PSVariable.Get($entry.Key)
                
                if ($null -ne $variable) {
                    if ($SessionState -eq $ExecutionContext.SessionState) {
                        Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false
                    } else {
                        $SessionState.PSVariable.Set($variable.Name, $variable.Value)
                    }
                }
            }
        }
        if ($PSCmdlet.ParameterSetName -eq 'Filtered') {
            foreach ($varName in $filterHash.Keys) {
                if (-not $vars.ContainsKey($varName)) {
                    $variable = $Cmdlet.SessionState.PSVariable.Get($varName)
                
                    if ($null -ne $variable) {
                        if ($SessionState -eq $ExecutionContext.SessionState) {
                            Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false
                        } else {
                            $SessionState.PSVariable.Set($variable.Name, $variable.Value)
                        }
                    }
                }
            }
        }
    } # end
}
Function Invoke-CMMethod {
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName="ClassName")]
    PARAM (
        # Specifies the ConfigMgr WMI provider Class Name
        # Needs to be supplied for static class methods
        [Parameter(Mandatory,ParameterSetName="ClassName")]
        [ValidateNotNullOrEmpty()]
        [string]$ClassName,
            
        # Specifies the ConfigMgr WMI provider object
        # Needs to be supplied for instance methods
        [Parameter(Mandatory,ParameterSetName="ClassInstance")]
        [ValidateNotNullOrEmpty()]
        [object]$ClassInstance,  
        # Specifies the Method Name
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$MethodName,
        # Specifies the Arguments to be supplied to the method.
        # Should be a hashtable with key/name pairs.
        [hashtable]$Arguments,
        # If set, ReturnValue will not be evaluated
        # Usefull if ReturnValue does not indicated successfull execution
        [switch]$SkipValidation
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
        
    Process {
        if ($PSCmdlet.ShouldProcess("$CMProviderServer", "Invoke $MethodName")) {  
            # Ensure ConfigMgr Provider information is available
            if (Test-CMConnection) {
                    if ($ClassInstance -ne $null) {
                        $Result = Invoke-CimCommand {Invoke-CimMethod -InputObject $ClassInstance -MethodName $MethodName -Arguments $Arguments -ErrorAction Stop}
                    } else {
                        $Result = Invoke-CimCommand {Invoke-CimMethod -CimSession $global:CMSession -Namespace $CMNamespace -ClassName $ClassName -MethodName $MethodName -Arguments $Arguments  -ErrorAction Stop}
                    }
                    if ((!($SkipValidation.IsPresent)) -and ($Result -ne $null)) {
                        if ($Result.ReturnValue -eq 0) {
                            Write-Verbose "Successfully invoked $MethodName on $CMProviderServer."
                        } else {
                            Write-Verbose "Failed to invoked $MethodName on $CMProviderServer. ReturnValue: $($Result.ReturnValue)"
                        }
                    }
                Return $Result
            }
        }
    }
}
Function New-CMConnection {
    [CmdletBinding()]
    PARAM (
        # Specifies the ConfigMgr Provider Server name.
        # If no value is specified, the script assumes to be executed on the Site Server.
        [Alias("ServerName", "Name")]
        [string]$ProviderServerName = $env:COMPUTERNAME,
        # Specifies the ConfigMgr provider Site Code.
        # If no value is specified, the script will evaluate it from the Site Server.
        [string]$SiteCode,
        # Specifies the Credentials to connect to the Provider Server.
        [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
    Process {        
        # Get or Create session object to connect to currently provided Providerservername
        # Ensure processing stops if it fails to create a session
        $SessionParams = @{
            ErrorAction = "Stop"
            ComputerName = $ProviderServerName
        }
        if ($PSBoundParameters["Credential"]) {
            $SessionParams.Credential = $Credential
        }
        
        $CMSession = Get-CMSession @SessionParams
        # Get Provider location
        if ($CMSession -ne $null) {
            $ProviderLocation = $null
            if ($SiteCode -eq $null -or $SiteCode -eq "") {
                Write-Verbose "Get provider location for default site on server $ProviderServerName"
                $ProviderLocation = Invoke-CimCommand {Get-CimInstance -CimSession $CMSession -Namespace "root\sms" -ClassName SMS_ProviderLocation -Filter "ProviderForLocalSite = true" -ErrorAction Stop}
            } else {
                Write-Verbose "Get provider location for site $SiteCode on server $ProviderServerName"
                $ProviderLocation = Invoke-CimCommand {Get-CimInstance -CimSession $CMSession -Namespace "root\sms" -ClassName SMS_ProviderLocation -Filter "SiteCode = '$SiteCode'" -ErrorAction Stop}
            }
            if ($ProviderLocation -ne $null) {
                # Split up the namespace path
                $Parts = $ProviderLocation.NamespacePath -split "\\", 4
                Write-Verbose "Provider is located on $($ProviderLocation.Machine) in namespace $($Parts[3])"
                # Set Script variables used by ConfigMgr related functions
                $global:CMProviderServer = $ProviderLocation.Machine
                $global:CMNamespace = $Parts[3]
                $global:CMSiteCode = $ProviderLocation.SiteCode
                $global:CMCredential = $Credential
                # Create and store session if necessary
                if ($global:CMProviderServer -ne $ProviderServerName) {
                    $SessionParams.ComputerName = $global:CMProviderServer
                    $CMSession = Get-CMSession @SessionParams
                }
                if ($CMSession -eq $null) {
                    Throw "Unable to establish CIM session to $global:CMProviderServer"
                } else {
                    $global:CMSession = $CMSession
                }
            } else {
                # Clear global variables
                $global:CMProviderServer = [string]::Empty
                $global:CMNamespace = [string]::Empty
                $global:CMSiteCode = [string]::Empty
                $global:CMCredential = $null
                Throw "Unable to connect to specified provider"
            }
        } else {
            # Clear global variables
            $global:CMProviderServer = [string]::Empty
            $global:CMNamespace = [string]::Empty
            $global:CMSiteCode = [string]::Empty
            $global:CMCredential = $null
            Throw "Unable to create CIM session to $ProviderServerName"
        }
    }
}

 


Function Get-TaskSequencePackage {
    [CmdletBinding(SupportsShouldProcess,DefaultParameterSetName="ID")]
    PARAM (
        # PackageID
        [Parameter(Mandatory,ParameterSetName="ID")]
        [ValidateNotNullOrEmpty()]
        [string]$ID,
        # PackageName
        [Parameter(Mandatory,ParameterSetName="Name")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [switch]$IncludeLazyProperties
    )
        
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
    Process{
        if (!([string]::IsNullOrEmpty($ID))) {
            Write-Verbose "Get Task Sequence Package by PackageID '$ID'."
            Get-CMInstance -ClassName "SMS_TaskSequencePackage" -Filter "PackageID='$ID'" -ContainsLazy:$IncludeLazyProperties
        } elseif (!([string]::IsNullOrEmpty($Name))) {
            Write-Verbose "Get Task Sequence Package by Name '$Name'."
            Get-CMInstance -ClassName "SMS_TaskSequencePackage" -Filter "Name='$Name'" -ContainsLazy:$IncludeLazyProperties
        }
    }
}
Function Convert-XMLToTaskSequence {
    [CmdletBinding(SupportsShouldProcess)]
    PARAM (
        # PackageID
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        $TaskSequenceXML,
        [switch]$PassThru
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
    Process {
        # TODO: Add ConfigMgr 2007 handling (LoadFromXML method in sms_TaskSequence)
        if ($TaskSequenceXML -ne $null) {
            $TSXMLString = $TaskSequenceXML.OuterXml
                
            Write-Verbose "Convert Task Sequence XML object to WMI object."
            $Result = Invoke-CMMethod -ClassName "SMS_TaskSequencePackage" -MethodName "ImportSequence" -Arguments @{SequenceXML=$TSXMLString}
            if ($Result -ne $null) {
                if ($Result.ReturnValue -eq 0) {
                    Write-Verbose "Successfully converted Task Sequence XML object to WMI object."                        
                    if ($PassThru.IsPresent) {
                        Write-Verbose "Return Result object."
                        $Result
                    } else {
                        Write-Verbose "Return Task Sequence object."
                        $TaskSequence = $Result.TaskSequence
                        $TaskSequence
                    }
                } else {
                    Write-Verbose "Failed to convert xml to wmi. ReturnValue: $($Result.ReturnValue)"
                    if ($PassThru.IsPresent) {
                        $Result
                    } else {
                        $null
                    }
                }
            } else {
                Write-Verbose "Failed to execute ImportSequence."
                $null
            }
        } else {
            Write-Verbose "Task Sequence XML not supplied."
        }
    }
}
Function New-TaskSequencePackage {
    [CmdletBinding(SupportsShouldProcess)]
    PARAM (
        # Specifies the Task Sequence Name
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        # Specifies the Task Sequence
        [Parameter(Mandatory)]
        [object]$TaskSequence,
        # Specifies the optional Task Sequence Description
        [string]$Description = "",
        # Specifies additional Properties
        [Hashtable]$Property = {},
        [switch]$PassThru
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
    Process{
        if ($Property -ne $null) {
            # Remove Name and Description from the supplied properties, as supplied named parameters will be used instead
            if ($Property.ContainsKey("Name")) {$Property.Remove("Name")}
            if (($Property.ContainsKey("Description")) -and (-not([string]::IsNullOrEmpty($Description)))) {$Property.Remove("Description")}
        } else {
            $Property = @{}
        }
        # Add name and Description
        $Property.Name = $Name
        if (-not ([string]::IsNullOrEmpty($Description))){ $Property.Description = $Description}
        # Create local instance of SMS_TaskSequencePackage
        Write-Verbose "Create new Task Sequence Package '$Name'"
        $TaskSequencePackage = New-CMInstance -ClassName "SMS_TaskSequencePackage" -Property $Property -ClientOnly
        # Invoke SetSequence to add the sequence and create the package
        Write-Verbose "Add Task Sequence to new Task Sequence Package"
        [string]$Result = Set-TaskSequence -TaskSequencePackage $TaskSequencePackage -TaskSequence $TaskSequence -Confirm:$false
        # Result should contain "PackageID"
        if (!([string]::IsNullOrEmpty($Result)) -and ($Result.Contains("SMS_TaskSequencePackage.PackageID="))) {
            $PackageID = $Result.Replace("SMS_TaskSequencePackage.PackageID=","").Replace("""","")
            Write-Verbose "Successfully created Task Sequence Package $PackageID."
            if ($PassThru.IsPresent) {
                $TaskSequencePackage = Get-TaskSequencePackage -ID $PackageID
                Write-Verbose "Return new Task Sequence Package"
                $TaskSequencePackage
            }
        } else {
            Write-Verbose "Failed to create new Task Sequence Package."
            if ($PassThru.IsPresent) {
                $Result
            } else {
                $null
            }
        }
    }
}
Function Set-TaskSequence {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High", DefaultParameterSetName = "ID")]
    PARAM (
        # Specifies the Task Sequence PackageID
        [Parameter(Mandatory,ParameterSetName="ID")]
        [ValidateNotNullOrEmpty()]
        [string]$ID,
        # Specifies the Task Sequence Package
        [Parameter(Mandatory,ParameterSetName="Package")]
        [ValidateNotNullOrEmpty()]
        [object]$TaskSequencePackage,
        # Specifies the Task Sequence object
        [Parameter(Mandatory)]
        [object]$TaskSequence,
        # Specifies if the Path to the saved package shall be returned
        [switch]$PassThru
    )
    Begin {
        Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    }
    Process {
        if (!([string]::IsNullOrEmpty($ID))) {
            $TaskSequencePackage = Get-TaskSequencePackage -ID $ID
        }
        if ($TaskSequencePackage -ne $null) {
            Write-Verbose "Update Task Sequence of Task Sequence Package $($TaskSequencePackage.PackageID)"
            if ($PSCmdLet.ShouldProcess("TaskSequencePackage $($TaskSequencePackage.PackageID)", "Update Task Sequence")) {
                $Result = Invoke-CMMethod -ClassName "SMS_TaskSequencePackage" -MethodName "SetSequence" -Arguments @{TaskSequencePackage=$TaskSequencePackage;TaskSequence=$TaskSequence}
              
                if ($PassThru.IsPresent) {
                    # Don't evaluate on PassThru
                    $Result
                } else {
                    if ($Result -ne $null) {
                        if ($Result.ReturnValue -eq 0) {
                            Write-Verbose "Successfully updated Task Sequence at $($Result.SavedTaskSequencePackagePath)"
                            Write-Verbose "Return SavedTaskSequencePackagePath."
                            $Path = $Result.SavedTaskSequencePackagePath
                            $Path
                        }
                    } else {
                        Write-Error "Failed to update Task Sequence. ReturnValue: $($Result.ReturnValue)"
                    }
                }
            }
        } else {
            Write-Error "TaskSequencePackage not supplied."
        }
    }
}
    Set-StrictMode -Version Latest
    # Import ConfigMgr Module
    
    function Start-Import {
        [CmdLetBinding(SupportsShouldProcess,DefaultParameterSetName="ID")]
        PARAM(
            # Specifies the xml document of the exported Task Sequence Package
            [Parameter(Mandatory, ParameterSetName="ID")]
            [Parameter(Mandatory, ParameterSetName="Create")]
            [Parameter(Mandatory, ParameterSetName="Name")]
            [ValidateNotNullOrEmpty()]
            [xml]$TaskSequencePackageXML,
            # Specifies the Task Sequence PackageID
            [Parameter(Mandatory, ParameterSetName="ID")]
            [ValidateNotNullOrEmpty()]
            [Alias("PackageID")]
            [string]$ID,
            # If specified, a new TaskSequencePackage with the supplied Name will be created
            [Parameter(Mandatory,ParameterSetName="Create")]
            [switch]$Create,
            # Name of the existing or new TaskSequencePackage
            [Parameter(Mandatory, ParameterSetName="Name")]
            [Parameter(ParameterSetName="Create")]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
            [Parameter(ParameterSetName="Create")]
            [string]$Description,
            # Enables the Progress output.
            [switch]$ShowProgress,
            # Specifies if the script should pass through the updated or created task sequence package
            [switch]$PassThru
        )
        Begin {
            # Prepare Progress parameters
            $ProgressParams = @{
                Activity = "Import Task Sequence $ID$Name"
                TotalSteps = 4
                ShowProgress = $ShowProgress.IsPresent
            }
        }
        
        Process {
            Write-Verbose "Start Import Process for Task Sequence '$ID$Name'."
            # Get the Sequence node
            # Try to find a sequence node (ConfigMgr 2007 and Export-TaskSequence.ps1 format
            $TaskSequenceXML = $TaskSequencePackageXML.SelectSingleNode('//sequence')
            if ($TaskSequenceXML -eq $null) {
                # ConfigMgr 2012 format (object.xml from expanded zip file)
                $TaskSequenceXML.SelectSingleNode('//PROPERTY[@NAME="Sequence"]').VALUE
            }
            if ($TaskSequenceXML -ne $null) {
                # Convert XML to Sequence
                Set-Progress @ProgressParams -Status "Convert xml to Task Sequence." -Step 2
                $TaskSequence = Convert-XMLToTaskSequence -TaskSequenceXML $TaskSequenceXML
                # Create a new Task Sequence package if requested
                if ($Create.IsPresent){
                    Write-Verbose "Create new Task Sequence Package."
                    if ([string]::IsNullOrEmpty($Name)) {
                        $Name = Get-XmlNodeText -XmlDocument $TaskSequencePackageXML -NodePath "SmsTaskSequencePackage.Name"
                        if ([string]::IsNullOrEmpty($Name)) {
                            $Name = "New Task Sequence"   
                            Write-Verbose "No name supplied. Use '$Name'."
                        } else {
                            Write-Verbose "Use Name '$Name'."
                        }
                    }
                    if ([string]::IsNullOrEmpty($Description)) {
                        $Description = Get-XmlNodeText -XmlDocument $TaskSequencePackageXML -NodePath "SmsTaskSequencePackage.Description"
                        Write-Verbose "Use Description '$Description'."
                    }
                    $Properties = @{}
                    
                    # Add additional info if available
                    $BootImageID = Get-XmlNodeText -XmlDocument $TaskSequencePackageXML -NodePath "SmstaskSequencePackage.BootImageID"
                    if (!([string]::IsNullOrEmpty($BootImageID))) {
                        $Properties.BootImageID = $BootImageID
                        Write-Verbose "Use BootImageID '$BootImageID'."
                    }
                    
                    $Category = Get-XmlNodeText -XmlDocument $TaskSequencePackageXML -NodePath "SmstaskSequencePackage.Category"
                    if (!([string]::IsNullOrEmpty($Category))) {
                        $Properties.Category = $Category
                        Write-Verbose "Use Category '$Category'."
                    }
                    
                    $Duration = Get-XmlNodeText -XmlDocument $TaskSequencePackageXML -NodePath "SmstaskSequencePackage.Duration"
                    if (!([string]::IsNullOrEmpty($Duration))) {
                        $Properties.Duration = $Duration
                        Write-Verbose "Use Duration '$Duration'."
                    }
                    
                    $DependentProgram = Get-XmlNodeText -XmlDocument $TaskSequencePackageXML -NodePath "SmstaskSequencePackage.DependentProgram"
                    if (!([string]::IsNullOrEmpty($DependentProgram))) {
                        $Properties.DependentProgram = $DependentProgram
                        Write-Verbose "Use DependentProgram '$DependentProgram'."
                    }
                    Set-Progress @ProgressParams -Status "Create new Task Sequence Package." -Step 4
                    $Params = @{
                        Name = $Name
                        Description = $Description
                        TaskSequence = $TaskSequence
                        Property = $Properties
                    }
                    if ($PassThru.IsPresent) {$Params.PassThru = $true}
                    $TaskSequencePackage = New-TaskSequencePackage @Params
                    if ($PassThru.IsPresent) {
                        # Drop TaskSequencePackage back to pipeline
                        $TaskSequencePackage
                    }
                } else {
                    Set-Progress @ProgressParams -Status "Get Task Sequence Package." -Step 3
                    if (!([string]::IsNullOrEmpty($ID))) {
                        $TaskSequencePackage = Get-TaskSequencePackage -ID $ID
                    } elseif (!([string]::IsNullOrEmpty($Name))) {
                        $TaskSequencePackage = Get-TaskSequencePackage -Name $Name
                    }
                    if (($TaskSequence -ne $null) -and ($TaskSequencePackage -ne $null)) {
                        Set-Progress @ProgressParams -Status "Update Task Sequence Package." -Step 4
                        $Result = Set-TaskSequence -TaskSequencePackage $TaskSequencePackage -TaskSequence $TaskSequence -PassThru
                        if (($Result -ne $null) -and ($Result.ReturnValue -eq 0)) {
                            Write-Verbose "Successfully updated Task Sequence Package '$($TaskSequencePackage.PackageID)'."
                            if ($PassThru.IsPresent) {
                                # Drop TaskSequencePackage back to pipeline
                                $TaskSequencePackage = Get-TaskSequencePackage -ID $ID
                                $TaskSequencePackage
                            }
                        } else {
                            Write-Error "Failed to update Task Sequence Package '$($TaskSequencePackage.PackageID)'."
                        }
                    } else{
                        Write-Error "Failed to get Task Sequence Package and/or Task Sequence."
                    }
                }
            } else {
                Write-Error "Unable to find Task Sequence in supplied xml object."
            }
        }
    }

 


    # Returns a xml node based on the supplied path or null if the node does not exist
    function Get-XmlNode([ xml ]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.') {
        # If a Namespace URI was not given, use the Xml document's default namespace.
        if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }   
     
        # In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
        $XmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
        $XmlNsManager.AddNamespace("ns", $NamespaceURI)
        $FullyQualifiedNodePath = "/ns:$($NodePath.Replace($($NodeSeparatorCharacter), '/ns:'))"
     
        # Try and get the node, then return it. Returns $null if the node was not found.
        $Node = $XmlDocument.SelectSingleNode($FullyQualifiedNodePath, $XmlNsManager)
        return $Node
    }
    # Returns the inner text of a xml node based on the supplied path or an empty string if the node does not exist.
    function Get-XmlNodeText([ xml ]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.') {
        # Try and get the node
        $Node = Get-XmlNode @PSBoundParameters
        if ($Node -ne $null) {
            return $Node.InnerText
        } else {
            return ""
        }
    }
    # Wraps the Write-Progress CmdLet
    Function Set-Progress {
        [CmdLetBinding()]
        PARAM(
            # Specifies the current ID of the Progress output
            [int]$ID = 1,
            # Specifies the Parent ID of the Progress output
            [int]$ParentID = -1,
            # Specifies the total amount of steps
            [int]$TotalSteps,
            # Specifies the current step
            [int]$Step = 1,
            # Specifies the current status
            [string]$Status,
            # Specifies the current Activity
            [string]$Activity,
            # Specifies if Progress shall be shown
            [bool]$ShowProgress = $False
        )
        if ($ShowProgress) {
            $PercentComplete = (100/$TotalSteps*$Step)
            if ($ParentID -gt 0) {
                Write-Progress -ParentId $ParentID -Id $ID -Status $Status -Activity $Activity -PercentComplete $PercentComplete
            } else {
                Write-Progress -Id $ID -Status $Status -Activity $Activity -PercentComplete $PercentComplete
            }
        }
    }
}

 

Commenter cet article