[{"data":1,"prerenderedAt":646},["ShallowReactive",2],{"blog-post-/ansible":3},{"id":4,"title":5,"body":6,"date":633,"description":634,"draft":635,"extension":636,"image":637,"meta":638,"navigation":124,"path":639,"seo":640,"stem":641,"tags":642,"__hash__":645},"content_en/16.ansible.md","Ansible",{"type":7,"value":8,"toc":627},"minimark",[9,13,17,20,28,31,34,39,42,45,150,153,163,167,170,172,399,420,426,429,476,483,528,551,560,564,567,588,595,614,620,623],[10,11,5],"h1",{"id":12},"ansible",[14,15,16],"p",{},"I decided to take a look at some common and most popular devops tools out there to see what kind of issue do they solve (besides Docker, because i'm currently familiar with that).",[14,18,19],{},"After some research, i found that Ansible is a pretty good starting point in this journey.",[14,21,22,23,27],{},"Ansible is an open-source automation engine used for IT tasks such as configuration management, application deployment. It simplifies infrastructure management by allowing admins to describe their desired state and execute them on one or multiple machines. This concept is actually called ",[24,25,26],"strong",{},"infrastructure as code",". The idea is, besides your actual software that you want to deploy, you should also write your desired infrastructure state as a code. So it can automatically and (quite) easily created again from ground up. In general, when you are taking some kind of devops responsibility, you basically try to automate every process like this as you can.",[14,29,30],{},"Ansible is actually fairly easy to work with. I found it pretty intuitive and easy to pick up and use.",[14,32,33],{},"First let's see what are some essential concepts we need to know in order to use Ansible:",[35,36,38],"h2",{"id":37},"inventory","Inventory",[14,40,41],{},"An Ansible inventory is a collection of managed hosts that Ansible targets for automation and configuration management tasks. It defines the \"who\" of your automation. Listing the servers, network devices, or other systems that Ansible will interact with are defined here.",[14,43,44],{},"Example:",[46,47,52],"pre",{"className":48,"code":49,"language":50,"meta":51,"style":51},"language-js shiki shiki-themes github-dark","[homelab]\n10.0.0.30 ansible_port=2222\n10.0.0.31 ansible_port=2222\n10.0.0.32 ansible_port=2222\n\n[cloud]\n188.65.100.54 ansible_port=22\n\n\n\n\n","js","",[53,54,55,64,87,103,119,126,132],"code",{"__ignoreMap":51},[56,57,60],"span",{"class":58,"line":59},"line",1,[56,61,63],{"class":62},"s95oV","[homelab]\n",[56,65,67,71,74,77,80,84],{"class":58,"line":66},2,[56,68,70],{"class":69},"sDLfK","10.0",[56,72,73],{"class":62},".",[56,75,76],{"class":69},"0.30",[56,78,79],{"class":62}," ansible_port",[56,81,83],{"class":82},"snl16","=",[56,85,86],{"class":69},"2222\n",[56,88,90,92,94,97,99,101],{"class":58,"line":89},3,[56,91,70],{"class":69},[56,93,73],{"class":62},[56,95,96],{"class":69},"0.31",[56,98,79],{"class":62},[56,100,83],{"class":82},[56,102,86],{"class":69},[56,104,106,108,110,113,115,117],{"class":58,"line":105},4,[56,107,70],{"class":69},[56,109,73],{"class":62},[56,111,112],{"class":69},"0.32",[56,114,79],{"class":62},[56,116,83],{"class":82},[56,118,86],{"class":69},[56,120,122],{"class":58,"line":121},5,[56,123,125],{"emptyLinePlaceholder":124},true,"\n",[56,127,129],{"class":58,"line":128},6,[56,130,131],{"class":62},"[cloud]\n",[56,133,135,138,140,143,145,147],{"class":58,"line":134},7,[56,136,137],{"class":69},"188.65",[56,139,73],{"class":62},[56,141,142],{"class":69},"100.54",[56,144,79],{"class":62},[56,146,83],{"class":82},[56,148,149],{"class":69},"22\n",[14,151,152],{},"Here i define remote machines that i want to use as a target of my ansible tasks. As you can see, we can separate them by grouping. We can these aliases later.",[14,154,155,156,159,160,162],{},"Also i define the ",[53,157,158],{},"ansible_port"," variable for them. ",[53,161,158],{}," is actually a special variable that ansible uses by default for initiating the ssh connection to remote.",[35,164,166],{"id":165},"playbook","Playbook",[14,168,169],{},"A playbook is a collection of tasks that you use to confugure remote servers. And it's utilizes YAML file format for defining a playbook logic.",[14,171,44],{},[46,173,177],{"className":174,"code":175,"language":176,"meta":51,"style":51},"language-yaml shiki shiki-themes github-dark","---\n- name: Intro to Ansible Playbooks\n  hosts: homelab # here you can specify remote machines by their alias, or pointing to all of them using \"all\" keyword\n  become: yes\n  become_method: sudo\n  remote_user: ubuntu\n  tasks:\n    - name: Copy file hosts with permissions\n      ansible.builtin.copy:\n        src: ./hosts\n        dest: /tmp/hosts_backup\n        mode: '0644'\n\n    - name: Add the user 'bob'\n      ansible.builtin.user:\n        name: bob\n\n    - name: Upgrade all apt packages\n      apt:\n        force_apt_get: yes\n        upgrade: dist\n        become: yes\n","yaml",[53,178,179,185,201,215,225,235,245,253,266,274,285,296,307,312,324,332,343,348,360,368,378,389],{"__ignoreMap":51},[56,180,181],{"class":58,"line":59},[56,182,184],{"class":183},"svObZ","---\n",[56,186,187,190,194,197],{"class":58,"line":66},[56,188,189],{"class":62},"- ",[56,191,193],{"class":192},"s4JwU","name",[56,195,196],{"class":62},": ",[56,198,200],{"class":199},"sU2Wk","Intro to Ansible Playbooks\n",[56,202,203,206,208,211],{"class":58,"line":89},[56,204,205],{"class":192},"  hosts",[56,207,196],{"class":62},[56,209,210],{"class":199},"homelab",[56,212,214],{"class":213},"sAwPA"," # here you can specify remote machines by their alias, or pointing to all of them using \"all\" keyword\n",[56,216,217,220,222],{"class":58,"line":105},[56,218,219],{"class":192},"  become",[56,221,196],{"class":62},[56,223,224],{"class":69},"yes\n",[56,226,227,230,232],{"class":58,"line":121},[56,228,229],{"class":192},"  become_method",[56,231,196],{"class":62},[56,233,234],{"class":199},"sudo\n",[56,236,237,240,242],{"class":58,"line":128},[56,238,239],{"class":192},"  remote_user",[56,241,196],{"class":62},[56,243,244],{"class":199},"ubuntu\n",[56,246,247,250],{"class":58,"line":134},[56,248,249],{"class":192},"  tasks",[56,251,252],{"class":62},":\n",[56,254,256,259,261,263],{"class":58,"line":255},8,[56,257,258],{"class":62},"    - ",[56,260,193],{"class":192},[56,262,196],{"class":62},[56,264,265],{"class":199},"Copy file hosts with permissions\n",[56,267,269,272],{"class":58,"line":268},9,[56,270,271],{"class":192},"      ansible.builtin.copy",[56,273,252],{"class":62},[56,275,277,280,282],{"class":58,"line":276},10,[56,278,279],{"class":192},"        src",[56,281,196],{"class":62},[56,283,284],{"class":199},"./hosts\n",[56,286,288,291,293],{"class":58,"line":287},11,[56,289,290],{"class":192},"        dest",[56,292,196],{"class":62},[56,294,295],{"class":199},"/tmp/hosts_backup\n",[56,297,299,302,304],{"class":58,"line":298},12,[56,300,301],{"class":192},"        mode",[56,303,196],{"class":62},[56,305,306],{"class":199},"'0644'\n",[56,308,310],{"class":58,"line":309},13,[56,311,125],{"emptyLinePlaceholder":124},[56,313,315,317,319,321],{"class":58,"line":314},14,[56,316,258],{"class":62},[56,318,193],{"class":192},[56,320,196],{"class":62},[56,322,323],{"class":199},"Add the user 'bob'\n",[56,325,327,330],{"class":58,"line":326},15,[56,328,329],{"class":192},"      ansible.builtin.user",[56,331,252],{"class":62},[56,333,335,338,340],{"class":58,"line":334},16,[56,336,337],{"class":192},"        name",[56,339,196],{"class":62},[56,341,342],{"class":199},"bob\n",[56,344,346],{"class":58,"line":345},17,[56,347,125],{"emptyLinePlaceholder":124},[56,349,351,353,355,357],{"class":58,"line":350},18,[56,352,258],{"class":62},[56,354,193],{"class":192},[56,356,196],{"class":62},[56,358,359],{"class":199},"Upgrade all apt packages\n",[56,361,363,366],{"class":58,"line":362},19,[56,364,365],{"class":192},"      apt",[56,367,252],{"class":62},[56,369,371,374,376],{"class":58,"line":370},20,[56,372,373],{"class":192},"        force_apt_get",[56,375,196],{"class":62},[56,377,224],{"class":69},[56,379,381,384,386],{"class":58,"line":380},21,[56,382,383],{"class":192},"        upgrade",[56,385,196],{"class":62},[56,387,388],{"class":199},"dist\n",[56,390,392,395,397],{"class":58,"line":391},22,[56,393,394],{"class":192},"        become",[56,396,196],{"class":62},[56,398,224],{"class":69},[14,400,401,402,404,405,408,409,412,413,412,416,419],{},"We can break up the playbook into different sections. Let's start with the header. In the header you set things like the ",[53,403,193],{}," of the playbook, the ",[53,406,407],{},"hosts"," to specify targets, the ",[53,410,411],{},"remote_user"," and ",[53,414,415],{},"become",[53,417,418],{},"become_method"," for configuring the privilege escelation method.",[421,422,423],"info",{},[14,424,425],{},"Visit the Ansible documentation. because explaining all of the options here is pointless.",[14,427,428],{},"These header fields are mostly boilerplate so let's get familiar with them really fast:",[430,431,432,438,448,454,471],"ol",{},[433,434,435,437],"li",{},[24,436,193],{},": Simply, the intent of the playbook! write something meaningful here so debugging it can be easier later.",[433,439,440,443,444,447],{},[24,441,442],{},"hosts:"," You will select target hosts from inventory here. It is also possible to use ",[53,445,446],{},"all"," as a hosts for applying to all of them.",[433,449,450,453],{},[24,451,452],{},"become:"," some operations on the host require special privileges, so ansible allows you to become to another user with privileges.",[433,455,456,459,460,462,463,466,467,470],{},[24,457,458],{},"become_method:"," Which privilege escalation method should be used. It works in conjunction with ",[53,461,415],{},". Essentially, it determines how you become another user with elevated privileges, such as ",[53,464,465],{},"sudo",", ",[53,468,469],{},"su",", or others.",[433,472,473,475],{},[24,474,411],{},": specifies the username that Ansible uses to connect to the remote target machines via SSH. This is the user account under which Ansible will log in and execute commands on the remote system.",[14,477,478,479,482],{},"The next section ",[53,480,481],{},"tasks"," is where you list and define all of your tasks that you want to be performed. think of it as an instruction manual that is exectued one by one.",[46,484,486],{"className":174,"code":485,"language":176,"meta":51,"style":51},"- name: Copy file hosts with permissions\n      ansible.builtin.copy:\n        src: ./hosts\n        dest: /tmp/hosts_backup\n        mode: '0644'\n",[53,487,488,498,504,512,520],{"__ignoreMap":51},[56,489,490,492,494,496],{"class":58,"line":59},[56,491,189],{"class":62},[56,493,193],{"class":192},[56,495,196],{"class":62},[56,497,265],{"class":199},[56,499,500,502],{"class":58,"line":66},[56,501,271],{"class":192},[56,503,252],{"class":62},[56,505,506,508,510],{"class":58,"line":89},[56,507,279],{"class":192},[56,509,196],{"class":62},[56,511,284],{"class":199},[56,513,514,516,518],{"class":58,"line":105},[56,515,290],{"class":192},[56,517,196],{"class":62},[56,519,295],{"class":199},[56,521,522,524,526],{"class":58,"line":121},[56,523,301],{"class":192},[56,525,196],{"class":62},[56,527,306],{"class":199},[430,529,530,533,548],{},[433,531,532],{},"name: The intent of the tasks. useful for debugging and readability in general.",[433,534,535,536,539,540,547],{},"module: Ansible tasks are usually performed by leveraging ansible modules. For most of the tasks that you want to perform, there is exist a module for that; whether a built in module or community module. here we want to copy a file from our local machine to remote machine. And there is a built in module exist for this usecase named ",[53,537,538],{},"copy"," (",[541,542,546],"a",{"href":543,"rel":544},"https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html",[545],"nofollow","docs",").",[433,549,550],{},"parameters: you provide the params and the module simply does its job. I think the example above is pretty much self explanatory. reading the documentation would be helpful to see which parameter are required and which are optional.",[421,552,553],{},[14,554,555,556,559],{},"You should try to create tasks to be as idempotent as possible. Ansible modules are also designed with this principle in mind. Idempotent means You can run the same task multiple times, and the result will always be the same — ",[24,557,558],{},"without causing unintended side effects",". And actually this may be tricky for tasks like \"changing the default ssh listening port\" and etc.",[35,561,563],{"id":562},"execute","Execute",[14,565,566],{},"After installing ansible, run the playbook by using the command below :",[46,568,572],{"className":569,"code":570,"language":571,"meta":51,"style":51},"language-bash shiki shiki-themes github-dark","ansible-playbook -i inventory.ini my-playbook.yaml \n","bash",[53,573,574],{"__ignoreMap":51},[56,575,576,579,582,585],{"class":58,"line":59},[56,577,578],{"class":183},"ansible-playbook",[56,580,581],{"class":69}," -i",[56,583,584],{"class":199}," inventory.ini",[56,586,587],{"class":199}," my-playbook.yaml\n",[14,589,590,591,594],{},"Here, ",[53,592,593],{},"-i"," stands for inventory. And as a first parameter, you simply pass the playbook that you wrote.",[14,596,597,598,466,601,466,604,466,607,610,611,613],{},"By default, Ansible prints a human-readable summary of play and task execution, including task names, host names, and status indicators (e.g., ",[53,599,600],{},"changed",[53,602,603],{},"ok",[53,605,606],{},"failed",[53,608,609],{},"skipped","). For example the status ",[53,612,603],{}," means there is nothing to do usually because the desired state is currently exists! (Idempotence)",[35,615,617],{"id":616},"to-be-continued",[24,618,619],{},"To be continued...",[14,621,622],{},"In the next post, we will write some powerful and useful playbooks aimed at hardening our hosts.",[624,625,626],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":51,"searchDepth":66,"depth":66,"links":628},[629,630,631,632],{"id":37,"depth":66,"text":38},{"id":165,"depth":66,"text":166},{"id":562,"depth":66,"text":563},{"id":616,"depth":66,"text":619},"2025-07-25T00:00:00.000Z","Infrastructure as a code? What?",false,"md","/blog-images/cows.webp",{},"/ansible",{"title":5,"description":634},"16.ansible",[643,644],"linux","Devops","hAeqz89Vd4D52UKnmAZqphhvwWFigwVZ2cnu8Pw8uOw",1772192624689]