Skip to content

Commit

Permalink
Rename Lab017
Browse files Browse the repository at this point in the history
  • Loading branch information
chance2021 committed Jan 7, 2025
1 parent d2a5331 commit 14d4bee
Show file tree
Hide file tree
Showing 35 changed files with 200 additions and 107 deletions.
File renamed without changes.
2 changes: 0 additions & 2 deletions 020-GithubActionJavaDockerfileCI/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
Here’s the document in proper Markdown format:

# Continuous Integration (CI) Pipeline for Dockerized Java Application

This project demonstrates a **Continuous Integration (CI)** pipeline using **GitHub Actions** to build and push a Docker image for a Java application. The pipeline performs the following tasks:
Expand Down
305 changes: 200 additions & 105 deletions 021-GithubActionDockerImageFunctionCD/README.md
Original file line number Diff line number Diff line change
@@ -1,133 +1,228 @@
Here’s the document in proper Markdown format:
# Deploy Docker Image to Azure Function Using GitHub Actions

# Continuous Integration (CI) Pipeline for Dockerized Java Application

This project demonstrates a **Continuous Integration (CI)** pipeline using **GitHub Actions** to build and push a Docker image for a Java application. The pipeline performs the following tasks:

1. **Build the Java application** using Maven.
2. **Build a Docker image** from the application.
3. **Tag the Docker image** with the current Git commit hash.
4. **Scan the Docker image** for security vulnerability.
5. **Push the Docker image** to **GitHub Container Registry (GHCR)**.

---
This guide provides a step-by-step process to set up a GitHub Actions pipeline for deploying a Docker image to an Azure Function. The pipeline uses an Azure Service Principal with a federated credential for secure, passwordless authentication via OpenID Connect (OIDC).

**Table of Contents**

