<?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>DEV Community: Nathan Bridgewater</title>
    <description>The latest articles on DEV Community by Nathan Bridgewater (@nathanb).</description>
    <link>https://dev.to/nathanb</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%2F1213758%2F05d3b46d-6e49-4351-8b11-c05c92c1c22c.jpg</url>
      <title>DEV Community: Nathan Bridgewater</title>
      <link>https://dev.to/nathanb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nathanb"/>
    <language>en</language>
    <item>
      <title>Setting Up a Basic Prometheus Deployment with CDK8s</title>
      <dc:creator>Nathan Bridgewater</dc:creator>
      <pubDate>Mon, 19 Feb 2024 09:34:30 +0000</pubDate>
      <link>https://dev.to/nathanb/setting-up-a-basic-prometheus-deployment-with-cdk8s-1hbo</link>
      <guid>https://dev.to/nathanb/setting-up-a-basic-prometheus-deployment-with-cdk8s-1hbo</guid>
      <description>&lt;p&gt;I was genuinely surprised by the scarcity of CDK8s examples available. Consequently, I decided to pitch in and share what I've learned by presenting how I setup a basic Prometheus deployment using CDK8s.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CDK8s i hear you ask?
&lt;/h2&gt;

&lt;p&gt;CDK8s is a development framework tailored for Kubernetes. It enables users to define Kubernetes manifests using a library of constructs in their preferred programming language. In essence, CDK8s is akin to AWS CDK but designed for Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we dive into the tutorial, make sure you have the following prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of Kubernetes and Prometheus&lt;/li&gt;
&lt;li&gt;CDK8s CLI installed&lt;/li&gt;
&lt;li&gt;kubectl installed&lt;/li&gt;
&lt;li&gt;A Running Kubernetes cluster (Minikube works well)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Initialise your CDK8s application
&lt;/h2&gt;

&lt;p&gt;Begin by setting up your CDK8s app using the init command. While I'll be using TypeScript, there are alternative options available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cdk8s init typescript-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a folder structure which looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-cdk8s-app/
├── imports/
├── dist/
├── cdk8s.yaml
├── package.json
├── tsconfig.json
└── main.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key file here is main.ts, this contains our CDK8s chart. Charts are just a container for your CDK8s code, they are synthesised into a single K8s manifest.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Add imports for necessary constructs
&lt;/h2&gt;

&lt;p&gt;Import the required constructs at the top of your main.ts file to abstract elements like configmaps, volumes, deployments, etc.&lt;/p&gt;

&lt;p&gt;main.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { 
  ClusterRole, 
  ApiResource, 
  NonApiResource, 
  ServiceAccount, 
  Deployment, 
  Volume,
  ConfigMap } 
from 'cdk8s-plus-25'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Create a namespace for Prometheus and configure the chart
&lt;/h2&gt;

&lt;p&gt;Run the following command in your terminal to create a namespace for Prometheus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create namespace monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify your main.ts file to ensure all API objects use this namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new MyChart(app, 'CDK8s-Prometheus', { namespace: 'monitoring' })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Configure RBAC
&lt;/h2&gt;

&lt;p&gt;For Prometheus to access the Kubernetes API, create a service account, cluster role, and role binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const serviceAccount = new ServiceAccount(this, 'service-account')
    const clusterRole = new ClusterRole(this, 'cluster-role')

    clusterRole.allowRead(ApiResource.NODES) 
    clusterRole.allowRead(ApiResource.SERVICES)
    clusterRole.allowRead(ApiResource.ENDPOINTS)
    clusterRole.allowRead(ApiResource.PODS)
    clusterRole.allowRead(ApiResource.INGRESSES)
    clusterRole.allowGet(NonApiResource.of('/metrics'))


    clusterRole.bindInNamespace('monitoring', serviceAccount)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Create a file for Prometheus configuration
&lt;/h2&gt;

&lt;p&gt;Add a prometheus.yml file to the root directory of your project with the following content. This is a simple setup where Prometheus scrape its own metrics&lt;/p&gt;

&lt;p&gt;prometheus.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global:
  scrape_interval: 5s
scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets:
        - localhost:9090
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Create volumes for storage and config
&lt;/h2&gt;

&lt;p&gt;Define two volumes to be mounted in the pods later—one for Prometheus config and another for storing metrics data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const configMap = new ConfigMap(this, 'configmap')
    configMap.addFile(join(__dirname + '/prometheus.yml'))

    const configVolume = Volume.fromConfigMap(this, 'config-volume', configMap)
    const dataVolume = Volume.fromEmptyDir(this, 'data-volume', 'data')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Create our deployment
&lt;/h2&gt;

&lt;p&gt;Specify the deployment details for Prometheus in your main.ts file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const deployment = new Deployment(this, 'deployment', {

      containers: [ 
        {
          name: 'prometheus',
          image: 'prom/prometheus',
          args: ['--config.file=/etc/prometheus/prometheus.yml'],
          portNumber: 9090,
          securityContext: {ensureNonRoot: false}

        }
      ]
    })

    deployment.containers[0].mount('/etc/prometheus/', configVolume)
    deployment.containers[0].mount('/data/', dataVolume)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Deploy your application
&lt;/h2&gt;

