Pulumi

Figure 1138. Pulumi facts Slide presentation
  1. Infrastructure as code (IAC)

  2. Infrastructure state model like Terraform

  3. Looks imperative but IS actually declarative.

  4. Various language bindings: TypeScript, Python, Go, C#, Java ...


Figure 1139. Sample Slide presentation
Pulumi / Java Terraform
import com.pulumi.hcloud.Server;       
...
var server = new Server (
   "helloServer",
   ServerArgs.builder()
     .name("hello")
     .image("debian-13")
     .serverType("cx23")
     .location("nbg1")
     .build()
);
resource "hcloud_server" "helloServer" {          
  name         = "hello"
  image        =  "debian-13"
  server_type  =  "cx23"
  location     =  "nbg1"
}

Figure 1140. Architecture Slide presentation
Architecture

Figure 1141. Asynchronous execution Slide presentation
public static void stack(Context ctx) {
   Server node1 = new Server ("Server1", ServerArgs.builder()        
                .name("node1") ... .build()
   );

   Output<String> ipv4Node1 =  node1.ipv4Address();
   ctx.export("Node1 ipv4", ipv4Node1);

   Server node2 = new Server ("Server2", ServerArgs.builder()
                        .name("node2") ... .build()
   );
   ctx.export("Node2 ipv4", node2.ipv4Address());
}

Figure 1142. Executing plan Slide presentation
$ pulumi up
Previewing update (java):
     Type                    Name        Plan       
 +   pulumi:pulumi:Stack     Hello-java  create     
 +   ├─ hcloud:index:Server  Server2     create     
 +   └─ hcloud:index:Server  Server1     create     

Outputs:
    Node1 ipv4: [unknown]
    Node2 ipv4: [unknown]

Resources:
    + 3 to create

Do you want to perform this update?  [Use arrows to move, type to filter]
> yes
  no
  details

Figure 1143. Execution result Slide presentation
Updating (java):
     Type                    Name        Status            
 +   pulumi:pulumi:Stack     Hello-java  created (10s)     
 +   ├─ hcloud:index:Server  Server2     created (8s)      
 +   └─ hcloud:index:Server  Server1     created (9s)      

Outputs:
    Node1 ipv4: "88.99.224.53"
    Node2 ipv4: "91.99.143.208"

Resources:
    + 3 created

Duration: 12s

exercise No. 30

Hello server creation

Q:

  1. Install the Pulumi runtime on your system.

  2. Create a Maven based Pulumi project using either of:

    • pulumi new java  # Maven based

      Adjust parameters in your project's pom.xml at your discretion. The Hetzner Pulumi provider requires:

      <dependency>
         <groupId>com.pulumi</groupId>
         <artifactId>hcloud</artifactId>
         <version>1.38.0</version>
         <scope>compile</scope>
      </dependency>
    • pulumi new java-gradle # Gradle based
  3. Create an environment file defining a HCLOUD_TOKEN environment variable containing your token. You may want to define PULUMI_CONFIG_PASSPHRASE as well.

    Caution

    Do not version related files!

Figure 1144. Using cloud-init Slide presentation
static private final String cloudInitCode = """
  #cloud-config
  packages:
    - nginx
  runcmd:
    - systemctl enable nginx
    - rm /var/www/html/*
    - echo I am Nginx @ $(dig -4 TXT +short o-o.myaddr.l.google.com @ns1.google.com)
      created $(date -u) >> /var/www/html/index.html""";

var server = new Server ("helloServer", ServerArgs.builder()
     .name("hello")
        ...
     .userData(cloudInitCode)
     .build()
);

Figure 1145. Login by public key Slide presentation
static private final String sshPublicKeyIdentifier = "userSshPublicKey";
...
final String content = Files.readString(Path.of("/home/goik/.ssh/id_ed25519.pub"));

new SshKey(sshPublicKeyIdentifier, SshKeyArgs.builder()
    .name(sshPublicKeyIdentifier).publicKey(content).build()
);
...
var server = new Server ("helloServer", ServerArgs.builder()
     .name("hello")
        ...
     .sshKeys(sshPublicKeyIdentifier)
     .build()
);

exercise No. 31

ssh keys and cloud-init script

Q:

Extend your previous application by adding:

  1. A login user's ssh public key allowing for remote logins without password.

  2. A cloud-init script as in Working on Cloud-init .

Figure 1146. A minimal system of hosts Slide presentation

Requirements:

  • Firewall

  • Host(s) carrying web server

  • DNS


Figure 1147. Firewall Slide presentation
var firewall = new Firewall("stdFirewall", FirewallArgs.builder()
        .name("my-firewall")
        .rules( FirewallRuleArgs.builder()
                        .direction("in")
                        .protocol("icmp")
                        .sourceIps(
                                "0.0.0.0/0",
                                "::/0")
                        .build(),
                ...
                FirewallRuleArgs.builder()
                        .direction("in")
                        .protocol("tcp")
                        .port("22")
                        .sourceIps(
                                "0.0.0.0/0",
                                "::/0")
                        .build())
        .build());

Figure 1148. Importing Terraform DNS provider Slide presentation
> pulumi package add terraform-provider hashicorp/dns
src
└── main
    ├── java
    │   ├── com
    │   │   └── pulumi
    │   │       └── dns
    │   │           ├── AaaaRecordSetArgs.java
    │   │           ├── AaaaRecordSet.java
    │   │           ├── ARecordSetArgs.java
    │   │           ├── ARecordSet.java
    │   │           ├── CnameRecordArgs.java
    │   │           ├── CnameRecord.java
    │   │           ├── config
    │   │           │   ├── inputs
    │   │           │   │   └── Updates.java
    │   │           │   └── outputs
    │   │           │       └── UpdatesGssapi.java
    │   │           ├── Config.java
    │   │           ├── DnsFunctions.java
    │   │           ├── inputs
    │   │           │   ├── AaaaRecordSetState.java
    │   │           │   ├── ARecordSetState.java
     ...             ...

Figure 1149. DNS provider Slide presentation
var dnsProvider = new Provider("dnsProvider", ProviderArgs.builder()
               .updates(ProviderUpdateArgs.builder()
                .server("ns1.sdi.hdm-stuttgart.cloud")
                 .keyName("gxy.key.")
                 .keyAlgorithm("hmac-sha512")
                 .keySecret(DNS_SECRET)
                 .build() )
               .build()
);

Figure 1150. DNS record Slide presentation
var ipv4 = new ARecordSet("www_server_A_record", ARecordSetArgs.builder()
                .zone("gxy.sdi.hdm-stuttgart.cloud.")
                .name("www")
                .addresses (server.ipv4Address() //** Ugly!
                  .applyValue(id -> List.of(id))
                )
                .ttl(100)
                .build()
                , CustomResourceOptions.builder()
                    .provider(dnsProvider)
                    .build()
);

var ipv6 = ... // HdM, dream on ...

**: See Erroneous API Docs samples


Figure 1151. Server Slide presentation
final var server = new Server("www-server", ServerArgs.builder()
                    .name("www")
                    .image("debian-13")
                       ...
                    .firewallIds(firewall.id()
                       .applyValue(id -> List.of(Integer.parseInt(id)) // Ugly!
                    )
                    .build());

exercise No. 32

A set of servers

Q:

Create a Pulumi configuration starting from an arbitrary array of DNS base names:

final String[] servers = {"www", "cloud"};

Your configuration is about to create:

  1. A firewall allowing for ICMP based pinging, http and ssh access.

  2. The aforementioned servers related to your firewall and each carrying a web server providing an entry page showing the server's DNS name.

  3. Related DNS entries.