<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pulumi on despatches</title><link>https://icle.es/tags/pulumi/</link><description>Recent content in Pulumi on despatches</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 20 Jun 2025 08:42:17 +0100</lastBuildDate><atom:link href="https://icle.es/tags/pulumi/index.xml" rel="self" type="application/rss+xml"/><item><title>Use `protoc` bin instead of building from source in `bazel`</title><link>https://icle.es/2025/06/09/use-protoc-bin-in-bazel/</link><pubDate>Mon, 09 Jun 2025 20:57:53 +0000</pubDate><guid>https://icle.es/2025/06/09/use-protoc-bin-in-bazel/</guid><description>&lt;p>I work on a project that uses &lt;code>pulumi&lt;/code> automation api, which in turn uses
&lt;code>protobuf&lt;/code>. I think there are other bits in &lt;code>bazel&lt;/code> that also uses it.&lt;/p>
&lt;p>For a while, it was fine - except that some &lt;code>CI&lt;/code> runs would take 15 minutes and
we couldn&amp;rsquo;t quite figure out why.&lt;/p>
&lt;p>I finally had to update &lt;code>bazel&lt;/code> from 7.x to 8 and in that process (which
honestly could have been easier, but oh well), I ran into a problem with
compiling protobuf.&lt;/p>
&lt;p>Namely, I kept running into this error:&lt;/p>
```
error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory
```
&lt;p>I tried many things but was not able to get past this error. In the end, I was
able to narrow it down to devbox/nix and how it messes with the environment that
doesn&amp;rsquo;t quite agree with &lt;code>bazel&lt;/code>.&lt;/p>
&lt;h2 id="what-didnt-work">What didn&amp;rsquo;t work&lt;/h2>
&lt;ul>
&lt;li>installing &lt;code>gcc&lt;/code> from devbox&lt;/li>
&lt;li>installing &lt;code>stdenv.cc.cc.lib&lt;/code>&lt;/li>
&lt;li>Setting &lt;code>LD_LIBRARY_PATH&lt;/code> (to
&lt;code>&amp;lt;workspace-dir&amp;gt;/.devbox/nix/profile/default/lib&lt;/code>, which is where the devbo
version of &lt;code>libstdc++.so.6&lt;/code> is installed by &lt;code>stdenv.cc.cc.lib&lt;/code>)&lt;/li>
&lt;li>Using the &lt;code>--action-env=&lt;/code>&lt;/li>
&lt;li>&lt;a href="https://github.com/tweag/rules_nixpkgs">rules-nixpkgs&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>None of which really worked.&lt;/p>
&lt;p>Disabling &lt;code>devbox&lt;/code> &lt;strong>did&lt;/strong> work, which was at least partly encouraging. I also
found a bunch of evidence online around this issue
(&lt;a href="https://github.com/bazelbuild/bazel/issues/12978">1&lt;/a>,
&lt;a href="https://github.com/jetify-com/devbox/issues/1100">2&lt;/a>,
&lt;a href="https://github.com/jetify-com/devbox/issues/1596">3&lt;/a>,
&lt;a href="https://github.com/jetify-com/devbox/issues/710">4&lt;/a>,
&lt;a href="https://github.com/tweag/rules_nixpkgs/issues/573">5&lt;/a>). Bazel seems to
&lt;a href="https://discuss.ray.io/t/bazel-protobuf-build-errors-libstdc-with-non-system-gcc/3329">generally dislike non-system gcc&lt;/a>.&lt;/p></description><content:encoded><![CDATA[<p>I work on a project that uses <code>pulumi</code> automation api, which in turn uses
<code>protobuf</code>. I think there are other bits in <code>bazel</code> that also uses it.</p>
<p>For a while, it was fine - except that some <code>CI</code> runs would take 15 minutes and
we couldn&rsquo;t quite figure out why.</p>
<p>I finally had to update <code>bazel</code> from 7.x to 8 and in that process (which
honestly could have been easier, but oh well), I ran into a problem with
compiling protobuf.</p>
<p>Namely, I kept running into this error:</p>
```
error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory
```
<p>I tried many things but was not able to get past this error. In the end, I was
able to narrow it down to devbox/nix and how it messes with the environment that
doesn&rsquo;t quite agree with <code>bazel</code>.</p>
<h2 id="what-didnt-work">What didn&rsquo;t work</h2>
<ul>
<li>installing <code>gcc</code> from devbox</li>
<li>installing <code>stdenv.cc.cc.lib</code></li>
<li>Setting <code>LD_LIBRARY_PATH</code> (to
<code>&lt;workspace-dir&gt;/.devbox/nix/profile/default/lib</code>, which is where the devbo
version of <code>libstdc++.so.6</code> is installed by <code>stdenv.cc.cc.lib</code>)</li>
<li>Using the <code>--action-env=</code></li>
<li><a href="https://github.com/tweag/rules_nixpkgs">rules-nixpkgs</a></li>
</ul>
<p>None of which really worked.</p>
<p>Disabling <code>devbox</code> <strong>did</strong> work, which was at least partly encouraging. I also
found a bunch of evidence online around this issue
(<a href="https://github.com/bazelbuild/bazel/issues/12978">1</a>,
<a href="https://github.com/jetify-com/devbox/issues/1100">2</a>,
<a href="https://github.com/jetify-com/devbox/issues/1596">3</a>,
<a href="https://github.com/jetify-com/devbox/issues/710">4</a>,
<a href="https://github.com/tweag/rules_nixpkgs/issues/573">5</a>). Bazel seems to
<a href="https://discuss.ray.io/t/bazel-protobuf-build-errors-libstdc-with-non-system-gcc/3329">generally dislike non-system gcc</a>.</p>
<h2 id="a-ray-of-hope">A ray of hope</h2>
<p>I was just about ready to throw in the towel when ChatGPT, in passing suggested
using the <code>protobuf</code> binary directly. I wasn&rsquo;t even using this binary - it was
being pulled in as a dependency and I just needed it to work. I did not need it
to be built from source.</p>
<p>Furthermore, I found, from my research into trying to solve this that building
<code>protobuf</code> was the likely culprit in the <code>CI</code> taking 15 minutes.</p>
<p>I also remembered that someone else on the team had issues trying to get it to
build on a mac at some point.</p>
<p>All in all, it was a problematic piece of software and I was seriously
considering switching to <code>opentofu</code> - but they might be using it too.</p>
<h2 id="bin-protoc">bin <code>protoc</code></h2>
<p>It was difficult to find decent documentation about how to achieve this though,
apart from a partially answered on:</p>
<ul>
<li><a href="https://stackoverflow.com/questions/68918369/is-it-possible-to-use-bazel-without-compiling-protobuf-compiler">stackoverflow question</a>,</li>
<li><a href="https://groups.google.com/g/bazel-discuss/c/3Q_GEqNZrC0">google groups for bazel-discuss</a>
<ul>
<li>which also led me to
<a href="https://gitlab.com/mvfwd/issue-bazel-protobuf-compile/-/tree/main">a gitlab repo with some code</a></li>
</ul>
</li>
</ul>
<p>These resources gave me just enough to be able to cobble together a working
solution, which needs:</p>
<h3 id="install-protoc">Install <code>protoc</code></h3>
<p>The first thing we need is a locally installed <code>protoc</code> bin (I&rsquo;ll used devbox to
install it for consistency across dev environments)</p>
```bash
devbox add protobuf
```
<p>You can add a version specifier for better reproducibility.</p>
<h3 id="runnable-target">Runnable Target</h3>
<p>We also need a shell target that will execute this correctly. The <code>bazel</code> flag
to override <code>protoc</code> takes a local target, not a bin.</p>
<p><code>third_party/tools/BUILD.bazel</code></p>
```python
package(default_visibility = ["//visibility:public"])

sh_binary(
    name = "protoc",
    srcs = ["protoc.sh"],
)

# https://github.com/protocolbuffers/protobuf/blob/b4b0e304be5a68de3d0ee1af9b286f958750f5e4/BUILD#L773
proto_lang_toolchain(
    name = "cc_toolchain",
    command_line = "--cpp_out=$(OUT)",
    runtime = ":protoc",
    visibility = ["//visibility:public"],
)
```
<p><code>third_party/tools/protoc.sh</code></p>
```bash
#!/bin/env bash
protoc "$@"
```
<h3 id="override-protoc">Override protoc</h3>
<p>You should now be able to override <code>protoc</code> with:</p>
```bash
bazel build --proto_compiler=//third_party/tools:protoc ...
```
<p>Having to pass in a flag each time is annoying though, and you can add it to
your <code>.bazelrc</code></p>
```
build --proto_compiler=//third_party/tools:protoc
```
<h3 id="sample-code">Sample code</h3>
<p>You can find the sample code in
<a href="https://github.com/drone-ah/wordsonsand">the wordsonsand repo</a> which uses this
exact solution to override <code>protoc</code> and get the <code>pulumi</code> sample code to work ;)</p>
<p>PS: It also includes a fully migrated <code>MODULES.bazel</code> with support for <code>golang</code>.
You will also want to check out <code>BUILD</code> in the root.</p>]]></content:encoded></item><item><title>Build and push container image using bazel</title><link>https://icle.es/2025/06/06/bazel-go-docker-gcloud/</link><pubDate>Fri, 06 Jun 2025 13:06:14 +0000</pubDate><guid>https://icle.es/2025/06/06/bazel-go-docker-gcloud/</guid><description>&lt;p>I am building a small &lt;a href="https://icle.es/tags/golang">golang&lt;/a> &lt;a href="https://icle.es/tags/webapp">webapp&lt;/a>, and I want
to push a &lt;a href="https://icle.es/tags/oci">container&lt;/a> up for it, which can eventually be used in
Google Cloud Run, or elsewhere.&lt;/p>
&lt;p>In this post, I want to describe how I got it to push images build locally up to
googles artifact repository. It will include creating the artifac repository
using infrastructure as code&lt;/p>
&lt;p>The project is in a &lt;a href="https://icle.es/tags/monorepo">monorepo&lt;/a> that uses &lt;a href="https://icle.es/tags/bazel">bazel&lt;/a>.&lt;/p>
&lt;h2 id="the-executable-to-be-packaged">The executable to be packaged&lt;/h2>
```starlark
# //products/example/cmd/server/BUILD.bazel
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")

go_library(
 name = "server_lib",
 srcs = ["main.go"],
 visibility = ["//visibility:private"],
)

go_binary(
 name = "server",
 embed = [":server_lib"],
 visibility = ["//visibility:public"],
)
```</description><content:encoded><![CDATA[<p>I am building a small <a href="https://icle.es/tags/golang">golang</a> <a href="https://icle.es/tags/webapp">webapp</a>, and I want
to push a <a href="https://icle.es/tags/oci">container</a> up for it, which can eventually be used in
Google Cloud Run, or elsewhere.</p>
<p>In this post, I want to describe how I got it to push images build locally up to
googles artifact repository. It will include creating the artifac repository
using infrastructure as code</p>
<p>The project is in a <a href="https://icle.es/tags/monorepo">monorepo</a> that uses <a href="https://icle.es/tags/bazel">bazel</a>.</p>
<h2 id="the-executable-to-be-packaged">The executable to be packaged</h2>
```starlark
# //products/example/cmd/server/BUILD.bazel
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")

go_library(
    name = "server_lib",
    srcs = ["main.go"],
    visibility = ["//visibility:private"],
)

go_binary(
    name = "server",
    embed = [":server_lib"],
    visibility = ["//visibility:public"],
)
```
<h2 id="build-container-image">Build Container Image</h2>
<h3 id="add-rules_oci">add <code>rules_oci</code></h3>
<p><a href="https://github.com/bazel-contrib/rules_oci">rules_oci</a> is a good place to start
to integrate container support into your bazel configuration. It&rsquo;s also worth
<a href="https://github.com/GoogleContainerTools/distroless">reading up a bit on distroless</a>
if you are not aware of it.</p>
<p>Adding <code>rules_oci</code> into bazel <code>MODULES</code> is straightforward:</p>
```starlark
bazel_dep(name = "rules_oci", version = "2.2.6")
# For testing, we also recommend https://registry.bazel.build/modules/container_structure_test

oci = use_extension("@rules_oci//oci:extensions.bzl", "oci")

# Declare external images you need to pull, for example:
oci.pull(
    name = "distroless_base",
    # 'latest' is not reproducible, but it's convenient.
    # During the build we print a WARNING message that includes recommended 'digest' and 'platforms'
    # values which you can use here in place of 'tag' to pin for reproducibility.
    tag = "latest",
    image = "gcr.io/distroless/base",
    platforms = ["linux/amd64"],
)

# For each oci.pull call, repeat the "name" here to expose them as dependencies.
use_repo(oci, "distroless_base")
```
<p>If you use the <code>WORKSPACE</code>, or if you want the latest version, you
<a href="https://github.com/bazel-contrib/rules_oci/releases">can find details on their releases page</a>.</p>
<h3 id="tarbzl"><code>tar.bzl</code></h3>
<p><code>oci_rules</code> uses tar files to build the image. To build tar images, you will
want to use <a href="https://github.com/bazel-contrib/tar.bzl">tar.bzl</a>.</p>
<p>Add the following to you <code>MODULES</code></p>
```starlark
bazel_dep(name = "tar.bzl", version = "0.3.0")
```
<p>Latest version and instructions for <code>WORKSPACE</code>
<a href="https://github.com/bazel-contrib/tar.bzl/releases/tag/v0.3.0">can be found on their releases page</a></p>
<h3 id="buildbazel"><code>BUILD.bazel</code></h3>
<p>There are
<a href="https://github.com/bazel-contrib/rules_oci?tab=readme-ov-file#usage">language specific sample build files</a>
that you can start from.</p>
<p>Starting
<a href="https://github.com/aspect-build/bazel-examples/blob/main/oci_go_image/BUILD.bazel">from the example go <code>BUILD.bazel</code></a>,
and simplifying it, I have:</p>
```starlark
# //products/example/deploy/BUILD.bazel
load("@rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load", "oci_push")
load("@tar.bzl", "mutate", "tar")

# Put app go_binary into a tar layer.
tar(
    name = "app_layer",
    srcs = ["//products/muster/cmd/server:server"],
    out = "app_layer.tar",
    mutate = mutate(strip_prefix = package_name() + "/app_"),
)


oci_image(
    name = "image",
    # This is defined by an oci.pull() call in /MODULE.bazel
    base = "@distroless_base",
    entrypoint = ["/app"],
    # Link the resulting image back to the repository where the build is defined.
    labels = {
        "org.opencontainers.image.source": "https://github.com/aspect-build/bazel-examples",
    },
    tars = [":app_layer"],
)
```
<p>There is
<a href="https://github.com/bazel-contrib/rules_oci/blob/main/docs/go.md">more information at the go doc page</a>
including details of migrating from
<a href="https://github.com/bazelbuild/rules_docker"><code>rules_docker</code></a></p>
```bash
products/example/deploy $ bazel build ...
INFO: Analyzed 2 targets (0 packages loaded, 0 targets configured).
INFO: Found 2 targets...
INFO: Elapsed time: 0.444s, Critical Path: 0.07s
INFO: 2 processes: 1 internal, 1 linux-sandbox.
INFO: Build completed successfully, 2 total actions
```
<p>Building of the images now completes</p>
<h2 id="push-image">Push Image</h2>
<p>Pushing the image can be pretty straightforward:</p>
<ul>
<li>Enable the container repository in <a href="https://icle.es/tags/google-cloud">Google Cloud</a></li>
<li>Configure it in the <code>BUILD.bazel</code> file</li>
<li><code>bazel run</code> to push</li>
</ul>
<p>I would like to do the whole thing through <a href="https://icle.es/tags/iac">IaC</a> so that it is</p>
<ul>
<li>Repeatable, and</li>
<li>more importantly documented</li>
</ul>
<h3 id="iac-tool-options">IaC Tool Options</h3>
<p><a href="https://developer.hashicorp.com/terraform">terraform</a> has no bazel support, and
is a questionable choice from an ethics perspective. <a href="https://icle.es/">opentofu</a> has</p>
<p><a href="https://github.com/yanndegat/rules_tf">rules_tf</a>. However, that
<a href="https://github.com/yanndegat/rules_tf/issues/5">does not support <code>apply</code></a></p>
<p><a href="https://cloud.google.com/infrastructure-manager/docs">Google Cloud Infrastucture Manager</a>
is vendor lock in.</p>
<p>That leaves us with <a href="https://www.pulumi.com/">pulumi</a> (If there is another
alternative, please let me know).</p>
<p>I like the stacks concept in pulumi, and while it doesn&rsquo;t have bazel
integration, what it does have is an
<a href="https://github.com/pulumi/automation-api-examples">automation api</a>. While not
he best documented, there is enough documentation out there to be able to piece
it together.</p>
<p>With this, we can build a runnable unit, and then run it too. I&rsquo;ve experimented
with tagging specific runnables and then running them through the CI. I won&rsquo;t
get that far in this post. My focus here is to get it working locally.</p>
<h3 id="enable-artifact-repository">Enable Artifact Repository</h3>
<p>In pulumi, you can enable the artifact repository with:</p>
```go

func enableArtifactRepository(ctx *pulumi.Context) error {
	// Set your GCP project and region
	projectID := "<projectId>"
	region := "europe-west1"
	repoName := "<repo-name>"

	// Create Artifact Registry repository
	repo, err := artifactregistry.NewRepository(ctx, repoName, &artifactregistry.RepositoryArgs{
		Format:       pulumi.String("DOCKER"),
		Location:     pulumi.String(region),
		RepositoryId: pulumi.String(repoName),
		Description:  pulumi.String("Repository for OCI container images"),
		Project:      pulumi.String(projectID),
	})
	if err != nil {
		return err
	}

	// Export the repository URL (used for pushes)
	ctx.Export("repositoryURL", pulumi.Sprintf("%s-docker.pkg.dev/%s/%s", region, projectID, repo.Name))

	return nil

}
```
<h3 id="authenticating-against-the-repo">Authenticating against the repo</h3>
<p>You need to authenticate against the repository so that the <code>oci_rule</code> can pick
it up</p>
```bash
gcloud auth configure-docker europe-west1-docker.pkg.dev
```
<h3 id="define-the-push-target">Define the push target</h3>
```starlark
oci_push(
    name = "push",
    image = ":image",
    remote_tags = [
        "latest",
        "1h",
    ],
    repository = "europe-west1-docker.pkg.dev/<projectId>/<repo-name>/<image-name>",
)
```
<h2 id="pushing">Pushing</h2>
<p>Once all the above is set up, you can then push the image with one of the
following:</p>
```bash
bazel run :push # if you in the directory
bazel run //<target/path>:push # fron anywhere in the repo
```
<h2 id="side-note">Side note</h2>
<p>Please do not take my heavy usage of google and its products to be a fan letter
or an endorsement. They <em>might</em> at best, be the lesser evil.</p>]]></content:encoded></item><item><title>How to run your lambda code locally as its role (for testing)</title><link>https://icle.es/2023/11/14/automated-testing-of-aws-lambda-locally/</link><pubDate>Tue, 14 Nov 2023 14:23:37 +0000</pubDate><guid>https://icle.es/2023/11/14/automated-testing-of-aws-lambda-locally/</guid><description>&lt;p>While AWS Lambda is fantastic in providing a serverless platform with few
worries about maintaining servers, it is not the easiest to test in an automated
fashion with rapid feedback.&lt;/p>
&lt;p>You could write end to end tests, but it means a deployment after each change
and then checking the logs to see what failed. Even if you use iac
(terraform/pulumi), the deployment will take seconds or a minute or two - not
exact rapid test feedback.&lt;/p>
&lt;p>What I have been doing is to set up a hook which is called from the lambda
handler, which can also be called locally. Within the test, I then assume the
role that runs the lambda and then test the hook.&lt;/p>
&lt;p>This mechanism allows to me easily test that the permissions are set up
correctly and that details are in place for the code to work.&lt;/p>
&lt;p>For the full end to end test, I then have a simple smoke test or two.&lt;/p>
&lt;p>The code samples are in golang(only because it happens to be my current language
of choice), but the idea should be equally applicable in other languages.&lt;/p></description><content:encoded><![CDATA[<p>While AWS Lambda is fantastic in providing a serverless platform with few
worries about maintaining servers, it is not the easiest to test in an automated
fashion with rapid feedback.</p>
<p>You could write end to end tests, but it means a deployment after each change
and then checking the logs to see what failed. Even if you use iac
(terraform/pulumi), the deployment will take seconds or a minute or two - not
exact rapid test feedback.</p>
<p>What I have been doing is to set up a hook which is called from the lambda
handler, which can also be called locally. Within the test, I then assume the
role that runs the lambda and then test the hook.</p>
<p>This mechanism allows to me easily test that the permissions are set up
correctly and that details are in place for the code to work.</p>
<p>For the full end to end test, I then have a simple smoke test or two.</p>
<p>The code samples are in golang(only because it happens to be my current language
of choice), but the idea should be equally applicable in other languages.</p>
<h1 id="assuming-the-role">Assuming The Role</h1>
```go
  roleToAssume := os.Getenv("AUTH_LAMBDA_ROLE_ARN")

    ctx := context.TODO()
    cfg, err := config.LoadDefaultConfig(ctx)

    if err != nil {
        logger.Fatal("error: ", err)
    }
    // Create the credentials from AssumeRoleProvider to assume the role
    // referenced by the "myRoleARN" ARN using the MFA token code provided.
    creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), roleToAssume)

    logger.Debugf("creds: %v", creds)

    cfg.Credentials = aws.NewCredentialsCache(creds)
```
<p>the cfg is then passed into the <code>New</code> method for the resource you are interested
in. e.g.:</p>
```go
   ssmClient := ssm.NewFromConfig(cfg)
```
<h1 id="working-example">Working Example</h1>
<p>You can find a full, working example test in
<a href="https://github.com/drone-ah/wordsonsand">my github repo</a> under
<a href="https://github.com/drone-ah/wordsonsand/tree/main/post/2023/11/autolambdatest">post/2023/11/autolambdatest</a></p>
<p>NOTE: It WILL automatically try and deploy a role and a ssm parameter, and it
will delete it after the test.</p>
<p>The <code>BeforeSuite</code> will deploy the minimum configuration to be able to run the
test, and the <code>AfterSuite</code> will destroy the same stack.</p>
<p>You will likely need to log into pulumi to get this test to work.</p>
<p>If you run into permissions issue for AssumeRole, read on.</p>
<h1 id="assumerole-permissions">AssumeRole Permissions</h1>
<p>For this to work, the user running the tests need to have permissions to
<code>AssumeRole</code>.</p>
<p>There are two steps to this. The first part is to allow &quot;anyone&quot; to
<code>AssumeRole</code> the relevant role:</p>
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<your-account-id>:root"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```
<p>This will allow &quot;any user&quot; to assume the role, as long as they have the
permission to do so.</p>
<p><code>arn:aws:iam::[your-account]:root</code> is a special user the represents the account
(and the non-IAM root user). Since IAM (user, roles etc.) exists under the
&quot;root&quot; account, all calls are also authenticated by this account - i.e. all
users, roles etc. in IAM is also this account. There is
<a href="https://www.reddit.com/r/aws/comments/oorjl2/what_exactly_is_the_root_iam_principal/">a post on reddit discussing what exactly the root iam principal is for more information</a></p>
<p>Finally, unless you have the <code>Administrator</code>Access policy set against your
account, you will also need to attach a policy to the relevant group (or your
user) that grants permissions to call <code>sts:AssumeRole</code> (or <code>*</code>)</p>
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "123",
      "Effect": "Allow",
      "Action": ["sts:AssumeRole"],
      "Resource": ["arn:aws:iam::123456789012:role/desired-role"]
    }
  ]
}
```
<p>You can of course, also use <code>*</code> for Resource above to allow the user/group to
Assume Any role. In practice, you might want to automate this as part of the
creation of the relevant roles. (i.e. create the role, then give the relevant
group permissions to Assume that role).</p>]]></content:encoded></item><item><title>Streaming Progress With Pulumi Automation API</title><link>https://icle.es/2023/11/08/streaming-progress-with-pulumi-automation-api/</link><pubDate>Wed, 08 Nov 2023 12:38:32 +0000</pubDate><guid>https://icle.es/2023/11/08/streaming-progress-with-pulumi-automation-api/</guid><description>&lt;p>When using the pulumi automation API, you lose some of the niceties of the
pulumi CLI, like having to set up command line args processing and the output is
not as friendly or pretty as before. It also doesn't stream the output - though
this one is easier to fix.&lt;/p>
&lt;p>This is lifted straight out of
&lt;a href="https://github.com/pulumi/automation-api-examples/blob/3114b754ea84ebd0cc1e1b67f128df75795bd4c3/go/local_program/automation/main.go#L74C2-L82C3">their golang example code&lt;/a>,
so if you're working in another language - you should be able to find the
relevant code in the same repo&lt;/p></description><content:encoded><![CDATA[<p>When using the pulumi automation API, you lose some of the niceties of the
pulumi CLI, like having to set up command line args processing and the output is
not as friendly or pretty as before. It also doesn't stream the output - though
this one is easier to fix.</p>
<p>This is lifted straight out of
<a href="https://github.com/pulumi/automation-api-examples/blob/3114b754ea84ebd0cc1e1b67f128df75795bd4c3/go/local_program/automation/main.go#L74C2-L82C3">their golang example code</a>,
so if you're working in another language - you should be able to find the
relevant code in the same repo</p>
```go
   // wire up our update to stream progress to stdout
    stdoutStreamer := optup.ProgressStreams(os.Stdout)

    // run the update to deploy our fargate web service
    res, err := stack.Up(ctx, stdoutStreamer)
    if err != nil {
        fmt.Printf("Failed to update stack: %v\n\n", err)
    }
