<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jason Content</title>
    <description>The latest articles on Forem by Jason Content (@jasric89).</description>
    <link>https://forem.com/jasric89</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1164045%2F0d84c980-d148-4803-b4d8-3458ad8f7cba.jpeg</url>
      <title>Forem: Jason Content</title>
      <link>https://forem.com/jasric89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jasric89"/>
    <language>en</language>
    <item>
      <title>Building My Own LLM: A Journey into Language Models Building a Tokenizer 🛠️</title>
      <dc:creator>Jason Content</dc:creator>
      <pubDate>Thu, 18 Jul 2024 08:55:47 +0000</pubDate>
      <link>https://forem.com/jasric89/building-my-own-llm-a-journey-into-language-models-building-a-tokenizer-np8</link>
      <guid>https://forem.com/jasric89/building-my-own-llm-a-journey-into-language-models-building-a-tokenizer-np8</guid>
      <description>&lt;p&gt;Welcome to Cloud Dude's Blog page! This blog post is based on the second video in our series on building Large Language Models (LLMs) from scratch, following the book "Build a Large Language Model from Scratch" by Sebastian Raschka, available at Manning.com.&lt;/p&gt;

&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;In this series, we're diving deep into the world of LLMs, like ChatGPT, to understand how they work and how to build one from scratch. This post covers Chapter 2 of the book, which focuses on building a dataset for training our LLM.&lt;/p&gt;

&lt;p&gt;Chapter 2: Building a Dataset&lt;/p&gt;

&lt;p&gt;Understanding the Dataset&lt;/p&gt;

&lt;p&gt;The core idea behind an LLM is to take user input, process it, and generate meaningful output. For instance, if you ask the model to write a book about different breeds of cats, it needs to understand the request, fetch relevant information, and then generate the content. This process involves training the model on a dataset that acts as its knowledge base.&lt;/p&gt;

&lt;p&gt;Below is an image on how this might look from a high level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9trngfmeea2lhpsb9zn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9trngfmeea2lhpsb9zn.png" alt="Image description" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Training the Dataset&lt;/p&gt;

&lt;p&gt;Training a dataset involves encoding a large amount of text into tokens that the model can understand and use. Essentially, you have to give it a vocabulary to then be able to go out into the world and understand and process the information. I mentioned in the YouTube video that I believe you should give it the English Dictionary because then it would have all the words and letters and would be able to process the information.&lt;/p&gt;

&lt;p&gt;In the book, Sebastian Raschka uses "The Verdict" as an example dataset. The goal is to encode this book into tokens, which the model can then use to process other tasks.&lt;/p&gt;

&lt;p&gt;Practical Implementation&lt;/p&gt;

&lt;p&gt;Using Jupyter Notebooks&lt;/p&gt;

&lt;p&gt;The book provides code snippets in Jupyter Notebooks, which are great for running pieces of code interactively. However, as a developer, you might prefer to build a complete program. This involves creating classes and importing them into a main file to run the entire process as a cohesive unit.&lt;/p&gt;

&lt;p&gt;Your folder structure would look like so:&lt;/p&gt;

&lt;p&gt;┣ 📜main.py&lt;br&gt;
┣ 📜simple_tokenizer.py&lt;/p&gt;

&lt;p&gt;You would have the class in simple_tokenizer.py and that would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import re
class SimpleTokenizerV1:
    def __init__(self, vocab):
        self.str_to_int = vocab
        self.int_to_str = {i:s for s,i in vocab.items()}

    def encode(self, text):
        preprocessed = re.split(r'([,.?_!"()\']|--|\s)', text)
        preprocessed = [
            item.strip() for item in preprocessed if item.strip()
        ]
        #ids = [self.str_to_int[s] for s in preprocessed]
        ids = []
        for s in preprocessed:
            if s in self.str_to_int:
                ids.append(self.str_to_int[s])
            else:
                print(f"Warning: '{s}' not found in vocabulary. Using '&amp;lt;UNK&amp;gt;' token.")
                ids.append(self.str_to_int[self.unknown_token])
        return ids

    def decode(self, ids):
        text = " ".join([self.int_to_str[i] for i in ids]) 

        text = re.sub(r'\s+([,.?!"()\'])', r'\1', text)
        return text

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, as we are working in Python, you would import that class into your m̀ain.py file. Like so&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import simple_tokenizer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To now use that class and process the vocabulary so that it can be put into tokens, you would write something similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import urllib.request
import re
import simple_tokenizer

## Download the Text File
url = ("https://raw.githubusercontent.com/rasbt/"
       "LLMs-from-scratch/main/ch02/01_main-chapter-code/"
       "the-verdict.txt")
file_path = "the-verdict.txt"
urllib.request.urlretrieve(url, file_path)

## Read the Text File
with open("the-verdict.txt", "r", encoding="utf-8") as f:
    raw_text = f.read()

# Define The Vocab
# For the Vocab to work on a book you need to pass it a lot of vocabulary. Essentially to get this working I had to pass it the book
# to process the book. Is this not counter interuitive?
# Once it had all the vocab then it can run and process the book into tokens. 
#vocab = {
#    'The': 1, 'verdict': 2, 'is': 3, 'in': 4, '.': 5,
#    'It': 6, 'was': 7, 'a': 8, 'great': 9, 'success': 10, 's':11
#}

## Now Process the Book and convert it to Tokens.
preprocessed = re.split(r'([,.?_!"()\']|--|\s)', raw_text)
preprocessed = [item.strip() for item in preprocessed if item.strip()]
print(len(preprocessed))

## Sort words Alphabetically This I believe removes white space and vocab punctuation as well.
all_words = sorted(set(preprocessed))
vocab_size = len(all_words)
print(vocab_size)
all_tokens = sorted(list(set(preprocessed)))
all_tokens.extend(["&amp;lt;|endoftext|&amp;gt;", "&amp;lt;|unk|&amp;gt;"])

## Now print the first 51 TokenIDs. 
vocab = {token:integer for integer,token in enumerate(all_words)}
vocab = {token:integer for integer,token in enumerate(all_tokens)}
for i, item in enumerate(vocab.items()):
    print(item)
    if i &amp;gt;= 50:
        break

## Initialise the tokenizer
tokenizetext = simple_tokenizer.SimpleTokenizerV1(vocab)

## Encode the Text
converttoids = tokenizetext.encode(raw_text)
print("Encoded Text:", converttoids)

