{"id":4511,"date":"2025-09-29T22:49:00","date_gmt":"2025-09-29T22:49:00","guid":{"rendered":"https:\/\/www.it-react.com\/?p=4511"},"modified":"2025-10-27T23:14:33","modified_gmt":"2025-10-27T23:14:33","slug":"vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3","status":"publish","type":"post","link":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/","title":{"rendered":"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3"},"content":{"rendered":"\n<p>In the last post I proved the flow locally: Ansible talks to vCenter, clones from a template, and the VM comes up with our settings. That was the \u201cmanual gear.\u201d Now I want the &#8220;automatic gear&#8221;\u2014a GitLab pipeline that runs the same steps for me, consistently, from any commit. My goals are simple: keep secrets out of the repo, install the right tooling inside the job (so it works on any runner), and support both deploy one VM and deploy everything.<\/p>\n\n\n\n<p>Below is exactly how I wired it up.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prepare Git Repo<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. <strong>Create the GitLab project<\/strong><\/h3>\n\n\n\n<p>In GitLab UI: <strong>New project \u2192 Create blank project<\/strong><br>I named it <code>vsphere-auto<\/code>. I\u2019ll get a URL like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SSH: git@gitlab.racklab.local:root\/vsphere-auto.git<\/li>\n\n\n\n<li>HTTP: http:\/\/gitlab.racklab.local\/root\/vsphere-auto.git<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>2. Set up SSH access<\/strong><\/h3>\n\n\n\n<p>On the Ubuntu Linux machine, I&#8217;ll generate a ssh key, without passphrase:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># generate a key (press Enter for defaults)\nssh-keygen -t ed25519 -C \"ioan@racklab\"\ncat ~\/.ssh\/id_ed25519.pub<\/code><\/pre>\n\n\n\n<p>Copy the <strong>public<\/strong> key to GitLab: <strong>User \u2192 Preferences \u2192 SSH Keys \u2192 Add key<\/strong>.<br>Test connection.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh -T git@gitlab.racklab.local<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1013\" height=\"178\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/grafik-9.png\" alt=\"\" class=\"wp-image-4515\" srcset=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/grafik-9.png 1013w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/grafik-9-300x53.png 300w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/grafik-9-768x135.png 768w\" sizes=\"auto, (max-width: 1013px) 100vw, 1013px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>3. Initialize Git in my local folder<\/strong><\/h3>\n\n\n\n<p>From my local project root (<code>~\/vsphere-auto<\/code>):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git init\ngit switch -c main\ngit config user.name \"Ioan\"\ngit config user.email \"ioan@racklab.local\"<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/nordvpn.com\/de\/special\/?utm_medium=affiliate&amp;utm_term=&amp;utm_content&amp;utm_source=aff119651&amp;utm_campaign=off15\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/affiliate-black-friday-campaign-25-970x250-1.jpg\" alt=\"\" class=\"wp-image-4577\"\/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">4. <strong>We&#8217;ll add a .gitignore file to avoid commiting junk\/secrets<\/strong><\/h3>\n\n\n\n<p>We&#8217;ll add the file locally, in the ansible repo root.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Ansible artifacts\n*.retry\n.cache\/\n\n# Local secrets (never commit your Vault password file)\nvault.pass\n\n# Optional: vendor dir for collections (we reinstall in CI)\ncollections\/\n\n# Python noise (if any helper scripts)\n__pycache__\/\n*.pyc\n\n# Editor\/OS crumbs\n.DS_Store\n.vscode\/\n.idea\/<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">5. <strong>Add the remote repository<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>git remote add origin git@gitlab.racklab.local:root\/vsphere-auto.git\ngit remote -v<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">6. <strong>Start from the remote <code>main<\/code> and create a feature branch<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>git fetch origin\ngit pull --rebase origin main\ngit switch -c init-repo origin\/main <em># new branch starting from remote main<\/em><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">7. <strong>Add my files and commit locally<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>git add .<br>git commit -m \"Initial commit: Ansible + vSphere automation\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">8. <strong>Push the feature branch<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>git push -u origin init-repo<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>9. Open a Merge Request (GitLab will show the link in the push output)<\/strong><\/h3>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>10. Merge into main (requires permissions)<\/strong><\/h3>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>11. After merge, update my local main<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>git switch main\ngit pull --ff-only origin main<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>12. Delete the local feature branch <\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>git branch -D init-repo<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Building the GitLab CI\/CD Pipeline for Ansible<\/strong><\/h2>\n\n\n\n<p>Now that our GitLab Runner is properly installed and registered and the project files are availabe in GitLab, we can connect everything together with a very simple pipeline. The goal at this stage is not to automate the full VM lifecycle yet, but to verify that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the runner is available and can execute jobs<\/li>\n\n\n\n<li>Ansible and its VMware dependencies can be installed in the job environment<\/li>\n\n\n\n<li>a playbook from our repository can be executed through GitLab CI\/CD.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Build a simple Pipeline<\/h3>\n\n\n\n<p>This minimal pipeline gives us a solid foundation. Once it runs successfully, we can extend it step by step to deploy and configure VMs in vSphere.<\/p>\n\n\n\n<p>This is the pipeline: .gitlab-ci.yml<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>stages:\n  - deploy\n\n# install exact tooling + collections, then run a playbook\ndeploy:run:\n  stage: deploy\n  tags: &#91;vmware]\n  before_script:\n    - python3 -m pip install --upgrade pip\n    - python3 -m pip install \"ansible==9.5.1\" \"pyvmomi==8.0.2\" requests netaddr\n    - ansible-galaxy collection install -r requirements.yml -p .\/collections --force\n    \n  script:\n    # show versions after install\n    - ansible --version\n    - python3 -m pip show pyvmomi\n    - ansible-galaxy collection list | sed -n '1,80p'\n    # run your playbook \n    - ansible-playbook -i 'localhost,' -c local playbooks\/test_local.yml -v<\/code><\/pre>\n\n\n\n<p>This is the test playbook &#8211; test_local.yml<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>---\n- name: test against localhost\n  hosts: localhost\n  gather_facts: false\n  tasks:\n    - name: show ansible version\n      ansible.builtin.command: ansible --version\n      register: out<\/code><\/pre>\n\n\n\n<p>The playbook was executed successfully and the job succeeded. Now it&#8217;s time to move further.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Build a VM Deploy Pipeline<\/h3>\n\n\n\n<p>Now that we are sure that all our components are working properly, let&#8217;s build the whole puzzle. <\/p>\n\n\n\n<p><strong>1. New Ansible Playbook for CI\/CD <\/strong><br>We will edit now the playbook and the pipeline that will deploy the VM. Because the CI\/CD will not use anymore the information from secret.yml, we will remove its reference from pre_tasks and we also remove explicit username\/password\/hostname fields from the <code>vmware_guest<\/code> task. The following workbook will result.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>---\n- name: Deploy VM from hosts\/&lt;hostname&gt;.yml\n  hosts: localhost\n  connection: local\n  gather_facts: false\n  collections: &#91;community.vmware]\n\n  pre_tasks:\n    - include_vars: { file: \"{{ playbook_dir }}\/..\/vars\/common.yml\", name: common }\n    - include_vars: { file: \"{{ playbook_dir }}\/..\/templates\/os_map.yml\", name: osmap }\n    - include_vars: { file: \"{{ playbook_dir }}\/..\/{{ vm_file }}\", name: vm }\n\n  tasks:\n    - name: Clone and customize\n      community.vmware.vmware_guest:\n        # hostname, username, password, validate_certs are read from CI\/CD var: VMWARE_*\n        datacenter: \"{{ vm.vm_datacenter | default(common.vm_datacenter) }}\"\n        cluster:    \"{{ vm.vm_cluster    | default(common.vm_cluster) }}\"\n        folder:     \"{{ vm.vm_folder     | default(common.vm_folder) }}\"\n        name:       \"{{ vm.hostname }}\"\n        template:   \"{{ vm.vm_template   | default(osmap.os_templates&#91;vm.os]) }}\"\n        state:      poweredon\n        hardware:\n          num_cpus:  \"{{ vm.cpu }}\"\n          memory_mb: \"{{ vm.mem_mb }}\"\n        disk:       \"{{ vm.disks }}\"\n        networks:   \"{{ vm.networks }}\"\n        customization:\n          hostname:    \"{{ vm.hostname }}\"\n          domain:      \"{{ vm.domain_name }}\"\n          dns_servers: \"{{ vm.dns_servers }}\"\n<\/code><\/pre>\n\n\n\n<p><strong>2. Repository files<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>vars\/secret.yml<\/code> could be deleted completely, or keep it only for local\/manual runs (but not used in CI).<\/li>\n\n\n\n<li>keep <code>vars\/common.yml<\/code> (it still provides cluster\/datacenter\/folder defaults).<\/li>\n\n\n\n<li>keep <code>templates\/os_map.yml<\/code> and the <code>hosts\/&lt;VM&gt;.yml<\/code> definitions as they are.<\/li>\n<\/ul>\n\n\n\n<p><strong>3. GitLab CI\/CD<\/strong> <br>We will add these variables in the project\u2019s CI\/CD settings.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li> <code>VMWARE_HOST<\/code><\/li>\n\n\n\n<li><code>VMWARE_USER<\/code><\/li>\n\n\n\n<li>VMWARE_PASSWORD<\/li>\n\n\n\n<li><code>VMWARE_VALIDATE_CERTS<\/code> (usually <code>\"false\"<\/code> if vCenter uses self-signed certs)<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"347\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-32-1024x347.png\" alt=\"\" class=\"wp-image-4540\" srcset=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-32-1024x347.png 1024w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-32-300x102.png 300w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-32-768x260.png 768w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-32.png 1196w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><strong> 4. New CI\/CD Pipeline <\/strong><br>Here is the .gitlab-ci.yml that uses my VMWARE_* CI\/CD variables and runs the VM deploy playbook. We also add a syntax check to the new pipeline and add one test stage. Because the changes are pushed from a feature branch and then merged in the main branch, we introduced the following behavior &#8211; feature branches only do a fast syntax check, and the full VM deploy runs only on main branch.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>stages:\n  - test\n  - deploy\n\n# Run on any branch EXCEPT main (e.g., feature\/MR branches)\ntest:syntax:\n  stage: test\n  tags: &#91;vmware]\n  rules:\n    - if: '$CI_COMMIT_BRANCH != \"main\"'\n  script:\n    - python3 -m pip install --upgrade pip\n    - python3 -m pip install \"ansible==9.5.1\"\n    - ansible-playbook --syntax-check playbooks\/vm_create.yml\n\n# Full deploy ONLY on main (after merge)\ndeploy:vm-create:\n  stage: deploy\n  tags: &#91;vmware]\n  rules:\n    - if: '$CI_COMMIT_BRANCH == \"main\"'\n  variables:\n    ANSIBLE_STDOUT_CALLBACK: yaml\n  before_script:\n    - python3 -m pip install --upgrade pip\n    - python3 -m pip install \"ansible==9.5.1\" \"pyvmomi==8.0.2\" requests netaddr\n    - ansible-galaxy collection install -r requirements.yml -p .\/collections --force\n  script:\n    - ansible --version\n    - python3 -m pip show pyvmomi\n    - ansible-galaxy collection list | sed -n '1,80p'\n    - ansible-playbook playbooks\/vm_create.yml -e \"vm_file=hosts\/HAM01VM001.yml\" -v\n<\/code><\/pre>\n\n\n\n<p><strong>How it behaves:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>no vault password or secret.yml needed anymore.<\/li>\n\n\n\n<li>CI\/CD jobs stay simple: Ansible will read vCenter connection info directly from environment variables.<\/li>\n\n\n\n<li>push\/MR to a feature branch \u2192 only <code>test:syntax<\/code> runs.<\/li>\n\n\n\n<li>merge to <code>main<\/code> \u2192 only <code>deploy:vm-create<\/code> runs.<\/li>\n<\/ul>\n\n\n\n<p><strong>VM deployed<br><\/strong>As you can see, we have deployed successfully the VM. Mission accomplished.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6a15ed036ac5a&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"251\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-33-1024x251.png\" alt=\"\" class=\"wp-image-4542\" srcset=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-33-1024x251.png 1024w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-33-300x74.png 300w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-33-768x189.png 768w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-33-1536x377.png 1536w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-33.png 1934w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Enlarge\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"state.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Pipeline Auto-Deploy<\/h2>\n\n\n\n<p>Let\u2019s make the pipeline to auto-deploy when a NEW host file appears under hosts\/*.yml, and ignore edits to existing ones.<br>In order to do that, we&#8217;ll tweak the CI job to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>run only when something under hosts\/*.yml changed<\/li>\n\n\n\n<li>inside the job, detect files that were added (not modified) in this commit and run the playbook once per new file<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>stages: &#91;test, deploy]\n\n# feature branches: validate only changed host files\ntest:hosts:\n  stage: test\n  tags: &#91;vmware]\n  rules:\n    - if: '$CI_COMMIT_BRANCH != \"main\"'\n      changes: &#91;hosts\/*.yml]\n  script:\n    - python3 -m pip install --upgrade pip\n    - python3 -m pip install \"ansible==9.5.1\"\n    - |\n      set -e\n      # Added or Modified host files in this change\n      if &#91; -z \"$CI_COMMIT_BEFORE_SHA\" ] || &#91; \"$CI_COMMIT_BEFORE_SHA\" = \"0000000000000000000000000000000000000000\" ]; then\n        CHANGED_HOSTS=$(git diff-tree --no-commit-id --name-only --diff-filter=AM -r \"$CI_COMMIT_SHA\" -- 'hosts\/*.yml')\n      else\n        CHANGED_HOSTS=$(git diff --name-only --diff-filter=AM \"$CI_COMMIT_BEFORE_SHA\" \"$CI_COMMIT_SHA\" -- 'hosts\/*.yml')\n      fi\n      &#91; -z \"$CHANGED_HOSTS\" ] &amp;&amp; echo \"no host files changed\" &amp;&amp; exit 0\n      for file in $CHANGED_HOSTS; do\n        echo \"syntax-check with vm_file=$file\"\n        ansible-playbook --syntax-check playbooks\/vm_create.yml -e \"vm_file=$file\"\n      done\n\n# main branch: deploy only when new host files are added\ndeploy:auto-vm-create:\n  stage: deploy\n  tags: &#91;vmware]\n  rules:\n    - if: '$CI_COMMIT_BRANCH == \"main\"'\n      changes: &#91;hosts\/*.yml]\n  before_script:\n    - python3 -m pip install --upgrade pip\n    - python3 -m pip install \"ansible==9.5.1\" \"pyvmomi==8.0.2\" requests netaddr\n    - ansible-galaxy collection install -r requirements.yml -p .\/collections --force || echo \"no requirements.yml, skipping\"\n  script:\n    - |\n      set -e\n      # Newly Added host files in this change\n      if &#91; -z \"$CI_COMMIT_BEFORE_SHA\" ] || &#91; \"$CI_COMMIT_BEFORE_SHA\" = \"0000000000000000000000000000000000000000\" ]; then\n        NEW_HOSTS=$(git diff-tree --no-commit-id --name-only --diff-filter=A -r \"$CI_COMMIT_SHA\" -- 'hosts\/*.yml')\n      else\n        NEW_HOSTS=$(git diff --name-only --diff-filter=A \"$CI_COMMIT_BEFORE_SHA\" \"$CI_COMMIT_SHA\" -- 'hosts\/*.yml')\n      fi\n      &#91; -z \"$NEW_HOSTS\" ] &amp;&amp; echo \"no newly added host files \u2192 nothing to deploy\" &amp;&amp; exit 0\n      for vmfile in $NEW_HOSTS; do\n        echo \"deploying VM from $vmfile\"\n        ansible-playbook playbooks\/vm_create.yml -e \"vm_file=$vmfile\" -v\n      done\n<\/code><\/pre>\n\n\n\n<p>We have added two new files in hosts &#8211; BER01VM005.yml and HAM02VM003.yml and pushed.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>hostname: \"BER01VM005\"\nos: \"centos9\"\n\ncpu: 2\nmem_mb: 2048\n\ndisks:\n  - size_gb: 30\n    type: thin\n    datastore: \"Datastore\"\n\nnetworks:\n  - name: \"Production\"\n    type: static\n    ip: \"192.168.1.53\"\n    netmask: \"255.255.255.0\"\n    gateway: \"192.168.1.1\"\n\ndns_servers: &#91;\"192.168.1.1\", \"1.1.1.1\"]\ndomain_name: \"racklab.local\"\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>hostname: \"HAM02VM003\"\nos: \"ubuntu22\"\n\ncpu: 2\nmem_mb: 2048\n\ndisks:\n  - size_gb: 25\n    type: thin\n    datastore: \"Datastore\"\n\nnetworks:\n  - name: \"Production\"\n    type: static\n    ip: \"192.168.1.52\"\n    netmask: \"255.255.255.0\"\n    gateway: \"192.168.1.1\"\n\ndns_servers: &#91;\"192.168.1.1\", \"1.1.1.1\"]\ndomain_name: \"racklab.local\"\n<\/code><\/pre>\n\n\n\n<p>After a few minutes both VMs were deployed successfully.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"249\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-34-1024x249.png\" alt=\"\" class=\"wp-image-4545\" srcset=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-34-1024x249.png 1024w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-34-300x73.png 300w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-34-768x187.png 768w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-34-1536x374.png 1536w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-34.png 1927w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"250\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-35-1024x250.png\" alt=\"\" class=\"wp-image-4546\" srcset=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-35-1024x250.png 1024w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-35-300x73.png 300w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-35-768x188.png 768w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-35-1536x376.png 1536w, https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/image-35.png 1926w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p>With this setup, we built a working <strong>GitLab CI\/CD pipeline<\/strong> that connects Ansible to vCenter. By simply adding a new host definition file to the repository, a VM is automatically created in vSphere. This makes VM deployment <strong>repeatable, auditable, and version-controlled<\/strong>.<\/p>\n\n\n\n<p>But this is only the beginning. From here, you can go much further:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Update flow<\/strong> \u2013 Detect modified host files and apply changes to CPU, memory, disks, or networks.<\/li>\n\n\n\n<li><strong>Delete flow<\/strong> \u2013 Remove VMs automatically when a host file is deleted from the repo (full GitOps lifecycle).<\/li>\n\n\n\n<li><strong>Linting &amp; validation<\/strong> \u2013 Enforce structure and best practices in host files with Yamllint or Ansible-lint.<\/li>\n\n\n\n<li><strong>Environments<\/strong> \u2013 Extend the pipeline to multiple vCenters (Dev, Stage, Prod) with environment-specific rules.<\/li>\n\n\n\n<li><strong>Parallel jobs<\/strong> \u2013 Create VMs faster by spawning one job per host file instead of a sequential loop.<\/li>\n\n\n\n<li><strong>Self-service portal<\/strong> \u2013 Integrate with GitLab UI or a simple frontend so that other teams can request VMs by creating host files without touching Ansible.<\/li>\n<\/ul>\n\n\n\n<p>This foundation already saves time and reduces manual work. Each next step brings you closer to a <strong>fully automated, GitOps-style VMware infrastructure<\/strong>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the last post I proved the flow locally: Ansible talks to vCenter, clones from a template, and the VM comes up with our settings. That was the \u201cmanual gear.\u201d Now I want the &#8220;automatic gear&#8221;\u2014a GitLab pipeline that runs the same steps for me, consistently, from any commit. My goals are simple: keep secrets [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4559,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_FSMCFIC_featured_image_caption":"","_FSMCFIC_featured_image_nocaption":"","_FSMCFIC_featured_image_hide":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-4511","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3 - IT-REACT<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3 - IT-REACT\" \/>\n<meta property=\"og:description\" content=\"In the last post I proved the flow locally: Ansible talks to vCenter, clones from a template, and the VM comes up with our settings. That was the \u201cmanual gear.\u201d Now I want the &#8220;automatic gear&#8221;\u2014a GitLab pipeline that runs the same steps for me, consistently, from any commit. My goals are simple: keep secrets [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/\" \/>\n<meta property=\"og:site_name\" content=\"IT-REACT\" \/>\n<meta property=\"article:published_time\" content=\"2025-09-29T22:49:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-10-27T23:14:33+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"570\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Ioan Penu\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ioan Penu\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/\"},\"author\":{\"name\":\"Ioan Penu\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\"},\"headline\":\"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\\\/CD \u2013 Part 3\",\"datePublished\":\"2025-09-29T22:49:00+00:00\",\"dateModified\":\"2025-10-27T23:14:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/\"},\"wordCount\":937,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\"},\"image\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/\",\"url\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/\",\"name\":\"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\\\/CD \u2013 Part 3 - IT-REACT\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg\",\"datePublished\":\"2025-09-29T22:49:00+00:00\",\"dateModified\":\"2025-10-27T23:14:33+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg\",\"contentUrl\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2025\\\/09\\\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg\",\"width\":1024,\"height\":570,\"caption\":\"Photo by Bernd \ud83d\udcf7 Dittrich on Unsplash\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2025\\\/09\\\/29\\\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.it-react.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\\\/CD \u2013 Part 3\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#website\",\"url\":\"https:\\\/\\\/www.it-react.com\\\/\",\"name\":\"it-react\",\"description\":\"Ctrl\u2022Alt\u2022Automate\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.it-react.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\",\"name\":\"Ioan Penu\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\",\"caption\":\"Ioan Penu\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3 - IT-REACT","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/","og_locale":"en_US","og_type":"article","og_title":"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3 - IT-REACT","og_description":"In the last post I proved the flow locally: Ansible talks to vCenter, clones from a template, and the VM comes up with our settings. That was the \u201cmanual gear.\u201d Now I want the &#8220;automatic gear&#8221;\u2014a GitLab pipeline that runs the same steps for me, consistently, from any commit. My goals are simple: keep secrets [&hellip;]","og_url":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/","og_site_name":"IT-REACT","article_published_time":"2025-09-29T22:49:00+00:00","article_modified_time":"2025-10-27T23:14:33+00:00","og_image":[{"width":1024,"height":570,"url":"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg","type":"image\/jpeg"}],"author":"Ioan Penu","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Ioan Penu","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#article","isPartOf":{"@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/"},"author":{"name":"Ioan Penu","@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0"},"headline":"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3","datePublished":"2025-09-29T22:49:00+00:00","dateModified":"2025-10-27T23:14:33+00:00","mainEntityOfPage":{"@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/"},"wordCount":937,"commentCount":0,"publisher":{"@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0"},"image":{"@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#primaryimage"},"thumbnailUrl":"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/","url":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/","name":"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3 - IT-REACT","isPartOf":{"@id":"https:\/\/www.it-react.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#primaryimage"},"image":{"@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#primaryimage"},"thumbnailUrl":"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg","datePublished":"2025-09-29T22:49:00+00:00","dateModified":"2025-10-27T23:14:33+00:00","breadcrumb":{"@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#primaryimage","url":"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg","contentUrl":"https:\/\/www.it-react.com\/wp-content\/uploads\/2025\/09\/bernd-dittrich-mEbre97xyl4-unsplash-e1759185375254.jpg","width":1024,"height":570,"caption":"Photo by Bernd \ud83d\udcf7 Dittrich on Unsplash"},{"@type":"BreadcrumbList","@id":"https:\/\/www.it-react.com\/index.php\/2025\/09\/29\/vmware-vsphere-automating-vm-deployment-with-ansible-and-gitlab-ci-cd-part-3\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.it-react.com\/"},{"@type":"ListItem","position":2,"name":"VMware vSphere \u2013 Automating VM Deployment with Ansible and GitLab CI\/CD \u2013 Part 3"}]},{"@type":"WebSite","@id":"https:\/\/www.it-react.com\/#website","url":"https:\/\/www.it-react.com\/","name":"it-react","description":"Ctrl\u2022Alt\u2022Automate","publisher":{"@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.it-react.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0","name":"Ioan Penu","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g","caption":"Ioan Penu"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g"}}]}},"_links":{"self":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts\/4511","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/comments?post=4511"}],"version-history":[{"count":23,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts\/4511\/revisions"}],"predecessor-version":[{"id":4578,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts\/4511\/revisions\/4578"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/media\/4559"}],"wp:attachment":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/media?parent=4511"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/categories?post=4511"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/tags?post=4511"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}