Error passing property from object to Stop-EC2Instance cmdlet

1.1k views Asked by At

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{
$_
}
}
3

There are 3 answers

0
Anthony Neace On BEST ANSWER

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, )
0
Ally Reilly On

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{
    $_
    }
}
1
Tim A On

Ally,

I believe you've got an error in your syntax for stopping.

Your start command (working):

Start-EC2Instance -InstanceId $EC2Instance[0].InstanceId

Your stop command (not working):

Stop-EC2Instance -Instance $EC2Instance[0].InstanceID

Try updating the '-Instance' flag to '-InstanceId' and see how it goes. :-)

-Tim