```
]]></content:encoded></item><item><title>Bazel + Pulumi Automation API. Deploying an inline stack along with Pulumi.[stack].yaml</title><link>https://icle.es/2023/11/07/bazel-pulumi-automation-api-deploying-an-inline-stack-along-with-pulumi-yaml/</link><pubDate>Tue, 07 Nov 2023 15:57:52 +0000</pubDate><guid>https://icle.es/2023/11/07/bazel-pulumi-automation-api-deploying-an-inline-stack-along-with-pulumi-yaml/</guid><description>&lt;p>I believe in CI/CD/CD as in Continuous, integration, delivery and deployment. As
part of this, I am setting up a workflow where on merge to develop (or
main/trunk), the deployment is triggered automatically. Pulumi deploys the
current state of code and infrastructure through GitHub actions and
&lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services">OpenID Connect(OIDC)&lt;/a>
as part of the GitHub Action.&lt;/p>
&lt;p>I used to configure Pulumi to be triggered directly from the build process, but
bazel (as far I know), does not support Pulumi. When I used pants, there was a
custom module, developed by one of the community members which did support
pulumi (You might have to ask in the slack channel if you're interested), but
they stopped maintaining it as they moved to the Pulumi Automation API.&lt;/p></description><content:encoded><![CDATA[<p>I believe in CI/CD/CD as in Continuous, integration, delivery and deployment. As
part of this, I am setting up a workflow where on merge to develop (or
main/trunk), the deployment is triggered automatically. Pulumi deploys the
current state of code and infrastructure through GitHub actions and
<a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services">OpenID Connect(OIDC)</a>
as part of the GitHub Action.</p>
<p>I used to configure Pulumi to be triggered directly from the build process, but
bazel (as far I know), does not support Pulumi. When I used pants, there was a
custom module, developed by one of the community members which did support
pulumi (You might have to ask in the slack channel if you're interested), but
they stopped maintaining it as they moved to the Pulumi Automation API.</p>
<p>I am using Automation API from the start, and configuring a &quot;deployer&quot; per
product/project within the monorepo. The intention is for the deployer to be as
smart as possible - eventually <code>up</code>-ing only the stacks that have changes since
the last time- but that's a way down the line.</p>
<p>Another benefit from the Automation API is to pick up the stack outputs
automatically when running integration/e2e tests, making the test configuration
smoother.</p>
<p>The first step is to be able to define a stack within a product, hook it into
the main iac executable and have it working. My directory structure is roughly
as below: While I am using golang as my language of choice, it's probably not
hugely different in other languages.</p>
<ul>
<li>products
<ul>
<li>productA
<ul>
<li>auth
<ul>
<li>iac_auth
<ul>
<li>BUILD</li>
<li>deploy.go</li>
<li>Pulumi.dev.yaml</li>
<li>Pulumi.yaml</li>
</ul>
</li>
<li>OtherAuthModules</li>
</ul>
</li>
<li>iac
<ul>
<li>BUILD</li>
<li>main.go</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="iac_authbuild"><code>iac_auth/BUILD</code></h1>
```wp-block-syntaxhighlighter-code
# load etc.
go_library(
    name = "iac_auth",
    srcs = ["deploy.go"],
    data = glob([
        "Pulumi.*.yaml",
    ]) + [
        "Pulumi.yaml",
    ],
    visibility = ["//visibility:public"],
    deps = [
       # dependencies automated with gazelle
    ],
)
```
<h1 id="iac_authdeploy.go"><code>iac_auth/deploy.go</code></h1>
```wp-block-syntaxhighlighter-code
package iac_sync