## Decode the Text
decodeids = tokenizetext.decode(converttoids)
print("Decoded Text", decodeids)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would be simple enough to build a very basic vocabulary to process the book.&lt;/p&gt;

&lt;p&gt;Handling Errors&lt;/p&gt;

&lt;p&gt;One common issue when working with datasets is handling unknown tokens. For example, if the model encounters a word that isn't in its vocabulary, it should handle this gracefully rather than throwing an error. In Python, this can be managed by checking for the presence of each token in the vocabulary and using a placeholder for unknown tokens.&lt;/p&gt;

&lt;p&gt;You could do this like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SimpleTokenizerBetterErrorV1:
    def __init__(self, vocab):
        self.str_to_int = vocab
        self.int_to_str = {i: s for s, i in vocab.items()}
        self.unknown_token = '&amp;lt;UNK&amp;gt;'
        self.str_to_int[self.unknown_token] = len(self.str_to_int) + 1
        self.int_to_str[len(self.int_to_str) + 1] = self.unknown_token

    def encode(self, text):
        preprocessed = re.split(r'([,.?_!"()\']|--|\s)', text)
        preprocessed = [item.strip() for item in preprocessed if item.strip()]
        ids = []
        for s in preprocessed:
            if s in self.str_to_int:
                ids.append(self.str_to_int[s])
            else:
                print(f"Warning: '{s}' not found in vocabulary. Using '&amp;lt;UNK&amp;gt;' token.")
                ids.append(self.str_to_int[self.unknown_token])
        return ids

    def decode(self, ids):
        text = " ".join([self.int_to_str[i] for i in ids])
        text = re.sub(r'\s+([,.?!"()\'])', r'\1', text)
        return text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of Python just spitting out to me lines of code in my error that really do not make much sense:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traceback (most recent call last):
  File "/home/jason/Dev_Ops/LLMS/MyLLMVersion/Ch2/main.py", line 52, in &amp;lt;module&amp;gt;
    converttoids = tokenizetext.encode(raw_text)
  File "/home/jason/Dev_Ops/LLMS/MyLLMVersion/Ch2/simple_tokenizer.py", line 12, in encode
    ids = [self.str_to_int[s] for s in preprocessed]
  File "/home/jason/Dev_Ops/LLMS/MyLLMVersion/Ch2/simple_tokenizer.py", line 12, in &amp;lt;listcomp&amp;gt;
    ids = [self.str_to_int[s] for s in preprocessed]
KeyError: 'I'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above code, I am able to make the error make better sense.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning: 'I' not found in vocabulary. Using '&amp;lt;UNK&amp;gt;' token.
Traceback (most recent call last):
  File "/home/jason/Dev_Ops/LLMS/MyLLMVersion/Ch2/main.py", line 52, in &amp;lt;module&amp;gt;
    converttoids = tokenizetext.encode(raw_text)
  File "/home/jason/Dev_Ops/LLMS/MyLLMVersion/Ch2/simple_tokenizer.py", line 19, in encode
    ids.append(self.str_to_int[self.unknown_token])
AttributeError: 'SimpleTokenizerV1' object has no attribute 'unknown_token'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I touched on in the video, this really is a letdown of the Python language, and in Go, you can't do this. Go error handles well, and it also gets you, as a developer, to think about what type of error is going to come back to the user. Making you essentially a better developer.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;I would highly recommend buying this book; you can get it from here: &lt;a href="https://www.manning.com/books/build-a-large-language-model-from-scratch" rel="noopener noreferrer"&gt;https://www.manning.com/books/build-a-large-language-model-from-scratch&lt;/a&gt;. Also, please do check out my blog for more posts similar to this one, as I don't post here all the time. &lt;a href="https://theclouddude.co.uk/" rel="noopener noreferrer"&gt;Link To Blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To begin building an LLM, you need to first build a tokenizer that can take in human language and convert it into numbers or, essentially, a database. Then, you can ask it to process data and return human-readable results.&lt;/p&gt;

&lt;p&gt;I hope that was insightful, and I look forward to writing the next post about this project.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;br&gt;
Cloud Dude&lt;/p&gt;

</description>
      <category>llm</category>
      <category>ai</category>
      <category>python</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Working with Stacks, AI, Go and Pulumi</title>
      <dc:creator>Jason Content</dc:creator>
      <pubDate>Fri, 16 Feb 2024 14:09:19 +0000</pubDate>
      <link>https://forem.com/jasric89/working-with-stacks-ai-go-and-pulumi-ca8</link>
      <guid>https://forem.com/jasric89/working-with-stacks-ai-go-and-pulumi-ca8</guid>
      <description>&lt;p&gt;Recently, I have been tasked with checking out other IAC tools where I work. One of those given to me was Pulumi. At first, I was not sure. I come from a Terraform background and have been using Terraform for around six years now, so I was hesitant to pick this up at first.&lt;/p&gt;

&lt;p&gt;At first, Pulumi did not make much sense to me. My initial thought was they were trying to copy Hashicorp. Yet the more I used it, the more it has grown on me.&lt;/p&gt;

&lt;p&gt;I like it because if you are a Dev and not a DevOps person, I believe you will like Pulumi perhaps more than Terraform. The main reason is that you can bring your language of choice to the platform and start making cloud Infrastructure without going off and learning a whole new language.&lt;/p&gt;

&lt;p&gt;After two weeks of messing with Pulumi and Go, let me show you what I did.&lt;/p&gt;

&lt;p&gt;The most impressive part I have seen from Pulumi and Go is the Auto Package. &lt;a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/auto"&gt;Auto Pkg Docs&lt;/a&gt; The Auto package brings a particular element of A. I to your program. It essentially lets you automate the whole process of running a Pulumi program.&lt;/p&gt;

&lt;p&gt;If you want to run a Pulumi program, in the cmd line, you run Pulumi Up. In the picture below, you will see when Pulumi runs, it will ask if you want to create a Stack. A Stack is like a state file if you come from a Terraform background. You say yes and give your Stack a name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpkb2tfkbahqxlsbfkfx.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpkb2tfkbahqxlsbfkfx.PNG" alt="Image description" width="630" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, that's all good if you're running this locally and don't mind interacting with Pulumi.&lt;/p&gt;

&lt;p&gt;But what about if you want to run this in a Pipeline? You can't have the pipeline prompt you to create a new stack. This is where the Auto Pkg starts to solve this for you. There may be other options for this. It would be worth checking out &lt;code&gt;pulumi --config&lt;/code&gt; argument.&lt;a href="https://www.pulumi.com/docs/cli/commands/pulumi_up/"&gt;Pulumi Up Doc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, you must set your context, as many Pulumi Functions use the Context package. (I am not sure why this is; their documentation says it's necessary for Go to work, yet I've seen Go work fine without context. Yet that's what is required.)&lt;/p&gt;

