I can't pretend to be an expert at this stuff; I just fiddle with it until it works. It would probably be better to ask questions like this in stackoverflow, but, some comments/questions:
Is this the entire content of your playbook? The examples in the ansible docs for dnf look like your yaml above, but these examples are leaving off all the stuff at the top for the sake of brevity, and assuming you already know what to put there. But those examples from the module docs won't run as written.
I ran it as you provided it, and received the same error.
I am going to assume this is the entire playbook. In that case, the problem is that you've only provided a task list. You haven't provided the 'play'. But as the ansible docs say, each playbook consists of one or more 'plays' in a list. A play is what maps the tasks to specific hosts (in either an explicit or implicit inventory). But because it's missing the 'play' contents, ansible is analyzing your task list as a 'play', and 'dnf' is not valid attribute for 'plays'.
Minimal content that would add the 'play' would be something like:
---
- hosts: localhost
tasks:
I changed your runbook to:
---
- hosts: localhost
tasks:
- name: install VLC
dnf:
name: vlc
state: latest
and that now no longer fails for syntactical reasons. Although it does still fail because I need to run it as root, so here's what the working playbook looks like:
---
- hosts: localhost
tasks:
- name: install VLC
dnf:
name: vlc
state: latest
become: yes