<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="/pretty-feed-v3.xsl"?>
<rss
  version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:source="https://source.scripting.com/"
>
  <channel>
    <title>Paul Tibbetts</title>
    <link>https://paultibbetts.uk/tags/ansible/</link>
    <description>Recent posts by Paul Tibbetts on Ansible</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <lastBuildDate>Wed, 04 Mar 2026 15:27:40 +0000</lastBuildDate>
    <atom:link href="https://paultibbetts.uk/tags/ansible/feed.xml" rel="self" type="application/rss+xml" /><item>
      <title>I Made an Ansible Role for Caddy</title>
      <link>https://paultibbetts.uk/2026/02/24/i-made-an-ansible-role-for-caddy/</link>
      <pubDate>Tue, 24 Feb 2026 12:10:45 +0000</pubDate>
      <guid>https://paultibbetts.uk/2026/02/24/i-made-an-ansible-role-for-caddy/</guid>
      <category>Ansible</category>
      <category>Caddy</category>
      <category>code</category>
      <description>paultibbetts/ansible-role-caddy is an Ansible role for Caddy web server that lets you install Caddy with plugins.
I use it for my personal site and in my homelab.
This is what it does and how to use it.</description>
      <content:encoded><![CDATA[<p><a href="https://github.com/paultibbetts/ansible-role-caddy">paultibbetts/ansible-role-caddy</a>
 is an Ansible role for Caddy web server that lets you install Caddy with plugins.</p>
<p>I use it for my personal site and in my homelab.</p>
<p>This is what it does and how to use it.</p>
<h2 id="what-it-does">What it does</h2>
<p>An Ansible role is like a plugin that extends Ansible with the ability to do something, and in this case it&rsquo;s to install and manage <a href="https://caddyserver.com/">Caddy</a>
.</p>
<p>Specifically it:</p>
<ul>
<li>installs Caddy</li>
<li>
<ul>
<li>defaults to using the <code>apt</code> package manager</li>
</ul>
</li>
<li>
<ul>
<li>or it can be given the URL to a specific binary</li>
</ul>
</li>
<li>
<ul>
<li>or it can be given a list of plugins</li>
</ul>
</li>
<li>can be used to manage Caddy</li>
<li>
<ul>
<li>or completely uninstall it</li>
</ul>
</li>
<li>can write the Caddyfile from a template</li>
</ul>
<h2 id="why-caddy">Why Caddy?</h2>
<p>I use Caddy because it manages TLS certificates automatically and, with a plugin, supports <a href="https://letsencrypt.org/docs/challenge-types/#dns-01-challenge">DNS-01 challenges</a>
.</p>
<p>DNS-01 is useful when services aren&rsquo;t reachable by Lets Encrypt, like in a homelab, so private networks can also have TLS.</p>
<h2 id="design-goals">Design goals</h2>
<p>The role was designed with the following in mind:</p>
<ul>
<li>idempotent and safe to re-run</li>
<li>Debian and Ubuntu</li>
<li>works on x86 and ARM</li>
<li>supports installing with plugins</li>
<li>can optionally write the Caddyfile</li>
</ul>
<p>There are already Ansible roles for Caddy, like the one listed in <a href="https://caddyserver.com/docs/install#ansible">the docs</a>
, but I needed a role that could install Caddy with plugins.</p>
<h2 id="how-to-use-it">How to use it</h2>
<p>The role is available on <a href="https://galaxy.ansible.com/ui/standalone/roles/paultibbetts/caddy/">Ansible Galaxy</a>
.</p>
<h3 id="install">Install</h3>
<p>You can install it by adding:</p>
<pre><code class="language-yaml">- name: paultibbetts.caddy
  version: 1.0.0</code></pre>
<p>to your <code>requirements.yml</code> file and then running:</p>
<pre><code class="language-sh">ansible-galaxy role install -r requirements.yml</code></pre>
<h3 id="variables">Variables</h3>
<p>The variables for the role, as well as the default settings, can all be overridden.</p>
<p>The most important ones are the install method, the plugins to include, and whether the role writes the Caddyfile for you.</p>
<pre><code class="language-yaml">caddy_install_method: apt</code></pre>
<p>The role defaults to installing Caddy via <code>apt</code>, the package manager for Debian/Ubuntu.</p>
<p>This means Caddy does not include any plugins but it does get updated when you run <code>apt upgrade</code>.</p>
<pre><code class="language-yaml">caddy_install_method: download</code></pre>
<p>Alternatively you can change the install method to <code>download</code>.</p>
<p>Now you can pass in the URL of a specific binary using:</p>
<pre><code class="language-yaml">caddy_download_url: &#34;&#34;</code></pre>
<p>Using this you could build a binary for Caddy (including plugins), host it, then pass that URL to the role and that specific binary will be installed.</p>
<p>This is currently the only way to install a specific version of Caddy that isn&rsquo;t &ldquo;latest&rdquo;.</p>
<p>If you want to install plugins for Caddy and don&rsquo;t want to build your own binary you can leave the download URL empty and pass in the plugins:</p>
<pre><code class="language-yaml">caddy_plugins:
  - github.com/caddy-dns/cloudflare</code></pre>
<p>and the role will generate a URL from the <a href="https://caddyserver.com/download">download page</a>
.</p>
<pre><code class="language-yaml">caddy_caddyfile_template: &#34;&#34; | &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;</code></pre>
<p>The Caddyfile template variable lets you pass in a template of a Caddyfile for Caddy to use and it will be written to the appropriate place.</p>
<p>Letting the role write the Caddyfile means you can import the role, set its variables, and have all of Caddy configured by one thing - you don&rsquo;t need to write extra tasks after installation to get it up and running.</p>
<h3 id="examples">Examples</h3>
<p>The following examples show how the role can be used for a public website and for a private homelab reverse proxy.</p>
<p>In both instances Caddy acquires TLS certificates from Lets Encrypt, using Cloudflare to do DNS-01 challenges.</p>
<h4 id="example-personal-website">Example: Personal website</h4>
<p>The role can be used to setup Caddy to host a personal website.</p>
<pre><code class="language-yaml">- hosts: server
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;</code></pre>
<p>The <code>download</code> install method is used to install Caddy with the <a href="https://github.com/caddy-dns/cloudflare">Cloudflare plugin</a>
, the Cloudflare API token is passed to Caddy from Ansible vault, and a template is provided for the Caddyfile, which could look like:</p>
<pre><code class="language-Caddyfile">(cf_tls) {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
}

{% for site in web_sites %}
{{ site }} {
  import cf_tls
  root * {{ web_sites_root }}/{{ site }}/current
  file_server
  encode zstd gzip
  log
}
{% endfor %}</code></pre>
<p>This sets up Caddy to use Cloudflare to do DNS-01 challenges with Lets Encrypt to get a TLS certificate for each <code>site in web_sites</code>.</p>
<h4 id="example-homelab">Example: Homelab</h4>
<p>The role can also be used to setup Caddy in a private network, like a homelab.</p>
<pre><code class="language-yaml">- hosts: homelab
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;</code></pre>
<pre><code class="language-Caddyfile">{
        acme_dns cloudflare {env.CF_API_TOKEN}
}

service.homelab.example {
        reverse_proxy &lt;ip&gt;:&lt;port&gt;
}</code></pre>
<p>where the Cloudflare plugin is configured once and is then used to do DNS-01 challenges for any TLS certificates Caddy requests. Note this style has been deprecated and you should do it per host, like the earlier example for a personal website.</p>
<p>DNS-01 challenges are solved by writing a TXT record to prove domain ownership. This means Lets Encrypt is able to issue certificates for domains it can&rsquo;t reach, which makes DNS-01 ideal when Caddy is running in a homelab.</p>
<h2 id="molecule-scenarios">Molecule Scenarios</h2>
<p>Ansible Molecule is a way to run tests for an Ansible role.</p>
<p>The role includes molecule scenarios that test it works on x86 as well as Arm, because I use the role on a Raspberry Pi 4, as well as scenarios for the Cloudflare plugin and the Caddyfile templating.</p>
<h2 id="in-use">In use</h2>
<p>I use this role as part of the code that sets up the infrastructure for my website.</p>
<p>Terraform is used to provision the server, then Ansible is used to configure it, with this role setting up Caddy and the Caddyfile it uses.</p>
<p>The complete setup - Terraform, Ansible, Caddy, and the rest of the files - is documented at <a href="https://infra.paultibbetts.uk">infra.paultibbetts.uk</a>
, where I explain how it all fits together.</p>]]></content:encoded><source:markdown>
[paultibbetts/ansible-role-caddy](https://github.com/paultibbetts/ansible-role-caddy) is an Ansible role for Caddy web server that lets you install Caddy with plugins.

I use it for my personal site and in my homelab.

This is what it does and how to use it.

&lt;!--more--&gt;

## What it does

An Ansible role is like a plugin that extends Ansible with the ability to do something, and in this case it&#39;s to install and manage [Caddy](https://caddyserver.com/).

Specifically it:

- installs Caddy
- - defaults to using the `apt` package manager
- - or it can be given the URL to a specific binary
- - or it can be given a list of plugins
- can be used to manage Caddy
- - or completely uninstall it
- can write the Caddyfile from a template

## Why Caddy?

I use Caddy because it manages TLS certificates automatically and, with a plugin, supports [DNS-01 challenges](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge).

DNS-01 is useful when services aren&#39;t reachable by Lets Encrypt, like in a homelab, so private networks can also have TLS.

## Design goals

The role was designed with the following in mind:

- idempotent and safe to re-run
- Debian and Ubuntu
- works on x86 and ARM
- supports installing with plugins
- can optionally write the Caddyfile

There are already Ansible roles for Caddy, like the one listed in [the docs](https://caddyserver.com/docs/install#ansible), but I needed a role that could install Caddy with plugins.

## How to use it

The role is available on [Ansible Galaxy](https://galaxy.ansible.com/ui/standalone/roles/paultibbetts/caddy/).

### Install

You can install it by adding:

```yaml
- name: paultibbetts.caddy
  version: 1.0.0
```

to your `requirements.yml` file and then running:

```sh
ansible-galaxy role install -r requirements.yml
```

### Variables

The variables for the role, as well as the default settings, can all be overridden.

The most important ones are the install method, the plugins to include, and whether the role writes the Caddyfile for you.

```yaml
caddy_install_method: apt
```

The role defaults to installing Caddy via `apt`, the package manager for Debian/Ubuntu.

This means Caddy does not include any plugins but it does get updated when you run `apt upgrade`.

```yaml
caddy_install_method: download
```

Alternatively you can change the install method to `download`.

Now you can pass in the URL of a specific binary using:

```yaml
caddy_download_url: &#34;&#34;
```

Using this you could build a binary for Caddy (including plugins), host it, then pass that URL to the role and that specific binary will be installed.

This is currently the only way to install a specific version of Caddy that isn&#39;t &#34;latest&#34;.

If you want to install plugins for Caddy and don&#39;t want to build your own binary you can leave the download URL empty and pass in the plugins:

```yaml
caddy_plugins:
  - github.com/caddy-dns/cloudflare
```

and the role will generate a URL from the [download page](https://caddyserver.com/download).

```yaml
caddy_caddyfile_template: &#34;&#34; | &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
```

The Caddyfile template variable lets you pass in a template of a Caddyfile for Caddy to use and it will be written to the appropriate place.

Letting the role write the Caddyfile means you can import the role, set its variables, and have all of Caddy configured by one thing - you don&#39;t need to write extra tasks after installation to get it up and running.

### Examples

The following examples show how the role can be used for a public website and for a private homelab reverse proxy.

In both instances Caddy acquires TLS certificates from Lets Encrypt, using Cloudflare to do DNS-01 challenges.

#### Example: Personal website

The role can be used to setup Caddy to host a personal website.

```yaml
- hosts: server
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;
```

The `download` install method is used to install Caddy with the [Cloudflare plugin](https://github.com/caddy-dns/cloudflare), the Cloudflare API token is passed to Caddy from Ansible vault, and a template is provided for the Caddyfile, which could look like:

```Caddyfile
(cf_tls) {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
}

{% for site in web_sites %}
{{ site }} {
  import cf_tls
  root * {{ web_sites_root }}/{{ site }}/current
  file_server
  encode zstd gzip
  log
}
{% endfor %}
```

This sets up Caddy to use Cloudflare to do DNS-01 challenges with Lets Encrypt to get a TLS certificate for each `site in web_sites`.

#### Example: Homelab

The role can also be used to setup Caddy in a private network, like a homelab.

```yaml
- hosts: homelab
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;
```

```Caddyfile
{
        acme_dns cloudflare {env.CF_API_TOKEN}
}

service.homelab.example {
        reverse_proxy &lt;ip&gt;:&lt;port&gt;
}
```

where the Cloudflare plugin is configured once and is then used to do DNS-01 challenges for any TLS certificates Caddy requests. Note this style has been deprecated and you should do it per host, like the earlier example for a personal website.

DNS-01 challenges are solved by writing a TXT record to prove domain ownership. This means Lets Encrypt is able to issue certificates for domains it can&#39;t reach, which makes DNS-01 ideal when Caddy is running in a homelab.

## Molecule Scenarios

Ansible Molecule is a way to run tests for an Ansible role.

The role includes molecule scenarios that test it works on x86 as well as Arm, because I use the role on a Raspberry Pi 4, as well as scenarios for the Cloudflare plugin and the Caddyfile templating.

## In use

I use this role as part of the code that sets up the infrastructure for my website.

Terraform is used to provision the server, then Ansible is used to configure it, with this role setting up Caddy and the Caddyfile it uses.

The complete setup - Terraform, Ansible, Caddy, and the rest of the files - is documented at [infra.paultibbetts.uk](https://infra.paultibbetts.uk), where I explain how it all fits together.
</source:markdown></item><item>
      <title>Launched: infra.paultibbetts.uk A documentation site for the infrastructure that …</title>
      <link>https://paultibbetts.uk/2026/02/15/2026-02-15-145458/</link>
      <pubDate>Sun, 15 Feb 2026 14:54:58 +0000</pubDate>
      <guid>https://paultibbetts.uk/2026/02/15/2026-02-15-145458/</guid>
      <category>Ansible</category>
      <category>Terraform</category>
      <category>meta</category>
      <description>Launched: infra.paultibbetts.uk A documentation site for the infrastructure that runs paultibbetts.uk.</description>
      <content:encoded><![CDATA[<p>Launched: <a href="https://infra.paultibbetts.uk/">infra.paultibbetts.uk</a>
</p>
<p>A documentation site for the infrastructure that runs <code>paultibbetts.uk</code>.</p>]]></content:encoded><source:markdown>
Launched: [infra.paultibbetts.uk](https://infra.paultibbetts.uk/)

A documentation site for the infrastructure that runs `paultibbetts.uk`.
</source:markdown></item><item>
      <title>Published: paultibbetts/ansible-role-caddy An Ansible role to install and …</title>
      <link>https://paultibbetts.uk/2026/02/03/2026-02-03-175500/</link>
      <pubDate>Tue, 03 Feb 2026 17:55:00 +0000</pubDate>
      <guid>https://paultibbetts.uk/2026/02/03/2026-02-03-175500/</guid>
      <category>Ansible</category>
      <category>code</category>
      <category>Caddy</category>
      <description>Published: paultibbetts/ansible-role-caddy An Ansible role to install and operate Caddy web server.</description>
      <content:encoded><![CDATA[<p>Published: <a href="https://github.com/paultibbetts/ansible-role-caddy">paultibbetts/ansible-role-caddy</a>
</p>
<p>An Ansible role to install and operate Caddy web server.</p>]]></content:encoded><source:markdown>
Published: [paultibbetts/ansible-role-caddy](https://github.com/paultibbetts/ansible-role-caddy)

An Ansible role to install and operate Caddy web server.
</source:markdown></item><item>
      <title>Using Terraform as the Inventory for Ansible</title>
      <link>https://paultibbetts.uk/2025/06/13/using-terraform-as-the-inventory-for-ansible/</link>
      <pubDate>Fri, 13 Jun 2025 11:30:21 +0100</pubDate>
      <guid>https://paultibbetts.uk/2025/06/13/using-terraform-as-the-inventory-for-ansible/</guid>
      <category>Ansible</category>
      <category>Terraform</category>
      <description>Terraform and Ansible are complementary tools with which you can do Infrastructure as Code. You would use Terraform to request machines from providers and then Ansible to configure them.
Using both of them together, with a dynamic inventory to link them, has been technically possible for years but never obvious enough for me to work out.
Until I found the Terraform provider.</description>
      <content:encoded><![CDATA[<p>Terraform and Ansible are complementary tools with which you can do Infrastructure as Code. You would use <a href="https://www.terraform.io/">Terraform</a>
 to request machines from providers and then <a href="https://docs.ansible.com/ansible/latest/index.html">Ansible</a>
 to configure them.</p>
<p>Using both of them together, with a dynamic inventory to link them, has been technically possible for years but never obvious enough for me to work out.</p>
<p>Until I found the Terraform provider.</p>
<h2 id="ansible-provider-for-terraform">Ansible provider for Terraform</h2>
<p>By using the <a href="https://registry.terraform.io/providers/ansible/ansible/latest">Ansible provider</a>
 you can teach Terraform what an Ansible inventory entry would look like next to the rest of its code.</p>
<p>You start by defining the provider:</p>
<pre><code class="language-terraform">terraform {
  required_providers {
    ansible = {
      source  = &#34;ansible/ansible&#34;
      version = &#34;1.3.0&#34;
    }
  }
}</code></pre>
<p>and then you can define the Ansible inventory entry right next to the resource:</p>
<pre><code class="language-terraform">resource &#34;proxmox_vm_qemu&#34; &#34;gitea&#34; {
  cores = 1
	...
}

resource &#34;ansible_host&#34; &#34;gitea&#34; {
  name   = proxmox_vm_qemu.gitea.ssh_host
  groups = [&#34;gitea&#34;]
  variables = {
    ansible_user = &#34;ansible&#34;
  }
}</code></pre>
<p>which means you don&rsquo;t need to hardcode IP addresses anywhere.</p>
<p>You would need to <code>terraform apply</code> this plan so that Terraform adds these resources to the state file so that Ansible can use them.</p>
<h2 id="terraform-plugin-for-ansible">Terraform plugin for Ansible</h2>
<p>On the Ansible side you use <a href="https://github.com/ansible-collections/cloud.terraform">a plugin</a>
 to tell it how to use Terraform as its inventory.</p>
<p>Add the following to your <code>requirements.yaml</code> file:</p>
<pre><code class="language-yaml">collections:
  - name: cloud.terraform
    version: 1.1.0</code></pre>
<p>and then install the collection using <code>ansible-galaxy collection install -r requirements.yaml</code>,</p>
<p>then create an <code>inventory.yaml</code> with the following content:</p>
<pre><code class="language-yaml">plugin: cloud.terraform.terraform_provider
# project_path: ../terraform # if your Terraform code is in a different directory
# state_file: mycustomstate.tfstate # if you wanted to define which state file</code></pre>
<p>and set it as the default inventory in <code>ansible.cfg</code>:</p>
<pre><code class="language-ini">[defaults]
inventory = inventory.yaml</code></pre>
<p>You can confirm the plugin works by doing:</p>
<pre><code class="language-sh">ansible-inventory --graph</code></pre>
<p>which should return something like the following:</p>
<pre><code class="language-sh">@all:
  |--@ungrouped:
  |--@gitea:
  |  |--192.168.1.211</code></pre>
<h2 id="heading">🎉</h2>
<p>What&rsquo;s good about this is now you can be hands-off with IP addresses.</p>
<p>Terraform can request a new machine, wait for it to receive an IP address and then make it available for Ansible to use, without you needing to do anything.</p>
<h3 id="remote-state">Remote state</h3>
<p>The Ansible plugin will default to using whichever backend you use for the Terraform state file, whether that&rsquo;s local or in an S3-compatible location.</p>
<p>This means Ansible is always running against the current infrastructure, which helps when colleagues and automations are updating things a <del>hundred</del> thousand times a day.</p>
<h2 id="in-conclusion">In Conclusion</h2>
<p>Using Terraform and Ansible together can cover all aspects of Infrastructure as Code.</p>
<p>The Ansible provider lets you keep all your resource code together in Terraform and then makes it available to Ansible, which uses a plugin to let it use the state file as its inventory.</p>]]></content:encoded><source:markdown>
Terraform and Ansible are complementary tools with which you can do Infrastructure as Code. You would use [Terraform](https://www.terraform.io/) to request machines from providers and then [Ansible](https://docs.ansible.com/ansible/latest/index.html) to configure them.

Using both of them together, with a dynamic inventory to link them, has been technically possible for years but never obvious enough for me to work out.

Until I found the Terraform provider.

&lt;!--more--&gt;

## Ansible provider for Terraform

By using the [Ansible provider](https://registry.terraform.io/providers/ansible/ansible/latest) you can teach Terraform what an Ansible inventory entry would look like next to the rest of its code.

You start by defining the provider:

```terraform
terraform {
  required_providers {
    ansible = {
      source  = &#34;ansible/ansible&#34;
      version = &#34;1.3.0&#34;
    }
  }
}
```

and then you can define the Ansible inventory entry right next to the resource:

```terraform
resource &#34;proxmox_vm_qemu&#34; &#34;gitea&#34; {
  cores = 1
	...
}

resource &#34;ansible_host&#34; &#34;gitea&#34; {
  name   = proxmox_vm_qemu.gitea.ssh_host
  groups = [&#34;gitea&#34;]
  variables = {
    ansible_user = &#34;ansible&#34;
  }
}
```

which means you don&#39;t need to hardcode IP addresses anywhere.

You would need to `terraform apply` this plan so that Terraform adds these resources to the state file so that Ansible can use them.

## Terraform plugin for Ansible

On the Ansible side you use [a plugin](https://github.com/ansible-collections/cloud.terraform) to tell it how to use Terraform as its inventory.

Add the following to your `requirements.yaml` file:

```yaml
collections:
  - name: cloud.terraform
    version: 1.1.0
```

and then install the collection using `ansible-galaxy collection install -r requirements.yaml`,

then create an `inventory.yaml` with the following content:

```yaml
plugin: cloud.terraform.terraform_provider
# project_path: ../terraform # if your Terraform code is in a different directory
# state_file: mycustomstate.tfstate # if you wanted to define which state file
```

and set it as the default inventory in `ansible.cfg`:

```ini
[defaults]
inventory = inventory.yaml
```

You can confirm the plugin works by doing:

```sh
ansible-inventory --graph
```

which should return something like the following:

```sh
@all:
  |--@ungrouped:
  |--@gitea:
  |  |--192.168.1.211
```

## 🎉

What&#39;s good about this is now you can be hands-off with IP addresses.

Terraform can request a new machine, wait for it to receive an IP address and then make it available for Ansible to use, without you needing to do anything.

### Remote state

The Ansible plugin will default to using whichever backend you use for the Terraform state file, whether that&#39;s local or in an S3-compatible location.

This means Ansible is always running against the current infrastructure, which helps when colleagues and automations are updating things a ~~hundred~~ thousand times a day.

## In Conclusion

Using Terraform and Ansible together can cover all aspects of Infrastructure as Code.

The Ansible provider lets you keep all your resource code together in Terraform and then makes it available to Ansible, which uses a plugin to let it use the state file as its inventory.
</source:markdown></item>
  </channel>
</rss>
