AWS

[aws cli] 다중 조건 필터링

비번변경 2022. 9. 11. 01:01

cli 필터링 옵션

aws cli로 목록을 확인할 때 조건을 주어 필터링해야 하는 경우가 있다. 필터링을 할 수 있는 옵션으로는 --filter, --query 옵션 2개가 존재한다.

  • --filters : AWS CLI의 서버 측 필터링. 필터를 만족하는 레코드만 반환한다.
  • --query : 내장 JSON 기반 클라이언트 측 필터링. 서버에서 응답받은 결과를 필터링하여 표시한다. 쿼리는 JMESPath 구문을 사용하여 표현식을 만든다. --output 옵션을 사용하는 경우 필터링하기 전에 출력문이 페이징 처리된다.

 

이 글에서는 사용 중인 EC2의 비용 추적을 위해, 동작 중인 EC2 인스턴스에 특정 태그가 비어 있는 게 있는지 점검해보려고 한다. 즉, aws ec2 describe-instance 명령을 예시로 한다.

 

--filter 옵션

전체 EC2 목록에서 원하는 인스턴스 목록만 출력할 때 사용한다.

이 글에서는 동작 중인 EC2 목록에 대해서만 태그 확인을 하도록 필터링할 것이다.

# 사용 방법
aws ec2 describe-instances \
	--filter Name=[속성],Values=[조건]
    
# 예시 - 동작 상태(Running)인 인스턴스
aws ec2 describe-instances \
	--filter "Name=instance-state-name,Values=running"

aws cli은 기본적으로 JSON 형식으로 결과를 출력한다. 사진에서는 값이 너무 길어서 instance id만 출력하게끔 필터링했다.

--filter

실제로 콘솔에서도 2개의 인스턴스만 동작 중인 것을 확인할 수 있었다.

 

콘솔에서 결과 비교

 

만약 TAG로 필터링한다면 아래와 같이 사용해야 한다.

aws ec2 describe-instances \
    --filter Name=tag:[태그 이름],Values=[조건]

필터링 조건이 여럿인 경우, 조건을 공백으로 구분하여 나열한다.

aws ec2 describe-instances \
	--filters "Name=instance-state-name,Values=running" "Name=tag:backup,Values=yes"

 

--query 옵션

--query 옵션은 조회 결과에서 원하는 필드 값만 출력하도록 정의하는 옵션이다. 다만 간단한 필터링도 수행할 수 있다.

 

필드 필터링

예로 들어, aws ec2 describe-instances의 결과는 아래와 같다.

