User data scripts are convenient for simple, one-time setups in demos or small environments, but they have significant limitations at scale. They lack native signaling for completion or failure — you typically have to implement workarounds like writing a sentinel file to S3 or polling the filesystem. They are also difficult to make idempotent, meaning running them multiple times can produce inconsistent results. Ansible, by contrast, is designed for idempotent, repeatable configuration management and provides structured output and error handling, making it far more reliable for production use across many hosts.