- [Continuous Integration (CI) Pipeline for Dockerized Java Application](#continuous-integration-ci-pipeline-for-dockerized-java-application)
- [Deploy Docker Image to Azure Function Using GitHub Actions](#deploy-docker-image-to-azure-function-using-github-actions)
- [Overview](#overview)
- [Prerequisites](#prerequisites)
- [Project Structure](#project-structure)
- [Pipeline Workflow](#pipeline-workflow)
- [Steps to Configure](#steps-to-configure)
- [1. Generate a GitHub Personal Access Token (PAT)](#1-generate-a-github-personal-access-token-pat)
- [2. Create an Azure Function](#2-create-an-azure-function)
- [3. Create a Service Principal](#3-create-a-service-principal)
- [4. Add a Federated Credential](#4-add-a-federated-credential)
- [5. Configure GitHub Secrets](#5-configure-github-secrets)
- [6. Set Up GitHub Actions Workflow](#6-set-up-github-actions-workflow)
- [Workflow Details](#workflow-details)
- [Trigger Conditions](#trigger-conditions)
- [Pipeline Steps](#pipeline-steps)
- [Setup and Usage](#setup-and-usage)
- [Step 1: Configure Secrets](#step-1-configure-secrets)
- [Step 2: Define the Workflow](#step-2-define-the-workflow)
- [Step 3: Add the Dockerfile](#step-3-add-the-dockerfile)
- [Step 4: Push Changes to GitHub](#step-4-push-changes-to-github)
- [Step 5: Verify the Pipeline](#step-5-verify-the-pipeline)
- [Step 6: Validate the Output](#step-6-validate-the-output)
- [Next Steps](#next-steps)

---
- [Steps](#steps)
- [Testing and Validation](#testing-and-validation)
- [Best Practices](#best-practices)
- [Post Deployment](#post-deployment)

## Prerequisites

Before setting up the pipeline, ensure the following:
## Overview

1. A **GitHub repository** containing:
- A Java project with a `pom.xml`.
- A `Dockerfile` in the folder `020-GithubActionJavaDockerfileCI`.
This solution uses the following components:
1. **Azure Function**: A serverless compute resource where the Docker image is deployed.
2. **Service Principal (SPN)**: Provides Azure resource access with minimal permissions.
3. **Federated Credential**: Enables secure authentication from GitHub Actions to Azure without storing long-lived secrets.
4. **GitHub Actions**: Automates the deployment process triggered by changes in the codebase.

2. **GitHub Secrets**:
- **`YOUR_GITHUB_TOKEN`**: A personal access token (PAT) with the required permissions to push images to **GitHub Container Registry (GHCR)**. Please ensure the token has **write:packages** and **read:packages** permissions for GitHub Container Registry.

3. **Docker CLI** (Optional): To verify the Docker image locally.
## Prerequisites
1. GitHub Repository with a Docker image and workflow configuration.
2. Access to an Azure Subscription and Resource Group.
3. Basic knowledge of GitHub Actions and Azure Function deployment.

---
## Steps to Configure
### 1. Generate a GitHub Personal Access Token (PAT)
To enable an Azure Function to pull a container image from a private GitHub Container Registry (GHCR), you need to create a Personal Access Token (PAT) with the appropriate permissions and configure your Azure Function to use these credentials.

## Project Structure
Steps to achieve this:
1. Navigate to your GitHub account settings.
2. Go to Developer settings > Personal access tokens > Tokens (classic).
3. Click on Generate new token.
4. Provide a note for the token, set an expiration date, and select the necessary scopes.
5. For accessing GHCR, ensure you select the **read:packages** scope to grant read access to your packages.
6. Generate the token and copy it securely; you won’t be able to view it again.

The project should follow this structure:
### 2. Create an Azure Function

```plaintext
devopsdaydayup/
├── .github/
│ └── workflows/
│ └── ci-docker-build.yml # GitHub Actions CI workflow
├── 020-GithubActionJavaDockerfileCICD/
│ ├── Dockerfile # Dockerfile for the
│ ├── src/ # Java source files
│ └── pom.xml # Maven configuration
Create an Azure Function that uses a custom container image.
```
TENANT_ID="<Your Azure Tenant ID>"
SUBSCRIPTION_ID="<Your Azure Subscription ID>"
RESOURCE_GROUP_NAME="<Your Azure Resource Group Name>"
REGION="eastus" # Change to your preferred region
PLAN_NAME="devOpsLab21ServicePlan"
FUNCTION_APP_NAME="currentTime"
SPN_NAME="<Your Azure Service Princile Name>"
STORAGE_ACCOUNT_NAME="lab21$(head /dev/urandom | tr -dc 'a-z0-9'| head -c 18)"
GITHUB_USERNAME="<Your GitHub Username>"
GITHUB_REPO_NAME="<Your GitHub Repository Name>"
GITHUB_BRANCH_NAME="<Your GitHub Branch Name>"
GITHUB_IMAGE_TAG="<Your GitHub Image Tag>" # You can find the image under Package tab under your main GitHub portal page.
GITHUB_PAT="<THE PAT You Got from Step 1>"
CONTAINER_IMAGE="ghcr.io/$GITHUB_USERNAME/$GITHUB_REPO_NAME:$GITHUB_IMAGE_TAG" # Replace with your Docker image
# Create a Resource Group
az group create --name $RESOURCE_GROUP_NAME --location $REGION
# Create a Storage Account
az storage account create --name $STORAGE_ACCOUNT_NAME --location $REGION --resource-group $RESOURCE_GROUP_NAME --sku Standard_LRS
# Create a Service Plan
az appservice plan create --name $PLAN_NAME --resource-group $RESOURCE_GROUP_NAME --is-linux --sku B1
# Create a Function
az functionapp create \
--resource-group $RESOURCE_GROUP_NAME \
--plan $PLAN_NAME \
--runtime custom \
--functions-version 4 \
--name $FUNCTION_APP_NAME \
--storage-account $STORAGE_ACCOUNT_NAME
az functionapp config appsettings set --name $FUNCTION_APP_NAME \
--resource-group $RESOURCE_GROUP_NAME \
--settings \
"DOCKER_REGISTRY_SERVER_URL=https://ghcr.io" \
"DOCKER_REGISTRY_SERVER_USERNAME=$GITHUB_USERNAME"\
"DOCKER_REGISTRY_SERVER_PASSWORD=$GITHUB_PAT"
az functionapp config container set --name $FUNCTION_APP_NAME \
--resource-group $RESOURCE_GROUP_NAME \
--image $CONTAINER_IMAGE
```

## Pipeline Workflow

### Trigger Conditions

The pipeline triggers when:
- Changes are pushed to any branch.
- Files in the `020-GithubActionJavaDockerfileCI` folder are modified.
- The workflow can be manually dispatched as well.

## Pipeline Steps
1. Checkout Code: Fetch the latest code from the repository.
2. Set Up Docker: Configure Docker for building and pushing images.
3. Get Git Commit Hash: Dynamically tag the Docker image with the current Git commit hash.
4. Build Docker Image: Build the image from the Dockerfile.
5. Scan Docker Image: Scan the image for the security vulnerability.
6. Push Image to GHCR: Push the tagged Docker image to GitHub Container Registry.

## Setup and Usage
### 3. Create a Service Principal

### Step 1: Configure Secrets
1. Go to your GitHub repository.
2. Navigate to Settings > Secrets and Variables > Actions.
3. Add the following secret:
- YOUR_GITHUB_TOKEN: A personal access token (PAT) with write:packages and read:packages permissions for GitHub Container Registry.
- To generate a PAT:
- Go to your GitHub profile.
- Navigate to Settings > Developer Settings > Personal Access Tokens.
- Generate a new token with the necessary permissions and copy it.
Create a Service Principal with the required roles.
```
az ad sp create-for-rbac --name "$SPN_NAME" --role Reader --scopes /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP_NAME
### Step 2: Define the Workflow
```
Output Example:
```
{
"appId": "12345678-abcd-1234-efgh-56789ijklmn0",
"password": "your-client-secret",
"tenant": "12345678-1234-5678-9101-112131415abc"
}
```
Save the App ID (Client ID), Tenant ID, and Subscription ID for later use. Create a role assignement
```
CLIENT_ID=$(az ad sp list --display-name $SPN_NAME --query "[0].appId" --output tsv)
Create a workflow file at `.github/workflows/ci-docker-build.yml`
az role assignment create \
--assignee $CLIENT_ID \
--role Contributor \
--scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP_NAME/providers/Microsoft.Web/sites/$FUNCTION_APP_NAME
```
Then create a federation credentials for the SPN.
```
az role assignment list --assignee $CLIENT_ID --all --output table
```

Note: You can copy this file from `020-GithubActionJavaDockerfileCI` folder to `.github/workflows` folder.
### 4. Add a Federated Credential
Configure the Service Principal to use a federated credential for GitHub Actions.
```
az ad app federated-credential create \
--id $CLIENT_ID \
--parameters "{
\"name\": \"GitHubActionsFederatedCredentialForTestBranch\",
\"issuer\": \"https://token.actions.githubusercontent.com\",
\"subject\": \"repo:$GITHUB_OWNER/$GITHUB_REPO_NAME:ref:refs/heads/$GITHUB_BRANCH_NAME\",
\"audiences\": [\"api://AzureADTokenExchange\"]
}"
az ad app federated-credential create \
--id $CLIENT_ID \
--parameters "{
\"name\": \"GitHubActionsFederatedCredentialForMainBranch\",
\"issuer\": \"https://token.actions.githubusercontent.com\",
\"subject\": \"repo:$GITHUB_OWNER/$GITHUB_REPO_NAME:ref:refs/heads/main\",
\"audiences\": [\"api://AzureADTokenExchange\"]
}"
```

### Step 3: Add the Dockerfile
### 5. Configure GitHub Secrets

Ensure the Dockerfile `020-GithubActionJavaDockerfileCI` folder exists.
Add the following variables to your GitHub repository as **secrets**:
- **AZURE_CLIENT_ID**: App ID of the Service Principal.
- **AZURE_TENANT_ID**: Tenant ID from the SPN creation step.
- **AZURE_SUBSCRIPTION_ID**: Subscription ID of your Azure account.

### Step 4: Push Changes to GitHub
Add the following variables to your GitHub repository as **variables**:
- **RESOURCE_GROUP_NAME**: Azure resource group name which the function app is created in
- **FUNCTION_APP_NAME**: Azure Function app name
- **IMAGE_NAME**: The image address without tag
- **IMAGE_TAG**: The image tag

Push the code to your repository:
> Note: Regarding how to set up secrets/variables, please refer to the following links
> - Setup Secrets: https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository
> - Setup Variables: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#creating-configuration-variables-for-a-repository
### 6. Set Up GitHub Actions Workflow

Create a workflow file `.github/workflows/deploy-to-azure.yml`:
```
git add .
git commit -m "Add CI pipeline for Docker build"
git push
name: Deploy Docker Image to Azure Function
on:
push:
paths:
- '021-GithubActionDockerImageFunctionCD/**'
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
deploy:
name: Deploy to Azure Function
runs-on: ubuntu-latest
steps:
# Step 1: Log in to Azure
- name: Log in to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Step 2: Deploy the Docker image to Azure Function
- name: Deploy to Azure Function
run: |
az functionapp config container set \
--name ${{ vars.FUNCTION_APP_NAME }} \
--resource-group ${{ vars.RESOURCE_GROUP_NAME }} \
--docker-custom-image-name ${{ vars.IMAGE_NAME }}:${{ vars.IMAGE_TAG }}
```
Note: You have to make some change under `020-GithubActionJavaDockerfileCI` folder in order to trigger the GitHub action pipeline.

### Step 5: Verify the Pipeline
1. Navigate to the Actions tab in your GitHub repository.
2. Select the workflow CI Pipeline - Build and Push Docker Image.
3. Monitor the pipeline’s execution and ensure all steps pass successfully.

### Step 6: Validate the Output
1. After the pipeline runs, the Docker image will be available in GitHub Container Registry:
```
ghcr.io/<repository_owner>/current-time-app:<commit-hash>
```
## Workflow Details

2. Verify the image locally:
```
docker pull ghcr.io/<repository_owner>/current-time-app:<commit-hash>
docker run --rm ghcr.io/<repository_owner>/current-time-app:<commit-hash>
### Trigger Conditions
- The workflow triggers on:
- Pushes to files in the 021-GithubActionDockerImageFunctionCD directory.
- Manual dispatch.

### Steps
1. Login to Azure:
Uses OIDC-based authentication to securely log in to Azure.
2. Deploy Docker Image:
Configures the Azure Function to use the specified Docker image.

### Testing and Validation
1. Push changes to the `021-GithubActionDockerImageFunctionCD` directory.
2. Navigate to the Actions tab in GitHub and monitor the workflow execution.
3. Validate that the Azure Function is updated with the new image by checking its Deployment Center in the Azure Portal.

## Best Practices
1. Limit Permissions: Assign the Service Principal the least privilege required for the task.

## Post Deployment
Remove all resources by deleting the entire resource group once your lab is completed to avoid any further cost.
```

## Next Steps
- Extend the pipeline to include a CD (Continuous Deployment) process.
- Deploy the built Docker image to a cloud platform, such as Azure Functions.
az group delete --name $Resource_Group_Name> --yes --no-wait
```

0 comments on commit 14d4bee

Please sign in to comment.