&lt;p&gt;So, you start with &lt;code&gt;context.Background()&lt;/code&gt; then, you need to declare the following variables. Now, with the pipeline, You could put these in as ENV variables, but for the sake of this demo, we will hard-code these. (Later on, I will write more blog posts on this, and it will probably show a pipeline run in GitLab, so keep tuned in if you want to see that.) The Variables are: &lt;code&gt;stackName&lt;/code&gt; , and the &lt;code&gt;projectName&lt;/code&gt;. For obvious reasons, this &lt;code&gt;stackName&lt;/code&gt; is what we will call the Stack. The &lt;code&gt;projectName&lt;/code&gt; It is my understanding, and this could be wrong, but I believe the project is the top-level folder of what you will create; for example, this project will build an EC2 instance in AWS, so I called my Project &lt;code&gt;ec2_deploy&lt;/code&gt;. If you run Pulumi manually, it will just go and grab the top-level folder name and call that the project.&lt;/p&gt;

&lt;p&gt;Now, we are going to pass. &lt;code&gt;context.Background()&lt;/code&gt;, &lt;code&gt;stackName&lt;/code&gt;, and the &lt;code&gt;ProjectName&lt;/code&gt; with a separate deploy func variable function called &lt;code&gt;deployFunc&lt;/code&gt; (More on this later). To the function &lt;code&gt;UpsertStackInlineSource()&lt;/code&gt;, this function takes all our variables and returns a created stack and an error to us. If the Stack is already created, the function won't error. (This may enable us to handle errors better than just running the &lt;code&gt;pulumi up&lt;/code&gt; command and capturing a CLI error.) It will select the Stack and return the created Stack to us. This is handy if you run your pipeline over an already-created environment. So, the code will look a bit like this. (Side note: ensure you have &lt;code&gt;"github.com/pulumi/pulumi/sdk/v3/go/auto"&lt;/code&gt; on your imports; otherwise, this won't work.)&lt;/p&gt;

&lt;p&gt;`package main&lt;/p&gt;

&lt;p&gt;import (&lt;br&gt;
    "context"&lt;br&gt;
    "fmt"&lt;br&gt;
    "os"&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"github.com/pulumi/pulumi/sdk/v3/go/auto"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;)&lt;/p&gt;

&lt;p&gt;func main() {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ctx := context.Background()

projectName := "ec2_deploy"
// we use a simple stack name here, but recommend using auto.FullyQualifiedStackName for maximum specificity.
stackName := "dev"
// stackName := auto.FullyQualifiedStackName("myOrgOrUser", projectName, stackName)

// create or select a stack matching the specified name and project.
// this will set up a workspace with everything necessary to run our inline program (deployFunc)
createdStack, err := auto.UpsertStackInlineSource(ctx, stackName, projectName, deployFunc)
if err != nil {
    fmt.Printf("Failed to set up a workspace: %v\n", err)
    os.Exit(1)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;As we are working with AWS, we need to create a workspace; my only explanation for this workspace idea is the Python equivalent of a Virtual Environment. I still don't fully understand this concept, but according to various examples, we need it. We also need to install the AWS tools to deploy our EC2 instance. The code will look like this.&lt;/p&gt;

&lt;p&gt;`workspace := createdStack.Workspace()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fmt.Println("Installing the AWS plugin")

// for inline source programs, we must manage plugins ourselves
err = workspace.InstallPlugin(ctx, "aws", "v6.22.0")
if err != nil {
    fmt.Printf("Failed to install program plugins: %v\n", err)
    os.Exit(1)
}

