[identity] AKS live tests (#28939)
### Packages impacted by this PR @azure/identity ### Issues associated with this PR Contributes to #26434 ### Describe the problem that is addressed by this PR Adds AKS managed identity integration tests
This commit is contained in:
Родитель
aca940f3c1
Коммит
4e7bca982c
|
@ -188,5 +188,5 @@ sdk/template/template-dpg/src/src
|
|||
.tshy-build-tmp
|
||||
|
||||
# sshkey
|
||||
sdk/**/sshkey
|
||||
sdk/**/sshkey.pub
|
||||
sdk/**/sshKey
|
||||
sdk/**/sshKey.pub
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
src/**/*.js
|
||||
integration/AzureFunctions/app.zip
|
||||
integration/AzureWebApps/.azure/
|
||||
integration/kubeconfig.yaml
|
||||
!assets/fake-cert.pem
|
||||
!assets/fake-cert-password.pem
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
|
||||
import { BlobServiceClient } from "@azure/storage-blob";
|
||||
import { ManagedIdentityCredential } from "@azure/identity";
|
||||
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
|
||||
|
||||
export async function authenticateStorage(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
|
||||
export async function authenticateStorage(
|
||||
request: HttpRequest,
|
||||
context: InvocationContext,
|
||||
): Promise<HttpResponseInit> {
|
||||
try {
|
||||
context.log('Http function was triggered.');
|
||||
context.log("Http function was triggered.");
|
||||
//parse the request body
|
||||
await authToStorageHelper(context);
|
||||
|
||||
|
@ -20,26 +22,26 @@ export async function authenticateStorage(request: HttpRequest, context: Invocat
|
|||
body: error,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
app.http('authenticateStorage', {
|
||||
methods: ['GET', 'POST'],
|
||||
app.http("authenticateStorage", {
|
||||
methods: ["GET", "POST"],
|
||||
authLevel: "anonymous",
|
||||
handler: authenticateStorage
|
||||
handler: authenticateStorage,
|
||||
});
|
||||
|
||||
async function authToStorageHelper(context: InvocationContext): Promise<void> {
|
||||
// This will use the system managed identity
|
||||
const credential1 = new ManagedIdentityCredential();
|
||||
|
||||
const clientId = process.env.IDENTITY_USER_DEFINED_IDENTITY_CLIENT_ID!;
|
||||
const clientId = process.env.IDENTITY_USER_DEFINED_CLIENT_ID!;
|
||||
const account1 = process.env.IDENTITY_STORAGE_NAME_1;
|
||||
const account2 = process.env.IDENTITY_STORAGE_NAME_2;
|
||||
|
||||
const credential2 = new ManagedIdentityCredential({ "clientId": clientId });
|
||||
const credential2 = new ManagedIdentityCredential({ clientId });
|
||||
const client1 = new BlobServiceClient(`https://${account1}.blob.core.windows.net`, credential1);
|
||||
const client2 = new BlobServiceClient(`https://${account2}.blob.core.windows.net`, credential2);
|
||||
context.log("Getting containers for storage account client: system managed identity")
|
||||
context.log("Getting containers for storage account client: system managed identity");
|
||||
let iter = client1.listContainers();
|
||||
let i = 1;
|
||||
context.log("Client with system assigned identity");
|
||||
|
@ -49,7 +51,7 @@ async function authToStorageHelper(context: InvocationContext): Promise<void> {
|
|||
containerItem = await iter.next();
|
||||
}
|
||||
|
||||
context.log("Getting properties for storage account client: user assigned managed identity")
|
||||
context.log("Getting properties for storage account client: user assigned managed identity");
|
||||
iter = client2.listContainers();
|
||||
context.log("Client with user assigned identity");
|
||||
containerItem = await iter.next();
|
||||
|
@ -57,5 +59,4 @@ async function authToStorageHelper(context: InvocationContext): Promise<void> {
|
|||
context.log(`Container ${i++}: ${containerItem.value.name}`);
|
||||
containerItem = await iter.next();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ ARG NODE_VERSION=20
|
|||
ARG REGISTRY=""
|
||||
|
||||
FROM ${REGISTRY}node:${NODE_VERSION}-alpine as repo
|
||||
RUN apk --no-cache add git
|
||||
RUN git clone https://github.com/azure/azure-sdk-for-js --single-branch --branch main --depth 1 /azure-sdk-for-js
|
||||
WORKDIR /app
|
||||
|
||||
WORKDIR /azure-sdk-for-js/sdk/identity/identity/test/integration/AzureKubernetes
|
||||
RUN npm install
|
||||
RUN npm install -g typescript
|
||||
RUN tsc -p .
|
||||
CMD ["node", "index"]
|
||||
COPY . .
|
||||
|
||||
# Install the latest nightly build of identity
|
||||
RUN npm install --no-package-lock
|
||||
|
||||
# Wait for the test to `exec` into the container and run the script
|
||||
CMD ["sleep", "infinity"]
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
const { BlobServiceClient } = require("@azure/storage-blob");
|
||||
const { ManagedIdentityCredential, WorkloadIdentityCredential } = require("@azure/identity");
|
||||
|
||||
async function main() {
|
||||
const storageAccount = process.env.IDENTITY_STORAGE_NAME_2;
|
||||
if (!storageAccount) {
|
||||
throw new Error("Missing IDENTITY_STORAGE_NAME_2 env var");
|
||||
}
|
||||
|
||||
const clientId = process.env.IDENTITY_USER_DEFINED_CLIENT_ID;
|
||||
if (!clientId) {
|
||||
throw new Error("Missing IDENTITY_USER_DEFINED_CLIENT_ID env var");
|
||||
}
|
||||
|
||||
const blobUrl = `https://${storageAccount}.blob.core.windows.net`;
|
||||
|
||||
try {
|
||||
const blobServiceClient = new BlobServiceClient(
|
||||
blobUrl,
|
||||
new ManagedIdentityCredential({
|
||||
clientId,
|
||||
}),
|
||||
);
|
||||
await blobServiceClient.getProperties();
|
||||
|
||||
// The test looks for this line in the output
|
||||
console.log("ManagedIdentity: Successfully authenticated with storage");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
try {
|
||||
const blobServiceClient = new BlobServiceClient(
|
||||
blobUrl,
|
||||
new WorkloadIdentityCredential({
|
||||
clientId,
|
||||
}),
|
||||
);
|
||||
await blobServiceClient.getProperties();
|
||||
|
||||
// The test looks for this line in the output
|
||||
console.log("WorkloadIdentity: Successfully authenticated with storage");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
main().then(console.log).catch(console.error);
|
|
@ -1,22 +1,13 @@
|
|||
{
|
||||
"name": "@azure-samples/azure-kubernetes-test",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "ts-node src/index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@azure/identity": "^4.0.0",
|
||||
"@azure/storage-blob": "^12.17.0",
|
||||
"tslib": "^1.10.0",
|
||||
"ts-node": "10.9.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
"name": "@azure-samples/azure-kubernetes-test",
|
||||
"version": "1.0.0",
|
||||
"description": "A simple node JS script that can be used to test MSI on Kubernetes",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@azure/identity": "dev",
|
||||
"@azure/storage-blob": "^12.17.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ManagedIdentityCredential } from "@azure/identity";
|
||||
import { BlobServiceClient } from "@azure/storage-blob";
|
||||
import * as dotenv from "dotenv";
|
||||
// Initialize the environment
|
||||
dotenv.config();
|
||||
|
||||
async function main(): Promise<void> {
|
||||
let systemSuccessMessage = "";
|
||||
try{
|
||||
const account1 = process.env.IDENTITY_STORAGE_NAME_1;
|
||||
const account2 = process.env.IDENTITY_STORAGE_NAME_2;
|
||||
const credentialSystemAssigned = new ManagedIdentityCredential();
|
||||
const credentialUserAssigned = new ManagedIdentityCredential({clientId: process.env.IDENTITY_USER_DEFINED_IDENTITY_CLIENT_ID})
|
||||
const client1 = new BlobServiceClient(`https://${account1}.blob.core.windows.net`, credentialSystemAssigned);
|
||||
const client2 = new BlobServiceClient(`https://${account2}.blob.core.windows.net`, credentialUserAssigned);
|
||||
let iter = client1.listContainers();
|
||||
|
||||
let i = 1;
|
||||
console.log("Client with system assigned identity");
|
||||
let containerItem = await iter.next();
|
||||
while (!containerItem.done) {
|
||||
console.log(`Container ${i++}: ${containerItem.value.name}`);
|
||||
containerItem = await iter.next();
|
||||
}
|
||||
systemSuccessMessage = "Successfully acquired token with system-assigned ManagedIdentityCredential"
|
||||
console.log("Client with user assigned identity")
|
||||
iter = client2.listContainers();
|
||||
i = 1;
|
||||
containerItem = await iter.next();
|
||||
while (!containerItem.done) {
|
||||
console.log(`Container ${i++}: ${containerItem.value.name}`);
|
||||
containerItem = await iter.next();
|
||||
}
|
||||
console.log("Successfully acquired tokens with async ManagedIdentityCredential")
|
||||
}
|
||||
catch(e){
|
||||
console.error(`${e} \n ${systemSuccessMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.log("error code: ", err.code);
|
||||
console.log("error message: ", err.message);
|
||||
console.log("error stack: ", err.stack);
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "."
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@azure/identity": "^4.0.0",
|
||||
"@azure/identity": "dev",
|
||||
"@azure/storage-blob": "^12.17.0",
|
||||
"express": "^4.18.2",
|
||||
"tslib": "^1.10.0"
|
||||
|
|
|
@ -10,15 +10,18 @@ dotenv.config();
|
|||
const app = express();
|
||||
|
||||
app.get("/", (req: express.Request, res: express.Response) => {
|
||||
res.send("Ok")
|
||||
})
|
||||
res.send("Ok");
|
||||
});
|
||||
|
||||
app.get("/sync", async (req: express.Request, res: express.Response) => {
|
||||
let systemSuccessMessage = "";
|
||||
try {
|
||||
const account1 = process.env.IDENTITY_STORAGE_NAME_1;
|
||||
const credentialSystemAssigned = new ManagedIdentityCredential();
|
||||
const client1 = new BlobServiceClient(`https://${account1}.blob.core.windows.net`, credentialSystemAssigned);
|
||||
const client1 = new BlobServiceClient(
|
||||
`https://${account1}.blob.core.windows.net`,
|
||||
credentialSystemAssigned,
|
||||
);
|
||||
let iter = client1.listContainers();
|
||||
let i = 0;
|
||||
console.log("Client with system assigned identity");
|
||||
|
@ -29,31 +32,35 @@ app.get("/sync", async (req: express.Request, res: express.Response) => {
|
|||
}
|
||||
console.log("Client with system assigned identity");
|
||||
console.log("Properties of the 1st client =", iter);
|
||||
systemSuccessMessage = "Successfully acquired token with system-assigned ManagedIdentityCredential"
|
||||
systemSuccessMessage =
|
||||
"Successfully acquired token with system-assigned ManagedIdentityCredential";
|
||||
console.log(systemSuccessMessage);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
try {
|
||||
const account2 = process.env.IDENTITY_STORAGE_NAME_2;
|
||||
const credentialUserAssigned = new ManagedIdentityCredential({ clientId: process.env.IDENTITY_USER_DEFINED_IDENTITY_CLIENT_ID })
|
||||
const client2 = new BlobServiceClient(`https://${account2}.blob.core.windows.net`, credentialUserAssigned);
|
||||
const credentialUserAssigned = new ManagedIdentityCredential({
|
||||
clientId: process.env.IDENTITY_USER_DEFINED_CLIENT_ID,
|
||||
});
|
||||
const client2 = new BlobServiceClient(
|
||||
`https://${account2}.blob.core.windows.net`,
|
||||
credentialUserAssigned,
|
||||
);
|
||||
let iter = client2.listContainers();
|
||||
let i = 0;
|
||||
console.log("Client with user assigned identity")
|
||||
console.log("Client with user assigned identity");
|
||||
let containerItem = await iter.next();
|
||||
while (!containerItem.done) {
|
||||
console.log(`Container ${i++}: ${containerItem.value.name}`);
|
||||
containerItem = await iter.next();
|
||||
}
|
||||
res.status(200).send("Successfully acquired tokens with async ManagedIdentityCredential")
|
||||
}
|
||||
catch (e) {
|
||||
res.status(200).send("Successfully acquired tokens with async ManagedIdentityCredential");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
res.status(500).send(`${e} \n ${systemSuccessMessage}`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
app.listen(8080, () => {
|
||||
console.log(`Authorization code redirect server listening on port 8080`);
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
"format": "dev-tool run vendored prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"samples-dev/**/*.ts\" \"*.{js,json}\"",
|
||||
"check-format": "dev-tool run vendored prettier --list-different --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"samples-dev/**/*.ts\" \"*.{js,json}\"",
|
||||
"integration-test:browser": "echo skipped",
|
||||
"integration-test:node": "dev-tool run test:node-ts-input -- --timeout 180000 'test/public/node/*.spec.ts' 'test/internal/node/*.spec.ts' 'test/integration/*.spec.ts'",
|
||||
"integration-test:node": "dev-tool run test:node-ts-input -- --timeout 180000 'test/public/node/*.spec.ts' 'test/internal/node/*.spec.ts' 'test/integration/**/*.spec.ts'",
|
||||
"integration-test": "npm run integration-test:node && npm run integration-test:browser",
|
||||
"lint:fix": "eslint package.json api-extractor.json src test --ext .ts --fix --fix-type [problem,suggestion]",
|
||||
"lint": "eslint package.json api-extractor.json src test --ext .ts",
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { assert } from "chai";
|
||||
import { execSync } from "child_process";
|
||||
import { isLiveMode } from "@azure-tools/test-recorder";
|
||||
|
||||
describe("Azure Kubernetes Integration test", function () {
|
||||
let podOutput: string;
|
||||
before(async function () {
|
||||
if (!isLiveMode()) {
|
||||
this.skip();
|
||||
}
|
||||
const resourceGroup = requireEnvVar("IDENTITY_RESOURCE_GROUP");
|
||||
const aksClusterName = requireEnvVar("IDENTITY_AKS_CLUSTER_NAME");
|
||||
const subscriptionId = requireEnvVar("IDENTITY_SUBSCRIPTION_ID");
|
||||
const podName = requireEnvVar("IDENTITY_AKS_POD_NAME");
|
||||
|
||||
if (process.env.IDENTITY_CLIENT_SECRET) {
|
||||
// Log in as service principal in CI
|
||||
const clientId = requireEnvVar("IDENTITY_CLIENT_ID");
|
||||
const clientSecret = requireEnvVar("IDENTITY_CLIENT_SECRET");
|
||||
const tenantId = requireEnvVar("IDENTITY_TENANT_ID");
|
||||
runCommand(
|
||||
"az",
|
||||
`login --service-principal -u ${clientId} -p ${clientSecret} --tenant ${tenantId}`,
|
||||
);
|
||||
}
|
||||
|
||||
runCommand("az", `account set --subscription ${subscriptionId}`);
|
||||
runCommand(
|
||||
"az",
|
||||
`aks get-credentials --resource-group ${resourceGroup} --name ${aksClusterName}`,
|
||||
);
|
||||
const pods = runCommand("kubectl", `get pods -o jsonpath='{.items[0].metadata.name}'`);
|
||||
assert.include(pods, podName);
|
||||
|
||||
podOutput = runCommand("kubectl", `exec ${podName} -- node /app/index.js`);
|
||||
});
|
||||
|
||||
it("can authenticate using managed identity", async function () {
|
||||
if (!isLiveMode()) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
assert.include(
|
||||
podOutput,
|
||||
"ManagedIdentity: Successfully authenticated with storage",
|
||||
`Expected ${podOutput} to include a ManagedIdentity success message`,
|
||||
);
|
||||
});
|
||||
|
||||
it("can authenticate using workload identity", async function () {
|
||||
if (!isLiveMode()) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
assert.include(
|
||||
podOutput,
|
||||
"WorkloadIdentity: Successfully authenticated with storage",
|
||||
`Expected ${podOutput} to include a WorkloadIdentity success message`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function runCommand(command: string, args: string = ""): string {
|
||||
return execSync(`${command} ${args}`).toString().trim();
|
||||
}
|
||||
|
||||
function requireEnvVar(name: string): string {
|
||||
const value = process.env[name];
|
||||
if (!value) {
|
||||
throw new Error(`Required env var ${name} is not set`);
|
||||
}
|
||||
return value;
|
||||
}
|
|
@ -8,14 +8,18 @@ param (
|
|||
$RemainingArguments,
|
||||
|
||||
[Parameter()]
|
||||
[hashtable] $DeploymentOutputs
|
||||
[hashtable] $DeploymentOutputs,
|
||||
|
||||
[Parameter()]
|
||||
[switch] $CI = ($null -ne $env:SYSTEM_TEAMPROJECTID)
|
||||
)
|
||||
|
||||
# If not Linux, skip this script.
|
||||
# if ($isLinux -ne "Linux") {
|
||||
# Write-Host "Skipping post-deployment because not running on Linux."
|
||||
# return
|
||||
# }
|
||||
$MIClientId = $DeploymentOutputs['IDENTITY_USER_DEFINED_CLIENT_ID']
|
||||
$MIName = $DeploymentOutputs['IDENTITY_USER_DEFINED_IDENTITY_NAME']
|
||||
$saAccountName = 'workload-identity-sa'
|
||||
$podName = $DeploymentOutputs['IDENTITY_AKS_POD_NAME']
|
||||
$storageName2 = $DeploymentOutputs['IDENTITY_STORAGE_NAME_2']
|
||||
$userDefinedClientId = $DeploymentOutputs['IDENTITY_USER_DEFINED_CLIENT_ID']
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$PSNativeCommandUseErrorActionPreference = $true
|
||||
|
@ -25,8 +29,11 @@ $workingFolder = $webappRoot;
|
|||
|
||||
Write-Host "Working directory: $workingFolder"
|
||||
|
||||
az login --service-principal -u $DeploymentOutputs['IDENTITY_CLIENT_ID'] -p $DeploymentOutputs['IDENTITY_CLIENT_SECRET'] --tenant $DeploymentOutputs['IDENTITY_TENANT_ID']
|
||||
az account set --subscription $DeploymentOutputs['IDENTITY_SUBSCRIPTION_ID']
|
||||
if ($CI) {
|
||||
Write-Host "Logging in to service principal"
|
||||
az login --service-principal -u $DeploymentOutputs['IDENTITY_CLIENT_ID'] -p $DeploymentOutputs['IDENTITY_CLIENT_SECRET'] --tenant $DeploymentOutputs['IDENTITY_TENANT_ID']
|
||||
az account set --subscription $DeploymentOutputs['IDENTITY_SUBSCRIPTION_ID']
|
||||
}
|
||||
|
||||
# Azure Functions app deployment
|
||||
Write-Host "Building the code for functions app"
|
||||
|
@ -38,102 +45,76 @@ Write-Host "starting azure functions deployment"
|
|||
Compress-Archive -Path "$workingFolder/AzureFunctions/RunTest/*" -DestinationPath "$workingFolder/AzureFunctions/app.zip" -Force
|
||||
az functionapp deployment source config-zip -g $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] -n $DeploymentOutputs['IDENTITY_FUNCTION_NAME'] --src "$workingFolder/AzureFunctions/app.zip"
|
||||
Remove-Item -Force "$workingFolder/AzureFunctions/app.zip"
|
||||
|
||||
Write-Host "Deployed function app"
|
||||
# $image = "$loginServer/identity-functions-test-image"
|
||||
# docker build --no-cache -t $image "$workingFolder/AzureFunctions"
|
||||
# docker push $image
|
||||
|
||||
# az functionapp config container set -g $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] -n $DeploymentOutputs['IDENTITY_FUNCTION_NAME'] -i $image -r $loginServer -p $(az acr credential show -n $DeploymentOutputs['IDENTITY_ACR_NAME'] --query "passwords[0].value" -o tsv) -u $(az acr credential show -n $DeploymentOutputs['IDENTITY_ACR_NAME'] --query username -o tsv)
|
||||
|
||||
# Azure Web Apps app deployment
|
||||
# Push-Location "$webappRoot/AzureWebApps"
|
||||
# npm install
|
||||
# npm run build
|
||||
# Compress-Archive -Path "$workingFolder/AzureWebApps/*" -DestinationPath "$workingFolder/AzureWebApps/app.zip" -Force
|
||||
# az webapp deploy --resource-group $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --name $DeploymentOutputs['IDENTITY_WEBAPP_NAME'] --src-path "$workingFolder/AzureWebApps/app.zip" --async true
|
||||
# Remove-Item -Force "$workingFolder/AzureWebApps/app.zip"
|
||||
# Pop-Location
|
||||
|
||||
Write-Host "Deplying Identity Web App"
|
||||
Push-Location "$webappRoot/AzureWebApps"
|
||||
npm install
|
||||
npm run build
|
||||
az webapp up --resource-group $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --name $DeploymentOutputs['IDENTITY_WEBAPP_NAME'] --plan $DeploymentOutputs['IDENTITY_WEBAPP_PLAN'] --runtime NODE:18-lts
|
||||
Pop-Location
|
||||
Write-Host "Deployed Identity Web App"
|
||||
|
||||
Write-Host "Deployed webapp"
|
||||
Write-Host "Sleeping for a bit to ensure logs is ready."
|
||||
Start-Sleep -Seconds 300
|
||||
Write-Host "Deploying Identity Docker image to ACR"
|
||||
az acr login -n $DeploymentOutputs['IDENTITY_ACR_NAME']
|
||||
$loginServer = az acr show -n $DeploymentOutputs['IDENTITY_ACR_NAME'] --query loginServer -o tsv
|
||||
$image = "$loginServer/identity-aks-test-image"
|
||||
docker build --no-cache -t $image "$workingFolder/AzureKubernetes"
|
||||
docker push $image
|
||||
Write-Host "Deployed image to ACR"
|
||||
|
||||
# Write-Host "Sleeping for a bit to ensure container registry is ready."
|
||||
# Start-Sleep -Seconds 20
|
||||
# Write-Host "trying to login to acr"
|
||||
# az acr login -n $DeploymentOutputs['IDENTITY_ACR_NAME']
|
||||
# $loginServer = az acr show -n $DeploymentOutputs['IDENTITY_ACR_NAME'] --query loginServer -o tsv
|
||||
Write-Host "Configuring kubernetes to use our image"
|
||||
az aks update -n $DeploymentOutputs['IDENTITY_AKS_CLUSTER_NAME'] -g $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --attach-acr $DeploymentOutputs['IDENTITY_ACR_NAME']
|
||||
|
||||
# # Azure Kubernetes Service deployment
|
||||
# $image = "$loginServer/identity-aks-test-image"
|
||||
# docker build --no-cache -t $image "$workingFolder/AzureKubernetes"
|
||||
# docker push $image
|
||||
# Get the aks cluster credentials
|
||||
Write-Host "Getting AKS credentials"
|
||||
az aks get-credentials --resource-group $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --name $DeploymentOutputs['IDENTITY_AKS_CLUSTER_NAME']
|
||||
|
||||
# Attach the ACR to the AKS cluster
|
||||
# Write-Host "Attaching ACR to AKS cluster"
|
||||
# az aks update -n $DeploymentOutputs['IDENTITY_AKS_CLUSTER_NAME'] -g $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --attach-acr $DeploymentOutputs['IDENTITY_ACR_NAME']
|
||||
#Get the aks cluster OIDC issuer
|
||||
Write-Host "Getting AKS OIDC issuer"
|
||||
$AKS_OIDC_ISSUER = az aks show -n $DeploymentOutputs['IDENTITY_AKS_CLUSTER_NAME'] -g $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --query "oidcIssuerProfile.issuerUrl" -otsv
|
||||
|
||||
# $MIClientId = $DeploymentOutputs['IDENTITY_USER_DEFINED_IDENTITY_CLIENT_ID']
|
||||
# $MIName = $DeploymentOutputs['IDENTITY_USER_DEFINED_IDENTITY_NAME']
|
||||
# $SaAccountName = 'workload-identity-sa'
|
||||
# $PodName = $DeploymentOutputs['IDENTITY_AKS_POD_NAME']
|
||||
# $storageName = $DeploymentOutputs['IDENTITY_STORAGE_NAME_2']
|
||||
|
||||
# # Get the aks cluster credentials
|
||||
# Write-Host "Getting AKS credentials"
|
||||
# az aks get-credentials --resource-group $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --name $DeploymentOutputs['IDENTITY_AKS_CLUSTER_NAME']
|
||||
# Create the federated identity
|
||||
Write-Host "Creating federated identity"
|
||||
az identity federated-credential create --name $MIName --identity-name $MIName --resource-group $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --issuer $AKS_OIDC_ISSUER --subject system:serviceaccount:default:workload-identity-sa
|
||||
|
||||
# #Get the aks cluster OIDC issuer
|
||||
# Write-Host "Getting AKS OIDC issuer"
|
||||
# $AKS_OIDC_ISSUER = az aks show -n $DeploymentOutputs['IDENTITY_AKS_CLUSTER_NAME'] -g $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --query "oidcIssuerProfile.issuerUrl" -otsv
|
||||
# Build the kubernetes deployment yaml
|
||||
$kubeConfig = @"
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
annotations:
|
||||
azure.workload.identity/client-id: $MIClientId
|
||||
name: $saAccountName
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: $podName
|
||||
namespace: default
|
||||
labels:
|
||||
azure.workload.identity/use: "true"
|
||||
spec:
|
||||
serviceAccountName: $saAccountName
|
||||
containers:
|
||||
- name: $podName
|
||||
image: $image
|
||||
env:
|
||||
- name: IDENTITY_STORAGE_NAME_2
|
||||
value: "$storageName2"
|
||||
- name: IDENTITY_USER_DEFINED_CLIENT_ID
|
||||
value: "$userDefinedClientId"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
"@
|
||||
|
||||
# # Create the federated identity
|
||||
# Write-Host "Creating federated identity"
|
||||
# az identity federated-credential create --name $MIName --identity-name $MIName --resource-group $DeploymentOutputs['IDENTITY_RESOURCE_GROUP'] --issuer $AKS_OIDC_ISSUER --subject system:serviceaccount:default:workload-identity-sa
|
||||
Write-Host $kubeConfig
|
||||
Set-Content -Path "$workingFolder/kubeconfig.yaml" -Value $kubeConfig
|
||||
|
||||
# # Build the kubernetes deployment yaml
|
||||
# $kubeConfig = @"
|
||||
# apiVersion: v1
|
||||
# kind: ServiceAccount
|
||||
# metadata:
|
||||
# annotations:
|
||||
# azure.workload.identity/client-id: $MIClientId
|
||||
# name: $SaAccountName
|
||||
# namespace: default
|
||||
# ---
|
||||
# apiVersion: v1
|
||||
# kind: Pod
|
||||
# metadata:
|
||||
# name: $PodName
|
||||
# namespace: default
|
||||
# labels:
|
||||
# azure.workload.identity/use: "true"
|
||||
# spec:
|
||||
# serviceAccountName: $SaAccountName
|
||||
# containers:
|
||||
# - name: $PodName
|
||||
# image: $image
|
||||
# env:
|
||||
# - name: IDENTITY_STORAGE_NAME
|
||||
# value: "$StorageName"
|
||||
# ports:
|
||||
# - containerPort: 80
|
||||
# nodeSelector:
|
||||
# kubernetes.io/os: linux
|
||||
# "@
|
||||
|
||||
# Set-Content -Path "$workingFolder/kubeconfig.yaml" -Value $kubeConfig
|
||||
# Write-Host "Created kubeconfig.yaml with contents:"
|
||||
# Write-Host $kubeConfig
|
||||
|
||||
# # Apply the config
|
||||
# kubectl apply -f "$workingFolder/kubeconfig.yaml" --overwrite=true
|
||||
# Write-Host "Applied kubeconfig.yaml"
|
||||
# az logout
|
||||
# Apply the config
|
||||
kubectl apply -f "$workingFolder/kubeconfig.yaml" --overwrite=true
|
||||
Write-Host "Applied kubeconfig.yaml"
|
||||
|
|
|
@ -8,24 +8,6 @@ param (
|
|||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
$RemainingArguments,
|
||||
|
||||
[Parameter()]
|
||||
[string] $Location = '',
|
||||
|
||||
[Parameter()]
|
||||
[ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
|
||||
[string] $TestApplicationId,
|
||||
|
||||
[Parameter()]
|
||||
[string] $TestApplicationSecret,
|
||||
|
||||
[Parameter()]
|
||||
[ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
|
||||
[string] $SubscriptionId,
|
||||
|
||||
[Parameter(ParameterSetName = 'Provisioner', Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $TenantId,
|
||||
|
||||
[Parameter()]
|
||||
[switch] $CI = ($null -ne $env:SYSTEM_TEAMPROJECTID)
|
||||
|
||||
|
@ -33,27 +15,20 @@ param (
|
|||
|
||||
Import-Module -Name $PSScriptRoot/../../eng/common/scripts/X509Certificate2 -Verbose
|
||||
|
||||
Remove-Item $PSScriptRoot/sshKey* -Force
|
||||
ssh-keygen -t rsa -b 4096 -f $PSScriptRoot/sshKey -N '' -C ''
|
||||
$sshKey = Get-Content $PSScriptRoot/sshKey.pub
|
||||
|
||||
$templateFileParameters['sshPubKey'] = $sshKey
|
||||
|
||||
Write-Host "Sleeping for a bit to ensure service principal is ready."
|
||||
Start-Sleep -s 45
|
||||
|
||||
if ($CI) {
|
||||
# Install this specific version of the Azure CLI to avoid https://github.com/Azure/azure-cli/issues/28358.
|
||||
pip install azure-cli=="2.56.0"
|
||||
# The owner is a service principal
|
||||
$templateFileParameters['principalUserType'] = 'ServicePrincipal'
|
||||
Write-Host "Sleeping for a bit to ensure service principal is ready."
|
||||
Start-Sleep -s 45
|
||||
}
|
||||
|
||||
$az_version = az version
|
||||
Write-Host "Azure CLI version: $az_version"
|
||||
|
||||
az login --service-principal -u $TestApplicationId -p $TestApplicationSecret --tenant $TenantId
|
||||
az account set --subscription $SubscriptionId
|
||||
$versions = az aks get-versions -l westus -o json | ConvertFrom-Json
|
||||
Write-Host "AKS versions: $($versions | ConvertTo-Json -Depth 100)"
|
||||
$patchVersions = $versions.values | Where-Object { $_.isPreview -eq $null } | Select-Object -ExpandProperty patchVersions
|
||||
Write-Host "AKS patch versions: $($patchVersions | ConvertTo-Json -Depth 100)"
|
||||
$latestAksVersion = $patchVersions | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | Sort-Object -Descending | Select-Object -First 1
|
||||
Write-Host "Latest AKS version: $latestAksVersion"
|
||||
$templateFileParameters['latestAksVersion'] = $latestAksVersion
|
||||
|
|
|
@ -15,7 +15,7 @@ param testApplicationOid string
|
|||
param acrName string = 'acr${uniqueString(resourceGroup().id)}'
|
||||
|
||||
@description('The latest AKS version available in the region.')
|
||||
param latestAksVersion string
|
||||
param latestAksVersion string = '1.27.7'
|
||||
|
||||
@description('The SSH public key to use for the Linux VMs.')
|
||||
param sshPubKey string
|
||||
|
@ -23,9 +23,12 @@ param sshPubKey string
|
|||
@description('The admin user name for the Linux VMs.')
|
||||
param adminUserName string = 'azureuser'
|
||||
|
||||
@description('The user type - ServicePrincipal in CI, User locally')
|
||||
param principalUserType string = 'User'
|
||||
|
||||
// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles
|
||||
// var blobContributor = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') // Storage Blob Data Contributor
|
||||
var blobOwner = subscriptionResourceId('Microsoft.Authorization/roleDefinitions','b7e6dc6d-f1e8-4753-8033-0f276bb0955b') // Storage Blob Data Owner
|
||||
var serviceOwner = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')
|
||||
var websiteContributor = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') // Website Contributor
|
||||
|
||||
// Cluster parameters
|
||||
|
@ -61,7 +64,7 @@ resource blobRoleCluster 'Microsoft.Authorization/roleAssignments@2022-04-01' =
|
|||
name: guid(resourceGroup().id, blobOwner, 'kubernetes')
|
||||
properties: {
|
||||
principalId: kubernetesCluster.identity.principalId
|
||||
roleDefinitionId: blobOwner
|
||||
roleDefinitionId: serviceOwner
|
||||
principalType: 'ServicePrincipal'
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +74,7 @@ resource blobRole2 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
|||
name: guid(resourceGroup().id, blobOwner, userAssignedIdentity.id)
|
||||
properties: {
|
||||
principalId: userAssignedIdentity.properties.principalId
|
||||
roleDefinitionId: blobOwner
|
||||
roleDefinitionId: serviceOwner
|
||||
principalType: 'ServicePrincipal'
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +85,7 @@ resource webRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
|||
properties: {
|
||||
principalId: testApplicationOid
|
||||
roleDefinitionId: websiteContributor
|
||||
principalType: 'ServicePrincipal'
|
||||
principalType: principalUserType
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,12 +95,12 @@ resource webRole2 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
|||
properties: {
|
||||
principalId: testApplicationOid
|
||||
roleDefinitionId: websiteContributor
|
||||
principalType: 'ServicePrincipal'
|
||||
principalType: principalUserType
|
||||
}
|
||||
}
|
||||
|
||||
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
|
||||
name: baseName
|
||||
name: uniqueString(resourceGroup().id)
|
||||
location: location
|
||||
sku: {
|
||||
name: 'Standard_LRS'
|
||||
|
@ -109,7 +112,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
|
|||
}
|
||||
|
||||
resource storageAccount2 'Microsoft.Storage/storageAccounts@2021-08-01' = {
|
||||
name: '${baseName}2'
|
||||
name: '${uniqueString(resourceGroup().id)}2'
|
||||
location: location
|
||||
sku: {
|
||||
name: 'Standard_LRS'
|
||||
|
@ -169,7 +172,7 @@ resource web 'Microsoft.Web/sites@2022-09-01' = {
|
|||
value: storageAccount2.name
|
||||
}
|
||||
{
|
||||
name: 'IDENTITY_USER_DEFINED_IDENTITY_CLIENT_ID'
|
||||
name: 'IDENTITY_USER_DEFINED_CLIENT_ID'
|
||||
value: userAssignedIdentity.properties.clientId
|
||||
}
|
||||
{
|
||||
|
@ -210,7 +213,7 @@ resource azureFunction 'Microsoft.Web/sites@2022-09-01' = {
|
|||
value: storageAccount2.name
|
||||
}
|
||||
{
|
||||
name: 'IDENTITY_USER_DEFINED_IDENTITY_CLIENT_ID'
|
||||
name: 'IDENTITY_USER_DEFINED_CLIENT_ID'
|
||||
value: userAssignedIdentity.properties.clientId
|
||||
}
|
||||
{
|
||||
|
@ -321,7 +324,7 @@ resource kubernetesCluster 'Microsoft.ContainerService/managedClusters@2023-06-0
|
|||
output IDENTITY_WEBAPP_NAME string = web.name
|
||||
output IDENTITY_WEBAPP_PLAN string = farm.name
|
||||
output IDENTITY_USER_DEFINED_IDENTITY string = userAssignedIdentity.id
|
||||
output IDENTITY_USER_DEFINED_IDENTITY_CLIENT_ID string = userAssignedIdentity.properties.clientId
|
||||
output IDENTITY_USER_DEFINED_CLIENT_ID string = userAssignedIdentity.properties.clientId
|
||||
output IDENTITY_USER_DEFINED_IDENTITY_NAME string = userAssignedIdentity.name
|
||||
output IDENTITY_STORAGE_NAME_1 string = storageAccount.name
|
||||
output IDENTITY_STORAGE_NAME_2 string = storageAccount2.name
|
||||
|
|
Загрузка…
Ссылка в новой задаче