import (
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

const moduleName = "auth"

func DeployGithubLambda(ctx *pulumi.Context) error {
    fmt.Println("Deploy")

    conf := config.Require(ctx, "key")
    fmt.Printf("conf: %s\n", conf) // Will successfully pick up the value if key is in Pulumi.<stack>.yaml

    // Actual deployment code here

    return nil
}
```
<h1 id="iacbuild"><code>iac/BUILD</code></h1>
```wp-block-syntaxhighlighter-code
# other config including standard config of :iac_lib by gazelle

go_binary(
    name = "iac",
    args = [
        "-iac_auth",
        "$(location //products/productA/auth/iac_auth)",
    ],
    data = [
        "//products/gitolink/productA/iac_auth",
    ],
    embed = [":iac_lib"],
    visibility = ["//visibility:public"],
)
```
<p>Worth noting the <code>args</code> bit, which is what we use to identify the path where the
<code>Pulumi.yaml</code> and <code>Pulumi.&lt;stack&gt;.yaml</code> files are:</p>
<h1 id="iacmain.go"><code>iac/main.go</code></h1>
```wp-block-syntaxhighlighter-code
package main

import (
    "context"
    "flag"
    "fmt"
    iacAuth "github.com/drone-ah/monorepo/products/gitolink/auth/iac_auth"
    "github.com/pulumi/pulumi/sdk/v3/go/auto"
    "path/filepath"
)

func main() {

    // Pick up the path from args
    var iacAuthPath = flag.String("iac_auth", "", "bin for iac_auth")
    flag.Parse()
    fmt.Printf("param iac: %s \n", *iacAuthPath)

    ctx := context.Background()

    projectName := "gitolink"
    stackName := "dev"

    stack, err := auto.NewStackInlineSource(ctx,
        stackName,
        projectName,
        iacAuth.DeployGithubLambda,
        // Define workdir for locatin of the Pulumi yaml files
        auto.WorkDir(filepath.Dir(*iacAuthPath)))

    preview, err := stack.Preview(ctx)

    fmt.Println(err)
    fmt.Println(preview)
}
```
<p>You might also have to use <code>RLocation</code> (see my previous post about
<a href="https://drone-ah.com/2023/11/01/including-a-built-artifact-in-another-target-bazel-golang/">including an artifact in another target</a>
for an example of this), though when I tried it, it was missing one of the yaml
files and I didn't investigate further.</p>]]></content:encoded></item><item><title>Including a built artifact in another target (Bazel, golang)</title><link>https://icle.es/2023/11/01/including-a-built-artifact-in-another-target-bazel-golang/</link><pubDate>Wed, 01 Nov 2023 19:42:30 +0000</pubDate><guid>https://icle.es/2023/11/01/including-a-built-artifact-in-another-target-bazel-golang/</guid><description>&lt;p>We use pulumi to do IaC and we use a monorepo with Bazel as the build tool. We
have out modules set out as following&lt;/p>
&lt;p>One of the requirements we have is to build a lambda module and then deploy it.
The lambda module is a target being built by Bazel (golang, but shouldn't
matter):&lt;/p>
```go
go_binary(
 name = "lambda_module",
 visibility = ["//visibility:public"],
)
```</description><content:encoded><![CDATA[<p>We use pulumi to do IaC and we use a monorepo with Bazel as the build tool. We
have out modules set out as following</p>
<p>One of the requirements we have is to build a lambda module and then deploy it.
The lambda module is a target being built by Bazel (golang, but shouldn't
matter):</p>
```go
go_binary(
    name = "lambda_module",
    visibility = ["//visibility:public"],
)
```
<p>We then have the iac module, which should get the built version of the above
module, so that it can then upload it into lambda</p>
```go
go_binary(
    name = "iac",
    args = [
        "-lambda_module",
        "$(location //products/productA/module/lambda_module)",
    ],
    data = ["//products/productA/module/lambda_module"],
    visibility = ["//visibility:public"],
)
```
<p>There are two key parameters here to note:</p>
<ul>
<li><code>args</code>: We generate the path to the target module using
<code> //products/productA/module/lambda_module)</code></li>
<li><code>data</code>: We use the data tag to ensure that the built output is included when
building/running this target</li>
</ul>
<p>We then need to use runfiles support within golang to be ablet to identify the
correct location for the built binary. The reason this part is complex is to be
able to support multiple operating systems. I should caveat that I have only got
this working on Linux, but Mac/Win shouldn't be too different.</p>
```go
package main

import (
    _ "embed"
    "flag"
    "fmt"
    "github.com/bazelbuild/rules_go/go/runfiles"
    "path/filepath"
)

func main() {

    var webhookAuth = flag.String("webhook_auth", "", "bin for webhook_auth")
    flag.Parse()
    fmt.Printf("param : %s \n", *webhookAuth)

    path, err := runfiles.Rlocation(fmt.Sprintf("workspace_name/%s", *webhookAuth))
    fmt.Printf("rLoc path: %s, err: %v \n", path, err)

    symlinks, err := filepath.EvalSymlinks(path)
    fmt.Printf("evaluated path: %s, err: %v \n", symlinks, err)

}
```
<p>We use the flag module to retrieve the path passed in as a runtime parameter</p>
<p>We use <code>runfiles.Rlocation</code> to pick up the &quot;real&quot; path to the file, prepending
the workspace name to the start. You can
<a href="https://bazel.build/rules/lib/globals/workspace#workspace">define the workspace name</a>
in the root WORKSPACE file with <code>workspace(name = &quot;workspace_name&quot;)</code></p>
<p>Finally, resolve the Symlink to get the actual file path</p>
<h2 id="references">References</h2>
<p>There are similar mechanisms to find the rLocation in other languages, a couple
of which are described in
<a href="https://docs.google.com/document/d/e/2PACX-1vSDIrFnFvEYhKsCMdGdD40wZRBX3m3aZ5HhVj4CtHPmiXKDCxioTUbYsDydjKtFDAzER5eg7OjJWs3V/pub">its design document</a></p>
<p>There is some documentation in <code>rules_go</code> around
<a href="https://github.com/bazelbuild/rules_go#how-do-i-access-go_binary-executables-from-go_test">accessing <code>go_binary</code> from <code>go_test</code></a>
which I referenced and updated to get the above example</p>
<p>I found the above link from
<a href="https://stackoverflow.com/questions/70193581/feed-bazel-output-to-another-bazel-rule">a stackoverflow post about feeding bazel output to another bazel rule</a></p>]]></content:encoded></item></channel></rss>