Check for an Object in AWS S3 with Ansible

I've been playing a fair bit of Valheim recently, and I wanted to take a stab at hosting my own server on AWS EC2. Like any other sane and not overengineered solution, I tried to leverage good infrastructure as code and configuration as code practices so that my server was quickly reproducible (this has been very helpful). Given that, I decided to use Ansible to configure the EC2 instance.

The rest of this article will describe how I used Ansible to check if save files existed in S3 while configuring the EC2 instance with Ansible.

Why is it necessary to check if an Object exists?

The amazon.aws.aws_s3 module will throw an error when downloading a file that doesn't exist. Because of this, it can be helpful to check if that Object exists before downloading it. Another scenario is that you may want to check if an object exists before replacing it with a new or default object. The sky is the limit!

1. List Objects in the Bucket

Ansible provides a helpful module for interacting with S3 buckets. The module is called amazon.aws.s3_object. Using this module, we can list all the Objects in a bucket and then check if a specific Object exists. The module is well documented, and you can find the documentation here.

Using mode: list, I list all of the objects in the valheim-saves bucket and store the result in a fact called objects_in_saves_bucket.

- name: List Objects in Saves bucket
  amazon.aws.aws_s3:
    bucket: "valheim-saves"
    mode: list
  register: objects_in_saves_bucket

2. Store the Desire Object Name in a Fact

Storing the subject Object name in a fact is a convenience step. This way, I could easily change the name of the Object I was looking for and not have to change it in multiple places.

- set_fact:
    world_save_fwl_file_name: "save-file-name.fwl"

Dynamic Object Name Example

Ansible Lint discourages using interpolation in conditional clauses, such as when. When I want to use a dynamic object name, I get around this by storing the object name in a fact, which makes Ansible Lint happy and makes the code more readable.

- set_fact:
    world_save_fwl_file_name: "{{valheim.world}}.fwl"

3. Store the State of the Object in a Fact

I wanted to store the state of the Object in a fact. This way, I could easily reference the Object's state in a later conditional.

- set_fact:
    s3_bucket_has_fwl_save_file: "{{world_save_fwl_file_name in objects_in_saves_bucket.s3_keys}}"

4. Download the S3 Object if it Exists

If the value of s3_bucket_has_fwl_save_file is true, then we will download the Object from S3 into the desired location.

- name: Download World Save FML File from S3
  when: s3_bucket_has_fwl_save_file
  amazon.aws.aws_s3:
    bucket: "valheim-saves"
    object: "{{ world_save_fwl_file_name }}"
    dest: "ubuntu/valheim/saves/worlds_local/{{world_save_fwl_file_name}}"
    mode: get

Putting it all together

After putting it all together, we have the following playbook with steps that will check if an object exists in an S3 bucket, and if it does, download it to the host.

- name: List Objects in Saves bucket
  amazon.aws.aws_s3:
    bucket: "valheim-saves"
    mode: list
  register: objects_in_saves_bucket

- set_fact:
    world_save_fwl_file_name: "save-file-name.fwl"

- set_fact:
    s3_bucket_has_fwl_save_file: "{{world_save_fwl_file_name in objects_in_saves_bucket.s3_keys}}"

- name: Download World Save FML File from S3
  when: s3_bucket_has_fwl_save_file
  amazon.aws.aws_s3:
    bucket: "valheim-saves"
    object: "{{ world_save_fwl_file_name }}"
    dest: "ubuntu/valheim/saves/worlds_local/{{world_save_fwl_file_name}}"
    mode: get

Conclusion

I hope this article was helpful. If you have any questions or comments, don't hesitate to get in touch with me via any of my social media links. Thanks for reading!