DevOps
Publishing .NET NuGet Packages to AWS CodeArtifact Using GitHub Actions (OIDC)
3 min read

Overview
Publishing internal .NET libraries securely is a common challenge for DevOps teams—especially when multiple services share common packages. In this post, we’ll build a secretless pipeline to publish NuGet packages to AWS CodeArtifact using GitHub Actions + OIDC, so no static AWS keys are stored in GitHub.
What You’ll Build
A two-workflow setup:
- CI workflow: build + pack on branch/PR (no publish)
- Publish workflow: publish on release tag (or manual trigger)
This gives you governance (tag-driven releases) and clean separation between build and release.
Architecture Flow
- Developer pushes code to GitHub
- CI builds and generates
.nupkg - Publish pipeline assumes AWS role via OIDC
- Package is pushed to CodeArtifact
- Apps restore from the private feed
Prerequisites
- AWS CodeArtifact domain + repository created
- IAM role configured for GitHub OIDC
- .NET library project (csproj) ready to pack
CI Workflow (Build & Pack)
Create: .github/workflows/ci-nuget.yml
yaml1name: ci-nuget 2on: 3 push: 4 branches: [ develop ] 5 pull_request: 6 branches: [ develop, main ] 7 workflow_dispatch: {} 8 9jobs: 10 build-pack: 11 runs-on: ubuntu-latest 12 steps: 13 - uses: actions/checkout@v4 14 15 - uses: actions/setup-dotnet@v4 16 with: 17 dotnet-version: '8.0.x' 18 cache: true 19 cache-dependency-path: '**/packages.lock.json' 20 21 - name: Restore (locked) 22 run: dotnet restore src/My.Library/My.Library.csproj --locked-mode 23 24 - name: Build (Release) 25 run: dotnet build src/My.Library/My.Library.csproj -c Release --no-restore 26 27 - name: Pack (no publish) 28 run: dotnet pack src/My.Library/My.Library.csproj -c Release -o artifacts --no-build 29 30 - uses: actions/upload-artifact@v4 31 with: 32 name: nupkg 33 path: artifacts/*.nupkg
Publish Workflow (Tag Release) Create: .github/workflows/publish-nuget.yml
yaml1name: publish-nuget 2on: 3 push: 4 tags: ['v*'] # e.g. v1.0.0 or v0.1.0-beta.1 5 workflow_dispatch: 6 inputs: 7 version: 8 description: "Package version (e.g., 0.1.0-beta.6) if running manually" 9 required: false 10 11jobs: 12 publish: 13 runs-on: ubuntu-latest 14 permissions: 15 id-token: write 16 contents: read 17 18 env: 19 AWS_REGION: us-east-1 20 CA_DOMAIN: my-domain 21 CA_REPOSITORY: nuget-internal 22 AWS_ACCOUNT_ID: "123456789012" 23 PROJECT: src/My.Library/My.Library.csproj 24 25 steps: 26 - uses: actions/checkout@v4 27 28 - name: Set VERSION 29 run: | 30 if [ -n "${{ github.event.inputs.version }}" ]; then 31 echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV 32 else 33 echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV 34 fi 35 36 - uses: actions/setup-dotnet@v4 37 with: 38 dotnet-version: '8.0.x' 39 40 - name: Configure AWS credentials (OIDC) 41 uses: aws-actions/configure-aws-credentials@v4 42 with: 43 role-to-assume: arn:aws:iam::123456789012:role/GitHubActions-CodeArtifact-Publish 44 aws-region: ${{ env.AWS_REGION }} 45 46 - name: CodeArtifact login for NuGet 47 run: | 48 aws codeartifact login \ 49 --tool dotnet \ 50 --repository $CA_REPOSITORY \ 51 --domain $CA_DOMAIN \ 52 --domain-owner $AWS_ACCOUNT_ID \ 53 --region $AWS_REGION 54 55 - name: Restore/build/pack 56 run: | 57 dotnet restore $PROJECT --locked-mode --source https://api.nuget.org/v3/index.json 58 dotnet build $PROJECT -c Release --no-restore 59 dotnet pack $PROJECT -c Release -o artifacts --no-build -p:PackageVersion=${VERSION} 60 61 - name: Publish to CodeArtifact 62 run: | 63 dotnet nuget push artifacts/*.nupkg \ 64 --source "$CA_DOMAIN/$CA_REPOSITORY" \ 65 --api-key aws \ 66 --skip-duplicate
Tags
#dotnet#nuget#aws#codeartifact#github-actions#oidc#cicd#devops