Search code examples
powershellamazon-ec2powershell-3.0aws-powershell

Error passing property from object to Stop-EC2Instance cmdlet


I am writing a number of functions for starting and stopping EC2 instances by the name tag not the ID. First I wrote a reporting function that can be found below.

Function Get-EC2InstanceReport{
    If((Get-Module -Name AWSPowerShell).Name -ne 'AWSPowerShell'){
        Throw 'AWSPowerShell module is not loaded'
    }
    Get-EC2Tag | `
        Where-Object {$_.ResourceType -eq 'instance' -and $_.Key -eq 'Name'} | `
        Select-Object @{Name='InstanceID'; Expression={$_.ResourceID}}, @{Name='Name'; Expression={$_.Value}}, `
        @{Name='Status'; Expression={Get-EC2InstanceStatus -IncludeAllInstances $true -InstanceId $_.ResourceID | %     {$_.InstanceState.Name}}}
}

And the function to start the instance works without error.

Function Start-EC2InstanceByName ([string]$Name){
    If((Get-Module -Name AWSPowerShell).Name -ne 'AWSPowerShell'){
        Throw 'AWSPowerShell module is not loaded'
    }
    [object]$EC2Instance = Get-EC2InstanceReport | Where-Object {$_.Name -eq $Name}

    Try{
        If($EC2Instance[0].Status -eq 'stopped'){
            Start-EC2Instance -InstanceId $EC2Instance[0].InstanceId | Out-Null
            Test-EC2InstanceStatus -Name $Name -EndState 'running'
        }
        Else{
            $ErrorMsg = "EC2 instance " + $EC2Instance[0].Name + " is not in the stopped state. It is " + $EC2Instance[0].Status + "."
            Throw $ErrorMsg
        }
    }
    Catch{
        $_
    }
}

But when using a similar method to stop the instance I get an error.

Function Stop-EC2InstanceByName ([string]$Name){
    If((Get-Module -Name AWSPowerShell).Name -ne 'AWSPowerShell'){
        Throw 'AWSPowerShell module is not loaded'
    }
    [object]$EC2Instance = Get-EC2InstanceReport | Where-Object {$_.Name -eq $Name}

    Try{
        If($EC2Instance[0].Status -eq 'running'){
            Stop-EC2Instance -Instance $EC2Instance[0].InstanceID | Out-Null
            Test-EC2InstanceStatus -Name $Name -EndState 'stopped'
        }
        Else{
            $ErrorMsg = "EC2 instance " + $EC2Instance[0].Name + " is not in the running state. It is " + $EC2Instance[0].Status + "."
            Throw $ErrorMsg
        }
    }
    Catch{
        $_
    }
}

The error can be found below.

Stop-EC2Instance : No instances specified
At C:\GitProjects\DBA\aws-powershell-scripts\AWSFunctions.psm1:61 char:4
+             Stop-EC2Instance -Instance $EC2Instance[0].InstanceID | Out-Null
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Stop-EC2Instance], AmazonEC2Exception
    + FullyQualifiedErrorId : Amazon.EC2.AmazonEC2Exception,Amazon.PowerShell.Cmdlets.EC2.StopEC2InstanceCmdlet

Any help would be greatly appreciated. If you require any further info please let me know.

Further progress.

Right, not resolved why the error occurs and have that open on the AWS forum over at amazon https://forums.aws.amazon.com/thread.jspa?threadID=143319

But the desired behaviour can be created by changing the function to that below.

Function Stop-EC2InstanceByName ([string]$Name){ If((Get-Module -Name AWSPowerShell).Name -ne 'AWSPowerShell'){ Throw 'AWSPowerShell module is not loaded' } [object]$EC2Instance = Get-EC2InstanceReport | Where-Object {$_.Name -eq $Name}

Try{
    If($EC2Instance[0].Status -eq 'running'){
        Get-EC2Instance -Filter @{Name="tag:Name"; Value=$Name} | Stop-EC2Instance | Out-Null
        Test-EC2InstanceStatus -Name $Name -EndState 'stopped'
    }
    Else{
        $ErrorMsg = "EC2 instance " + $EC2Instance[0].Name + " is not in the running state. It is " + $EC2Instance[0].Status + "."
        Throw $ErrorMsg
    }
}
Catch{
$_
}
}

Solution

  • Simply converting via .ToString() got the desired result for me, without having to pass input via pipeline.

    # Failed attempt
    PS C:\> Stop-EC2Instance -Instance $myInstance.InstanceId
    Stop-EC2Instance : No instances specified
    At line:1 char:17
    + Stop-EC2Instance <<<<  -Instance $myInstance.InstanceId
        + CategoryInfo          : NotSpecified: (:) [Stop-EC2Instance], AmazonEC2Exception
        + FullyQualifiedErrorId : Amazon.EC2.AmazonEC2Exception,Amazon.PowerShell.Cmdlets.EC2.StopEC2InstanceCmdlet
    
    # Successful attempt
    PS C:\> Stop-EC2Instance -Instance $myInstance.InstanceId.ToString()
    # Goodnight instance...
    

    Which is... odd, because when we use Get-Member on your instance object we can see that there is a string defined there in InstanceID:

       TypeName: Selected.Amazon.EC2.Model.ResourceTag
    
    Name        MemberType   Definition
    ----        ----------   ----------
    Equals      Method       bool Equals(System.Object obj)
    GetHashCode Method       int GetHashCode()
    GetType     Method       type GetType()
    ToString    Method       string ToString()
    InstanceID  NoteProperty System.String InstanceID=i-abcd1234
    Name        NoteProperty System.String Name=MyInstance
    Status      NoteProperty  Status=null
    

    Passing the InstanceID via pipeline input worked because it can accept System.Object[], whereas it seems explicitly using -Instance would rather you use a string Instance ID. The Stop-EC2Instance documentation corroborates this:

    -Instance <Object[]>
    Identifies the set of instances to stop or terminate. Accepts a string instance ID or a collection of RunningInstance or Reservation objects. If a Reservation object is supplied, all of the instances in the reservation are processed.
    Required?   False
    Position?   1
    Accept pipeline input?  True (ByValue, )