-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d2a5331
commit 14d4bee
Showing
35 changed files
with
200 additions
and
107 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |