internal/Invoke-ManagedComputerCommand.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
Function Invoke-ManagedComputerCommand {
    <#
  .SYNOPSIS
   Runs wmi commands against a target system.
   
  .DESCRIPTION
   Runs wmi commands against a target system.
   Either directly or over PowerShell remoting.
   
  .PARAMETER ComputerName
   The target to run against. Must be resolvable.
   
  .PARAMETER Credential
   Credentials to use when using PowerShell remoting.
   
  .PARAMETER ScriptBlock
   The scriptblock to execute.
   Use $wmi to access the smo wmi object.
   Must not include a param block!
   
  .PARAMETER ArgumentList
   The arguments to pass to your scriptblock.
   Access them within the scriptblock using the automatic variable $args
   
  .PARAMETER Silent
   Left in for legacy reasons. This command will throw no matter what
 #>
    
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("Server")]
        [dbainstanceparameter]
        $ComputerName,
        
        [PSCredential]
        
        $Credential,
        
        [Parameter(Mandatory = $true)]
        [scriptblock]
        $ScriptBlock,
        
        [string[]]
        $ArgumentList,
        
        [switch]
        $Silent # Left in for legacy but this command needs to throw
    )
    
    $computer = $ComputerName.ComputerName
    
    $null = Test-ElevationRequirement -ComputerName $computer -Silent $true
    
    $resolved = Resolve-DbaNetworkName -ComputerName $computer
    $ipaddr = $resolved.IpAddress
    $ArgumentList += $ipaddr
    
    [scriptblock]$setupScriptBlock = {
        $ipaddr = $args[$args.GetUpperBound(0)]
        
        # Just in case we go remote, ensure the assembly is loaded
        [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SqlWmiManagement')
        $wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $ipaddr
        $null = $wmi.Initialize()
    }
    
    $prescriptblock = $setupScriptBlock.ToString()
    $postscriptblock = $ScriptBlock.ToString()
    
    $scriptblock = [ScriptBlock]::Create("$prescriptblock $postscriptblock")
    
    try {
        Invoke-Command2 -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -Credential $Credential -ErrorAction Stop
    }
    catch {
        try {
            Write-Message -Level Verbose -Message "Local connection attempt to $computer failed. Connecting remotely."
            
            # For surely resolve stuff, and going by default with kerberos, this needs to match FullComputerName
            $hostname = $resolved.FullComputerName
            
            Invoke-Command2 -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -ComputerName $hostname -ErrorAction Stop
        }
        catch {
            throw "SqlWmi connection failed: $_"
        }
    }
}