Jere's Techblog

Join Azure VM into AD and install SCCM Client

Enclosed a script to join the Azure machine into AD and install the SCCM client. This is useful if you want to populate a native Azure VM that was not installed with SCCM. To make the AD-join a service user was assigned in the script, certainly not the most beautiful variant but this can be encrypted by a compiled EXE. This can be done with the following tool: https://gallery.technet.microsoft.com/scriptcenter/PS2EXE-GUI-Convert-e7cb69d5

Of course, network access to the AD and SCCM server must be available.

#by Jeremias Kühnis
#02.10.2019

### Vars Section ###
$SCCM_Server = "SCCMPrimary.f.q.d.n"
$site_code = "Your Site Code -like S01"
$SCCM_MPServer = "ManagementPoint.f.q.d.n"
$LocalSource_Path = "$env:SystemDrive\temp\"
$SCCM_ClientInstaller = "$LocalSource_Path" + "Client\ccmsetup.exe"
$SCCM_Repo = "\\$SCCM_Server\SMS_***YOURSITECODE***\Client"
$time = ([datetime]::now).tostring("dd_MM_yyyy_HH-mm-ss")
$ScriptFilename = Split-Path $MyInvocation.MyCommand.Definition -leaf
$LogfileName = $LocalSource_Path + 'Azure-SCCMInstaller' + '__' + $time + '.log'
$fqdn = 'someFQDN'
$JoinADUser = $fqdn + '\ServiceAccountjoinAD'
$JoinADUserPw = 'ServiceAccountPW'
$ADOU_NewAzuewDevice = "OU=Germany,DC=contoso,DC=com"



### External Functions ###
#by   https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0 


function Write-Log { 
    [CmdletBinding()] 
    Param 
    ( 
        [Parameter(Mandatory = $true, 
            ValueFromPipelineByPropertyName = $true)] 
        [ValidateNotNullOrEmpty()] 
        [Alias("LogContent")] 
        [string]$Message, 
 
        [Parameter(Mandatory = $false)] 
        [Alias('LogPath')] 
        [string]$Path = 'C:\Logs\PowerShellLog.log', 
         
        [Parameter(Mandatory = $false)] 
        [ValidateSet("Error", "Warn", "Info")] 
        [string]$Level = "Info", 
         
        [Parameter(Mandatory = $false)] 
        [switch]$NoClobber 
    ) 
 
    Begin { 
        # Set VerbosePreference to Continue so that verbose messages are displayed. 
        $VerbosePreference = 'Continue' 
    } 
    Process { 
         
        # If the file already exists and NoClobber was specified, do not write to the log. 
        if ((Test-Path $Path) -AND $NoClobber) { 
            Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name." 
            Return 
        } 
 
        # If attempting to write to a log file in a folder/path that doesn't exist create the file including the path. 
        elseif (!(Test-Path $Path)) { 
            Write-Verbose "Creating $Path." 
            $NewLogFile = New-Item $Path -Force -ItemType File 
        } 
 
        else { 
            # Nothing to see here yet. 
        } 
 
        # Format Date for our Log File 
        $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 
 
        # Write message to error, warning, or verbose pipeline and specify $LevelText 
        switch ($Level) { 
            'Error' { 
                Write-Error $Message 
                $LevelText = 'ERROR:' 
            } 
            'Warn' { 
                Write-Warning $Message 
                $LevelText = 'WARNING:' 
            } 
            'Info' { 
                Write-Verbose $Message 
                $LevelText = 'INFO:' 
            } 
        } 
         
        # Write log entry to $Path 
        "$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append 
    } 
    End { 
    } 
}

### own Functions ###

function Mount-SCCMShare {
    $DriveName = 'SCCMRepo'
    If (!(Get-PSDrive -name $DriveName -ErrorAction SilentlyContinue)) {
        try {
            New-PSDrive -name $DriveName -PSProvider "FileSystem" -Root  $SCCM_Repo -Credential $credential -Scope Script
        }
        catch {
            Write-Log -Path $LogfileName -Level Error -Message "Can not Mount SCCM Share $SCCM_Repo"
            Exit
        }

    
    }
}

