[
  {
    "title":"IaC yay or nay?",
    "url":"https://ctrl-find.nl/posts/iac_yay_or_nay/",
    "description":"Is IaC worth it or not.",
    "tags":["IaC"],
    "date":"2026-05-16",
    "content":"In this blog post we will explore the use of Infrastructure as Code also known as IaC. But before we go into details, let\u0026rsquo;s make sure we have same understanding of what IaC is.\nDefinition of IaC Infrastructure as Code (IaC) is the practice of provisioning and managinging infrastructure through code instead of manual configuration through UI\u0026rsquo;s. This means we can also use git, linters and industry-standards practices.\nThere are a many tools that can do IaC but here a short list of the common ones:\nAnsible, primarily a configuration management first tool but can also be used to create infrastructure. Terraform / Opentofu, infrastructure provisoning tools. Azure Bicep, Automation and provisoning tooling mainly designed specifically for Azure platform AWS CLI, primarily a managemant and automation tool but can also be used to create infrastructure. Yay All the tools in the list above can be easily installed on local computers, servers or intergrated into CI/CD pipelines. Terraform / and Opentofu are among the most capable IaC tools, while Azure bicep and AWS CLI are tied to there own specific cloud providers. By defining our IaC, we have a clear understanding of all our components in the cloud or locally. It also makes deploying a new environment or a component much easier,repeatable and consistent.\nNay You need to know how to write the tool specific language to create the necessary IaC. It can take multiple iterations to find the best setup specific for your environment. Most IaC tools can suffer from configuration drift, where infrastructure is manually changed after deployment. This can create differences between the deployed environment and the codebase. Terraform/OpenTofu handle this better through state management and planning, although statefiles can also become a maintenance challenge if they are corrupted, lost, or improperly managed.\nTerraform/Opentofu IaC example In the example code block below we will create a Azure resource group and a storage account. Most configurable resources needs to go into a group called resource group, in this group we place the storage account.\n1 2 3 4 5 6 7 8 9 10 11 12 resource \u0026#34;azurerm_resource_group\u0026#34; \u0026#34;example\u0026#34; { name = \u0026#34;example-resources\u0026#34; location = \u0026#34;West Europe\u0026#34; } resource \u0026#34;azurerm_storage_account\u0026#34; \u0026#34;example\u0026#34; { name = \u0026#34;example_storage_name\u0026#34; # Name of the storage account resource_group_name = azurerm_resource_group.example.name # connection to resource group name location = azurerm_resource_group.example.location # connection to resource group location account_tier = \u0026#34;Standard\u0026#34; # Azure storage options account_replication_type = \u0026#34;LRS\u0026#34; # Azure storage options } Conclusion For solo DevOps engineers,Sysadmin,developers or small environments, IaC can somethimes feel like its overkill. But when more people are deploying infrastructure, IaC becomes extremely valuable because of the consistancy, the ease of repeating items and the insights on all the deployed items.\nTroubleshooting IaC can sometimes feel like finding a needle in a haystack but the benifits usually outweigh the drawbacks. Its never been so easy to deploy a full cluster in minutes then clicking through webUI\u0026rsquo;s.\n"
  },
  {
    "title":"The dangers of using a random KMS license server",
    "url":"https://ctrl-find.nl/posts/internet_kms_service/",
    "description":"Looking into the dangers of a rogue KMS servers.",
    "tags":["Security"],
    "date":"2025-11-07",
    "content":"What are KMS licenses? KMS stands for key management services, this is a service which you can run your own client activation tools for windows licenses. The license on the specified machine asks to validate against the KMS service to check \u0026ldquo;am i a valid license?\u0026rdquo;. Which the service reports \u0026ldquo;yep, your valid until x date\u0026rdquo;, the machine your using this on gets all the benifits of having a license. This a standard which is created by Microsoft for their licensing setup.\nIs this legal?! We use this only for testing purposes, but the volume licenses keys are given out by Microsoft themselfs. So its kinda a gray area to license your own computer with a volume license and your own KMS server. The renew of each license is each 6 months or so on the test setup.\nThe setup I use the 11notes docker setup, modified the docker-compose file a little to make it work for myself. 11notes is a reverse enginered solution based on the KMS service but now it can on linux based systems. After running docker-compose up -d the webui should be available and with that the KMS service is also online.\nGetting a license Login on our test windows VM and we start a administrator powershell session. Lets points the Windows VM to our own KMS service with following command slmgr /skms \u0026lt;IP-docker-node\u0026gt;:1688. We use the following volume license (source) and to add the license we do slmgr /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX\nLicense validation To validate our license against our KMS service we need to activate the service on our Windows host with slmgr /ato. Sometimes the license will do a little finnicky stuff to validate, but if you check the KMS webui, we can see the following:\n1 2 3 4 5 6 7 8 \u0026lt;CLIENT ID\u0026gt; KMS EPID: \u0026lt;SOME HASH HERE\u0026gt; test Last IP:\u0026lt;IP HERE\u0026gt; Last active: 10/27/2025, 6:56:59 PM Windows Windows 10/11 Professional Activated 1 This means our license is valid and active on the Windows VM. From my understanding the Windows machine keeps polling the KMS service every so often. I haven\u0026rsquo;t tested it but my best guess is that the license will be deactivated when it couldn\u0026rsquo;t reach the KMS server in x time.\nConclusion I can see why larger businesses setup these constructions to get a better grip on the license usage.\nThere are many ways of activating a Windows machine, I was curious what a random KMS server on the internet could see when you activate against it. Main takeaway is dont activate your windows machine against any unknown or untrusted KMS service on the internet, the people that run that service can see alot of information polling for validation coming from your machine.\n"
  },
  {
    "title":"Advanced Ansible: Collections+",
    "url":"https://ctrl-find.nl/posts/ansible_collections/",
    "description":"A way to bundle our (custom) modules.",
    "tags":["Ansible"],
    "date":"2025-04-25",
    "content":"A set of modules together is a called a collection. Like ansible.builtin. I\u0026rsquo;ve used collections in the past to use the modules it provided but never really created them. In this post I want to take you on my journery to learn more about these collections.\nTo create a collection we need to do the following:\n1 2 3 4 ansible-galaxy collection init \u0026lt;namespace name\u0026gt;.\u0026lt;collection name\u0026gt; # EXAMPLE: ansible-galaxy collection init local.random This will build the scaffold collection.\n1 2 3 4 5 6 7 sebas@pop-os:~/Desktop$ tree local/ local/ └── random ├── galaxy.yml\t# Is a metadata file where information about the collection is written ├── plugins\t# Place where custom code will be placed and used for roles in the collection. │ └── README.md └── README.md\t# Collection documentation place. The README.md files gives some direction which information can be used or created in that folder. With the information from the README\u0026rsquo;s and a side by side comparision from ansible-collection and the Ansible Docs we can see how the structure is build.\nAdding our own custom module Create a folder called modules in the plugins folder, in this folder we place the python file we\u0026rsquo;ve created in advanced part 1.\nLets create a new folder in the root of random, called roles. when we go into roles we can generate a new role with ansible-galaxy init \u0026lt;name role\u0026gt; .\nPlace this code snippet in the main.yml tasks;\n1 2 3 4 5 6 7 8 --- - name: Test that my module works pokemon_info:\t# Name of the file we\u0026#39;ve added to modules folder type: pokemon\t# python var name: \u0026#34;{{ poke_name }}\u0026#34;\t# Variable which will be used in the playbook register: result - debug: var=result Create a playbook outside this folder somewhere different then the collection; Place the following code snippet inside the playbook\n1 2 3 4 5 6 7 --- - name: talk to Role # Name playbook hosts: localhost\t# Which hosts to connect to roles:\t- role: local.random.pokemon_info\t# local.random is the collection name with pokemon_info as module vars: poke_name: \u0026#34;charmander\u0026#34;\t# Variable which will be used inside the module. We cant just run it now, sadly our Ansible doesnt know anything about this collection. To make our Ansible know this collection we must do the following:\nGo into the collection, to the folder with galaxy.yml in it. Create a tar file with ansible-galaxy collection build, this will produce a collection-name-version.tgz file. Install the tgz file with ansible-galaxy collection install \u0026lt;collection-name-version\u0026gt;.tar.gz. When installed we can do ansible-playbook \u0026lt;playbook-name\u0026gt;.yml, when changing up the collection with new info or added functionality and you want to test locally we need to do the following;\nrm -rf ~/.ansible This removes everything in the .ansible folder, there is probably a better way but not found it yet. Bump the version in the galaxy.yml in the collection. Create a new tar file with the previous build command and install the new version. Now the new functionality can be used in the playbooks.\nConclusion The majority of the collection was fun to do and create but i\u0026rsquo;ve struggled much when I changed the collection. I couldn\u0026rsquo;t see the changes but after some web browsing I found a helpfull comment that helped me get my newer version up and running.\n"
  },
  {
    "title":"Advanced Ansible: Custom modules",
    "url":"https://ctrl-find.nl/posts/ansible_custom_modules/",
    "description":"The power of creating your own modules.",
    "tags":["Ansible"],
    "date":"2025-04-12",
    "content":"This is my first time trying to create a Ansible custom module. I dont have much python knowledge (the very bare minimum), but I want to expand my knowledge of python and Ansible by creating a custom module.\nWhat do we need? Installed: Ansible Python3 virtual environment for python3 packages A folder to place our files Starting with the module Activate the virtual environment and create a folder called library. Ansible knows that if there is a custom module its in the library folder. In this library folder we can create our python file, I\u0026rsquo;ve called mine pokemon_api.py\nI\u0026rsquo;ve added the contents of pokemon_api.py file below;\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #!/usr/bin/python3 # This is a must otherwise the module wont work. from ansible.module_utils.basic import * # Necessary imports for the module to use. import requests import json def main(): # The fields I want to use and the type of input. fields = { \u0026#34;name\u0026#34;: {\u0026#34;required\u0026#34;: True, \u0026#34;type\u0026#34;: \u0026#34;str\u0026#34;} } # Making the fields available to use. module = AnsibleModule(argument_spec=fields) # Getting the parameter(s) from the playbook into the module name = module.params[\u0026#39;name\u0026#39;] # Default python code to use for the module URL = f\u0026#34;https://pokeapi.co/api/v2/pokemon/{name}\u0026#34; REQ = requests.get(URL) OUT = REQ.text y = json.loads(OUT) def get_stats(): return y[\u0026#34;stats\u0026#34;] x = get_stats() print(x) # End of the module, this doesn\u0026#39;t change the file system so it is on false # And Meta gives the items back to the terminal in our playbook. module.exit_json(changed=False, meta={\u0026#34;name\u0026#34;: name, \u0026#34;response\u0026#34;: x}) if __name__ == \u0026#39;__main__\u0026#39;: main() The playbook Create a playbook file in the same folder where the folder library is. I\u0026rsquo;ve called it for simple reason playbook.yml the contents of the file are below;\n1 2 3 4 5 6 7 8 9 10 --- - hosts: localhost # Testing it on local machine tasks: - name: Local Module testing # Can choose the name you want pokemon_api: # This is the name of the file in library without the .py part name: \u0026#34;gengar\u0026#34; # The name field is passed through the module in name = module.params[\u0026#39;name\u0026#39;] register: result # this registered the output - debug: msg: \u0026#34;{{result}}\u0026#34; # This shows the registerd output Running the playbook To run the playbook we do the following:\n1 ansible-playbook playbook.yml The output of the playbook\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 sebas@pop-os:~/Desktop/custom_module$ ansible-playbook playbook.yml [WARNING]: No inventory was parsed, only implicit localhost is available [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match \u0026#39;all\u0026#39; PLAY [localhost] ******************************************************************************************************************************************* TASK [Gathering Facts] ************************************************************************************************************************************* ok: [localhost] TASK [Local Module testing] ******************************************************************************************************************************** ok: [localhost] TASK [debug] *********************************************************************************************************************************************** ok: [localhost] =\u0026gt; { \u0026#34;msg\u0026#34;: { \u0026#34;changed\u0026#34;: false, \u0026#34;failed\u0026#34;: false, \u0026#34;meta\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;gengar\u0026#34;, \u0026#34;response\u0026#34;: [ { \u0026#34;base_stat\u0026#34;: 60, \u0026#34;effort\u0026#34;: 0, \u0026#34;stat\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;hp\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://pokeapi.co/api/v2/stat/1/\u0026#34; } }, { \u0026#34;base_stat\u0026#34;: 65, \u0026#34;effort\u0026#34;: 0, \u0026#34;stat\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;attack\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://pokeapi.co/api/v2/stat/2/\u0026#34; } }, { \u0026#34;base_stat\u0026#34;: 60, \u0026#34;effort\u0026#34;: 0, \u0026#34;stat\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;defense\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://pokeapi.co/api/v2/stat/3/\u0026#34; } }, { \u0026#34;base_stat\u0026#34;: 130, \u0026#34;effort\u0026#34;: 3, \u0026#34;stat\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;special-attack\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://pokeapi.co/api/v2/stat/4/\u0026#34; } }, { \u0026#34;base_stat\u0026#34;: 75, \u0026#34;effort\u0026#34;: 0, \u0026#34;stat\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;special-defense\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://pokeapi.co/api/v2/stat/5/\u0026#34; } }, { \u0026#34;base_stat\u0026#34;: 110, \u0026#34;effort\u0026#34;: 0, \u0026#34;stat\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;speed\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://pokeapi.co/api/v2/stat/6/\u0026#34; } } ] } } } PLAY RECAP ************************************************************************************************************************************************* localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Conclusion After some tinkering it is fun to create a custom module for something that isn\u0026rsquo;t available. With this kind of knowledge, Ansible can be used for many more things then just the \u0026ldquo;default\u0026rdquo; collections and modules.\nStay tuned for the Advanced Ansible part 2: Our own collection!\n"
  },
  {
    "title":"Containers?!",
    "url":"https://ctrl-find.nl/posts/containers/",
    "description":"Physical solutions into the IT world.",
    "tags":["containers"],
    "date":"2025-03-30",
    "content":"Containers, we are using them more and more, in professional environments but also in selfhosting and homelabs, but what execly is it? While in the real world we use physical shipping containers to move the products or make creative things from them, this is the digital variant.We can do all kinds of things with containers, run our scripts, deploy software or use them testing purposes. These containers are mostly light weight Operating System(s) (OS) with a specific purpose and can be easily shared through registries (storage place for containers). The general rule of thumb is to use one container for a function.\nHow to start with containers? We need a container engine, this is software which is able to run our containers on top of our Operating System (OS). The most common are podman or docker. After following the documentation for required configuration and the installation is self, we can check if everything works like expected. open up a terminal or command prompt window and type:\n1 2 3 4 5 # when installed podman podman --version # When installed docker docker --version If a version pops-up, you know that the installation went oke, if it doesn\u0026rsquo;t pop-up. Open a new terminal or command prompt window and try again (because of the command context)\nConclusion We now know what containers are and have a container engine installed, we can now run containers on our computer and/or server. Stay tuned for Part 2: Running containers\n"
  },
  {
    "title":"Always check and verify!",
    "url":"https://ctrl-find.nl/posts/checksum/",
    "description":"How to know when something has been tempered with?",
    "tags":["Security"],
    "date":"2025-03-24",
    "content":"What is a checksum A Checksum is a digital fingerprint of a file or code. Software creators and/or companies are creating these digital fingerprints for safety reasons. One of the most common usecases is checking if a downloaded file is changed by a third party. if the checksum is different from the one that the creators provided, it most likely has been corrupted or tampered with.\nWe dont need external programs or a website (where we need to upload a file) to give us a checksum of a file. It is fairly easy and shown down below in two categories: Windows and Linux(MacOS).\nWindows By clicking on the windows icon on the server (given it is in graphic mode otherwise your already in a terminal) or desktop. Search for Powershell or Command Prompt, we do not need elevated priveleges (admin rights) on the machine. Then we run the following command;\n1 certutil -hashfile \u0026lt;filename\u0026gt; [HashAlgorithm] The following HashAgoriths can be used: MD2 MD4 MD5 SHA1 SHA256 SHA384 SHA512\nLinux When on desktop, find the launcher and search for terminal or use the keyboard shortcut: ctrl-alt-t On a server you are already on the systems terminal.\n1 [HashAlgorithm]sum \u0026lt;filename\u0026gt; The following HashAgoriths can be used: MD5 SHA1 SHA256 SHA384 SHA512\nExample This example works the same on linux with different commands. i\u0026rsquo;ve created a test.txt file with some info text in it.LEts check the hash\n1 certutil -hashfile test.txt sha256 Output 1 2 3 SHA256 hash of test.txt: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 CertUtil: -hashfile command completed successfully. When we change the text inside the test.txt file and run the command again, the hash changes:\n1 2 3 SHA256 hash of test.txt: a5b40cf7147479b452c3b51feefc2f7cd0c926121033b2cfd09f1ae1c825ec30 CertUtil: -hashfile command completed successfully. An overview of the changed sha256 checksums.\n1 2 Initial: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 changed: a5b40cf7147479b452c3b51feefc2f7cd0c926121033b2cfd09f1ae1c825ec30 "
  },
  {
    "title":"Testing Ansible code",
    "url":"https://ctrl-find.nl/posts/ansible_testing/",
    "description":"Making sure our code runs!",
    "tags":["Ansible"],
    "date":"2025-03-15",
    "content":"What is Ansible Molecule Ansible Molecule is created for development and testing of Ansible roles and collections. Through Molecule it is possible to test against different OS versions or complete new systems.\nprerequisites to local use Molecule: Podman of docker installation Locally installed ansible Python3 A python virtual environment Installation There are different ways of installing Ansible Molecule see this url. I Chose the pip way with python3 -m pip install molecule ansible-core. Whether we choose docker or podman we need to install the molecule connector, I personally use podman so thats pip install molecule-podman\nSetup With the installation out of the way, we can setup our Ansible role for testing. We can use the role we\u0026rsquo;ve created earlier in part4 and add a folder in the role called extensions.\n1 mkdir /Users/sebas/Desktop/ansible/\u0026lt;name role\u0026gt;/extensions Lets go to the folder /Users/sebas/Desktop/ansible/\u0026lt;name role\u0026gt;/extensions, and run the following command:\n1 python3 -m molecule init scenario It creates the following 4 files:\n1 create.yml destroy.yml molecule.yml converge.yml molecule.yml = Is used for central Molecule configuration create.yml = Is used for creating the test instances converge.yml = Is used to call upon the Ansible role destroy.yml = Is used for destroying the test instances To use a container instance we need to create one more file:\n1 touch /Users/sebas/Desktop/ansible/\u0026lt;name role\u0026gt;/extensions/molecule/default/requirements.yml requirements.yml = Is used by Molecule to ensure that the test environment works correctly Adding configurations We now overwrite the current data from the files named above, the info we are going to add is a simplefied config from the Ansible Molecule examples site. Copy the snippets into the dedicated files.\nmolecule.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 --- dependency: name: galaxy options: requirements-file: requirements.yml platforms: - name: molecule-ubuntu image: docker.io/geerlingguy/docker-ubuntu2004-ansible - name: molecule-fedora image: geerlingguy/docker-fedora39-ansible driver: name: podman options: managed: false login_cmd_template: \u0026#34;podman exec -ti {instance} bash\u0026#34; ansible_connection_options: ansible_connection: podman Create.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 - name: Create hosts: localhost gather_facts: false vars: molecule_inventory: all: children: molecule: hosts: {} tasks: - name: Create a container containers.podman.podman_container: name: \u0026#34;{{ item.name }}\u0026#34; image: \u0026#34;{{ item.image }}\u0026#34; state: started command: \u0026#34;{{ item.command | default(\u0026#39;sleep 1d\u0026#39;) }}\u0026#34; register: result loop: \u0026#34;{{ molecule_yml.platforms }}\u0026#34; - name: Add container to molecule_inventory vars: inventory_partial_yaml: | all: children: molecule: hosts: \u0026#34;{{ item.name }}\u0026#34;: ansible_connection: containers.podman.podman ansible.builtin.set_fact: molecule_inventory: \u0026gt; {{ molecule_inventory | combine(inventory_partial_yaml | from_yaml, recursive=true) }} loop: \u0026#34;{{ molecule_yml.platforms }}\u0026#34; - name: Dump molecule_inventory ansible.builtin.copy: content: \u0026#34;{{ molecule_inventory | to_yaml }}\u0026#34; dest: \u0026#34;{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml\u0026#34; mode: \u0026#34;0600\u0026#34; converge.yml 1 2 3 4 5 --- - name: Converge hosts: all roles: - role: \u0026lt;Name of the role\u0026gt; destroy.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 - name: Destroy molecule containers hosts: molecule gather_facts: false tasks: - name: Stop and remove container delegate_to: localhost containers.podman.podman_container: name: \u0026#34;{{ inventory_hostname }}\u0026#34; state: absent rm: true - name: Remove potentially stopped container delegate_to: localhost ansible.builtin.command: cmd: podman container rm --ignore {{ inventory_hostname }} changed_when: false - name: Remove dynamic molecule inventory hosts: localhost gather_facts: false tasks: - name: Remove dynamic inventory file ansible.builtin.file: path: \u0026#34;{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml\u0026#34; state: absent requirements.yml 1 2 collections: - containers.podman How to run For molecule to work correctly we need to export a variable to our terminal:\n1 export ANSIBLE_ROLES_PATH=/Users/sebas/Desktop/ansible/\u0026lt;name role\u0026gt; This variable is always pointed to the root of our Ansible role. Lets go to the extensions folder once more in our terminal cd /Users/sebas/Desktop/ansible/\u0026lt;name role\u0026gt;/extensions. Now we can run the following command: python3 -m molecule test. What this does it runs a full lifecycle sequence, which means that it will check all the stages below:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 Molecule full lifecycle sequence └── default ├── dependency ├── cleanup ├── destroy ├── syntax ├── create ├── prepare ├── converge ├── idempotence ├── side_effect ├── verify ├── cleanup └── destroy Output molecule test This output is an overview how on what the testing flow is, content can differ from what is in the ansible role itself. The output from below is with a simple task to install nginx.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 Starting galaxy collection install process Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`. [1;35m[WARNING]: Could not match supplied host pattern, ignoring: molecule[0m PLAY [Destroy molecule containers] ********************************************* [36mskipping: no hosts matched[0m PLAY [Remove dynamic molecule inventory] *************************************** TASK [Remove dynamic inventory file] ******************************************* [32mok: [localhost][0m PLAY RECAP ********************************************************************* [32mlocalhost[0m : [32mok=1 [0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 playbook: /Users/sebas/Desktop/ansible/my_role/extensions/molecule/default/converge.yml PLAY [Create] ****************************************************************** TASK [Create a container] ****************************************************** [33mchanged: [localhost] =\u0026gt; (item={\u0026#39;image\u0026#39;: \u0026#39;docker.io/geerlingguy/docker-ubuntu2004-ansible\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;molecule-ubuntu\u0026#39;})[0m [33mchanged: [localhost] =\u0026gt; (item={\u0026#39;image\u0026#39;: \u0026#39;geerlingguy/docker-fedora39-ansible\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;molecule-fedora\u0026#39;})[0m TASK [Add container to molecule_inventory] ************************************* [32mok: [localhost] =\u0026gt; (item={\u0026#39;image\u0026#39;: \u0026#39;docker.io/geerlingguy/docker-ubuntu2004-ansible\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;molecule-ubuntu\u0026#39;})[0m [32mok: [localhost] =\u0026gt; (item={\u0026#39;image\u0026#39;: \u0026#39;geerlingguy/docker-fedora39-ansible\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;molecule-fedora\u0026#39;})[0m TASK [Dump molecule_inventory] ************************************************* [33mchanged: [localhost][0m PLAY RECAP ********************************************************************* [33mlocalhost[0m : [32mok=3 [0m [33mchanged=2 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 PLAY [Converge] **************************************************************** TASK [Gathering Facts] ********************************************************* [32mok: [molecule-fedora][0m [32mok: [molecule-ubuntu][0m TASK [my_role : Gather facts] ************************************************** [32mok: [molecule-fedora][0m [32mok: [molecule-ubuntu][0m TASK [my_role : Get facts] ***************************************************** [36mincluded: /Users/sebas/Desktop/ansible/my_role/tasks/help.yml for molecule-fedora, molecule-ubuntu[0m TASK [my_role : Install python3-apt] ******************************************* [36mskipping: [molecule-fedora][0m [32mok: [molecule-ubuntu][0m TASK [my_role : Update all packages on Red Hat/CentOS/Fedora] ****************** [36mskipping: [molecule-ubuntu][0m [32mok: [molecule-fedora][0m TASK [my_role : Update apt cache] ********************************************** [36mskipping: [molecule-fedora][0m [33mchanged: [molecule-ubuntu][0m TASK [my_role : Install nginx] ************************************************* [33mchanged: [molecule-fedora][0m [33mchanged: [molecule-ubuntu][0m PLAY RECAP ********************************************************************* [33mmolecule-fedora[0m : [32mok=5 [0m [33mchanged=1 [0m unreachable=0 failed=0 [36mskipped=2 [0m rescued=0 ignored=0 [33mmolecule-ubuntu[0m : [32mok=6 [0m [33mchanged=2 [0m unreachable=0 failed=0 [36mskipped=1 [0m rescued=0 ignored=0 PLAY [Converge] **************************************************************** TASK [Gathering Facts] ********************************************************* [32mok: [molecule-ubuntu][0m [32mok: [molecule-fedora][0m TASK [my_role : Gather facts] ************************************************** [32mok: [molecule-fedora][0m [32mok: [molecule-ubuntu][0m TASK [my_role : Get facts] ***************************************************** [36mincluded: /Users/sebas/Desktop/ansible/my_role/tasks/help.yml for molecule-fedora, molecule-ubuntu[0m TASK [my_role : Install python3-apt] ******************************************* [36mskipping: [molecule-fedora][0m [32mok: [molecule-ubuntu][0m TASK [my_role : Update all packages on Red Hat/CentOS/Fedora] ****************** [36mskipping: [molecule-ubuntu][0m [32mok: [molecule-fedora][0m TASK [my_role : Update apt cache] ********************************************** [36mskipping: [molecule-fedora][0m [32mok: [molecule-ubuntu][0m TASK [my_role : Install nginx] ************************************************* [32mok: [molecule-fedora][0m [32mok: [molecule-ubuntu][0m PLAY RECAP ********************************************************************* [32mmolecule-fedora[0m : [32mok=5 [0m changed=0 unreachable=0 failed=0 [36mskipped=2 [0m rescued=0 ignored=0 [32mmolecule-ubuntu[0m : [32mok=6 [0m changed=0 unreachable=0 failed=0 [36mskipped=1 [0m rescued=0 ignored=0 PLAY [Destroy molecule containers] ********************************************* TASK [Stop and remove container] *********************************************** [33mchanged: [molecule-fedora -\u0026gt; localhost][0m [33mchanged: [molecule-ubuntu -\u0026gt; localhost][0m TASK [Remove potentially stopped container] ************************************ [32mok: [molecule-ubuntu -\u0026gt; localhost][0m [32mok: [molecule-fedora -\u0026gt; localhost][0m PLAY [Remove dynamic molecule inventory] *************************************** TASK [Remove dynamic inventory file] ******************************************* [33mchanged: [localhost][0m PLAY RECAP ********************************************************************* [33mmolecule-fedora[0m : [32mok=2 [0m [33mchanged=1 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [33mlocalhost[0m : [32mok=1 [0m [33mchanged=1 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [33mmolecule-ubuntu[0m : [32mok=2 [0m [33mchanged=1 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Conclusion This was the first time for me using Ansible Molecule, I like how you can test against different OSes. The initial setup was a bit bumpy but that was more on my end then on Ansible Molecule\u0026rsquo;s end. This is a great way of starting with Ansible testing framework, I will definitely intergrade this in all current and future Ansible roles.\nThis is the end of the Ansible series. When following all the parts you have the fundamentals of how to use and setup Ansible.\n"
  },
  {
    "title":"Ansible secrets",
    "url":"https://ctrl-find.nl/posts/ansible_secrets/",
    "description":"Keeping secrets safe!",
    "tags":["Ansible"],
    "date":"2025-02-11",
    "content":"Ansible-vault?! Always wondered how to use ansible with sensitive information like secrets,files,variables or configurations? Thats where ansible-vault comes to play, this is a way to encrypt the data with a password.\nThere are two ways to use ansible-vault, with cli parameters or use a password file. We will explorer both options below.\nHow to use the CLI parameters The CLI parameters variant asks for a password that you want to use for en/decrypt or view the encrypted content. I have created a file named in /Users/sebas/Desktop/ansible/supersecret.yml with the following content in it:\nContent for the supersecret.yml file\n1 2 --- vault_mysql_root_password: \u0026#34;Supersecretshere\u0026#34; CLI ansible-vault options There are plenty of options in the ansible-vault CLI tool but we focus on the main options like encryptions, viewing decrypting.\nEncrypting the file Choose a password to use to encrypt the file. WARNING! DO NOT FORGET IT! otherwise the file cant be decrypted.\n1 2 3 4 sebas@eyeofmordor ansible % ansible-vault encrypt supersecret.yml New Vault password: Confirm New Vault password: Encryption successful Viewing the encrypted file The ouput is transformed into a non readable format for us but it is for ansible-vault.\n1 2 3 4 5 6 7 sebas@eyeofmordor ansible % cat supersecret.yml $ANSIBLE_VAULT;1.1;AES256 64353937646132353735643538633165363439383532346666316561333537303432333033623661 3737613233323436616263356638363133313734653462650a316330623931356230326434383930 34353834366436643838306631666366333437303430666333303638323365643435343966386630 3638383866393463340a633337313466646231613862623330346333346363343834383536333935 34633937303066366235336165633362616434613931636666663365313435313862 Viewing the file Viewing is a options to view the encrypted secret without decrypting the file itself. This uses the password we set earlier when encrypting the file.\n1 2 3 sebas@eyeofmordor ansible % ansible-vault view supersecret.yml Vault password: vault_mysql_root_password: Supersecretshere Decrypting the file Decrypting is making the file readable way for us and exposable.\n1 2 3 4 5 6 sebas@eyeofmordor ansible % ansible-vault decrypt supersecret.yml Vault password: Decryption successful sebas@eyeofmordor ansible % cat supersecret.yml vault_mysql_root_password: Supersecretshere Playbook with cli parameter ask password To test this, we are creating a new playbook with vi /Users/sebas/Desktop/ansible/my_first_vault_playbook.yml and add the content below in it. Encrypt the supersecret.yml file with a selfchosen password through the way showed above.\n1 2 3 4 5 6 7 8 9 10 --- - name: playbook hosts: localhost vars_files: \u0026#34;./supersecret.yml\u0026#34; tasks: - name: Show MySQL root password debug: msg: \u0026#34;{{ vault_mysql_root_password }}\u0026#34; To run the playbook we use the following command ansible-playbook my_first_vault_playbook --ask-vault-password This will ask for the password we used to encrypt the supersecret.yml file.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 sebas@eyeofmordor ansible % ansible-playbook my_first_vault_playbook.yml --ask-vault-password Vault password: [WARNING]: No inventory was parsed, only implicit localhost is available [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match \u0026#39;all\u0026#39; PLAY [playbook] ********************************************************************************************************************************************* TASK [Gathering Facts] ************************************************************************************************************************************** ok: [localhost] TASK [Show MySQL root password] ***************************************************************************************************************************** ok: [localhost] =\u0026gt; { \u0026#34;msg\u0026#34;: \u0026#34;Supersecretshere\u0026#34; } PLAY RECAP ************************************************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 How to use ansible-vault with a password file? To use a ansible-vault with a password file we need two essential files:\nansible configuration file: /Users/sebas/Desktop/ansible/ansible.cfg password file: /Users/sebas/Desktop/ansible/.vault_pass Ansible configuration and a password file We can modify ansible to use certain parameters for the configuration, this will be done through ansible.cfg file. We create this file the ansible folder with touch /Users/sebas/Desktop/ansible/ansible.cfg and touch /Users/sebas/Desktop/ansible/.vault_pass Open the ansible.cfg file and place the following code in it:\n1 2 [defaults] vault_password_file = ./.vault_pass # The name can be different and locations also. Open the .vault_pass file and add something like this (preferable something strong password):\n1 welkom123 The ansible.cfg file works for the specific folder, this means we can add this file to more roles and specify for each its own .vault_pass password.\nUsing Encrypt, Decrypt again Because the ansible.cfg, password, supersecret.yml files are in the same folder we can do the same commands again without getting the password prompt.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 sebas@eyeofmordor ansible % ansible-vault encrypt supersecret.yml Encryption successful sebas@eyeofmordor ansible % cat supersecret.yml $ANSIBLE_VAULT;1.1;AES256 36623333656433346334396263643435326433373830643931653637336131343033636334313135 3361393833306235336335623638336237373965313939660a326432343536336439633537336236 63336665366261316339376634373231363135633661623436346636653832653230393532383532 6335623333393865310a353162653035643737363962356161306165623433666135626236376436 38636334613764663530633361636561376535363030356339383434356336313762 sebas@eyeofmordor ansible % ansible-vault decrypt supersecret.yml Decryption successful sebas@eyeofmordor ansible % cat supersecret.yml vault_mysql_root_password: Supersecretshere Password file for playbooks or roles? We are going to reuse our role we\u0026rsquo;ve created in part 4. Copy the .vault_pass file into the vars folder and copy the ansible.cfg file into to main role folder.\nThere is no solution to use the vault straight into the role or playbook with a password file. The only solution is to use a group/vars or host vars folders outside the roles, this is a wider ansible configuration setup\nAdd the information below to our main.yml file in the vars folder.\nThis lets us use the information from our vault as a variable inside the role itself.\n1 2 sql: password: \u0026#34;{{ vault_mysql_root_password }}\u0026#34; to use our password from our file we need to surround it with \u0026quot;{{ name from vault info }}\u0026quot; Ansible knows this syntax and can be used for other vars aswell. Add following code blocks somewhere in the file 01_hostname.yml.\n1 2 3 4 5 6 - name: Include encrypted variables include_vars: vars/supersecret.yml # This will load in the vault file. - name: show secret debug: msg: \u0026#34;{{ sql.password }}\u0026#34; Lets run our role playbook ansible-playbook ../role_book.yml\n1 2 3 4 5 6 7 TASK [machine_info : Show MySQL root password] ************************************************************************************************************** ok: [localhost] =\u0026gt; { \u0026#34;msg\u0026#34;: \u0026#34;Supersecretshere\u0026#34; } PLAY RECAP ************************************************************************************************************************************************** localhost : ok=11 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Conclusion We all use different kind of secrets if its variables, files or other configurations. Using an encryption method like ansible-vault helps automation alot. It is not without faults but is better then exposing our secrets to the world.\nStay tuned for the Part 6: Tesing our Ansible code!\n"
  },
  {
    "title":"Creating Ansible Roles",
    "url":"https://ctrl-find.nl/posts/ansible_roles/",
    "description":"Why stop at playbooks, when you can bundle them?",
    "tags":["Ansible"],
    "date":"2025-01-20",
    "content":"Why Roles over Playbooks? Single playbooks are great for tasks but when a project starts getting complexer, it way more managable in a role then a single playbook. Ansible role is a predefined set of playbooks that is build for a specific purpose. All the necessary configurations can be set and build into the role.\nCreating the role We create the role in the folder we\u0026rsquo;ve been using in the previous parts /Users/sebas/Desktop/ansible. With following command we can create a pre-setup role through ansible.\n1 ansible-galaxy init /Users/sebas/Desktop/ansible/\u0026lt;name role\u0026gt; Role layout This is the layout of the pre-setup role we\u0026rsquo;ve created through the previous command.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 machine_info # Name of the role (created through the init command) ├── README.md # Default setup for a readme of the role ├── defaults # Standard config place, which can be used in tasks │ └── main.yml ├── files # Place here all necessary files needed for your role ├── handlers # Default place for adding task handlers │ └── main.yml ├── meta # Informational place when you list your role to the internet │ └── main.yml ├── tasks # Tasks folder where playbooks go │ ├── 00_gather_facts.yml │ ├── 01_hostname.yml │ ├── 02_ram.yml │ └── main.yml ├── templates # A place for all templated files which can be used in tasks often defined in jinja templates. ├── tests # Created for testing your role │ ├── inventory │ └── test.yml └── vars # The is the folder for role specific variables. └── main.yml Tasks files Each of these tasks files is a separate playbook but together they create a role which can configure or build a service.\nWe use a special var called ansible_facts, this is information from ansible.builtin.setup. In the next part (5) we will go further into detail about vars and secrets. To show the output of the special var we use the ansible.builtin.debug module.\n00_gather_facts.yml In this playbook we gather all the information that ansible can learn. This is can be the gather_facts = true toggle in a single playbook.\n1 2 3 4 5 --- - name: Gather facts ansible.builtin.setup: gather_subset: - \u0026#39;all\u0026#39; 01_hostname.yml In this playbook we want to know what distribution the machine is running and which version.\n1 2 3 4 5 6 7 8 9 10 11 12 --- - name: Get distribution ansible.builtin.debug: var: ansible_facts.distribution - name: Get distribution_version ansible.builtin.debug: var: ansible_facts.distribution_version - name: Get hostname ansible.builtin.debug: var: ansible_facts.nodename 02_ram.yml We also want to know how much RAM we have and how much is used.\n1 2 3 4 5 6 7 8 --- - name: Get free ram ansible.builtin.debug: var: ansible_facts.memfree_mb - name: Get total ram ansible.builtin.debug: var: ansible_facts.memtotal_mb main.yml This is the file where every separate playbook comes together to bind the role together.\n1 2 3 4 5 6 7 8 9 --- - name: Get facts ansible.builtin.include_tasks: 00_gather_facts.yml - name: Get hostname ansible.builtin.include_tasks: 01_hostname.yml - name: Get ram ansible.builtin.include_tasks: 02_ram.yml Creating the playbook which runs the role 1 touch /Users/sebas/Desktop/ansible/role_book.yml Lets copy the following configurion into the role_book.yml file. Ansible uses the roles part in the configuration to know that it is a role which needs to be run.\nBecause this role is in the same directory as the role_book playbook is doesn\u0026rsquo;t matter. When you place your role somewhere else you need to add a path like role: \u0026lt;path\u0026gt;/role_name\n1 2 3 4 5 --- - name: playbook hosts: localhost roles: - role: machine_info We use the same command to run a playbook, but now inside the playbook there is a role defined. The working of the command is the same, Ansible knows when it needs to search for the main.yml file in the role when the roles tag is used.\n1 ansible-playbook role_book.yml Output 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 sebas@eyeofmordor ansible % ansible-playbook role_book.yml [WARNING]: No inventory was parsed, only implicit localhost is available [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match \u0026#39;all\u0026#39; PLAY [playbook] **************************************************************** TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [machine_info : Get facts] ************************************************ included: /Users/sebas/Desktop/ansible/machine_info/tasks/00_gather_facts.yml for localhost TASK [machine_info : Gather facts] ********************************************* ok: [localhost] TASK [machine_info : Get hostname] ********************************************* included: /Users/sebas/Desktop/ansible/machine_info/tasks/01_hostname.yml for localhost TASK [machine_info : Get distribution] ***************************************** ok: [localhost] =\u0026gt; { \u0026#34;ansible_facts.distribution\u0026#34;: \u0026#34;MacOSX\u0026#34; } TASK [machine_info : Get distribution_version] ********************************* ok: [localhost] =\u0026gt; { \u0026#34;ansible_facts.distribution_version\u0026#34;: \u0026#34;15.2\u0026#34; } TASK [machine_info : Get hostname] ********************************************* ok: [localhost] =\u0026gt; { \u0026#34;ansible_facts.nodename\u0026#34;: \u0026#34;eyeofmordor\u0026#34; } TASK [machine_info : Get ram] ************************************************** included: /Users/sebas/Desktop/ansible/machine_info/tasks/02_ram.yml for localhost TASK [machine_info : Get free ram] ********************************************* ok: [localhost] =\u0026gt; { \u0026#34;ansible_facts.memfree_mb\u0026#34;: \u0026#34;6562\u0026#34; } TASK [machine_info : Get total ram] ********************************************* ok: [localhost] =\u0026gt; { \u0026#34;ansible_facts.memtotal_mb\u0026#34;: \u0026#34;8192\u0026#34; } PLAY RECAP ********************************************************************* localhost : ok=10 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 How to use variables Variables are a great way of setting informations which can be reused on different places within our playbook or role. This url does a deepdive in variables https://spacelift.io/blog/ansible-variables and does a great way of explaining how to use it.\nConclusion When you when want to keep all the necessary files together for projects or configurations. Roles is a great solution for this, there is always a playbook needed to call on the role itself.\nStay tuned for the Part 5: Secrets!\n"
  },
  {
    "title":"Creating Ansible Playbooks",
    "url":"https://ctrl-find.nl/posts/ansible_playbooks/",
    "description":"Standard way of doing things with playbooks.",
    "tags":["Ansible"],
    "date":"2025-01-13",
    "content":"Why Playbooks over ADHOC commands ADHOC commands are great for short and quick informational actions but when you want to to get more information or configuration from a group.\nThen is one command at a time a really time consuming action, this is where playbooks shine.\nWhy are playbook better then adhoc commands?, there is more predictablity on playbooks and less manual work on it.\nA playbook is a repeatable script that we can run over and over again, with the same result. In these playbooks we can specify precise actions what we want to do and in which order.\nCreating the playbook We are going to create a new file in the folder we\u0026rsquo;ve created in the previous post /Users/sebas/Desktop/ansible.\nThis file is can be created through GUI or CLI I chose CLI then the command is touch /Users/sebas/Desktop/ansible/my_first_playbook.yml.\nIn the minor playbook below we do the following:\nGet Machine hostname View the output Create a file in /tmp/called eek2.txt. Copy the playbook below into the file we created my_first_playbook.yml.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 --- - name: playbook # General name of the playbook hosts: proxmox # Which host or hostgroup we want to talk to remote_user: root # The user on the remote machine tasks: - name: Get Hostname # Name of the task ansible.builtin.command: hostname # Command module, we run the linux command hostname register: output # Register the output of the builtin.command - name: get machine output # Name of the task ansible.builtin.debug: # Debug module to see the output from what we registerd in previous task var: output # Uses the output var to display the result of output - name: file with content # Name of the task ansible.builtin.copy: # Copy module does sort of the same as file but is capable of adding content dest: \u0026#34;/tmp/eek2.txt\u0026#34; # File path + filename content: | # Content block is where we can add information, the pike ( | ) logo is nothing more then start on next line. this is on the first line. etc. The command rundown We are going to use the inventory we\u0026rsquo;ve created in part two. We can see alot more information with -v tag, which stands for verbose.\nThis can scale up to three v like -vvv but thats not necessary for now. The ansible command we are going to run:\n1 ansible-playbook -i inventory my_first_playbook.yml -K Output command 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 sebas@eyeofmordor ansible % ansible-playbook -i inventory my_first_playbook.yml -K BECOME password: PLAY [playbook] ****************************************************************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************************************************************** [WARNING]: Platform linux on host 192.168.200.155 is using the discovered Python interpreter at /usr/bin/python3.11, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.18/reference_appendices/interpreter_discovery.html for more information. ok: [192.168.200.155] TASK [what machine is this] ****************************************************************************************************************************************** changed: [192.168.200.155] TASK [get machine output] ******************************************************************************************************************************************** ok: [192.168.200.155] =\u0026gt; { \u0026#34;output\u0026#34;: { \u0026#34;changed\u0026#34;: true, \u0026#34;cmd\u0026#34;: [ \u0026#34;hostname\u0026#34; ], \u0026#34;delta\u0026#34;: \u0026#34;0:00:00.003310\u0026#34;, \u0026#34;end\u0026#34;: \u0026#34;2025-01-06 22:55:42.248595\u0026#34;, \u0026#34;failed\u0026#34;: false, \u0026#34;msg\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;rc\u0026#34;: 0, \u0026#34;start\u0026#34;: \u0026#34;2025-01-06 22:55:42.245285\u0026#34;, \u0026#34;stderr\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;stderr_lines\u0026#34;: [], \u0026#34;stdout\u0026#34;: \u0026#34;proxmox\u0026#34;, \u0026#34;stdout_lines\u0026#34;: [ \u0026#34;proxmox\u0026#34; ] } } TASK [file eek create] *********************************************************************************************************************************************** changed: [192.168.200.155] TASK [file with content] ********************************************************************************************************************************************* changed: [192.168.200.155] PLAY RECAP *********************************************************************************************************************************************************** 192.168.200.155 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Host check 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 sebas@eyeofmordor ansible % ssh root@192.168.200.155 Linux proxmox 6.8.12-5-pve #1 SMP PREEMPT_DYNAMIC PMX 6.8.12-5 (2024-12-03T10:26Z) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Mon Jan 6 22:55:43 2025 from 192.168.200.27 root@proxmox:~# ls /tmp/ systemd-private-2405913b3d2e4137b7a6c92065b2a5a1-chrony.service-oHmDrN eek2.txt systemd-private-2405913b3d2e4137b7a6c92065b2a5a1-systemd-logind.service-e6apCa root@proxmox:~# cat /tmp/eek2.txt this is on the first line. etc. root@proxmox:~# We see in the output above, what the result is from our hostname command proxmox and we see the lines added to the file eek2.txt.\nConclusion Ansible has a wide varity of modules which we can use in playbooks and roles (which is still to come). The consistant way of configuring and creating infrastruture makes it reliable for even the hardest tasks.\nStay tuned for the Part 4: Creating Roles!\n"
  },
  {
    "title":"Managing Hosts with Ansible",
    "url":"https://ctrl-find.nl/posts/ansible_hosts/",
    "description":"first steps managing a host with Ansible.",
    "tags":["Ansible"],
    "date":"2025-01-07",
    "content":"What do we need? There are a few things we need to start with managing hosts with ansible.\nSSH keys Place to store our files Inventory file Creating SSH key To manage hosts, we need to use a SSH key. We can create them with the following command: ssh-keygen -t ed25519. Choose where the SSH files needs to be placed and if you want to use a password or not (much more secure if you do).\nI\u0026rsquo;ve setup a proxmox test host, were we can test our commands, playbooks and roles on. To use manage this host with ansible, we need to copy our SSH pub key to the test proxmox host. we do this with the following command, it will ask for the password of the hosts;\n1 ssh-copy-id root@\u0026lt;192.168.200.155\u0026gt; Place to store our files Lets create a Ansible folder on our desktop, for me is this /Users/sebas/Desktop.\nWe can create the Ansible folder through CLI or GUI, it doesn\u0026rsquo;t matter which way you use.\nI\u0026rsquo;ve used CLI and for that is the command mkdir /Users/sebas/Desktop/ansible.\\\nCreating an Inventory file Creating the file can be through GUI or CLI, I prefer CLI so the command for it is touch /Users/sebas/Desktop/ansible/inventory.\nWe can call this file anything we want but for easy reference we use inventory.\nIn the inventory file we place the following code block;\nIt creates a group called proxmox and added an ip address from a host. We are not bound to use ip addresses in the file we also can use FQDN names for it.\n1 2 [proxmox] 192.168.200.155 Time to use ADHOC commands When we use the following command:\n1 ansible proxmox -i inventory -m ping -u root We specify which group we want to call in the inventory file in this case proxmox or all to check all host(s) in thefile.\nSpecify with -i the inventory file which has the host(s) information we want to connect to.\nWe use a module (-m) called ping, which checks the connection to the host(s) we\u0026rsquo;ve added in the inventory file.\nWith -u we specify the user we want to use to connect to the host, only necessary if your local user is different from the remote user.\nthe output of the command;\n1 2 3 4 5 6 7 192.168.200.155 | SUCCESS =\u0026gt; { \u0026#34;ansible_facts\u0026#34;: { \u0026#34;discovered_interpreter_python\u0026#34;: \u0026#34;/usr/bin/python3.11\u0026#34; }, \u0026#34;changed\u0026#34;: false, \u0026#34;ping\u0026#34;: \u0026#34;pong\u0026#34; } We can use other ansible modules in our adhoc commands like ansible.builtin.shell any you can find on the docs. In the command below we use -a to specifiy a command we want to run, without calling a specific module we standard use the command module in the adhoc command. If you add \u0026ndash;ask-become-pass or -K, Ansible prompts you for the password to use for privilege escalation like sudo.\n1 ansible proxmox -i inventory -a \u0026#39;apt update\u0026#39; -K -u root The output of the output;\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 sebas@eyeofmordor ansible % ansible proxmox -i inventory -a \u0026#39;apt update\u0026#39; -K -u root BECOME password: [WARNING]: Platform linux on host 192.168.200.155 is using the discovered Python interpreter at /usr/bin/python3.11, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible- core/2.18/reference_appendices/interpreter_discovery.html for more information. 192.168.200.155 | CHANGED | rc=0 \u0026gt;\u0026gt; Hit:1 http://ftp.nl.debian.org/debian bookworm InRelease Hit:2 http://ftp.nl.debian.org/debian bookworm-updates InRelease Hit:3 http://security.debian.org bookworm-security InRelease Get:4 http://download.proxmox.com/debian/pve bookworm InRelease [2,768 B] Get:5 http://download.proxmox.com/debian/ceph-quincy bookworm InRelease [3,470 B] Get:6 http://download.proxmox.com/debian/pve bookworm/pve-no-subscription amd64 Packages [381 kB] Get:7 http://download.proxmox.com/debian/ceph-quincy bookworm/no-subscription amd64 Packages [41.5 kB] Fetched 429 kB in 1s (651 kB/s) Reading package lists... Building dependency tree... Reading state information... 14 packages can be upgraded. Run \u0026#39;apt list --upgradable\u0026#39; to see them. WARNING: apt does not have a stable CLI interface. Use with caution in scripts. Conclusion Managing hosts with Ansible is a way to automate and streamline infrastructure management. By leveraging adhoc commands, you can quickly connect and manage hosts.\nIn this series we will delve deeper into Ansible, where we explore more techniques such as playbooks and roles. Stay tuned for the next post in this series!\n"
  },
  {
    "title":"Ansible? what is that!",
    "url":"https://ctrl-find.nl/posts/ansible/",
    "description":"Automation and configuration with Ansible.",
    "tags":["Ansible"],
    "date":"2025-01-06",
    "content":"Ansible is a configuration management tool that can be used for configuring servers, clients and network equipment.\nThe basic way of using this tool is through SSH, utilizing passwords or preferably SSH keys.\nIn this series we will be using Ansible for configuring a server through the following options;\nansible-commands: Execute single command on a host ansible-playbooks\u0026quot;: Run repeatable script(s) on a host ansible-roles: Organize and reuse code for configurations tasks There is a newer version called ansible-navigator that\u0026rsquo;s for another post, for now lets stick to the current version called ansible.\nHow to install There are many different ways to install Ansible on your preferred Operating System.\nIn this Ansible series, we will be using MacOS for running Ansible.\nTo install Ansible on MacOS, all we need is:\nInstall homebrew: here (optional if you already have homebrew installed). 1 2 brew update brew install ansible brew update checks for updates and then upgrade the current installed packages and finally, install Ansible through the package manager.\nOnce the installation is done, we can verify it by running:\\\n1 ansible --version Now that we have Ansible installed, we can start managing our hosts. Stay tuned for the next post in this series!\n"
  },
  {
    "title":"About",
    "url":"https://ctrl-find.nl/about/",
    "description":"",
    "tags":null,
    "date":"2025-01-01",
    "content":" 1 whoami I am Sebastiaan, a Sysadmin/Platform/Devops Engineer or in Short IT Engineer. I\u0026rsquo;ve started this blog so to bundle my knowledge and help some people out with issue\u0026rsquo;s I have encounterd in my carreer.\nP.S: I am a huge movie and serie addict, so there is a possibility I sneak a reference into the topics here and there.\n"
  },
  {
    "title":"Create and check certificates",
    "url":"https://ctrl-find.nl/posts/certificates/",
    "description":"Quick access to creation and checking of certificates",
    "tags":["Security"],
    "date":"2025-01-01",
    "content":"Linux/MacOS OpenSSL MD5 modules You can use MD5 module to check if key and cert are a keypair toghter.\n1 2 openssl rsa -modulus -noout -in \u0026lt;name\u0026gt;.key| openssl md5 openssl x509 -modulus -noout -in \u0026lt;name\u0026gt;.crt| openssl md5 Check cert 1 openssl x509 -in \u0026lt;name\u0026gt;.crt -text -noout Check CSR 1 openssl req -in \u0026lt;name\u0026gt;.csr -noout -text Short monitoring 1 openssl x509 -in \u0026lt;path\u0026gt;/\u0026lt;name\u0026gt;.crt -noout -checkend 3888000 "
  },
  {
    "title":"Gitlab locked login backdoor",
    "url":"https://ctrl-find.nl/posts/gitlab_backdoor/",
    "description":"Getting into a locked Gitlab instance",
    "tags":["gitlab"],
    "date":"2025-01-01",
    "content":"When locked out of the Gitlab environment, you\u0026rsquo;ll need to enable default sign-in method when OIDC or AD is configured. This could be done from within the UI, but if you are locked out then this method will ofcourse not work.\nEnabling sign-in can also be done from the command line. SSH into the Gitlab server and perform the following command to enable sign-in.\nstart rails console (takes a while) 1 gitlab-rails console Enable 1 Gitlab::CurrentSettings.update!(password_authentication_enabled_for_web: true) Disable 1 Gitlab::CurrentSettings.update!(password_authentication_enabled_for_web: false) "
  },
  {
    "title":"How to configure Gitlab Advanced Search",
    "url":"https://ctrl-find.nl/posts/gitlab_advanced_search/",
    "description":"Powering up search in Gitlab",
    "tags":["gitlab"],
    "date":"2025-01-01",
    "content":"Docker-compose elastic setup This guide is based on docker-compose setup from elasticsearch. https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html\nAdd the Elastic IP Add the IP and DNS from docker host to Gitlab.\n1 vi /etc/hosts 1 \u0026lt;self specific ip\u0026gt; \u0026lt; DNS name like: test0.internal test1.internal test2.internal\u0026gt; Add Elastic CA certificate Add the CA certificate to /etc/gitlab/trusted-certs (give it a logical name) This CA file can be found on the docker host in the mapped data location.\nRun Gitlab reconfigure The reconfigure will add the CA to gitlab internally.\n1 gitlab-ctl reconfigure This will make a specific hash symlink in another gitlab folder.\n1 /opt/gitlab/embedded/ssl/certs This will show if openssl can make a connection to the Elasticsearch cluster\n1 echo | /opt/gitlab/embedded/bin/openssl s_client -connect test0.internal:9200 With the symlink in place we can configure through the GUI the connection to Elasticsearch cluster with https://test0.internal:9200 with username and password.\nRate Limiting change The default rate limit is set to 30, when using advanced search the rate limit will be triggerd per minute. Through the following change we set the new default to 1000. This can be set through following path in UI: admin portal \u0026gt; settings \u0026gt; Network \u0026gt; Search rate limits\nIndexing shard sizing Through the GUI we can setup our Shard sizing, after testing 1 shard and 1 replica is more then enough for our needs.\nIndexing Input parameters for connection; url: https://test0.internal:9200 Username: elastic Password: Configured Password\nEnable setting \u0026ldquo;Elasticsearch indexing\u0026rdquo;, Press the blue button on top off the page to start the indexing from all the projects. Progress can be found throughout the CLI commands below;\nEnabling Advance Search Enable setting \u0026ldquo;Search with Elasticsearch enabled\u0026rdquo;\nNice to know commands Create and recreate index for Elastic through gitlab-rake 1 2 gitlab-rake gitlab:elastic:create_empty_index gitlab-rake gitlab:elastic:recreate_index Index information 1 2 3 gitlab-rake gitlab:elastic:projects_not_indexed gitlab-rake gitlab:elastic:index_projects gitlab-rake gitlab:elastic:index_projects_status Gitlab Elastic main overview 1 gitlab-rake gitlab:elastic:info "
  },
  {
    "title":"How to do a Gitlab Major version upgrade",
    "url":"https://ctrl-find.nl/posts/gitlab_upgrade/",
    "description":"Easy way to upgrade to a newer version.",
    "tags":["gitlab"],
    "date":"2025-01-01",
    "content":"Upgrade Path You can check the upgrade path on the following site: https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/. This website is made by Gitlab, and will give a good overview about changes and new features for the upgrades you selected.\nChecks https://docs.gitlab.com/ee/update/index.html#checking-for-background-migrations-before-upgrading Check pending and failed DB migrations.\n1 2 gitlab-rails runner -e production \u0026#39;puts Gitlab::Database::BackgroundMigration::BatchedMigration.queued.count\u0026#39; gitlab-rails runner -e production \u0026#39;puts Gitlab::Database::BackgroundMigration::BatchedMigration.with_status(:failed).count\u0026#39; OPTIONAL: Check pending elasticsearch migrations\n1 gitlab-rake gitlab:elastic:list_pending_migrations Update check\n1 gitlab-rake gitlab:check SANITIZE=true Pre-upgrade Create a snapshot on the environment you are hosting your gitlab environment.\nSet instance in maintenance mode:\n1 gitlab-rails runner \u0026#39;::Gitlab::CurrentSettings.update!(maintenance_mode: true)\u0026#39; Create a gitlab backup of latest version! https://docs.gitlab.com/ee/administration/backup_restore/ Make sure gitlab has the rights to read and write with the backup.\nDisable advanced search before upgrading to a new major version. This is because of the index changes that are possible coming with new version. Checkout my reindex guide on gitlab advanced search to re-enable it.\nBefore the upgrade is started we need to stop subprocess puma and sidekiq on the gitlab server\n1 gitlab-ctl stop sidekiq \u0026amp;\u0026amp; gitlab-ctl stop puma We can check with following command if the services are really stopped.\n1 gitlab-ctl status The Upgrade https://docs.gitlab.com/ee/update/package/index.html Upgrading to a specific version:\n1 gitlab-ee-\u0026lt;gitlab_version\u0026gt;-ee.0.el\u0026lt;distribution_version\u0026gt; Upgrade to latest version:\n1 yum install gitlab-ee Reconfigure and restart gitlab instance:\n1 2 gitlab-ctl reconfigure gitlab-ctl restart Turn maintenance mode off:\n1 gitlab-rails runner \u0026#39;::Gitlab::CurrentSettings.update!(maintenance_mode: false)\u0026#39; Enjoy newer version Login on UI, to checkout the UI and the new features.\n"
  },
  {
    "title":"How to reindex Gitlab Advanced_search",
    "url":"https://ctrl-find.nl/posts/gitlab_reindex/",
    "description":"A quick way to reindex search",
    "tags":["gitlab"],
    "date":"2025-01-01",
    "content":"Advanced Search page Gitlab Go to the Admin page in Gitlab \u0026gt; Settings \u0026gt; Advanced Search Expand Advanced Search to see the configuration.\nDisabling feature Go to the Advanced search page in Gitlab De-select the following options;\nElasticsearch indexing Search with Elasticsearch enabled Recreate indices Login and connect to the gitlab instance. We can remove the current indices and recreate them as new with following command;\n1 gitlab-rake gitlab:elastic:recreate_index Enabling indexing Go to the Advanced search page in Gitlab. We enable the Elasticsearch indexing option and Press \u0026ldquo;Index all projects\u0026rdquo; button. In the background ElasticSearch will be populated with information The state can be found with following command;\n1 gitlab-rake gitlab:elastic:info Enabling Search capability When the information from the command above said 0 in queue, we can enable the search option. Go to the Advanced search page in Gitlab. Select the Search with Elasticsearch enabled option and verifiy with following command;\n1 gitlab-rake gitlab:elastic:info Now both Indexing and Search enabled should be on \u0026ldquo;yes\u0026rdquo;\nCheck GUI When we go to the gitlab page and search for something the Code and Merge Request options should be visible.\n"
  },
  {
    "title":"Search",
    "url":"https://ctrl-find.nl/search/",
    "description":"Search all posts on CTRL-Find.",
    "tags":null,
    "date":"0001-01-01",
    "content":""
  }
]
