The three core pieces are the inventory, modules, and playbooks. The inventory is a list of the hosts Ansible manages, grouped into named sets like webservers or databases. It can be a static INI or YAML file, or generated dynamically by querying AWS, GCP, or Azure for the live list of machines.
Modules are the small programs that do the actual work, one per task type. The apt module installs Debian packages, the copy module pushes files, the service module starts and stops daemons, the user module manages accounts. There are thousands of them. When a play runs, Ansible copies the relevant module to the target over SSH, executes it there, captures the JSON result, and deletes the module file. Nothing is left behind.
A playbook ties it together. It is a YAML file containing one or more plays, and each play maps a group of hosts to an ordered list of tasks. Reusable bundles of tasks, files, and variables are packaged as roles, and the public Ansible Galaxy registry hosts thousands of community roles you can pull in. Ansible runs tasks across many hosts in parallel, five at a time by default, controlled by the forks setting.