Function SCCMClientInstaller {

    ###########################################################################
    ##          SCCM Client Health check and Troubleshooting Script					
    ##          Author: Lokesh Agarwal
    ##          Date: 23-08-2017
    ##	        Input:- SCCM Client path, MP Address, Site Code
    ##	     Edited: Jeremias Kühnis 27.09.2019 -> Added/Modifying some Code and add some Log Warning
    ###########################################################################




    ############################### Main Code ####################################
    $machinename = hostname


    ############################### Check if WMI is working #######################
    if ((Get-WmiObject -Namespace root\ccm -Class SMS_Client -ErrorAction SilentlyContinue) -and (Get-WmiObject -Namespace root\ccm -Class SMS_Client -ErrorAction SilentlyContinue)) {
        Write-Log -Path $LogfileName -Level Info -Message "WMI is OK"
        $WMI_Status = "Working"
    }
    else {
        Write-Log -Path $LogfileName -Level Warn -Message "Try to Repair WMI"
        Stop-Service -Force winmgmt -ErrorAction SilentlyContinue
        cd  C:\Windows\System32\Wbem\
        Remove-item C:\Windows\System32\Wbem\Repository.old -Recurse -Force -ErrorAction SilentlyContinue -Confirm:$false
        rename-Item Repository Repository.old -ErrorAction SilentlyContinue -Confirm:$false
        Start-Service winmgmt 
    }

    ############################# Check if SCCM Client is installed ##################
    If (Get-Service -Name CcmExec -ErrorAction SilentlyContinue) {
        $Client_Status = "Yes"
        Write-Log -Path $LogfileName -Level Info -Message "SCCM Status OK, Client is installed"
	
        ########### Check if services are running ################################
        $CcmExec_Status = Get-Service -Name CcmExec | % { $_.status }
        $BITS_Status = Get-Service -Name BITS | % { $_.status }
        $wuauserv_Status = Get-Service -Name wuauserv | % { $_.status }
        $Winmgmt_Status = Get-Service -Name Winmgmt | % { $_.status }
        $RRegistry_Status = Get-Service -Name RemoteRegistry | % { $_.status }


        if ($CcmExec_Status -eq "Stopped") {
            Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $CcmExec_Status"
            Get-Service -Name CcmExec | Start-Service
        }

        if ($BITS_Status -eq "Stopped") {
            Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $BITS_Status"
            Get-Service -Name BITS | Start-Service
        }

        if ($wuauserv_Status -eq "Stopped") {
            Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $wuauserv_Status"
            Get-Service -Name wuauserv | Start-Service
        }

        if ($Winmgmt_Status -eq "Stopped") {
            Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $Winmgmt_Status"
            Get-Service -Name Winmgmt | Start-Service
        }

	
	
        $MachinePolicyRetrievalEvaluation = "{00000000-0000-0000-0000-000000000021}"
        $SoftwareUpdatesScan = "{00000000-0000-0000-0000-000000000113}"
        $SoftwareUpdatesDeployment = "{00000000-0000-0000-0000-000000000108}"

        #################### check if Scan cycles are working ###################
        $machine_status = Invoke-WmiMethod -Namespace root\ccm -Class sms_client -Name TriggerSchedule $MachinePolicyRetrievalEvaluation
        $software_status = Invoke-WmiMethod -Namespace root\ccm -Class sms_client -Name TriggerSchedule $SoftwareUpdatesScan
        $softwaredeploy_Status = Invoke-WmiMethod -Namespace root\ccm -Class sms_client -Name TriggerSchedule $SoftwareUpdatesDeployment

        if ($machine_status -and $software_status -and $softwaredeploy_Status) {
            $machine_Rstatus = "Successful"
            Write-Log -Path $LogfileName -Level Info -message "Scan cycles are working $machine_Rstatus"
        }
        else {
            $SMSCli = [wmiclass] "root\ccm:sms_client"
            Write-Log -Path $LogfileName -Level Warn -message "Scan cycles are not working, try to repair SMSCLI-Client"
            $repair = $SMSCli.RepairClient()
            
        }

    }
    else {
        ############## Install SCCM Client ###############################
        Write-Log -Path $LogfileName -Level Info -message "Missing SCCM Client, try to start installation"
        &$SCCM_ClientInstaller /mp:$SCCM_MPServer /logon SMSSITECODE=$site_code
        Start-Sleep 5
        DO {
            $ProcessesFound = (Get-Process -name ccmsetup -ErrorAction SilentlyContinue)
            If (($ProcessesFound) -and ($counter -le "90")) {
                Start-Sleep 10
                $counter++
                Write-Host "Still running: $($ProcessesFound)  $counter"
            }
            Else {
                IF ($counter -gt "90") {
                    Write-Log -Path $LogfileName -Level Error -message "Not OK: Try to kill running Process while InstallationProcess is taking more than 15 Minutes"
                    Get-Process ccmsetup | Stop-Process -Force
                    Exit
                }
                Else {
                    Write-Log -Path $LogfileName -Level Info -message "OK:Process ended by SCCM installer."
                }
            }
        } Until (!$ProcessesFound)

    }

    ####################################################################################################
}

