'Is getting Ansible to work with Pulumi possible?

I wish to create several droplets (DigitalOcean) in a loop and then run Ansible afterwards on the droplets to setup the required software and security measures. Is this possible like terraform? If so, how would this look in javascript/typescript code?

There isn't anything in google that has any examples to create this or any mention of Pulumi with Ansible.



Solution 1:[1]

Yes, this is possible.

You can do it one of 2 ways:

You can specify your ansible run inside the userdata of the DigitalOcean droplet. This works across all language SDKs.

Alternatively, if you want to have similar functionality to Terraform's remote-exec provisioner, you can use Pulumi's dynamic providers to create a provisioner.

Dynamic Providers are currently available in the TypeScript and Python SDKs. You can find python example here and a TypeScript example here

Solution 2:[2]

Here is an example of how you can use it to deploy wordpress via a playbook using local.Command provider and letting ansible provision on a remote host

import * as command from '@pulumi/command'
import * as pulumi from '@pulumi/pulumi'
import * as fs from 'fs'
import {URL} from 'url'
import YAML from 'yaml'

const __dirname = new URL('.', import.meta.url).pathname
nunjucks.configure(`${__dirname}/templates`)

export interface WordPressArgs {
  fqdn: pulumi.Input<string>
  ip: pulumi.Input<string>
  sshPort: pulumi.Input<number>
  sshPrivateKey: pulumi.Input<string>
  sshUser: pulumi.Input<string>
  domainUser: pulumi.Input<string>
  domainUserHomeDir?: pulumi.Input<string>
  domainUserDocumentRootDir?: pulumi.Input<string>
  nginxUser?: pulumi.Input<string>
  wordpressLanguage?: pulumi.Input<string>
  wordpressDbName: pulumi.Input<string>
  wordpressDbUser: pulumi.Input<string>
  wordpressDbPassword: pulumi.Input<string>
  wordpressDbHost?: pulumi.Input<string>
  wordpressTitle?: pulumi.Input<string>
  wordpressAdminUser?: pulumi.Input<string>
  wordpressAdminPassword: pulumi.Input<string>
  wordpressAdminEmail?: pulumi.Input<string>
  deploymentEnvironment?: pulumi.Input<'production' | 'staging' | 'testing'>
}

export class WordPress extends pulumi.ComponentResource {
  private readonly wordPressInstallCommand: pulumi.Output<string>

  constructor(
    name: string,
    args: WordPressArgs,
    opts?: pulumi.ComponentResourceOptions
  ) {
    super('system:virtualmin:wordpress', name, {}, opts)

    const cmd = pulumi
      .all(
        [
          args.fqdn,
          args.ip,
          args.sshPort,
          args.sshUser,
          args.sshPrivateKey,
          args.domainUser,
          args.nginxUser,
          args.wordpressLanguage,
          args.wordpressDbHost,
          args.wordpressDbName,
          args.wordpressDbUser,
          args.wordpressDbPassword,
          args.wordpressTitle,
          args.wordpressAdminUser,
          args.wordpressAdminPassword,
          args.wordpressAdminEmail,
          args.deploymentEnvironment]
      )
      .apply((
          [
            fqdn,
            ip,
            sshPort,
            sshUser,
            sshPrivateKey,
            domainUser,
            nginxUser,
            wordpressLanguage,
            wordpressDbHost,
            wordpressDbName,
            wordpressDbUser,
            wordpressDbPassword,
            wordpressTitle,
            wordpressAdminUser,
            wordpressAdminPassword,
            wordpressAdminEmail,
            deploymentEnvironment]
        ) => {

          fs.writeFileSync(
            `/tmp/ansible.pem`,
            sshPrivateKey.toString(),
            {mode: 0o600}
          )

          fs.writeFileSync(
            '/tmp/inventory',
            YAML.stringify(
              {
                all: {
                  hosts: {
                    remote: {
                      ansible_host: ip,
                      ansible_port: sshPort,
                      ansible_user: sshUser,
                      ansible_private_key_file: '/tmp/ansible.pem',
                      ansible_host_key_checking: false
                    }
                  }
                }
              }),
            {mode: 0o600}
          )

          const playbookVars = pulumi.interpolate`${JSON.stringify({
            fqdn,
            deployment_environment: deploymentEnvironment || 'staging',
            domain_user: domainUser,
            nginx_user: nginxUser || 'www-data',
            wordpress_language: wordpressLanguage || 'en_US',
            wordpress_db_host: wordpressDbHost || 'localhost:3306',
            wordpress_db_name: wordpressDbName,
            wordpress_db_user: wordpressDbUser,
            wordpress_db_password: wordpressDbPassword,
            wordpress_title: wordpressTitle || 'Just a WordPress site',
            wordpress_admin_user: wordpressAdminUser || 'admin',
            wordpress_admin_password: wordpressAdminPassword,
            wordpress_admin_email: wordpressAdminEmail,
            ssl_cert_dir: '/etc/ssl/certs',
            ssl_key_dir: '/etc/ssl/private'
          })}`
          return {create: pulumi.interpolate`ANSIBLE_STDOUT_CALLBACK=json ansible-playbook -i /tmp/inventory ${__dirname}/playbooks/install-wordpress.yaml -e '${playbookVars}'`}
        }
      )


    this.wordPressInstallCommand = cmd.create
    const wordPressInstallation = new command.local.Command(
      'wordpress-installation',
      {
        create: this.wordPressInstallCommand,
      },
      {parent: this}
    )

    this.registerOutputs()
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jaxxstorm
Solution 2 Abukamel