fmt.Println("Successfully installed AWS plugin")`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Depending on how you are signing into AWS from Pulumi, where I am, we are using SSO profiles. Again, this can be a manual process when running through the Pulumi command line, but when operating the Auto Pkg, you can set the config to use the AWS profile to sign in and deploy the EC2 instance.&lt;/p&gt;

&lt;p&gt;How we are going to do that is by using a function called, &lt;code&gt;SetAllConfig()&lt;/code&gt; There is another function called. &lt;code&gt;SetConfig()&lt;/code&gt; But that function will only let you set one item to the configuration. In our use case, we need to send over the AWS Region; for the project I'm working on, I need to send over a couple of custom values. It was also cool to send many values to see how this would work.&lt;/p&gt;

&lt;p&gt;These values, though, are weirdly not sent to the workspace but to the Stack we made at the beginning. When I was initially coding this, I presumed, with the linear process the program is running in, that we had moved from the Stack to the workspace and that all settings I was configuring were now in the workspace, not the Stack. Yet that's incorrect, all settings and configs are set to the workspace.&lt;/p&gt;

&lt;p&gt;To send our config to the Stack, we are going to use the map called &lt;code&gt;ConfigMap&lt;/code&gt; in the &lt;code&gt;Auto PKG&lt;/code&gt;, which contains a field called &lt;code&gt;ConfigValue&lt;/code&gt; you need to set the name fields of this map in a YAML format; for example, &lt;code&gt;aws:region&lt;/code&gt; don't do &lt;code&gt;aws:region:&lt;/code&gt; as this won't work. (Hours of troubleshooting and pain brought me to this conclusion). An example of what I am talking about is below. Side Note: You must also pass the context variable we set earlier to this function.&lt;/p&gt;

&lt;p&gt;`cfg := auto.ConfigMap{&lt;br&gt;
        "aws:region":             auto.ConfigValue{Value: "us-east-1"},&lt;br&gt;
        "company:ticket":          auto.ConfigValue{Value: "CLOUD"},&lt;br&gt;
        "company:hubname": auto.ConfigValue{Value: "MYHUB"},&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;createdStack.SetAllConfig(ctx, cfg)`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then, as we are sending it to the Stack, we also need to refresh our Stack to receive the new config. For that, we are going to use the &lt;code&gt;Refresh()&lt;/code&gt; function again, you need to send your context variable to the function for this to work.&lt;/p&gt;

&lt;p&gt;I can hear the cogs turning. Why don't you just set the configuration to the Stack at the beginning with the initial function? That is a great question; simply, it doesn't work; the only way you can set the config to the Stack that I could see with the documentation was this way. Again, I could be wrong, as I have just started this process with Pulumi.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`_, err = createdStack.Refresh(ctx)
if err != nil {
    fmt.Printf("Failed to refresh stack: %v\n", err)
    os.Exit(1)
}`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The last thing we need to code for working with Stacks is the Pulumi-up part to make it all work. For that, we are going to use the &lt;code&gt;Up()&lt;/code&gt; function. This works the same way as typing in &lt;code&gt;pulumi up -y&lt;/code&gt; into your terminal.&lt;/p&gt;

&lt;p&gt;For this function to work, it uses the package &lt;code&gt;optup&lt;/code&gt; function &lt;code&gt;ProgressStreams()&lt;/code&gt; , which uses &lt;code&gt;io.writer&lt;/code&gt; to write the output of what Pulumi is sending to a console. This should still work in a pipeline scenario, which I will see at a later date when I build one. Pkgs Documentation for more details: &lt;a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/auto/optup"&gt;Optup Pkg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your code for this part should look like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`// wire up our update to stream progress to stdout
stdoutStreamer := optup.ProgressStreams(os.Stdout)

// run the update to deploy our EC2 Cluster
_, err = createdStack.Up(ctx, stdoutStreamer)
if err != nil {
    fmt.Printf("Failed to update stack: %v\n\n", err)
    os.Exit(1)
}`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Your program code at this point should look like this:&lt;/p&gt;

&lt;p&gt;`package main&lt;/p&gt;

&lt;p&gt;import (&lt;br&gt;
    "context"&lt;br&gt;
    "fmt"&lt;br&gt;
    "os"&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"github.com/pulumi/pulumi/sdk/v3/go/auto"
"github.com/pulumi/pulumi/sdk/v3/go/auto/optup"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;)&lt;/p&gt;

&lt;p&gt;func main() {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ctx := context.Background()

projectName := "ec2_deploy"
// we use a simple stack name here, but recommend using auto.FullyQualifiedStackName for maximum specificity.
stackName := "dev"
// stackName := auto.FullyQualifiedStackName("myOrgOrUser", projectName, stackName)

// create or select a stack matching the specified name and project.
// this will set up a workspace with everything necessary to run our inline program (deployFunc)
createdStack, err := auto.UpsertStackInlineSource(ctx, stackName, projectName, deployFunc)
if err != nil {
    fmt.Printf("Failed to set up a workspace: %v\n", err)
    os.Exit(1)
}

workspace := createdStack.Workspace()

fmt.Println("Installing the AWS plugin")

// for inline source programs, we must manage plugins ourselves
err = workspace.InstallPlugin(ctx, "aws", "v6.22.0")
if err != nil {
    fmt.Printf("Failed to install program plugins: %v\n", err)
    os.Exit(1)
}

fmt.Println("Successfully installed AWS plugin")

cfg := auto.ConfigMap{
    "aws:region":             auto.ConfigValue{Value: "us-east-1"},
    "company:ticket":          auto.ConfigValue{Value: "CLOUD"},
    "company:hubname": auto.ConfigValue{Value: "MYHUB"},
}

createdStack.SetAllConfig(ctx, cfg)

_, err = createdStack.Refresh(ctx)
if err != nil {
    fmt.Printf("Failed to refresh stack: %v\n", err)
    os.Exit(1)
}

// wire up our update to stream progress to stdout
stdoutStreamer := optup.ProgressStreams(os.Stdout)

// run the update to deploy our EC2 Cluster
_, err = createdStack.Up(ctx, stdoutStreamer)
if err != nil {
    fmt.Printf("Failed to update stack: %v\n\n", err)
    os.Exit(1)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;Yet, if you were to run this program like in my picture below, it would fail...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniskffib16g09vnxt9lp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniskffib16g09vnxt9lp.png" alt="Image description" width="704" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WHY?&lt;/p&gt;

&lt;p&gt;It's failing because our first function &lt;code&gt;UpsertStackInlineSource()&lt;/code&gt; to piece all this together is missing the &lt;code&gt;deployFunc&lt;/code&gt; variable we have not yet set to deploy our EC2 instance. Now, I must say I don't like this, the reason being that the way you code a function essentially is with a variable, not the usual GO way with &lt;code&gt;func(){}&lt;/code&gt; . The &lt;code&gt;UpsertStackInlineSource()&lt;/code&gt; it's not very GO Idiomatic. I couldn't figure out how to pass a function to this function. I will at a later point in time, but for the purpose of this blog post, we will keep it a variable with the note to change it later so that we can keep to Go's Idiomaticness. (If that's a word.) Again, as always, I could be wrong and missing something here, but I could not get this function to work with another function during my tired hours.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Deploy the EC2 Instance&lt;br&gt;
    deployFunc := func(ctx *pulumi.Context) error {&lt;br&gt;
        ubuntu, err := ec2.LookupAmi(ctx, &amp;amp;ec2.LookupAmiArgs{&lt;br&gt;
            MostRecent: pulumi.BoolRef(true),&lt;br&gt;
            Filters: []ec2.GetAmiFilter{&lt;br&gt;
                {&lt;br&gt;
                    Name: "name",&lt;br&gt;
                    Values: []string{&lt;br&gt;
                        "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*",&lt;br&gt;
                    },&lt;br&gt;
                },&lt;br&gt;
                {&lt;br&gt;
                    Name: "virtualization-type",&lt;br&gt;
                    Values: []string{&lt;br&gt;
                        "hvm",&lt;br&gt;
                    },&lt;br&gt;
                },&lt;br&gt;
            },&lt;br&gt;
            Owners: []string{&lt;br&gt;
                "099720109477",&lt;br&gt;
            },&lt;br&gt;
        }, nil)&lt;br&gt;
        if err != nil {&lt;br&gt;
            return err&lt;br&gt;
        }&lt;br&gt;
        amiinstanceid := pulumi.Sprintf(ubuntu.Id)&lt;br&gt;
        _, err = ec2.NewInstance(ctx, "web", &amp;amp;ec2.InstanceArgs{&lt;br&gt;
            Ami:          amiinstanceid,&lt;br&gt;
            InstanceType: pulumi.String("t3.micro"),&lt;br&gt;
            Tags: pulumi.StringMap{&lt;br&gt;
                "Name": pulumi.String("HelloWorld"),&lt;br&gt;
            },&lt;br&gt;
        })&lt;br&gt;
        if err != nil {&lt;br&gt;
            return err&lt;br&gt;
        }&lt;br&gt;
        return nil&lt;br&gt;
    }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The above code originated from the Pulumi website on how to deploy an EC2 instance. Yet there is a big error: It's on the line before &lt;code&gt;_, err = ec2.NewInstance(ctx, "web", &amp;amp;ec2.InstanceArgs{&lt;/code&gt; where you have to get the Instance ID of the AMI. There seems to be an issue with Pulumi and passing IDs around. The way I got around this was to use &lt;code&gt;pulumi.Sprintf()&lt;/code&gt; , which converted the ID into a string that was acceptable to the AMI field.&lt;/p&gt;

&lt;p&gt;Also, please make sure you have the following library in your import statement:&lt;br&gt;
&lt;code&gt;"github.com/pulumi/pulumi/sdk/v3/go/pulumi"&lt;/code&gt; and &lt;code&gt;"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ec2"&lt;/code&gt; otherwise, this won't work.&lt;/p&gt;

&lt;p&gt;I won't go into the EC2 code too much now, as this Blog post is long enough. I will be, however, writing another post about deploying the EC2 instance with Go and Pulumi at a later date :).&lt;/p&gt;

&lt;p&gt;The code for this variable has to go up the top now in &lt;code&gt;main(){}&lt;/code&gt;, before our &lt;code&gt;UpsertStackInlineSource()&lt;/code&gt; function call because Go runs in a sequential order, so if you put the &lt;code&gt;deployFunc&lt;/code&gt; after the &lt;code&gt;UperStackInlineSource()&lt;/code&gt; call, it will error and say &lt;code&gt;deployFunc&lt;/code&gt; does not exist. Your whole program code should now look like this:&lt;/p&gt;

&lt;p&gt;`package main&lt;/p&gt;

&lt;p&gt;import (&lt;br&gt;
    "context"&lt;br&gt;
    "fmt"&lt;br&gt;
    "os"&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/auto"
"github.com/pulumi/pulumi/sdk/v3/go/auto/optup"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;)&lt;/p&gt;

&lt;p&gt;func main() {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Deploy the EC2 Instance
deployFunc := func(ctx *pulumi.Context) error {
    ubuntu, err := ec2.LookupAmi(ctx, &amp;amp;ec2.LookupAmiArgs{
        MostRecent: pulumi.BoolRef(true),
        Filters: []ec2.GetAmiFilter{
            {
                Name: "name",
                Values: []string{
                    "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*",
                },
            },
            {
                Name: "virtualization-type",
                Values: []string{
                    "hvm",
                },
            },
        },
        Owners: []string{
            "099720109477",
        },
    }, nil)
    if err != nil {
        return err
    }
    amiinstanceid := pulumi.Sprintf(ubuntu.Id)
    _, err = ec2.NewInstance(ctx, "web", &amp;amp;ec2.InstanceArgs{
        Ami:          amiinstanceid,
        InstanceType: pulumi.String("t3.micro"),
        Tags: pulumi.StringMap{
            "Name": pulumi.String("HelloWorld"),
        },
    })
    if err != nil {
        return err
    }
    return nil
}

ctx := context.Background()

projectName := "ec2_deploy"
// we use a simple stack name here, but recommend using auto.FullyQualifiedStackName for maximum specificity.
stackName := "dev"

// create or select a stack matching the specified name and project.
// this will set up a workspace with everything necessary to run our inline program (deployFunc)
createdStack, err := auto.UpsertStackInlineSource(ctx, stackName, projectName, deployFunc)
if err != nil {
    fmt.Printf("Failed to set up a workspace: %v\n", err)
    os.Exit(1)
}

workspace := createdStack.Workspace()

fmt.Println("Installing the AWS plugin")

// for inline source programs, we must manage plugins ourselves
err = workspace.InstallPlugin(ctx, "aws", "v6.22.0")
if err != nil {
    fmt.Printf("Failed to install program plugins: %v\n", err)
    os.Exit(1)
}

fmt.Println("Successfully installed AWS plugin")

cfg := auto.ConfigMap{
    "aws:region":             auto.ConfigValue{Value: "us-east-1"},
    "company:ticket":          auto.ConfigValue{Value: "CLOUD"},
    "company:hubname": auto.ConfigValue{Value: "MYHUB"},
}

createdStack.SetAllConfig(ctx, cfg)

_, err = createdStack.Refresh(ctx)
if err != nil {
    fmt.Printf("Failed to refresh stack: %v\n", err)
    os.Exit(1)
}

// wire up our update to stream progress to stdout
stdoutStreamer := optup.ProgressStreams(os.Stdout)

// run the update to deploy our EC2 Cluster
_, err = createdStack.Up(ctx, stdoutStreamer)
if err != nil {
    fmt.Printf("Failed to update stack: %v\n\n", err)
    os.Exit(1)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;I hope this Blog post is helpful to you and shows you how Auto PKG works with Go in Pulumi.&lt;/p&gt;

&lt;p&gt;Till next time :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Head First Go Book Review</title>
      <dc:creator>Jason Content</dc:creator>
      <pubDate>Sat, 21 Oct 2023 20:04:40 +0000</pubDate>
      <link>https://forem.com/jasric89/head-first-go-book-review-22e9</link>
      <guid>https://forem.com/jasric89/head-first-go-book-review-22e9</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7wAycOmE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otzrctbtamwf1nhdkmlr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7wAycOmE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otzrctbtamwf1nhdkmlr.jpg" alt="Image description" width="800" height="801"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have recently just finished reading Head First Go by Jay McGavren. When I opened the book, I thought this was a very different way of learning to program from a book.&lt;/p&gt;

&lt;p&gt;It's a bit like a school workbook; you have exercises where you need to use a pen or pencil for, and then there are also times when you need to build what they are writing about. My Github Repo for this book is here: &lt;a href="https://github.com/jasric89/headfirstgo"&gt;https://github.com/jasric89/headfirstgo&lt;/a&gt; if you wish to take a look.&lt;/p&gt;

&lt;p&gt;I have read quite a few books on Go, and honestly, I get halfway through the book and give up. The reason for this was I had spent the last two years trying to grasp the language, and no book for me personally could do that until I got to this book.&lt;/p&gt;

&lt;p&gt;The way Jay McGavren writes is like someone guiding you through the language. Each chapter creates an interesting problem that naturally, your brain kicks in and goes ohh, I wonder how this problem is solved. It's sort of like a Sherlock Holmes episode of who committed the crime. Or an NCIS series, depending on your taste. (If you haven't already figured it out, I like crime series.)&lt;/p&gt;

&lt;p&gt;Then there are the exercises where you have to get a pen out and fill in the blanks, which, in an interesting way, really starts cementing what you are learning. For me, when this first started happening. I had to take a moment with a smile and realised oh wait, this is actually going in.&lt;/p&gt;

&lt;p&gt;As I said, there are tons of books I have read on Go where you read the chapter, then the Author leaves you an exercise to complete, which really most of that exercise has no real bearing on what you were reading, then you spend most of your time on Stack Overflow trying to figure out how to complete the exercise. Then you get disheartened about why you can't do it; the book gets tossed to the side and back to the drawing board. Not with this book.&lt;/p&gt;

&lt;p&gt;The only major drawback with this book is that it was written before modules came out, so essentially, it uses GoPath for a lot of the book. I did head to their website, &lt;a href="https://headfirstgo.com/"&gt;https://headfirstgo.com/&lt;/a&gt;, when I first noticed this problem, but I could not see any notice about the issue or any other versions of the chapters released with the new Go Pkg framework.&lt;/p&gt;

&lt;p&gt;At first, this was frustrating because I could not get my head around how Packages work in Go. Yet I was already so impressed with the book that I spent two to three hours more learning how to write packages in Go. I have also written a blog post on it here: &lt;a href="https://theclouddude.co.uk/mastering-writing-packages-with-go-when-working-with-the-azure-sdk"&gt;https://theclouddude.co.uk/mastering-writing-packages-with-go-when-working-with-the-azure-sdk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This may save you the hours of figuring out how packages work if you give that post a read and then apply it to the parts of the book where GoPath is being spoken about. It could be enough, though, to put the person off buying this book because, essentially, that part is so out of date.&lt;/p&gt;

&lt;p&gt;There is also another area where things are out of date, and that is with the ioutil package that's been depreciated in Go. It's now the io or os package. Information about this is given here: &lt;a href="https://pkg.go.dev/io/ioutil"&gt;https://pkg.go.dev/io/ioutil&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other than those things, this book, in my opinion, is very good. It teaches you the Language in a way you remember; you are not lost and disheartened when something does not work, which I think when starting out with a Language is one of the most important aspects.&lt;/p&gt;

&lt;p&gt;I would say this book is very good for Beginners to Go; if you have come from another Language, you may start skimming through the book, which in turn would actually hinder you from finishing it properly and getting the programs working. I guess I am saying that this book is for you if you have basic or no programming knowledge. If you are a seasoned Developer in another language, perhaps choose another book. I think this one is very good for seasoned developers. &lt;a href="https://www.gopl.io/"&gt;https://www.gopl.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall, I enjoyed reading this book; it was easy reading, did not send me to sleep, made me confident in the Go Language, and learned a lot. I highly recommend this book if you can see past its outdated issues.&lt;/p&gt;

</description>
      <category>go</category>
      <category>books</category>
      <category>developer</category>
      <category>learning</category>
    </item>
    <item>
      <title>Mastering writing Packages with Go when working with the Azure SDK.</title>
      <dc:creator>Jason Content</dc:creator>
      <pubDate>Mon, 18 Sep 2023 09:10:43 +0000</pubDate>
      <link>https://forem.com/jasric89/mastering-writing-packages-with-go-when-working-with-the-azure-sdk-5fha</link>
      <guid>https://forem.com/jasric89/mastering-writing-packages-with-go-when-working-with-the-azure-sdk-5fha</guid>
      <description>&lt;p&gt;&lt;em&gt;Please note I am Dyslexic, so if things don't make grammatical sense, please do not hold it against me. Be Kind&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;** For more posts like this one, visit my site: &lt;a href="https://theclouddude.co.uk/"&gt;https://theclouddude.co.uk/&lt;/a&gt; **&lt;/p&gt;

&lt;p&gt;One thing you need to know well when working with the Go Azure SDK is how packages work with the removal of GOPATH.&lt;/p&gt;

&lt;p&gt;This has taken me some time to get my head around.&lt;/p&gt;

&lt;p&gt;Since Go 1.11 there was a fundamental change with the Go Compiler. GOPATH was gone; no more building your Go Projects in your machine's src/bin folder. Now, you can build your Go projects anywhere. Yay, I hear many of you say. Well, at first, when learning Go, I thought that made sense until I started messing about with making and importing packages.&lt;/p&gt;

&lt;p&gt;The way making packages work is you must run &lt;code&gt;go mod init module_name&lt;/code&gt; first. It's not such a big deal and easy enough to master.&lt;/p&gt;

&lt;p&gt;Until you get to the point of wanting to import that package.&lt;/p&gt;

&lt;p&gt;Take this example, for instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┣ 📂pkgwork
┃ ┗ 📂mypkg
┃   ┗ 📜mypkg.go
┣ 📜go.mod
┗ 📜packagework.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I have made a package called &lt;code&gt;packagework&lt;/code&gt;, which just sits outside the folder &lt;code&gt;pkgwork&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I tried to import the package like this in my code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "fmt"
    mypkg "pkgwork/mypkg"
)

func main() {
    var value mypkg.MyInterface
    value = mypkg.MyType(5)
    value.MethodWithoutParameters()
    value.MethodWithParamaters(127.3)
    fmt.Println(value.MethodWithReturnValue())

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my head, that makes sense because I'm thinking Well, the root of my package is &lt;code&gt;pkgwork/mypkg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Yet, when I run &lt;code&gt;go run packagework.go&lt;/code&gt; I get the following error:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;package pkgwork/mypkg is not in GOROOT (/usr/local/go/src/pkgwork/mypkg)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This means the GO compiler went to look for my package within the module and expected my module name, &lt;code&gt;pkgwork&lt;/code&gt;, to be in the pkgwork folder. Yet my &lt;code&gt;module_name&lt;/code&gt; in this instance, is not called &lt;code&gt;pkgwork&lt;/code&gt; it's called &lt;code&gt;packagework&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once Go realised that the module was not in this destination, what it did was It went outside the &lt;code&gt;pkgwork&lt;/code&gt; folder. Therefore, its next course of action is to go and check the main Go libraries in the GOROOT folder. When all places are searched and not found, it comes back with the error we just saw.&lt;/p&gt;

&lt;p&gt;Essentially, my understanding of importing packages was wrong, and I have seen many others think the same way. To import your package, it's not &lt;code&gt;package_path/package_name&lt;/code&gt; it's &lt;code&gt;module_name/package_name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To fix this and to make this easier to understand in my own head. I put my main go file called &lt;code&gt;packagework.go&lt;/code&gt; in a folder called &lt;code&gt;pkgwork&lt;/code&gt;, so my folder structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┗ 📂pkgwork
  ┣ 📂mypkg
  ┃ ┗ 📜mypkg.go
  ┣ 📜go.mod
  ┗ 📜packagework.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I re-modulated my project with the &lt;code&gt;module_name&lt;/code&gt; of &lt;code&gt;packagework&lt;/code&gt; and then imported that into my &lt;code&gt;packagework.go&lt;/code&gt; file like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mypkg "packagework/mypkg"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So essentially, my main Go file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "fmt"
    mypkg "packagework/mypkg"
)

func main() {
    var value mypkg.MyInterface
    value = mypkg.MyType(5)
    value.MethodWithoutParameters()
    value.MethodWithParamaters(127.3)
    fmt.Println(value.MethodWithReturnValue())

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This then allowed Go to find the package I was referring to.&lt;/p&gt;

&lt;p&gt;One other thing I have picked up whilst learning Go is labelling my packages when importing them; if you noticed, I import like this: &lt;code&gt;mypkg "packagework/mypkg"&lt;/code&gt; which is &lt;code&gt;label module_name/pkgname&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I find it is then much easier to call to my package when coding in main; it's also easier to read. Just a little hint.&lt;/p&gt;

&lt;p&gt;Now, you have come down this far, and I have not mentioned the Azure SDK once, so rightly you are thinking, why is this important to the Azure SDK?&lt;/p&gt;

&lt;p&gt;Well, what I have found when working with the Azure SDK is everything is split up into packages, which are essentially modules with pkg names. Let's look at the main one you will find yourself working with a lot:&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;azidentity&lt;/code&gt; package &lt;a href="https://tinyurl.com/azidentity"&gt;https://tinyurl.com/azidentity&lt;/a&gt;. This package is used to authenticate into Azure. To work with it, you have to import it into your project like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"github.com/Azure/azure-sdk-for-go/sdk/azidentity"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And you reference it like this: without a label, you take the end part of the full path, so in this instance, &lt;code&gt;azindentity&lt;/code&gt; Here is a small example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;azCLI, err := azidentity.NewAzureCLICredential(nil)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("Authenticated!!")
        armcompute.NewVirtualMachinesClient(subscriptionID, azCLI, nil)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On another note, the Github.com URLS are no different to just calling your module_name something like &lt;code&gt;packagework&lt;/code&gt;. Essentially, Go does not see them as URLS when you are first making your packages, so If I call my module github.com/jasric89/packagework, then Go will see that as a name. Only when it goes into GitHub does it become something to download as a package for another developer using the &lt;code&gt;go get -u URL to package_name.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I hope this helps someone. It took me a while to get my head around this subject.&lt;/p&gt;

&lt;p&gt;** For more posts like this one, visit my site: &lt;a href="https://theclouddude.co.uk/"&gt;https://theclouddude.co.uk/&lt;/a&gt; **&lt;/p&gt;

</description>
      <category>azure</category>
      <category>go</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Azure VM Extensions, written in Go</title>
      <dc:creator>Jason Content</dc:creator>
      <pubDate>Mon, 18 Sep 2023 09:00:09 +0000</pubDate>
      <link>https://forem.com/jasric89/azure-vm-extensions-written-in-go-4nhh</link>
      <guid>https://forem.com/jasric89/azure-vm-extensions-written-in-go-4nhh</guid>
      <description>&lt;p&gt;&lt;em&gt;Please note I am Dyslexic, so if things don't make grammatical sense, please do not hold it against me. Be Kind&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visit My Site for more Posts like this one: &lt;a href="https://theclouddude.co.uk/"&gt;https://theclouddude.co.uk/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is a project I was recently involved in where they wanted a VM in Azure running IIS. If you don't wish to do this with Go Lang, you can follow this Blog post by Facundo Gauna. He explains how to do this with PowerShell: &lt;a href="https://gaunacode.com/install-iis-on-azure-vm-using-terraform"&gt;https://gaunacode.com/install-iis-on-azure-vm-using-terraform&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was also a learning exercise to see how Go interacts with the Azure SDK; I've been learning and using Go for around a year, and the saying. "Go is easy to learn but hard to master." It could not be more true. So please, if you don't wish to go on this learning exercise and are thinking well, this can just be done with Powershell, then be my guest in following that link ;). Now that we have housekeeping out of the way let me explain what I did.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I made the VM using Terraform as this blog post is not about Terraform, but Go I will leave this link here to make a VM using Terraform.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/windows%5C_virtual%5C_machine.html"&gt;https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/windows\_virtual\_machine.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Journey begins at the &lt;code&gt;NewVirtualMachineExtensionsClient&lt;/code&gt; function from the package armcompute (links at the bottom of the post for more information): &lt;code&gt;NewVirtualMachineExtensionsClient(AzureSubscriptionID, cred, nil)&lt;/code&gt; This function tells the Azure API that I am about to create a new Virtual Machine Extension function and here are my Subscription ID and Credentials. This function does return an error, the way I handled the error is with the following piece of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if err != nil {
        log.Fatalf("failed to create client: %v", err)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;simple&lt;/strong&gt; error handling. The Program will terminate and tell me the whole error right from the SDK. (&lt;strong&gt;One Tip:&lt;/strong&gt; When working with the Azure SDK, don't write custom error messages; the SDK will forward detailed error messages for you, making researching these errors much easier.)&lt;/p&gt;

&lt;p&gt;Here's the whole function in its entirety:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client, err := armcompute.NewVirtualMachineExtensionsClient(AzureSubscriptionID, cred, nil)
    if err != nil {
        log.Fatalf("failed to create client: %v", err)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first major hurdle I found in this journey is the following function, &lt;code&gt;BeginCreateOrUpdate&lt;/code&gt;, actually updating the VM extension. The docs give an example of how this function is used, but when you fill the fields in with pointers, you will run into all sorts of errors with the SDK talking to Microsoft Azure.&lt;/p&gt;

&lt;p&gt;What I found was that actually, it was not the best example of working with an extensions script; in researching for a few hours, I came across this Microsoft Article here: &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-tutorial-deploy-vm-extensions#:%7E:text=Save%20As.-,Edit%20the%20template,-Add%20a%20virtual"&gt;https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-tutorial-deploy-vm-extensions#:~:text=Save%20As.-,Edit%20the%20template,-Add%20a%20virtual&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then edited my calls to the struct. &lt;code&gt;VirtualMachineExtensionsClient&lt;/code&gt;, which I believe all these fields were in the example JSON file given in the MS document. Here is an example of that JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "type": "Microsoft.Compute/virtualMachines/extensions",
  "apiVersion": "2021-04-01",
  "name": "[format('{0}/{1}', variables('vmName'), 'InstallWebServer')]",
  "location": "[parameters('location')]",
  "dependsOn": [
    "[format('Microsoft.Compute/virtualMachines/{0}',variables('vmName'))]"
  ],
  "properties": {
    "publisher": "Microsoft.Compute",
    "type": "CustomScriptExtension",
    "typeHandlerVersion": "1.7",
    "autoUpgradeMinorVersion": true,
    "settings": {
      "fileUris": [
        "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/tutorial-vm-extension/installWebServer.ps1"
      ],
      "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -File installWebServer.ps1"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from my code, I am now writing the following to the struct &lt;code&gt;VirtualMachineExtensionProperties&lt;/code&gt; through a pointer using the &lt;code&gt;armcompute&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Properties: &amp;amp;armcompute.VirtualMachineExtensionProperties{
            Type:                    to.Ptr("CustomScriptExtension"),
            AutoUpgradeMinorVersion: to.Ptr(true),
            ForceUpdateTag:          to.Ptr("False"),
            InstanceView: &amp;amp;armcompute.VirtualMachineExtensionInstanceView{
                Name: to.Ptr("InstallWebServer"),
                Type: to.Ptr("CustomScriptExtension"),
            },
            Publisher: to.Ptr("Microsoft.Compute"),
            Settings: map[string]interface{}{
                "commandToExecute": "powershell -ExecutionPolicy Unrestricted Install-WindowsFeature -Name Web-Server -IncludeAllSubFeature -IncludeManagementTools",
            },
            SuppressFailures:   to.Ptr(true),
            TypeHandlerVersion: to.Ptr("1.7"),
        },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I have found to be quite hard when working with Azure SDK is that there are many nested structs within structs. The struct fields themselves are based on JSON files. I do believe these are essentially just ARM templates being called. It's actually very hard to work out in the first instance how to change a property of a struct in the Azure SDK.&lt;/p&gt;

&lt;p&gt;For example, the current function we are working on requires us first to pass in: &lt;code&gt;armcompute.VirtualMachineExtension&lt;/code&gt;, which in itself is a struct from the package &lt;code&gt;armcompute&lt;/code&gt;. Then because the struct we actually want to edit is &lt;code&gt;VirtualMachineExtensionProperties&lt;/code&gt;, you have to do some crazy pointer work to edit those fields like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type:                    to.Ptr("CustomScriptExtension"),
            AutoUpgradeMinorVersion: to.Ptr(true),
            ForceUpdateTag:          to.Ptr("False"),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Miki Tebeka (Who has written the book Go Brain Teasers") showed me another way to do this, which I may try another time. For now, this works.&lt;/p&gt;

&lt;p&gt;Essentially to make a VM Extension using Go you are left with the following Code. I hope I have explained various parts of this code well enough for you to understand what is happening.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "context"
    "fmt"
    "log"

    "github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
    "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4"
)

func main() {
    AzureSubscriptionID := "VALUE"
    myVM := "vm-onpremvm"
    myVMLocation := "North Europe"
    resourceGroup := "RG-JC-Sandbox"
    myVMExtension := "IIS"
    // Authenticate To Azure

    azCLI, err := azidentity.NewAzureCLICredential(nil)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("Authenticated!!")
    }
    cred := azCLI
    ctx := context.Background()
    // Edit the VM Extension
    client, err := armcompute.NewVirtualMachineExtensionsClient(AzureSubscriptionID, cred, nil)
    if err != nil {
        log.Fatalf("failed to create client: %v", err)
    }
    poller, err := client.BeginCreateOrUpdate(ctx, resourceGroup, myVM, myVMExtension, armcompute.VirtualMachineExtension{
        Location: to.Ptr(myVMLocation),
        Tags: map[string]*string{
            "IISExtension": to.Ptr("IISExtension"),
        },
        Properties: &amp;amp;armcompute.VirtualMachineExtensionProperties{
            Type:                    to.Ptr("CustomScriptExtension"),
            AutoUpgradeMinorVersion: to.Ptr(true),
            ForceUpdateTag:          to.Ptr("False"),
            InstanceView: &amp;amp;armcompute.VirtualMachineExtensionInstanceView{
                Name: to.Ptr("InstallWebServer"),
                Type: to.Ptr("CustomScriptExtension"),
            },
            Publisher: to.Ptr("Microsoft.Compute"),
            Settings: map[string]interface{}{
                "commandToExecute": "powershell -ExecutionPolicy Unrestricted Install-WindowsFeature -Name Web-Server -IncludeAllSubFeature -IncludeManagementTools",
            },
            SuppressFailures:   to.Ptr(true),
            TypeHandlerVersion: to.Ptr("1.7"),
        },
    },
        nil)
    if err != nil {
        log.Fatalf("failed to finish the request: %v", err)
    }
    res, err := poller.PollUntilDone(ctx, nil)
    if err != nil {
        log.Fatalf("failed to pull the result: %v", err)
    }
    fmt.Printf("%v", res)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope this has helped someone; this was my first ever blog post about Go; I hope you have enjoyed it. I am no Go expert. If you wish to get in touch with me I can be reached here at &lt;a href="mailto:jason@theclouddude.co.uk"&gt;jason@theclouddude.co.uk&lt;/a&gt; Id like to say thank you for reading this far. :)&lt;/p&gt;

&lt;p&gt;I would also like to thank the &lt;strong&gt;Gophers&lt;/strong&gt; at the Gopher Slack found here: gophers.slack.com for helping me hugely with this project and pointing me in the right direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visit My Site for more Posts like this one: &lt;a href="https://theclouddude.co.uk/"&gt;https://theclouddude.co.uk/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Resources I used were:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-tutorial-deploy-vm-extensions"&gt;https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-tutorial-deploy-vm-extensions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4#VirtualMachineExtensionsClient"&gt;https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4#VirtualMachineExtensionsClient&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4#ClientFactory.NewVirtualMachineExtensionsClient:%7E:text=README,ClientFactory)%20NewVirtualMachineExtensionsClient%20%C2%B6"&gt;https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4#ClientFactory.NewVirtualMachineExtensionsClient:~:text=README,ClientFactory)%20NewVirtualMachineExtensionsClient%20%C2%B6&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>azure</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