function New-folder {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][string]$folderpath
    )

    If (!(Test-path $folderpath)) {
        New-Item $folderpath -ItemType Directory
    }
}   

function VerifyPrerequisits {
    #Check Powershell Version
    $psversion = $PSVersionTable.PSVersion.Major
    IF ($psversion -ge "5") {
        Write-Log -Path $LogfileName -Level Info -Message "Powershell Version is OK – $psversion"
    }
    Else {
        Write-Log -Path $LogfileName -Level Error -Message "Missing PowerShell or installed Version is to low  – $psversion "
        $Errorcouonter = "1"
    }
    
    #Check .Net FrameWork Version
    $DotNetVersion = (Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\' | Get-ItemPropertyValue -Name Version)
    IF (Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\' | Get-ItemPropertyValue -Name Release | Foreach-Object { $_ -ge 461814 }) {
        Write-Log -Path $LogfileName -Level Info -Message ".NetVersion ist OK – $DotNetVersion"
    }
    else {
        Write-Log -Path $LogfileName -Level Error -Message "Missing .NetFramework or installed Version is to low  – $DotNetVersion "
        $Errorcouonter = "1"
    }

    #Verify SourcePath
    IF (!(Test-Path $SCCM_Repo) ) {
        Write-Log -Path $LogfileName -Level Error -Message "Can not find SCCM installer Path: $SCCM_Repo"
        $Errorcouonter = "1"
    }

    ##Last Part
    If ($Errorcouonter -eq "1") {
        Write-Log -Path $LogfileName -Level Warn -Message "Installation is not performed because the checked prerequisites are Wrong/Missing. See further up in the log."
        exit
    }
    Else {
        Write-Log -Path $LogfileName -Level Info -Message "Prerequisits are Okay, try to lead trough installation"
    }


}

Function Remove-InstallerLocation {
    $removepaths = @(
        "$LocalSource_Path\Client"
    )
    foreach ($ToDelPath in $removepaths) {
        Remove-Item $ToDelPath -Recurse -Force -ErrorAction SilentlyContinue
    }
}

###################
### Main Script ###
###################
$password = $JoinADUserPw | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($JoinADUser, $password)

### Create C:\Temp Folder, if not Exists for logs ###
New-folder -folderpath $LocalSource_Path


### Join AD-Section ###
#Need to Add credentials and start Job for OU path
$Error.Clear()
Try {
    Add-Computer -DomainName $fqdn -Credential $credential -OUPath $ADOU_NewAzuewDevice -Force -ErrorAction Stop
    
    
}
catch [System.Net.WebException], [System.Exception] {
    Write-Log -Path $LogfileName -Level Error -Message "Failed @ Ad-join Part...maybe ADAccount already exists."
    Write-Log -Path $LogfileName -Level Error -Message $Error
    Exit
}

Mount-SCCMShare
VerifyPrerequisits


### Install SCCM-Client Section ###
Write-Log -Path $LogfileName -Level Info -message "Start-SCCM Repair/ Install Process..."
Copy-Item $SCCM_Repo -Recurse -Destination $LocalSource_Path -Force
SCCMClientInstaller
Write-Log -Path $LogfileName -Level Info -message "End Section: 'Start-SCCM Repair/ Install Process...'"
Write-Log -Path $LogfileName -Level Info -message "Finished all Jobs, if there are any Problems Check if Computer is joined in AD and Check the Execution Policy. Also Check de Install status of SCCM Client."
Write-Log -Path $LogfileName -Level Info -message "Try to clean Up local install Files"
Start-Sleep 3
Remove-InstallerLocation
Write-Log -Path $LogfileName -Level Info -message "Successfully finish..."

#reboot while AD-Join
shutdown -r -f -t 60
Continue reading...

Verify Citrx HostedMachineID with VmWare Hypervisor

Sometimes the HostedMachineID of Citrix does not match that of VmWare. This is often the case when cloning or moving machines. In Citrix Studio you will see the Powerstate of this machines as unknow and you can’t do any VM actions like reboot in the console.

With this script snippet it can be checked. To do this the variable “Broker” must be adjusted in the Script and the PowerCli and CitrixModule (BrokerSnapIn) must be loaded. Furthermore, the connection to the vCenter must be initiated via “Connect-VIServer vCName“.

#25.09.2019by J.Kühnis - Verfiy HostedMachineID with VmWare ESXi Hypervisoer

$Broker = "Enter your BrokerName"
$Brokermachines = Get-BrokerMachine -AdminAddress $Broker | Select MachineName,DNSName,HostedMachineID

Foreach ($Machine in $Brokermachines){
    IF(get-vm $Machine.DnsName -ErrorAction SilentlyContinue){
        IF($Machine.HostedMachineID -eq (Get-View -id (get-vm $machine.DNSName).id).config.uuid){
            Write-Host $Machine.DnsName "HostedMachineID is matching" $Machine.HostedMachineId -ForegroundColor Green
        }Else{
            Write-host $Machine.DnsName "Mismatch ID: VmWare UUID =" (Get-View -id (get-vm $machine.DNSName).id).config.uuid "; Citrix HostedMachineID =" $Machine.HostedMachineID -ForegroundColor Yellow
        }

    }Else{
        Write-host $Machine.DnsName "MachineName not Found on ESXi" -ForegroundColor Yellow
    }
}

Now that the machines have been read out, the connections can be fixed.

vCenter Cert thumbprint update:

# Open an admin POSH console, load the Citrix Modules (asnp citrix*) and cd to XDHyp:/Connections and run ls. Check the SSLThumbprints entry.
asnp citrix*
cd XDHyp:/Connections
ls
Set-Item -LiteralPath "XDHyp:\Connections\vCenters Name" -sslthumbprint "123456789ABCD123456789ABCDE123456789ABCD" -hypervisorAddress https://vcenter-name/sdk

The letters in SSL-Thumbprint must be uppercase.

In this blog the problem is also discussed in detail:

Continue reading...

Get reserved Memory of ESXi Host

With the command Get-VMHost you can read values ​​such as the current memory consumption or the total number memory of a host. But I didn’t find a way to read out the value of the allocated memory of the subobjects (the VMs).

Here is an example of how this can be done. The script outputs a list of all hosts. In the attribute “AllocatedVMMemoryGB” you can see how much memory has been over-provisioned or it shows how much memory you could still use.

#by J.Kühnis 20.08.2019

#Class
Class VMHost{
    [string]$Name
    [string]$ConnectionState
    [string]$PowerState
    [int]$NumCpu
    [int]$MemoryUsageGB
    [int]$AllocatedVMMemoryGB
    [int]$MemoryTotalGB
    [string]$ParentCluster
    [string]$Id
    [string]$ProcessorType
}

#Vars
$VmHostArray =@()

#MainScript
Foreach($server in Get-VMHost){
    $a = (($server | get-vm).MemoryGB | Measure-Object -sum).sum
    $server = Get-vmHost -name $server.name
    $a = ("{0:N0}" -f $server.MemoryTotalGB) - ("{0:N0}" -f $a);

    $vmhost = New-Object VMHost -Property @{Name=$server.name;ConnectionState=$server.ConnectionState;Powerstate=$server.ConnectionState;NumCpu=$server.NumCpu;MemoryUsageGB=$server.MemoryUsageGB;AllocatedVMMemoryGB=$a;MemoryTotalGB=$server.MemoryTotalGB;ParentCluster=$server.parent;ID=$server.Id;ProcessorType=$server.ProcessorType}
    
    $VmHostArray += $vmhost

    Clear-Variable -Name a,vmhost
}

#output
$VmHostArray | Format-Table

            
Continue reading...

Scriptblock to check PVS Connection

On the fly, I found no way to check which PVS servers & service are online. This is especially important if I want to issue PVS commands from Remote / Orchestrated. Enclosed I have made a small script block to check this. It is certainly not the best way, but it works. One possibility would be to retrieve the status of the PVS-services, but it is unclear whether one can actually connect to the servers trough PVS commands.

#by JKU 09.08.2019
$error.clear()
$k.clear()
$PVSConnectionServers.clear()

$PVSConnectionServers = @("PVS-Server01", "PVS-Server02")

$k = 0
Foreach($PVS_Server in $PVSConnectionServers){
    Set-PvsConnection $PVS_Server
    if($Error -match $PVS_Server){
        #Check Next Server
        $k++
        IF($k -eq $PVSConnectionServers.Count){
            Write-Warning "ALL PVS Servers are not reachable, stop script! $PVSConnectionServers"
            Return
        }
    }Else{
        Break
    }
}

Write-Host "Connecting to $PVS_Server"
Continue reading...

Hyper-V Create Machine

I’d like to share a mini Script to create VirtualMachines with Hyper-V and Powershell. It is certainly not enterprise diveable. But it’s enough to create a VM for testing.

#by JKU 02.08.2019

#VM Base Information
$VMName = "TestVM2"
$DataPath = "C:\temp"
$DataVMPath = $DataPath + "\" + $VMName
$DiskDataPath = $DataPath + "\" + $VMName + "\" + $VMName + ".vhdx"
$Memory = 2GB
$Disk = 12GB
$CPUCores = 4
$VMGeneration = 2   #Options are "1","2"   -> If you don't have any idea, please let this at Value 2

#Options
$MountIso = "true"
$StartVMaftreCreation = "true"
$IsoPath = "C:\Cloud_JK\NextCloud\jere@nx2309.your-next.cloud\OS\win10\SW_DVD5_Win_Pro_10_64BIT_German_MLF_X20-25597.ISO"




#Script
IF(Get-VM -Name $VMName -ErrorAction SilentlyContinue){
    Write-Warning "$VMName : Machine already exists! Please verify your Values."
    return
}
IF(Test-Path $DataVMPath){
    Write-Warning "$DataVMPath : Folder already exists! Please verify your Values."
    return
}

Write-Host "Start to create VM $VMName" -foregroudcolor yellow
New-VM -Name $VMName -path $DataPath -MemoryStartupBytes $Memory -NewVHDSizeBytes $Disk -NewVHDPath $DiskDataPath -Generation $VMGeneration

$Vm = (Get-VM -Name $VMName)
Set-VM -VM $Vm -ProcessorCount $CPUCores


IF($MountIso -eq "true"){
    $DVD = Add-VMDvdDrive -VMName ($Vm).Name -Path $ISOPath -Passthru
    Set-VMFirmware -VM $VM -FirstBootDevice $DVD
}
IF($StartVMaftreCreation -eq "true"){
    Start-VM -Name $VMName
}

Write-Host "Script End"

Below are two links to similar topics.

To create a virtual machine from a specific template (Hyper V)

Continue reading...

Example WPF Datagrid with Combobox (Powershell Binding)

With WPF GUI elements, it can happen that you like to connect two detached elements with each other. A classic example are comboboxes within tables. You want to read the value of the combobox with the corresponding row. For this the binding of elements is necessary. Enclosed a small example, how you can accomplish this:

https://github.com/jkuehnis/PowerShell-DataGrid-with-Combobox

Continue reading...

local & remote msu patchfile installation with Powershell / Windows Update Standalone Installer

Windows Update Standalone Installer

The patch installation is done via wusa.exe.

The following print screen shows the parameters of the Windows Update Standalone Installer.

local installation

As far as I know, the patch cannot be installed with native powershell, means we have to address the wusa.exe in powershell. Of course Powershell is a nice way to automate the whole process.

In the following example, the patch is copied from a UNC share and installed locally.

$patchname = "kb13245.msu"

$patchsource = "\\some\unc\path\kb13245.msu"

Copy-Item $patchsource -Destination "$env:SystemDrive\Temp" -Force

wusa.exe "$env:SystemDrive\Temp\$patchname" /quiet

remote installation

I was not able to run wusa.exe remotely, any tests with workflows, Remotepowershell (Invoke-Command CMDLeet) failed. Even triggering a localy copied batch file caused problems. The wusa.exe process was executed with the correct parameters but aborted after a few seconds.
Even with an Invoke-Command and the parameter -Wait it didn’t work.

Probably it’s because you intervene in the system and perform an unauthorized action.

With the PSExec.exe it is possible to start the process remotely.

The principle is very simple, you have to copy the patch to the appropriate target computer. Then PSExec starts a remote process on the target computer and executes the wusa.exe with the corresponding parameters. The wusae.exe must point to the path where the patch was copied.

#16.05.2019 by JKU
$Hotfix = 'kb-12345.msu'
$HostName = 'F.Q.D.N'
$DestinationPath = "\\$Hostname\c$\Temp\"

Copy-Item C:\temp\$Hotfix -Destination $DestinationPath
#Start Process with PSExec.exe
& C:\Temp\PsExec.exe -accepteula -s  \\$HostName wusa C:\Temp\$Hotfix /quiet /norestart

And so you can distribute a patch for multiple computers with a simple iteration.

full remote automation

There are of course several ready-made scripts to install multiple patches on multiple computers. The script which I use in the company environment, I have inserted below. The code does not come from me, but from the following forum post: https://community.spiceworks.com/topic/2054098-silently-install-patches-remotely-and-reboot?page=1#entry-7246666

#by https://community.spiceworks.com/topic/2054098-silently-install-patches-remotely-and-reboot?page=1#entry-7246666

$RootHotfixPath = 'Patches\'
 
$Hotfixes = @('KB3125574_x64.msu')
$Servers = Get-Content 'MachineList.txt'

foreach ($Server in $Servers)
{
    Write-Host "Processing $Server..."

    $needsReboot = $False
    $remotePath = "\\$Server\c$\Temp\Patches\"
    
        if( ! (Test-Connection $Server -Count 1 -Quiet)) 
    {
        Write-Warning "$Server is not accessible"
        continue
    }

        if(!(Test-Path $remotePath))
    {
        New-Item -ItemType Directory -Force -Path $remotePath | Out-Null
    }
    
    foreach ($Hotfix in $Hotfixes)
    {
        Write-Host "`thotfix: $Hotfix"
        $HotfixPath = "$RootHotfixPath$Hotfix"

        Copy-Item $Hotfixpath $remotePath
        # Run command as SYSTEM via PsExec (-s switch)
        & C:\Windows\PsExec -s \\$Server wusa C:\Temp\Patches\$Hotfix /quiet /norestart
        write-host "& C:\Windows\PsExec -s \\$Server wusa C:\Temp\Patches\$Hotfix /quiet /norestart"
        if ($LastExitCode -eq 3010) {
            $needsReboot = $true
        }
    }

    # Delete local copy of update packages
    Remove-Item $remotePath -Force -Recurse

    if($needsReboot)
    {
        Write-Host "Restarting $Server..."
        Restart-Computer -ComputerName $Server -Force -Confirm
    }
}
Continue reading...

Map Share to free Driveletter

With this code snippet a share can be mapped to the next free drive letter.

$share = "\\any\unc\path"
$PSProviderAlphabet = [char[]]([char]'C'..[char]'Z')
$UsedPSProvider = (get-psdrive).Name | Sort-Object
$FreePSProvider = $PSProviderAlphabet | ? {$UsedPSProvider -notcontains $_}

New-PSDrive -Name $FreePSProvider[0] -PSProvider "FileSystem" -Root $share

It is especially useful if you have to work with a driveletter in the code or if the share has to be mapped in the Scirpt and has to be removed at the end of the script.

Continue reading...