{
    "Reservations": [
        {
            "Groups": [],
            "Instances": [
                {
                "AmiLaunchIndex": 0,
                "ImageId": "ami-0abcdef1234567890",
                "InstanceId": "i-1234567890abcdef0",
                "InstanceType": "t2.micro",
                "KeyName": "MyKeyPair",
                "LaunchTime": "2018-05-10T08:05:20.000Z",
                "Monitoring": {
                    "State": "disabled"
                },
                "Placement": {
                    "AvailabilityZone": "us-east-2a",
                    "GroupName": "",
                    "Tenancy": "default"
                },
                "PrivateDnsName": "ip-10-0-0-157.us-east-2.compute.internal",
                "PrivateIpAddress": "10.0.0.157",
                "ProductCodes": [],
                "PublicDnsName": "",
                "State": {
                    "Code": 0,
                    "Name": "pending"
                },
                "StateTransitionReason": "",
                "SubnetId": "subnet-04a636d18e83cfacb",
                "VpcId": "vpc-1234567890abcdef0",
                "Architecture": "x86_64",
                "BlockDeviceMappings": [],
                "ClientToken": "",
                "EbsOptimized": false,
                "Hypervisor": "xen",
                "NetworkInterfaces": [
                    {
                        "Attachment": {
                            "AttachTime": "2018-05-10T08:05:20.000Z",
                            "AttachmentId": "eni-attach-0e325c07e928a0405",
                            "DeleteOnTermination": true,
                            "DeviceIndex": 0,
                            "Status": "attaching"
                        },
                        "Description": "",
                        "Groups": [
                            {
                                "GroupName": "MySecurityGroup",
                                "GroupId": "sg-0598c7d356eba48d7"
                            }
                        ],
                        "Ipv6Addresses": [],
                        "MacAddress": "0a:ab:58:e0:67:e2",
                        "NetworkInterfaceId": "eni-0c0a29997760baee7",
                        "OwnerId": "123456789012",
                        "PrivateDnsName": "ip-10-0-0-157.us-east-2.compute.internal",
                        "PrivateIpAddress": "10.0.0.157"
                        "PrivateIpAddresses": [
                            {
                                "Primary": true,
                                "PrivateDnsName": "ip-10-0-0-157.us-east-2.compute.internal",
                                "PrivateIpAddress": "10.0.0.157"
                            }
                        ],
                        "SourceDestCheck": true,
                        "Status": "in-use",
                        "SubnetId": "subnet-04a636d18e83cfacb",
                        "VpcId": "vpc-1234567890abcdef0",
                        "InterfaceType": "interface"
                    }
                ],
                "RootDeviceName": "/dev/xvda",
                "RootDeviceType": "ebs",
                "SecurityGroups": [
                    {
                        "GroupName": "MySecurityGroup",
                        "GroupId": "sg-0598c7d356eba48d7"
                    }
                ],
                "SourceDestCheck": true,
                "StateReason": {
                    "Code": "pending",
                    "Message": "pending"
                },
                "Tags": [],
                "VirtualizationType": "hvm",
                "CpuOptions": {
                    "CoreCount": 1,
                    "ThreadsPerCore": 1
                },
                "CapacityReservationSpecification": {
                    "CapacityReservationPreference": "open"
                },
                "MetadataOptions": {
                    "State": "pending",
                    "HttpTokens": "optional",
                    "HttpPutResponseHopLimit": 1,
                    "HttpEndpoint": "enabled"
                }
            }
        ],
        "OwnerId": "123456789012"
        "ReservationId": "r-02a3f596d91211712",
    }
}

이 결과에서 instanceId만 출력하고 싶다면 아래와 같이 구문을 작성할 수 있다. 원하는 깊이까지의 필드를 나열하면 된다.

aws ec2 describe-instances \
	--query '필드'

# 예시
aws ec2 describe-instances \
	--query 'Reservations[*].Instances[*].InstanceId'

필드 필터링

같은 깊이에 있는 필드를 여러 개 출력하고 싶다면 아래와 같이 작성한다.

aws ec2 describe-instances \
	--query '<listName>[].[<expression>, <expression>]'

# 예시
aws ec2 describe-instances \
	--query 'Reservations[*].Instances[].[InstanceId, PrivateIpAddress]'

여러 필드 출력

 

조건에 따른 필터링

값에 대해 조건을 줄 때는 아래와 같이 사용할 수 있다.

aws ec2 describe-instances \
	--filter <expression>.[? <expression> <comparator> <expression>]
  
# 예시
aws ec2 describe-instances \
	--filter 'Reservations[].Instances[?Tags[?Key == `Name`]].InstanceId'

조건문으로는 ==, !=, <, <=, >, >=을 사용할 수 있다.

AND 연산은 &&로 연결하고 OR 연산은 ||로 연결한다.

# AND
aws ec2 describe-instances \
	--query 'Reservations[].Instances[?Tags[?Key == `Name`] && Tags[?Key == `autoStartStop`]].InstanceId'
# OR
aws ec2 describe-instances \
	--query 'Reservations[].Instances[?Tags[?Key == `Name`] || Tags[?Key == `autoStartStop`]].InstanceId'

AND&#44; OR

따라서 특정 Tag 값이 비어있는지 여부는 다음과 같이 확인할 수 있다.

# Name 태그가 비어있거나 Cost Type이 비어있지 않는 경우
aws ec2 describe-instances \
	--query 'Reservations[].Instances[? !(not_null(Tags[?Key == `Name`].Value)) || not_null(Tags[?Key == `Cost Type`].Value)].InstanceId'

부정은 앞에 !을 붙인다.

 

+ jmespath가 not_null 함수는 제공하는데, 그 반대인 is_null은 제공하지를 않는 것 같다.

 

참고 문서

https://cloudest.oopy.io/posting/058

https://serverfault.com/questions/941250/what-is-the-difference-between-query-and-filter-in-aws-cli-which-is-suggest

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/describe-instances.html

https://jmespath.org/specification.html#built-in-functions