&lt;p&gt;With everything set up, deploy your Prometheus instance:&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f dist/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test your Prometheus deployment by accessing the UI using port forwarding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl port-forward &amp;lt;POD NAME&amp;gt; 8080:9090 -n=monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access the UI via &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You've successfully set up a basic Prometheus deployment using CDK8s. From here, you can explore further customisation and optimisation based on your specific needs.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Say Goodbye to Hardcoded AWS Creds in GitHub Actions with OIDC Magic ⭐</title>
      <dc:creator>Nathan Bridgewater</dc:creator>
      <pubDate>Wed, 27 Dec 2023 12:12:08 +0000</pubDate>
      <link>https://dev.to/nathanb/say-goodbye-to-hardcoded-aws-creds-in-github-actions-with-oidc-magic-ndm</link>
      <guid>https://dev.to/nathanb/say-goodbye-to-hardcoded-aws-creds-in-github-actions-with-oidc-magic-ndm</guid>
      <description>&lt;p&gt;I've been tinkering with GitHub actions over the last few months, often times with some kind of AWS interaction in my workflow. As it turns out, there are simple and less simple methods of authenticating your workflows with AWS. Let's explore how to use GitHub's OIDC provider to free yourself from hardcoded AWS credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardcoded access keys
&lt;/h2&gt;

&lt;p&gt;Lets be real, we've all hardcoded access keys before. For beginners and people who are just looking to get something running, hardcoded credentials are great, it's the most straightforward way to authenticate your workflow with AWS.&lt;/p&gt;

&lt;p&gt;Here's a typical example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this does not align with best practices, duplicating and storing long lived credentials for your AWS account is not the most secure way to authenticate your workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter OpenID Connect (OIDC)
&lt;/h2&gt;

&lt;p&gt;OIDC, or OpenID Connect, is an authentication protocol that lets a third party (in our case, AWS) verify a user's identity through a JSON Web Token (JWT). With OIDC, authentication is outsourced to the OIDC provider. Once authenticated, the user can obtain a JWT from the identity provider, using it to authenticate with the third party.&lt;/p&gt;

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

&lt;p&gt;In our scenario, GitHub acts as an OIDC provider for workflows. This means our workflow can request a JWT from GitHub and then use it to assume an AWS role by obtaining temporary credentials from AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use GitHub's OIDC provider in your workflows
&lt;/h2&gt;

&lt;p&gt;Let's walk through a simple example that builds a Docker image and pushes it to ECR using GitHub's OIDC provider. If you want to follow along, I've prepared a NodeJS application and Docker file in this following repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Nathan-Bridgewater/GitHub-AWS-OIDC.git" rel="noopener noreferrer"&gt;https://github.com/Nathan-Bridgewater/GitHub-AWS-OIDC.git&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create an identity provider in AWS
&lt;/h3&gt;

&lt;p&gt;Firstly, we add GitHub as an identity provider. This configures AWS to accept OIDC tokens from a workflow and return temporary access credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com  \
--client-id-list sts.amazonaws.com \
--thumbprint-list 1b511abead59c6ce207077c0bf0e0043b1382612 \
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create the trust policy
&lt;/h3&gt;

&lt;p&gt;Craft a JSON file containing a trust policy for an AWS role, specifying which GitHub workflows can assume our role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;trust-policy.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": "sts:AssumeRoleWithWebIdentity",
          "Principal": {
              "Federated": "arn:aws:iam::&amp;lt;ACCOUNT NUM&amp;gt;:oidc-provider/token.actions.githubusercontent.com"
          },
          "Condition": {
              "StringEquals": {
                  "token.actions.githubusercontent.com:aud": [
                      "sts.amazonaws.com"
                  ]
              },
              "StringLike": {
                  "token.actions.githubusercontent.com:sub": [
                      "repo:Nathan-Bridgewater/GitHub-AWS-OIDC:*"
                  ]
              }
          }
      }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Create the role
&lt;/h3&gt;

&lt;p&gt;We need to establish a role for our federated workflow to assume, and attach a policy allowing our workflow to push images to ECR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws iam create-role \
--role-name github-workflow-role \
--assume-role-policy-document file://trust-policy.json 

aws iam attach-role-policy \
--role-name github-workflow-role \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Create an ECR repository
&lt;/h3&gt;

&lt;p&gt;Set up an ECR repository to push Docker images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ecr create-repository \
--repository-name nodejs-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Create the GitHub workflow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;push-docker-actions.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: push-docker-ecr-actions
run-name: ${{ github.actor }} is running the push-docker-ecr workflow
on:
  push:
    branches:
      - 'main'

permissions:
  id-token: write
  contents: read  

jobs:
  build-image-push-to-ecr:
    name: Build docker image and push to ecr
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume:  arn:aws:iam::&amp;lt;ACCOUNT NUM&amp;gt;:role/github-workflow-role
          aws-region: us-east-1
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: Build, tag and push image to ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: nodejs-images
          IMAGE_TAG: latest
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Test it out
&lt;/h3&gt;

&lt;p&gt;Once added, your workflows should now authenticate with AWS without hardcoded credentials. Freedom awaits!&lt;/p&gt;

</description>
      <category>github</category>
      <category>aws</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
