# Add DynamoDB Query Lookup Plugin
## SUMMARY
This PR adds a new lookup pl…ugin `dynamodb_query` that enables querying AWS DynamoDB tables directly from Ansible playbooks. The plugin provides a convenient way to retrieve configuration data, application settings, and other dynamic values stored in DynamoDB tables as part of infrastructure-as-code workflows.
The plugin supports both Query (efficient, partition key-based) and Scan (full table scan) operations, with comprehensive support for DynamoDB features including secondary indexes, filter expressions, projection expressions, and automatic pagination.
### Key Capabilities
- Query items by partition key with optional sort key conditions
- Support for all DynamoDB sort key operators (EQ, LT, LE, GT, GE, BEGINS_WITH, BETWEEN)
- Query Global Secondary Indexes (GSI) and Local Secondary Indexes (LSI)
- Filter expressions with expression attribute names and values
- Projection expressions to retrieve specific attributes
- Automatic pagination with configurable limits
- Full support for all DynamoDB data types including Sets (SS, NS, BS)
### Use Cases
- Retrieve application configuration from DynamoDB tables
- Query deployment metadata and environment-specific settings
- Fetch dynamic inventory data stored in DynamoDB
- Access feature flags and runtime configuration
- Retrieve secrets metadata (not the secrets themselves)
## ISSUE TYPE
- New Plugin Pull Request
## COMPONENT NAME
`dynamodb_query` (lookup plugin)
## ADDITIONAL INFORMATION
### Implementation Details
The plugin is built on top of `AWSLookupBase` and follows Ansible AWS collection best practices:
- Uses `boto3` with automatic retry decorators for resilience
- Proper error handling with informative error messages
- Supports all standard AWS authentication methods (credentials, profiles, IAM roles)
- Handles DynamoDB pagination automatically
- Deserializes all DynamoDB data types to native Python types
### Testing
Comprehensive test coverage has been implemented:
#### Unit Tests (28 tests, ~90% code coverage)
- **Basic query operations** - partition key, sort key, operators
- **All sort key operators** - EQ, LT, LE, GT, GE, BEGINS_WITH, BETWEEN (including validation)
- **Filter expressions** - with expression attribute names/values
- **Projection expressions** - string and list formats
- **Scan operations** - full table scans
- **Pagination handling** - automatic pagination and with limits
- **Error handling** - ResourceNotFoundException, ValidationException, missing parameters
- **Data type deserialization** - String, Number, Boolean, List, Map, NULL, Binary, Sets (SS, NS, BS)
- **Secondary indexes** - GSI queries
- **Advanced parameters** - limit, descending order, pagination with limit
#### Integration Tests (34 tests)
- All sort key operators (EQ, LT, LE, GT, GE, BEGINS_WITH, BETWEEN)
- Global Secondary Index (GSI) and Local Secondary Index (LSI)
- Filter expressions with built-in functions (`attribute_exists`, `size`, `contains`)
- Projection expressions (string and list formats)
- Expression attribute names and values
- Pagination (automatic and with limit)
- All DynamoDB data types including Sets (SS, NS, BS)
- Empty results and error handling
- Scan operations
- Descending sort order
### Documentation
Complete documentation included:
- Module documentation with parameter descriptions
- 10+ usage examples covering common scenarios
- Return value documentation
### Example Usage
#### Simple Query by Partition Key
```yaml
- name: Get user data
ansible.builtin.debug:
msg: "{{ lookup('community.aws.dynamodb_query',
table_name='Users',
partition_key='user_id',
partition_value='12345') }}"
```
#### Query with Sort Key and Filter
```yaml
- name: Get recent active orders
ansible.builtin.debug:
msg: "{{ lookup('community.aws.dynamodb_query',
table_name='Orders',
partition_key='user_id',
partition_value='12345',
sort_key='order_date',
sort_value='2024-01-01',
sort_operator='GE',
filter_expression='#status = :active',
expression_attribute_names={'#status': 'status'},
expression_attribute_values={':active': 'active'}) }}"
```
#### Query Using Global Secondary Index
```yaml
- name: Find user by email
ansible.builtin.debug:
msg: "{{ lookup('community.aws.dynamodb_query',
table_name='Users',
index_name='email-index',
partition_key='email',
partition_value='[email protected]') }}"
```
#### Query with Projection Expression
```yaml
- name: Get specific user attributes
ansible.builtin.debug:
msg: "{{ lookup('community.aws.dynamodb_query',
table_name='Users',
partition_key='user_id',
partition_value='12345',
projection_expression=['user_id', 'name', 'email']) }}"
```
#### Query with Limit and Descending Order
```yaml
- name: Get last 10 orders
ansible.builtin.debug:
msg: "{{ lookup('community.aws.dynamodb_query',
table_name='Orders',
partition_key='user_id',
partition_value='12345',
limit=10,
scan_index_forward=false) }}"
```
#### Scan Operation
```yaml
- name: Scan small table with filter
ansible.builtin.debug:
msg: "{{ lookup('community.aws.dynamodb_query',
table_name='Config',
operation='scan',
filter_expression='#env = :prod',
expression_attribute_names={'#env': 'environment'},
expression_attribute_values={':prod': 'production'},
limit=100) }}"
```
### Tested With
- Python 3.11, 3.12, 3.13
- ansible-core 2.14+
- boto3 1.34.0+
- botocore 1.34.0+
All sanity tests and unit tests pass successfully.
## Integration Tests Note
Integration tests are failing in CI due to missing IAM permissions. The CI role needs additional permissions:
- `dynamodb:Query`
- `dynamodb:Scan`
All integration tests (34 tests) pass successfully when run locally with proper AWS credentials.
### Related Documentation
- [DynamoDB Query API](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html)
- [DynamoDB Scan API](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html)
- [DynamoDB Data Types](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html)
- [Ansible Lookup Plugins](https://docs.ansible.com/ansible/latest/plugins/lookup.html)