Public/Invoke-GceCommandAs.ps1
|
function Invoke-GceCommandAs { #Requires -Version 3.0 <# .SYNOPSIS Invoke Command on Google Cloud Engine (GCE) VM instances using IAP Tunnel. .DESCRIPTION Invoke Command on GCE VM instances using Identity-Aware Proxy (IAP) Tunnel. This function creates an IAP tunnel to the specified GCE VM instance and executes PowerShell commands remotely through the tunnel. Requires Google Cloud SDK (gcloud CLI) to be installed and configured. The user must have appropriate IAP permissions for the target VM instance. .PARAMETER Project The GCP project ID that contains the VM instance. .PARAMETER Zone The GCE zone where the VM instance is located. .PARAMETER InstanceName The name of the GCE VM instance. .PARAMETER ScriptBlock The PowerShell script block to execute on the remote VM. .PARAMETER FilePath Path to a PowerShell script file to execute on the remote VM. .PARAMETER ArgumentList Arguments to pass to the script block. .PARAMETER Credential Optional PSCredential for SSH authentication to the VM. If not provided, will use default SSH credentials. .PARAMETER LocalPort Local port to use for the IAP tunnel. Defaults to an available port. .PARAMETER RemotePort Remote port on the VM (default: 22 for SSH). .PARAMETER GcloudPath Path to gcloud CLI executable. Defaults to 'gcloud'. .PARAMETER AsJob Run the command as a background job. .PARAMETER ThrottleLimit Maximum number of concurrent connections. Defaults to 32. .EXAMPLE Invoke-GceCommandAs -Project "my-project" -Zone "us-central1-a" -InstanceName "my-vm" -ScriptBlock { Get-Process } .EXAMPLE Invoke-GceCommandAs -Project "my-project" -Zone "us-central1-a" -InstanceName "my-vm" -ScriptBlock { Get-Process } -Credential $Credential .EXAMPLE Invoke-GceCommandAs -Project "my-project" -Zone "us-central1-a" -InstanceName "my-vm" -FilePath "C:\Scripts\MyScript.ps1" #> #Requires -Version 3 [CmdletBinding(DefaultParameterSetName='ScriptBlock', HelpUri='http://go.microsoft.com/fwlink/?LinkID=135225', RemotingCapability='OwnedByCommand')] param( [Parameter(ParameterSetName='ScriptBlock', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] [Parameter(ParameterSetName='FilePath', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string] ${Project}, [Parameter(ParameterSetName='ScriptBlock', Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)] [Parameter(ParameterSetName='FilePath', Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string] ${Zone}, [Parameter(ParameterSetName='ScriptBlock', Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)] [Parameter(ParameterSetName='FilePath', Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)] [Alias('Instance')] [ValidateNotNullOrEmpty()] [string] ${InstanceName}, [Parameter(ParameterSetName='ScriptBlock', Mandatory=$true, Position=3)] [Alias('Command')] [ValidateNotNull()] [scriptblock] ${ScriptBlock}, [Parameter(ParameterSetName='FilePath', Mandatory=$true, Position=3)] [Alias('PSPath')] [ValidateNotNull()] [string] ${FilePath}, [Parameter(ValueFromPipeline=$true)] [psobject] ${InputObject}, [Alias('Args')] [System.Object[]] ${ArgumentList}, [Parameter(ValueFromPipelineByPropertyName=$true)] [pscredential] [System.Management.Automation.CredentialAttribute()] ${Credential}, [Parameter()] [ValidateRange(1, 65535)] [int] ${LocalPort} = 0, [Parameter()] [ValidateRange(1, 65535)] [int] ${RemotePort} = 22, [Parameter()] [string] ${GcloudPath} = 'gcloud', [Parameter()] [switch] ${AsJob}, [Parameter()] [int] ${ThrottleLimit} = 32 ) Process { $IsVerbose = $PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent # Handle FilePath parameter If ($FilePath) { $ScriptContent = Get-Content -Path $FilePath -Raw $ScriptBlock = [ScriptBlock]::Create($ScriptContent) } # Prepare parameters for Invoke-GceIapTunnel $IapTunnelParams = @{ Project = $Project Zone = $Zone InstanceName = $InstanceName ScriptBlock = $ScriptBlock GcloudPath = $GcloudPath LocalPort = $LocalPort RemotePort = $RemotePort } If ($ArgumentList) { $IapTunnelParams['ArgumentList'] = $ArgumentList } If ($Credential) { $IapTunnelParams['Credential'] = $Credential } If ($IsVerbose) { $IapTunnelParams['Verbose'] = $true } # Handle $Using variables by serializing them into the script block $UsingVariables = $ScriptBlock.ast.FindAll({$args[0] -is [System.Management.Automation.Language.UsingExpressionAst]},$True) If ($UsingVariables) { Write-Verbose "$(Get-Date): [GceCommandAs]: Processing $Using variables" $ScriptText = $ScriptBlock.Ast.Extent.Text $ScriptOffSet = $ScriptBlock.Ast.Extent.StartOffset ForEach ($SubExpression in ($UsingVariables.SubExpression | Sort-Object { $_.Extent.StartOffset } -Descending)) { $Name = '__using_{0}' -f (([Guid]::NewGuid().guid) -Replace '-') $Expression = $SubExpression.Extent.Text.Replace('$Using:','$').Replace('${Using:','${'); $Value = [System.Management.Automation.PSSerializer]::Serialize((Invoke-Expression $Expression)) # Inject variable initialization at the beginning of the script $InitCode = "`$Using:$Name = [System.Management.Automation.PSSerializer]::Deserialize('$Value'); " $ScriptText = $InitCode + $ScriptText.Substring(0, ($SubExpression.Extent.StartOffSet - $ScriptOffSet)) + "`${Using:$Name}" + $ScriptText.Substring(($SubExpression.Extent.EndOffset - $ScriptOffSet)) $ScriptOffSet = $ScriptBlock.Ast.Extent.StartOffset # Reset offset after insertion } $ScriptBlock = [ScriptBlock]::Create($ScriptText.TrimStart("{").TrimEnd("}")) $IapTunnelParams['ScriptBlock'] = $ScriptBlock } Write-Verbose "$(Get-Date): [GceCommandAs]: Invoking command via IAP Tunnel" # Execute via IAP Tunnel Invoke-GceIapTunnel @IapTunnelParams } } |