From 8728df8f62bfe617709483e7a19546e088b141e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Evil=C3=A1zaro=20Alves?= Date: Fri, 22 Aug 2025 10:08:34 -0500 Subject: [PATCH 1/5] Update project Catalogs --- infra/settings/workload/devcenter.schema.json | 983 ++++++++---------- infra/settings/workload/devcenter.yaml | 116 +-- src/workload/project/project.bicep | 36 +- src/workload/project/projectCatalog.bicep | 85 +- 4 files changed, 461 insertions(+), 759 deletions(-) diff --git a/infra/settings/workload/devcenter.schema.json b/infra/settings/workload/devcenter.schema.json index 3861bffb..c425d883 100644 --- a/infra/settings/workload/devcenter.schema.json +++ b/infra/settings/workload/devcenter.schema.json @@ -1,596 +1,433 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://schemas.devexp.io/devcenter-settings.json", - "title": "DevCenter Configuration", - "description": "Schema for defining Microsoft DevCenter settings including projects, pools, and environment configurations.", - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "projects", - "tags" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the DevCenter resource", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9\\-_.]*[a-zA-Z0-9]$", - "examples": [ - "contoso-devcenter", - "my-devcenter" - ] - }, - "catalogItemSyncEnableStatus": { - "type": "string", - "description": "Status of catalog item synchronization", - "enum": [ - "Enabled", - "Disabled" - ], - "default": "Enabled" - }, - "microsoftHostedNetworkEnableStatus": { - "type": "string", - "description": "Status of Microsoft hosted network feature", - "enum": [ - "Enabled", - "Disabled" - ], - "default": "Enabled" - }, - "installAzureMonitorAgentEnableStatus": { - "type": "string", - "description": "Status of Azure Monitor agent installation", - "enum": [ - "Enabled", - "Disabled" - ], - "default": "Enabled" - }, - "identity": { - "type": "object", - "description": "Identity configuration for the DevCenter resource", - "additionalProperties": false, - "required": [ - "type" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of managed identity", - "enum": [ - "SystemAssigned", - "UserAssigned" - ], - "default": "SystemAssigned" - }, - "roleAssignments": { - "type": "object", - "description": "Role assignments for the DevCenter identity", - "additionalProperties": false, - "properties": { - "devCenter": { - "type": "array", - "description": "Role assignments for DevCenter operations", - "items": { - "$ref": "#/definitions/roleAssignment" - } - }, - "orgRoleTypes": { - "type": "array", - "description": "Organizational role type configurations", - "items": { - "$ref": "#/definitions/orgRoleType" - } - } - } - } - } - }, - "catalogs": { - "type": "array", - "description": "Global catalogs available to all projects", - "items": { - "$ref": "#/definitions/catalog" - } - }, - "environmentTypes": { - "type": "array", - "description": "Global environment types available to all projects", - "items": { - "$ref": "#/definitions/environmentType" - } - }, - "projects": { - "type": "array", - "description": "DevCenter projects configuration", - "minItems": 1, - "items": { - "$ref": "#/definitions/project" - } - }, - "tags": { - "type": "object", - "description": "Resource tags for the DevCenter", - "additionalProperties": { - "type": "string", - "minLength": 1, - "maxLength": 256 - }, - "maxProperties": 50, - "examples": [ - { - "environment": "dev", - "division": "Platforms", - "team": "DevExP", - "project": "DevExP-DevBox", - "costCenter": "IT", - "owner": "Contoso", - "resources": "DevCenter" - } - ] - } - }, - "definitions": { - "roleAssignment": { - "type": "object", - "description": "Azure RBAC role assignment", - "additionalProperties": false, - "required": [ - "id", - "name", - "scope" - ], - "properties": { - "id": { - "type": "string", - "description": "UUID of the role definition", - "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", - "examples": [ - "b24988ac-6180-42a0-ab88-20f7382dd24c" - ] - }, + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Dev Center configuration schema", + "type": "object", + "additionalProperties": false, + "properties": { "name": { - "type": "string", - "description": "Name of the role", - "minLength": 1, - "examples": [ - "Contributor", - "Reader" - ] - }, - "scope": { - "type": "string", - "description": "Scope of the role assignment", - "enum": [ - "Subscription", - "ResourceGroup", - "Project" - ], - "default": "Subscription", - "examples": [ - "Subscription", - "ResourceGroup", - "Project" - ] - } - } - }, - "orgRoleType": { - "type": "object", - "description": "Organizational role type configuration", - "additionalProperties": false, - "required": [ - "type", - "azureADGroupId", - "azureADGroupName", - "azureRBACRoles" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of organizational role", - "examples": [ - "DevManager", - "Developer" - ] - }, - "azureADGroupId": { - "type": "string", - "description": "Azure AD group ID", - "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" - }, - "azureADGroupName": { - "type": "string", - "description": "Azure AD group name", - "minLength": 1 - }, - "azureRBACRoles": { - "type": "array", - "description": "Azure RBAC roles assigned to this group", - "items": { - "$ref": "#/definitions/roleAssignment" - } - } - } - }, - "catalog": { - "type": "object", - "description": "Catalog configuration", - "additionalProperties": false, - "required": [ - "name", - "type", - "uri", - "branch", - "path" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the catalog", - "minLength": 1 - }, - "type": { - "type": "string", - "description": "Type of catalog repository", - "enum": [ - "gitHub", - "adoGit" - ], - "examples": [ - "gitHub" - ] - }, - "uri": { - "type": "string", - "description": "URI of the catalog repository", - "format": "uri" - }, - "branch": { - "type": "string", - "description": "Branch name in the repository", - "default": "main" - }, - "path": { - "type": "string", - "description": "Path within the repository", - "examples": [ - ".configuration/devcenter/tasks" - ] - } - } - }, - "environmentType": { - "type": "object", - "description": "Environment type configuration", - "additionalProperties": false, - "required": [ - "name", - "deploymentTargetId" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the environment type", - "minLength": 1, - "examples": [ - "dev", - "staging", - "prod" - ] + "type": "string", + "minLength": 1 }, - "deploymentTargetId": { - "type": "string", - "description": "Deployment target identifier (empty for default subscription)", - "examples": [ - "", - "subscription-id" - ] - } - } - }, - "project": { - "type": "object", - "description": "DevCenter project configuration", - "additionalProperties": false, - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the project", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9\\-_.]*[a-zA-Z0-9]$" + "catalogItemSyncEnableStatus": { + "type": "string", + "enum": [ + "Enabled", + "Disabled" + ] }, - "description": { - "type": "string", - "description": "Description of the project", - "maxLength": 512 + "microsoftHostedNetworkEnableStatus": { + "type": "string", + "enum": [ + "Enabled", + "Disabled" + ] }, - "network": { - "type": "object", - "description": "Network configuration for the project", - "properties": { - "name": { - "type": "string", - "description": "Name of the network configuration", - "minLength": 1 - }, - "create": { - "type": "boolean", - "description": "Whether to create the network resources", - "default": true - }, - "resourceGroupName": { - "type": "string", - "description": "Name of the resource group for network resources", - "minLength": 1 - }, - "virtualNetworkType": { - "type": "string", - "description": "Type of virtual network", - "enum": [ - "Managed", - "Unmanaged" - ], - "default": "Managed" - }, - "addressPrefixes": { - "type": "array", - "description": "Address prefixes for the virtual network", - "items": { - "type": "string", - "format": "cidr" - } - }, - "subnets": { - "type": "array", - "description": "Subnets within the virtual network", - "items": { - "$ref": "#/definitions/subnet" - } - } - } + "installAzureMonitorAgentEnableStatus": { + "type": "string", + "enum": [ + "Enabled", + "Disabled" + ] }, "identity": { - "type": "object", - "description": "Identity configuration for the project", - "additionalProperties": false, - "required": [ - "type", - "roleAssignments" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of managed identity", - "enum": [ - "SystemAssigned", - "UserAssigned" - ], - "default": "SystemAssigned" + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "SystemAssigned", + "UserAssigned", + "SystemAssignedUserAssigned", + "None" + ] + }, + "roleAssignments": { + "type": "object", + "additionalProperties": false, + "properties": { + "devCenter": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignment" + } + }, + "orgRoleTypes": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string" + }, + "azureADGroupId": { + "$ref": "#/definitions/guid" + }, + "azureADGroupName": { + "type": "string" + }, + "azureRBACRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/rbacRole" + } + } + }, + "required": [ + "type", + "azureADGroupId", + "azureADGroupName" + ] + } + } + } + } }, - "roleAssignments": { - "type": "array", - "description": "Role assignments for project users", - "items": { - "$ref": "#/definitions/projectRoleAssignment" - } - } - } - }, - "pools": { - "type": "array", - "description": "DevBox pools for the project", - "items": { - "$ref": "#/definitions/pool" - } - }, - "environmentTypes": { - "type": "array", - "description": "Environment types available to the project", - "items": { - "$ref": "#/definitions/environmentType" - } + "required": [ + "type" + ] }, "catalogs": { - "type": "object", - "description": "Project-specific catalogs", - "additionalProperties": false, - "properties": { - "environmentDefinition": { - "$ref": "#/definitions/catalog" - }, - "imageDefinition": { - "$ref": "#/definitions/catalog" + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "visibility": { + "type": "string", + "enum": [ + "public", + "private" + ] + }, + "uri": { + "type": "string", + "format": "uri" + }, + "branch": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sourceControl": { + "type": "string" + } + }, + "required": [ + "name", + "type", + "uri" + ] } - } - }, - "tags": { - "type": "object", - "description": "Resource tags for the project", - "additionalProperties": { - "type": "string", - "minLength": 1, - "maxLength": 256 - }, - "maxProperties": 50 - } - } - }, - "networkConfiguration": { - "type": "object", - "description": "Network configuration for a project", - "additionalProperties": false, - "required": [ - "name", - "create", - "resourceGroupName", - "virtualNetworkType", - "addressPrefixes", - "subnets" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the network configuration", - "minLength": 1 - }, - "create": { - "type": "boolean", - "description": "Whether to create the network resources", - "default": true - }, - "resourceGroupName": { - "type": "string", - "description": "Name of the resource group for network resources", - "minLength": 1 }, - "virtualNetworkType": { - "type": "string", - "description": "Type of virtual network", - "enum": [ - "Managed", - "Unmanaged" - ], - "default": "Managed" - }, - "addressPrefixes": { - "type": "array", - "description": "Address prefixes for the virtual network", - "minItems": 1, - "items": { - "type": "string", - "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\/(?:[0-9]|[1-2][0-9]|3[0-2])$" - } + "environmentTypes": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "deploymentTargetId": { + "type": "string" + } + }, + "required": [ + "name" + ] + } }, - "subnets": { - "type": "array", - "description": "Subnets within the virtual network", - "minItems": 1, - "items": { - "$ref": "#/definitions/subnet" - } + "projects": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "network": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "create": { + "type": "boolean" + }, + "resourceGroupName": { + "type": "string" + }, + "virtualNetworkType": { + "type": "string" + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:\\d{1,3}\\.){3}\\d{1,3}\\/\\d{1,2}$" + } + }, + "subnets": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "properties": { + "type": "object", + "additionalProperties": false, + "properties": { + "addressPrefix": { + "type": "string", + "pattern": "^(?:\\d{1,3}\\.){3}\\d{1,3}\\/\\d{1,2}$" + } + }, + "required": [ + "addressPrefix" + ] + } + }, + "required": [ + "name", + "properties" + ] + } + }, + "tags": { + "type": "object", + "additionalProperties": false, + "properties": { + "environment": { + "type": "string" + }, + "division": { + "type": "string" + }, + "team": { + "type": "string" + }, + "project": { + "type": "string" + }, + "costCenter": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "resources": { + "type": "string" + } + } + } + }, + "required": [ + "name", + "create" + ] + }, + "identity": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string" + }, + "roleAssignments": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "azureADGroupId": { + "$ref": "#/definitions/guid" + }, + "azureADGroupName": { + "type": "string" + }, + "azureRBACRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/rbacRole" + } + } + }, + "required": [ + "azureADGroupId", + "azureADGroupName" + ] + } + } + }, + "required": [ + "type" + ] + }, + "pools": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "imageDefinitionName": { + "type": "string" + }, + "vmSku": { + "type": "string" + } + }, + "required": [ + "name", + "imageDefinitionName" + ] + } + }, + "environmentTypes": { + "type": "array", + "items": { + "$ref": "#/properties/environmentTypes/items" + } + }, + "catalogs": { + "type": "array", + "items": { + "$ref": "#/properties/catalogs/items" + } + }, + "tags": { + "type": "object", + "additionalProperties": false, + "properties": { + "environment": { + "type": "string" + }, + "division": { + "type": "string" + }, + "team": { + "type": "string" + }, + "project": { + "type": "string" + }, + "costCenter": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "resources": { + "type": "string" + } + } + } + }, + "required": [ + "name" + ] + } }, "tags": { - "type": "object", - "description": "Tags for the network resources", - "additionalProperties": { - "type": "string", - "minLength": 1, - "maxLength": 256 - } - } - } - }, - "subnet": { - "type": "object", - "description": "Subnet configuration", - "additionalProperties": false, - "required": [ - "name", - "properties" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the subnet", - "minLength": 1 - }, - "properties": { - "type": "object", - "description": "Subnet properties", - "additionalProperties": false, - "required": [ - "addressPrefix" - ], - "properties": { - "addressPrefix": { - "type": "string", - "description": "Address prefix for the subnet", - "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\/(?:[0-9]|[1-2][0-9]|3[0-2])$" + "type": "object", + "additionalProperties": false, + "properties": { + "environment": { + "type": "string" + }, + "division": { + "type": "string" + }, + "team": { + "type": "string" + }, + "project": { + "type": "string" + }, + "costCenter": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "resources": { + "type": "string" + } } - } } - } }, - "projectRoleAssignment": { - "type": "object", - "description": "Role assignment for project users", - "additionalProperties": false, - "required": [ - "azureADGroupId", - "azureADGroupName", - "azureRBACRoles" - ], - "properties": { - "azureADGroupId": { - "type": "string", - "description": "Azure AD group ID", - "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" - }, - "azureADGroupName": { - "type": "string", - "description": "Azure AD group name", - "minLength": 1 - }, - "azureRBACRoles": { - "type": "array", - "description": "Azure RBAC roles for the group", - "items": { - "$ref": "#/definitions/roleAssignment" - } - } - } - }, - "pool": { - "type": "object", - "description": "DevBox pool configuration", - "additionalProperties": false, - "required": [ - "name", - "imageDefinitionName", - "vmSku" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the DevBox pool", - "minLength": 1, - "examples": [ - "backend-engineer", - "frontend-engineer" - ] + "required": [ + "name" + ], + "definitions": { + "guid": { + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" }, - "imageDefinitionName": { - "type": "string", - "description": "Name of the image definition to use", - "minLength": 1 + "roleAssignment": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "$ref": "#/definitions/guid" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "Subscription", + "ResourceGroup", + "Project", + "Tenant", + "ManagementGroup" + ] + } + }, + "required": [ + "id", + "name", + "scope" + ] }, - "vmSku": { - "type": "string", - "description": "SKU of the virtual machine to use for this pool", - "minLength": 1, - "enum": [ - "general_i_8c32gb256ssd_v2", - "general_i_8c32gb512ssd_v2", - "general_i_8c32gb1024ssd_v2", - "general_i_8c32gb2048ssd_v2", - "general_i_16c64gb256ssd_v2", - "general_i_16c64gb512ssd_v2", - "general_i_16c64gb1024ssd_v2", - "general_i_16c64gb2048ssd_v2", - "general_i_32c128gb512ssd_v2", - "general_i_32c128gb1024ssd_v2", - "general_i_32c128gb2048ssd_v2" - ] + "rbacRole": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "id": { + "$ref": "#/definitions/guid" + }, + "scope": { + "type": "string" + } + }, + "required": [ + "name", + "id" + ] } - } } - } } \ No newline at end of file diff --git a/infra/settings/workload/devcenter.yaml b/infra/settings/workload/devcenter.yaml index 83851154..b4833053 100644 --- a/infra/settings/workload/devcenter.yaml +++ b/infra/settings/workload/devcenter.yaml @@ -62,9 +62,10 @@ identity: catalogs: - name: "customTasks" type: gitHub - uri: "https://github.com/Evilazaro/DevExp-DevBox.git" + visibility: public + uri: "https://github.com/microsoft/devcenter-catalog.git" branch: "main" - path: "/.configuration/devcenter/tasks" + path: "./Tasks" # Environment Types section - defines deployment environments for applications # Each environment type represents a different stage in the development lifecycle @@ -81,103 +82,6 @@ environmentTypes: # Each project has its own Dev Box configurations, catalogs, and permissions # Best practice: Create separate projects for different teams or workstreams projects: - # Identity Provider project - for authentication/authorization services - - name: "identityProvider" # Name of the project - description: "Identity Provider project." # Project description - - network: - name: identityProvider # Name of the virtual network - create: true # Set this parameter to true to create the network. If its value is false, the the accelerator will connect to an existing network. - resourceGroupName: "identityProvider-connectivity-RG" # Resource group for network. If the create parameter is set to false, the resource group must already exist. - virtualNetworkType: Managed # Managed network type is recommended for Dev Center projects to ensure proper connectivity and security - addressPrefixes: # Address space for the virtual network. This should be a unique CIDR block that does not overlap with other networks in your Azure subscription. If the create parameter is set to true, the accelerator will create the network with this address space. - - 10.0.0.0/16 - subnets: - - name: identityProvider-subnet # Subnet name. If the create parameter is set to true, the accelerator will create the subnet with this name and address prefix. If its value is false, the subnet must already exist. - properties: - addressPrefix: 10.0.1.0/24 # Subnet address range. If the create parameter is set to true, the accelerator will create the subnet with this address range. If its value is false, the subnet must already exist. - tags: - environment: dev - division: Platforms - team: DevExP - project: DevExP-DevBox - costCenter: IT - owner: Contoso - resources: Network - - # Project identity configuration - controls project-level security - identity: - type: SystemAssigned - # Role assignments for the Project - # These roles control who can manage the project and its resources - # You must create the corresponding Azure AD groups and assign users to them - # The default values in this example are "Identity Provider Engineers" - # You can customize these values based on your organization's requirements - # The following roles follow the principle of least privilege and best practices described in https://learn.microsoft.com/en-us/azure/dev-box/concept-dev-box-deployment-guide#organizational-roles-and-responsibilities guidance. - roleAssignments: - - azureADGroupId: "67a29bc3-f25c-4599-9cb1-4da19507e8ee" # Azure AD group ID for Identity Provider Engineers. You must create this group in Azure AD and replace the , the default value in this example is "Identity Provider Engineers" - azureADGroupName: "Identity Provider Engineers" # Azure AD group name - azureRBACRoles: - - name: "Contributor" - id: "b24988ac-6180-42a0-ab88-20f7382dd24c" - scope: Project - - name: "Dev Box User" - id: "45d50f46-0b78-4001-a660-4198cbe8cd05" - scope: Project - - name: "Deployment Environment User" - id: "18e40d4e-8d2e-438d-97e1-9528336e149c" - scope: Project - - name: "Key Vault Secrets User" - id: "4633458b-17de-408a-b874-0445c86b69e6" - scope: ResourceGroup - - id: "b86a8fe4-44ce-4948-aee5-eccb2c155cd7" - name: "Key Vault Secrets Officer" - scope: ResourceGroup - - # Dev Box pools - collections of Dev Boxes with specific configurations - # Best practice: Create role-specific pools with appropriate tools and settings - pools: - - name: "backend-engineer" - imageDefinitionName: "identityProvider-backend-engineer" - vmSku: general_i_32c128gb512ssd_v2 - - name: "frontend-engineer" - imageDefinitionName: "identityProvider-frontend-engineer" - vmSku: general_i_16c64gb256ssd_v2 - - # Project-specific environment types - # Defines which deployment environments are available to the project - environmentTypes: - - name: "dev" - deploymentTargetId: "" - - name: "staging" - deploymentTargetId: "" - - # Project-specific catalogs - repositories containing project configurations - catalogs: - environmentDefinition: - name: "environments" - type: gitHub - uri: "https://github.com/Evilazaro/IdentityProvider.git" - branch: "main" - path: "/.configuration/devcenter/environments" - imageDefinition: - name: "imageDefinitions" - type: gitHub - uri: "https://github.com/Evilazaro/IdentityProvider.git" - branch: "main" - path: "/.configuration/devcenter/imageDefinitions" - - # Project-specific tags for resource governance and organization - # Best practice: Apply consistent tags for cost allocation and ownership - tags: - environment: "dev" # Identifies the deployment environment - division: "Platforms" # Organizational division responsible for the project - team: "DevExP" # Team responsible for implementation - project: "DevExP-DevBox" # Project name for cost allocation - costCenter: "IT" # Financial tracking designation - owner: "Contoso" # Resource ownership - resources: "Project" # Resource type identifier - - name: "eShop" description: "eShop project." @@ -247,15 +151,17 @@ projects: # Project-specific catalogs - repositories containing project configurations catalogs: - environmentDefinition: - name: "environments" - type: gitHub + - name: "environments" + type: environmentDefinition + sourceControl: gitHub + visibility: public uri: "https://github.com/Evilazaro/eShop.git" branch: "main" path: "/.devcenter/environments" - imageDefinition: - name: "imageDefinitions" - type: gitHub + - name: "devboxImages" + type: imageDefinition + sourceControl: gitHub + visibility: public uri: "https://github.com/Evilazaro/eShop.git" branch: "main" path: "/.devcenter/imageDefinitions" diff --git a/src/workload/project/project.bicep b/src/workload/project/project.bicep index 92e00859..0653f2b2 100644 --- a/src/workload/project/project.bicep +++ b/src/workload/project/project.bicep @@ -12,7 +12,7 @@ param logAnalyticsId string param projectDescription string @description('Catalog configuration for the project') -param projectCatalogs object +param projectCatalogs object[] @description('Environment types to be associated with the project') param projectEnvironmentTypes array @@ -139,21 +139,23 @@ module projectADGroup '../../identity/projectIdentityRoleAssignment.bicep' = [ } ] -@description('Configure environment definition catalogs') -module catalogs 'projectCatalog.bicep' = { - name: 'catalog-${uniqueString(project.id)}-${dateTime}' - scope: resourceGroup() - params: { - projectName: project.name - catalogConfig: projectCatalogs - secretIdentifier: secretIdentifier +@description('Configure project catalogs') +module catalogs 'projectCatalog.bicep' = [ + for (catalog, i) in projectCatalogs: { + name: 'catalog-${i}-${uniqueString(project.id, catalog.name)}-${dateTime}' + scope: resourceGroup() + params: { + projectName: project.name + catalogConfig: catalog + secretIdentifier: secretIdentifier + } + dependsOn: [ + projectIdentity + projectIdentityRG + projectADGroup + ] } - dependsOn: [ - projectIdentity - projectIdentityRG - projectADGroup - ] -} +] @description('Configure project environment types') module environmentTypes 'projectEnvironmentType.bicep' = [ @@ -193,13 +195,13 @@ module connectivity '../../connectivity/connectivity.bicep' = { @description('Configure DevBox pools for the project') module pools 'projectPool.bicep' = [ - for (pool, i) in projectPools: { + for (pool, i) in projectPools: if (projectCatalogs[i].type == 'imageDefinition') { name: 'pool-${i}-${uniqueString(project.id, pool.name)}-${dateTime}' scope: resourceGroup() params: { name: pool.name projectName: project.name - catalogName: projectCatalogs.imageDefinition.name + catalogName: projectCatalogs[i].name imageDefinitionName: pool.imageDefinitionName vmSku: pool.vmSku networkConnectionName: connectivity.outputs.networkConnectionName diff --git a/src/workload/project/projectCatalog.bicep b/src/workload/project/projectCatalog.bicep index 21cd35a6..42f327ac 100644 --- a/src/workload/project/projectCatalog.bicep +++ b/src/workload/project/projectCatalog.bicep @@ -2,7 +2,7 @@ param projectName string @description('Catalog configurations for the project') -param catalogConfig ProjectCatalog +param catalogConfig Catalog @description('Secret identifier for Git repository authentication') @secure() @@ -13,8 +13,14 @@ type Catalog = { @description('Name of the catalog') name: string - @description('Type of repository (GitHub or Azure DevOps Git)') - type: CatalogType + @description('Type of catalog (environment or image)') + type: 'environmentDefinition' | 'imageDefinition' + + @description('Source control type') + sourceControl: 'gitHub' | 'adoGit' + + @description('Visibility of the catalog') + visibility: 'public' | 'private' @description('URI of the repository') uri: string @@ -26,81 +32,32 @@ type Catalog = { path: string } -@description('Project catalog configuration') -type ProjectCatalog = { - @description('Environment definition catalog configuration') - environmentDefinition: Catalog - - @description('Image definition catalog configuration') - imageDefinition: Catalog -} - -@description('Supported catalog repository types') -type CatalogType = string - @description('Reference to the existing DevCenter project') resource project 'Microsoft.DevCenter/projects@2025-04-01-preview' existing = { name: projectName } @description('Environment Definition Catalog') -resource environmentDefinitionCatalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { - name: catalogConfig.environmentDefinition.name - parent: project - properties: { - syncType: 'Scheduled' - gitHub: catalogConfig.environmentDefinition.type == 'gitHub' - ? { - uri: catalogConfig.environmentDefinition.uri - branch: catalogConfig.environmentDefinition.branch - path: catalogConfig.environmentDefinition.path - secretIdentifier: secretIdentifier - } - : null - adoGit: catalogConfig.environmentDefinition.type == 'adoGit' - ? { - uri: catalogConfig.environmentDefinition.uri - branch: catalogConfig.environmentDefinition.branch - path: catalogConfig.environmentDefinition.path - secretIdentifier: secretIdentifier - } - : null - } -} - -@description('Image Definition Catalog') -resource imageDefinitionCatalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { - name: catalogConfig.imageDefinition.name +resource catalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { + name: catalogConfig.name parent: project properties: { syncType: 'Scheduled' - gitHub: catalogConfig.imageDefinition.type == 'gitHub' + gitHub: catalogConfig.sourceControl == 'gitHub' ? { - uri: catalogConfig.imageDefinition.uri - branch: catalogConfig.imageDefinition.branch - path: catalogConfig.imageDefinition.path - secretIdentifier: secretIdentifier + uri: catalogConfig.uri + branch: catalogConfig.branch + path: catalogConfig.path + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null - adoGit: catalogConfig.imageDefinition.type == 'adoGit' + adoGit: catalogConfig.sourceControl == 'adoGit' ? { - uri: catalogConfig.imageDefinition.uri - branch: catalogConfig.imageDefinition.branch - path: catalogConfig.imageDefinition.path - secretIdentifier: secretIdentifier + uri: catalogConfig.uri + branch: catalogConfig.branch + path: catalogConfig.path + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null } } - -@description('The name of the environment definition catalog') -output environmentDefinitionCatalogName string = environmentDefinitionCatalog.name - -@description('The ID of the environment definition catalog') -output environmentDefinitionCatalogId string = environmentDefinitionCatalog.id - -@description('The name of the image definition catalog') -output imageDefinitionCatalogName string = imageDefinitionCatalog.name - -@description('The ID of the image definition catalog') -output imageDefinitionCatalogId string = imageDefinitionCatalog.id From e4b4adddf6ce82f01a7a822f828283c5b5be70b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Evil=C3=A1zaro=20Alves?= Date: Fri, 22 Aug 2025 10:40:39 -0500 Subject: [PATCH 2/5] Update project catalogs --- src/workload/project/project.bicep | 14 ++++---- src/workload/project/projectPool.bicep | 49 +++++++++++++------------- src/workload/workload.bicep | 2 +- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/workload/project/project.bicep b/src/workload/project/project.bicep index 0653f2b2..cc71bb22 100644 --- a/src/workload/project/project.bicep +++ b/src/workload/project/project.bicep @@ -12,7 +12,7 @@ param logAnalyticsId string param projectDescription string @description('Catalog configuration for the project') -param projectCatalogs object[] +param catalogs object[] @description('Environment types to be associated with the project') param projectEnvironmentTypes array @@ -140,8 +140,8 @@ module projectADGroup '../../identity/projectIdentityRoleAssignment.bicep' = [ ] @description('Configure project catalogs') -module catalogs 'projectCatalog.bicep' = [ - for (catalog, i) in projectCatalogs: { +module projectCatalogs 'projectCatalog.bicep' = [ + for (catalog, i) in catalogs: { name: 'catalog-${i}-${uniqueString(project.id, catalog.name)}-${dateTime}' scope: resourceGroup() params: { @@ -170,7 +170,7 @@ module environmentTypes 'projectEnvironmentType.bicep' = [ projectIdentity projectIdentityRG projectADGroup - catalogs + projectCatalogs ] } ] @@ -189,19 +189,19 @@ module connectivity '../../connectivity/connectivity.bicep' = { projectIdentity projectIdentityRG projectADGroup - catalogs + projectCatalogs ] } @description('Configure DevBox pools for the project') module pools 'projectPool.bicep' = [ - for (pool, i) in projectPools: if (projectCatalogs[i].type == 'imageDefinition') { + for (pool, i) in projectPools: { name: 'pool-${i}-${uniqueString(project.id, pool.name)}-${dateTime}' scope: resourceGroup() params: { name: pool.name projectName: project.name - catalogName: projectCatalogs[i].name + catalogs: catalogs imageDefinitionName: pool.imageDefinitionName vmSku: pool.vmSku networkConnectionName: connectivity.outputs.networkConnectionName diff --git a/src/workload/project/projectPool.bicep b/src/workload/project/projectPool.bicep index 923e7147..b0cb2f04 100644 --- a/src/workload/project/projectPool.bicep +++ b/src/workload/project/projectPool.bicep @@ -5,7 +5,7 @@ param name string param location string = resourceGroup().location @description('The name of the catalog to use for the pool') -param catalogName string +param catalogs object[] @description('The name of the dev box definition to use for the pool') param imageDefinitionName string @@ -28,30 +28,29 @@ resource project 'Microsoft.DevCenter/projects@2025-04-01-preview' existing = { } @description('Dev Box Pool resource') -resource pool 'Microsoft.DevCenter/projects/pools@2025-04-01-preview' = { - name: name - location: location - parent: project - properties: { - devBoxDefinitionType: 'Value' - devBoxDefinitionName: '~Catalog~${catalogName}~${imageDefinitionName}' - devBoxDefinition: { - imageReference: { - id: '${project.id}/images/~Catalog~${catalogName}~${imageDefinitionName}' - } - sku: { - name: vmSku +resource pool 'Microsoft.DevCenter/projects/pools@2025-04-01-preview' = [ + for (catalog, i) in catalogs: if (catalog.type == 'imageDefinition') { + name: '${name}-${i}-pool' + location: location + parent: project + properties: { + devBoxDefinitionType: 'Value' + devBoxDefinitionName: '~Catalog~${catalog.name}~${imageDefinitionName}' + devBoxDefinition: { + imageReference: { + id: '${project.id}/images/~Catalog~${catalog.name}~${imageDefinitionName}' + } + sku: { + name: vmSku + } } + networkConnectionName: networkConnectionName + licenseType: 'Windows_Client' + localAdministrator: 'Enabled' + singleSignOnStatus: 'Enabled' + displayName: name + virtualNetworkType: networkType + managedVirtualNetworkRegions: (networkType == 'Managed') ? [resourceGroup().location] : [] } - networkConnectionName: networkConnectionName - licenseType: 'Windows_Client' - localAdministrator: 'Enabled' - singleSignOnStatus: 'Enabled' - displayName: name - virtualNetworkType: networkType - managedVirtualNetworkRegions: (networkType == 'Managed') ? [resourceGroup().location] : [] } -} - -@description('The name of the pool') -output poolName string = pool.name +] diff --git a/src/workload/workload.bicep b/src/workload/workload.bicep index 277f0f72..113a4322 100644 --- a/src/workload/workload.bicep +++ b/src/workload/workload.bicep @@ -60,7 +60,7 @@ module projects 'project/project.bicep' = [ logAnalyticsId: logAnalyticsId projectDescription: project.description ?? project.name devCenterName: devcenter.outputs.AZURE_DEV_CENTER_NAME - projectCatalogs: project.catalogs + catalogs: project.catalogs projectEnvironmentTypes: project.environmentTypes projectPools: project.pools projectNetwork: project.network From 6ef8359b2b2f71f4d97bfa90a4a7dd35cad35b10 Mon Sep 17 00:00:00 2001 From: Evilazaro Date: Fri, 22 Aug 2025 11:08:59 -0500 Subject: [PATCH 3/5] images --- .../ADO/common-backend-config.dsc.yaml | 390 +++++----- .../common-backend-usertasks-config.dsc.yaml | 666 +++++++++--------- .../workloads/ADO/common-config.dsc.yaml | 320 ++++----- .../common-frontend-usertasks-config.dsc.yaml | 228 +++--- .../devcenter/workloads/winget-update.ps1 | 294 ++++---- .../winget-upgrade-packages.dsc.yaml | 150 ++-- src/workload/core/catalog.bicep | 7 +- src/workload/project/projectCatalog.bicep | 4 +- 8 files changed, 1031 insertions(+), 1028 deletions(-) diff --git a/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml index 79ba6fc2..26e6fc61 100644 --- a/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-backend-config.dsc.yaml @@ -1,195 +1,195 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Backend Development Configuration Sample for .NET Engineers -# ============================================= -# -# Purpose: -# This DSC configuration installs essential tools for Azure backend development including: -# - Source control tools (GitHub Desktop) -# - Azure command-line tools (CLI, AZD, Bicep) -# - Local development emulators for Azure services -# -# Best Practices: -# - All tools are installed via WinGet for consistent versioning -# - Dependencies are explicitly declared to ensure proper installation order -# - Tools are grouped logically by purpose -# - Configuration should be version controlled and tested in CI/CD pipelines -# - Regular updates to ensure latest security patches and features -# - Separation of development tools from production configurations -# -properties: - configurationVersion: "0.2.0" - resources: - #---------------------------------------------- - # Azure Command-Line Tools - #---------------------------------------------- - # Resource: Azure CLI - # Azure CLI is the foundation for Azure management and serves as a dependency for other Azure tools. - # It provides command-line access to nearly all Azure service operations. - # - # Key Azure integration points: - # - Unified authentication with Microsoft Entra ID (formerly Azure AD) - # - Support for service principals and managed identities - # - JSON-based output for automation and scripting - # - Cross-platform compatibility for consistent workflows - # - # Security best practices: - # - Use 'az login --tenant' to explicitly specify tenants - # - Leverage managed identities where available - # - Apply RBAC with principle of least privilege - # - Use service principals with certificate-based auth for automation - # - Regularly update CLI using 'az upgrade' command - # - Avoid storing credentials in CLI cache in shared environments - # - Configure CLI to use your organization's approved proxy if required - # - # Common development scenarios: - # - Resource provisioning via templates and scripts - # - Querying resource status and configurations - # - Integrated deployment workflows with CI/CD - # - Management of secrets and connection strings - # - # DSC-specific notes: - # - WinGet ensures automatic updates to secure versions - # - Installation is idempotent and will not reinstall if present - # - Consider adding environment PATH validation during configuration testing - # - # Reference: https://learn.microsoft.com/en-us/cli/azure/ - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.AzureCLI - directives: - allowPrerelease: true - description: Install Azure CLI for managing Azure resources from the command line - settings: - id: Microsoft.AzureCLI - - # Resource: Azure Developer CLI (azd) - # Azure Developer CLI (azd) simplifies application development workflow with templates - # and integrated deployment capabilities. - # - # Key Azure integration points: - # - End-to-end application development lifecycle management - # - Built-in templates for common Azure architectural patterns - # - Automated environment provisioning with infrastructure as code - # - Integration with GitHub Actions and Azure DevOps for CI/CD - # - Application monitoring and logging setup - # - # Development best practices: - # - Use environment variables for secrets ('azd env set') - # - Leverage service templates for consistent architecture - # - Implement standardized application structures - # - Follow Azure landing zone principles for environments - # - Store azd environment configurations in version control - # - Use azd pipeline integration for repeatable deployments - # - Include .env.template file but exclude .env files from source control - # - # Common development scenarios: - # - Setting up complete development environments - # - Implementing production-ready services with best practices - # - Consistent local-to-cloud development experience - # - Orchestrating multi-service deployments - # - # DSC-specific notes: - # - Dependency on Azure CLI is explicitly declared - # - Installation validates presence of required prerequisites - # - Consider adding post-install validation of azd version - # - # Reference: https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/ - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.Azd - directives: - allowPrerelease: true - description: Install Azure Developer CLI (azd) for end-to-end application development - settings: - id: Microsoft.Azd - dependsOn: - - Microsoft.AzureCLI # AZD requires Azure CLI to function properly - - # Resource: Bicep CLI - # Bicep provides a domain-specific language for deploying Azure resources - # with improved syntax over ARM templates. - # - # Key Azure integration points: - # - Native integration with Azure Resource Manager - # - Support for all Azure resource types and apiVersions - # - Resource visualization capabilities - # - Module composition for reusable infrastructure - # - Built-in functions for dynamic deployments - # - # IaC best practices: - # - Use modules for reusable components - # - Implement parameterization for environment flexibility - # - Apply Azure Policy as Code for governance - # - Use symbolic references instead of string manipulation - # - Implement deployment validation with 'what-if' - # - Structure Bicep modules with clear separation of concerns - # - Validate Bicep files with 'bicep build' before deployment - # - Use linting tools to enforce conventions and best practices - # - Test deployments in isolation before integrating - # - # Common development scenarios: - # - Defining infrastructure as code for Azure environments - # - Creating reusable infrastructure modules - # - Setting up complex multi-resource deployments - # - Implementing infrastructure governance - # - # DSC-specific notes: - # - Bicep CLI is installed via Azure CLI extension - # - Installation updates to latest version automatically - # - Consider adding VS Code extension for Bicep in developer environments - # - # Reference: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/ - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.Bicep - directives: - allowPrerelease: true - description: Install Bicep CLI for Infrastructure as Code development on Azure - settings: - id: Microsoft.Bicep - dependsOn: - - Microsoft.AzureCLI # Bicep extensions use Azure CLI for deployment - - #---------------------------------------------- - # Local Development Emulators - #---------------------------------------------- - # Resource: Azure Storage Emulator - # Azure Storage Emulator provides a local environment for testing Azure Storage applications - # without requiring an Azure subscription for development. - # - # Key Azure integration points: - # - Local emulation of Blob, Queue, and Table storage - # - Development connection string compatibility with Azure Storage - # - Support for Azure Storage SDK integration - # - Local debugging of storage-dependent applications - # - Reduced development costs by minimizing cloud resource usage - # - # Development best practices: - # - Use consistent connection strings between local and cloud - # - Validate local operations match cloud behavior - # - Implement proper exception handling for both environments - # - Test with both emulator and actual Azure resources before deployment - # - Create automated tests that work with both environments - # - Consider using Azurite (newer emulator) for feature parity - # - Configure proper data persistence for development data - # - Document how to initialize the emulator in your project - # - # Common development scenarios: - # - Building applications using Azure Storage - # - Performing rapid iterative development - # - Unit and integration testing without cloud dependencies - # - Offline development scenarios - # - Cost optimization during development phases - # - # DSC-specific notes: - # - Installation may require administrator privileges - # - Consider validating emulator is running after installation - # - May need to configure firewall exceptions - # - Check SQL Server dependency is met (LocalDB) - # - # Reference: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.Azure.StorageEmulator - directives: - allowPrerelease: true - description: Install Azure Storage Emulator for local development - settings: - id: Microsoft.Azure.StorageEmulator +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Backend Development Configuration Sample for .NET Engineers +# ============================================= +# +# Purpose: +# This DSC configuration installs essential tools for Azure backend development including: +# - Source control tools (GitHub Desktop) +# - Azure command-line tools (CLI, AZD, Bicep) +# - Local development emulators for Azure services +# +# Best Practices: +# - All tools are installed via WinGet for consistent versioning +# - Dependencies are explicitly declared to ensure proper installation order +# - Tools are grouped logically by purpose +# - Configuration should be version controlled and tested in CI/CD pipelines +# - Regular updates to ensure latest security patches and features +# - Separation of development tools from production configurations +# +properties: + configurationVersion: "0.2.0" + resources: + #---------------------------------------------- + # Azure Command-Line Tools + #---------------------------------------------- + # Resource: Azure CLI + # Azure CLI is the foundation for Azure management and serves as a dependency for other Azure tools. + # It provides command-line access to nearly all Azure service operations. + # + # Key Azure integration points: + # - Unified authentication with Microsoft Entra ID (formerly Azure AD) + # - Support for service principals and managed identities + # - JSON-based output for automation and scripting + # - Cross-platform compatibility for consistent workflows + # + # Security best practices: + # - Use 'az login --tenant' to explicitly specify tenants + # - Leverage managed identities where available + # - Apply RBAC with principle of least privilege + # - Use service principals with certificate-based auth for automation + # - Regularly update CLI using 'az upgrade' command + # - Avoid storing credentials in CLI cache in shared environments + # - Configure CLI to use your organization's approved proxy if required + # + # Common development scenarios: + # - Resource provisioning via templates and scripts + # - Querying resource status and configurations + # - Integrated deployment workflows with CI/CD + # - Management of secrets and connection strings + # + # DSC-specific notes: + # - WinGet ensures automatic updates to secure versions + # - Installation is idempotent and will not reinstall if present + # - Consider adding environment PATH validation during configuration testing + # + # Reference: https://learn.microsoft.com/en-us/cli/azure/ + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.AzureCLI + directives: + allowPrerelease: true + description: Install Azure CLI for managing Azure resources from the command line + settings: + id: Microsoft.AzureCLI + + # Resource: Azure Developer CLI (azd) + # Azure Developer CLI (azd) simplifies application development workflow with templates + # and integrated deployment capabilities. + # + # Key Azure integration points: + # - End-to-end application development lifecycle management + # - Built-in templates for common Azure architectural patterns + # - Automated environment provisioning with infrastructure as code + # - Integration with GitHub Actions and Azure DevOps for CI/CD + # - Application monitoring and logging setup + # + # Development best practices: + # - Use environment variables for secrets ('azd env set') + # - Leverage service templates for consistent architecture + # - Implement standardized application structures + # - Follow Azure landing zone principles for environments + # - Store azd environment configurations in version control + # - Use azd pipeline integration for repeatable deployments + # - Include .env.template file but exclude .env files from source control + # + # Common development scenarios: + # - Setting up complete development environments + # - Implementing production-ready services with best practices + # - Consistent local-to-cloud development experience + # - Orchestrating multi-service deployments + # + # DSC-specific notes: + # - Dependency on Azure CLI is explicitly declared + # - Installation validates presence of required prerequisites + # - Consider adding post-install validation of azd version + # + # Reference: https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/ + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.Azd + directives: + allowPrerelease: true + description: Install Azure Developer CLI (azd) for end-to-end application development + settings: + id: Microsoft.Azd + dependsOn: + - Microsoft.AzureCLI # AZD requires Azure CLI to function properly + + # Resource: Bicep CLI + # Bicep provides a domain-specific language for deploying Azure resources + # with improved syntax over ARM templates. + # + # Key Azure integration points: + # - Native integration with Azure Resource Manager + # - Support for all Azure resource types and apiVersions + # - Resource visualization capabilities + # - Module composition for reusable infrastructure + # - Built-in functions for dynamic deployments + # + # IaC best practices: + # - Use modules for reusable components + # - Implement parameterization for environment flexibility + # - Apply Azure Policy as Code for governance + # - Use symbolic references instead of string manipulation + # - Implement deployment validation with 'what-if' + # - Structure Bicep modules with clear separation of concerns + # - Validate Bicep files with 'bicep build' before deployment + # - Use linting tools to enforce conventions and best practices + # - Test deployments in isolation before integrating + # + # Common development scenarios: + # - Defining infrastructure as code for Azure environments + # - Creating reusable infrastructure modules + # - Setting up complex multi-resource deployments + # - Implementing infrastructure governance + # + # DSC-specific notes: + # - Bicep CLI is installed via Azure CLI extension + # - Installation updates to latest version automatically + # - Consider adding VS Code extension for Bicep in developer environments + # + # Reference: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/ + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.Bicep + directives: + allowPrerelease: true + description: Install Bicep CLI for Infrastructure as Code development on Azure + settings: + id: Microsoft.Bicep + dependsOn: + - Microsoft.AzureCLI # Bicep extensions use Azure CLI for deployment + + #---------------------------------------------- + # Local Development Emulators + #---------------------------------------------- + # Resource: Azure Storage Emulator + # Azure Storage Emulator provides a local environment for testing Azure Storage applications + # without requiring an Azure subscription for development. + # + # Key Azure integration points: + # - Local emulation of Blob, Queue, and Table storage + # - Development connection string compatibility with Azure Storage + # - Support for Azure Storage SDK integration + # - Local debugging of storage-dependent applications + # - Reduced development costs by minimizing cloud resource usage + # + # Development best practices: + # - Use consistent connection strings between local and cloud + # - Validate local operations match cloud behavior + # - Implement proper exception handling for both environments + # - Test with both emulator and actual Azure resources before deployment + # - Create automated tests that work with both environments + # - Consider using Azurite (newer emulator) for feature parity + # - Configure proper data persistence for development data + # - Document how to initialize the emulator in your project + # + # Common development scenarios: + # - Building applications using Azure Storage + # - Performing rapid iterative development + # - Unit and integration testing without cloud dependencies + # - Offline development scenarios + # - Cost optimization during development phases + # + # DSC-specific notes: + # - Installation may require administrator privileges + # - Consider validating emulator is running after installation + # - May need to configure firewall exceptions + # - Check SQL Server dependency is met (LocalDB) + # + # Reference: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.Azure.StorageEmulator + directives: + allowPrerelease: true + description: Install Azure Storage Emulator for local development + settings: + id: Microsoft.Azure.StorageEmulator diff --git a/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml index 394dbe7e..82c86bf6 100644 --- a/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-backend-usertasks-config.dsc.yaml @@ -1,333 +1,333 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Backend Development Configuration Sample for .NET Engineers -# ============================================= -# -# Purpose: -# This DSC configuration creates a complete Azure backend development environment with: -# - WSL2 with Ubuntu for Linux-based development -# - Azure CLI tools in both Windows and Ubuntu environments -# - .NET development tools in both environments -# - Container tools (Docker Desktop, Helm) for microservice development -# - API testing tools (Postman) -# -# Requirements: -# - with Windows 11 -# - Administrator access for tool installation -# - Internet connectivity for package downloads -# -# Security Considerations: -# - Uses secure communication channels for downloads -# - Applies principle of least privilege where possible -# - Uses official Microsoft installation methods -# -# Best Practices: -# - Uses Windows and WSL2 for a complete cross-platform experience -# - Applies principle of least privilege where possible -# - Maintains consistent environment between Windows and Linux -# - Uses official Microsoft installation methods for all tools -# -# Maintainer: DevExp Team -# -properties: - configurationVersion: 0.2.0 - - resources: - #---------------------------------------------- - # WSL2 Installation and Configuration - #---------------------------------------------- - # Windows Subsystem for Linux is the foundation for cross-platform development - # enabling Linux-based Azure services testing in a local environment - - resource: PSDscResources/Script - id: Microsoft-Windows-Subsystem-Linux - directives: - description: Enable Windows Features and Install Ubuntu for WSL - securityContext: elevated # Requires admin rights to enable Windows features - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - Write-Verbose "Installing Windows Subsystem for Linux (WSL)" -Verbose - wsl --install --no-launch 2>$null - } catch { - Write-Error "Failed to install WSL: $_" - } - GetScript: return $false - TestScript: return $false - - # Install Ubuntu distribution which provides a comprehensive Linux environment - # with broad Azure service compatibility and tool support - - resource: PSDscResources/Script - id: Ubuntu - directives: - description: Install Ubuntu for WSL - securityContext: elevated # Requires admin rights to install WSL distribution - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - Write-Verbose "Installing Ubuntu for WSL" -Verbose - wsl --install -d Ubuntu --no-launch 2>$null - } catch { - Write-Error "Failed to install Ubuntu: $_" - } - GetScript: return $false - TestScript: return $false - - # Configure user access to simplify development and avoid credential prompts - # while maintaining security through Windows authentication - - resource: PSDscResources/Script - id: Ubuntu.User - directives: - description: Add the current user as an administrator to Ubuntu - securityContext: elevated # Requires admin rights to configure WSL distribution - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - $newUser = $env:USERNAME.ToLower() - $password = "SecureP@ssw0rd" # Consider using a more secure password generation method - $escapedPassword = $password -replace '(["\\$`])', '\\$1' - # Create user account - wsl.exe -d Ubuntu -u root -- bash -c "sudo adduser --quiet --disabled-password --gecos '' $newUser" - # Set password - wsl.exe -d Ubuntu -u root -- bash -c "echo '${newUser}:${escapedPassword}' | sudo chpasswd" - # Add to sudo group - wsl.exe -d Ubuntu -u root -- bash -c "sudo usermod -aG sudo $newUser" - # Enable passwordless sudo for DevBox convenience - wsl.exe -d Ubuntu -u root -- bash -c "echo '$newUser ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/$newUser" - } catch { - Write-Error "Failed to add user as administrator: $_" - } - GetScript: return $false - TestScript: return $false - - # Update Ubuntu packages to ensure security patches and latest tool compatibility - # following Azure best practices for keeping development environments current - - resource: PSDscResources/Script - id: Ubuntu.Update - directives: - description: Update Ubuntu package repositories and install updates - securityContext: elevated # Requires admin rights to run WSL commands - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update && apt-get upgrade -y --quiet'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - } catch { - Write-Error "Failed to update Ubuntu packages: $_" - } - GetScript: return $false - TestScript: return $false - - #---------------------------------------------- - # Azure Development Tools for Ubuntu - #---------------------------------------------- - # Install Azure CLI tools in Ubuntu to provide cross-platform development capability - # and ensure consistent deployment behavior between local and cloud environments - - resource: PSDscResources/Script - id: Ubuntu.AzureCli - directives: - description: Install Azure CLI, Developer CLI and Bicep on Ubuntu - securityContext: elevated # Requires admin rights to run WSL commands - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - # Install curl as prerequisite - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install curl --quiet -y'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - # Install Azure CLI using official Microsoft script - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/InstallAzureCLIDeb | sudo bash'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - # Install Azure Developer CLI using official Microsoft script - $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/install-azd.sh | sudo bash'" - wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" - } catch { - Write-Error "Failed to install Azure CLI: $_" - } - GetScript: return $false - TestScript: return $false - - # Install .NET SDK and Runtime for cross-platform .NET development - # enabling development of Azure Functions, Web Apps, and microservices - - resource: PSDscResources/Script - id: Ubuntu.DotNetSDK - directives: - description: Install .NET 9 SDK and Runtime on Ubuntu - securityContext: elevated # Requires admin rights to run WSL commands - settings: - SetScript: | - try { - # Set execution policy for current process - Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue - # Ensure path is properly set - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - # Add Microsoft's PPA for .NET - $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; sudo add-apt-repository ppa:dotnet/backports -y'" - wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" - # Install .NET SDK for development - $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update --quiet && apt-get install --quiet -y dotnet-sdk-9.0'" - wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" - # Install ASP.NET Core Runtime for web applications - $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install --quiet -y aspnetcore-runtime-9.0'" - wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" - } catch { - Write-Error "Failed to install .NET SDK and Runtime: $_" - } - GetScript: return $false - TestScript: return $false - - #---------------------------------------------- - # Container and API Development Tools - #---------------------------------------------- - - # Resource: Docker Desktop - # Docker Desktop provides container management with Kubernetes integration - # essential for microservices development and container-based Azure services. - # - # This tool enables: - # - Local testing of containerized applications before Azure deployment - # - Integration with Azure Container Registry for image management - # - Local Kubernetes testing before AKS deployment - # - Development of container-based solutions for Azure App Service, ACI, and AKS - # - # Following Azure best practices by using the official package through WinGet. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Docker.DockerDesktop - directives: - description: Install Docker Desktop for container development and testing - allowPrerelease: true - securityContext: elevated # Requires admin rights to install system services - settings: - id: Docker.DockerDesktop - - # Resource: Helm Package Manager - # Helm simplifies Kubernetes application management with package-based deployments - # commonly used with Azure Kubernetes Service (AKS). - # - # This tool enables: - # - Consistent deployment of applications to AKS - # - Management of application release cycles - # - Simplified configuration of complex Kubernetes applications - # - Integration with Azure DevOps pipelines for CI/CD - # - # WinGet installation ensures the official package is used. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Helm.Helm - directives: - description: Install Helm for Kubernetes application deployment management - allowPrerelease: true - settings: - id: Helm.Helm - - # Resource: Postman API Testing Platform - # Postman enables API testing and documentation, ensuring reliable integrations - # between backend services and Azure APIs. - # - # This tool enables: - # - Testing of Azure Functions, Logic Apps, and API Management endpoints - # - Creation of comprehensive API test suites - # - Documentation of APIs for development teams - # - Automation of API testing for Azure integration scenarios - # - # WinGet installation follows Azure security best practices. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Postman.Postman - directives: - description: Install Postman API platform for designing, testing and documenting APIs - allowPrerelease: true - settings: - id: Postman.Postman - - # Resource: Visual Studio Code Extensions - # This resource installs essential VS Code extensions for Azure backend development. - # The extensions provide IntelliSense, debugging, and other features specific to - # Azure development workflows, ensuring a complete development environment. - # - # These extensions follow the Microsoft-recommended developer tooling for - # Azure cloud-native application development and DevOps practices. - - resource: PSDscResources/Script - id: Microsoft.VisualStudioCode.Extensions - directives: - description: Install VS Code Extensions for Azure backend development - securityContext: elevated # Required for system-wide extension installation - allowPrerelease: true # Allows latest preview features - settings: - SetScript: | - try { - # Set execution policy to bypass for automation purposes - # This is required for PowerShell script execution in restricted environments - Set-ExecutionPolicy Bypass -Scope Process -Force - - # Ensure VS Code is in the PATH for the code command - # Combines machine and user paths to ensure the command is found - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - - Write-Verbose "Installing VS Code extensions for Azure backend development..." -Verbose - - # PowerShell extension for scripting and Azure automation tasks - code --install-extension ms-vscode.powershell - - # WSL integration for cross-platform development between Windows/Linux - code --install-extension ms-vscode-remote.remote-wsl - - # C# development kit for .NET applications and Azure Functions - code --install-extension ms-dotnettools.csdevkit - - # TypeScript with latest features for modern API development - code --install-extension ms-vscode.vscode-typescript-next - - # YAML support for Kubernetes, ARM templates, and pipelines - code --install-extension redhat.vscode-yaml - - # Bicep for Azure infrastructure as code (recommended over ARM templates) - code --install-extension ms-azuretools.vscode-bicep - - # Azure Tools extension pack for comprehensive Azure development - code --install-extension ms-vscode.vscode-node-azure-pack - - # Azure CLI tools for command-line management of Azure resources - code --install-extension ms-vscode.azurecli - - # GitHub remote repositories for cloud-based development - code --install-extension GitHub.remotehub - - # GitHub Pull Requests for collaborative development - code --install-extension GitHub.vscode-pull-request-github - - # Docker extension for container management and development - code --install-extension ms-azuretools.vscode-docker - - # Kubernetes extension for managing Kubernetes clusters and resources - code --install-extension ms-kubernetes-tools.vscode-kubernetes-tools - - # Postman extension for API testing and documentation - code --install-extension Postman.postman-for-vscode - - Write-Verbose "VS Code extensions installed successfully" -Verbose - } catch { - Write-Error "Failed to install VS Code extensions: $_" - } - # In a fully idempotent implementation, this would return the current state - # of installed extensions for reporting and validation - GetScript: return $false - # In production environments, TestScript would check if specific - # extensions are already installed to avoid unnecessary reinstallation - TestScript: return $false +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Backend Development Configuration Sample for .NET Engineers +# ============================================= +# +# Purpose: +# This DSC configuration creates a complete Azure backend development environment with: +# - WSL2 with Ubuntu for Linux-based development +# - Azure CLI tools in both Windows and Ubuntu environments +# - .NET development tools in both environments +# - Container tools (Docker Desktop, Helm) for microservice development +# - API testing tools (Postman) +# +# Requirements: +# - with Windows 11 +# - Administrator access for tool installation +# - Internet connectivity for package downloads +# +# Security Considerations: +# - Uses secure communication channels for downloads +# - Applies principle of least privilege where possible +# - Uses official Microsoft installation methods +# +# Best Practices: +# - Uses Windows and WSL2 for a complete cross-platform experience +# - Applies principle of least privilege where possible +# - Maintains consistent environment between Windows and Linux +# - Uses official Microsoft installation methods for all tools +# +# Maintainer: DevExp Team +# +properties: + configurationVersion: 0.2.0 + + resources: + #---------------------------------------------- + # WSL2 Installation and Configuration + #---------------------------------------------- + # Windows Subsystem for Linux is the foundation for cross-platform development + # enabling Linux-based Azure services testing in a local environment + - resource: PSDscResources/Script + id: Microsoft-Windows-Subsystem-Linux + directives: + description: Enable Windows Features and Install Ubuntu for WSL + securityContext: elevated # Requires admin rights to enable Windows features + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + Write-Verbose "Installing Windows Subsystem for Linux (WSL)" -Verbose + wsl --install --no-launch 2>$null + } catch { + Write-Error "Failed to install WSL: $_" + } + GetScript: return $false + TestScript: return $false + + # Install Ubuntu distribution which provides a comprehensive Linux environment + # with broad Azure service compatibility and tool support + - resource: PSDscResources/Script + id: Ubuntu + directives: + description: Install Ubuntu for WSL + securityContext: elevated # Requires admin rights to install WSL distribution + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + Write-Verbose "Installing Ubuntu for WSL" -Verbose + wsl --install -d Ubuntu --no-launch 2>$null + } catch { + Write-Error "Failed to install Ubuntu: $_" + } + GetScript: return $false + TestScript: return $false + + # Configure user access to simplify development and avoid credential prompts + # while maintaining security through Windows authentication + - resource: PSDscResources/Script + id: Ubuntu.User + directives: + description: Add the current user as an administrator to Ubuntu + securityContext: elevated # Requires admin rights to configure WSL distribution + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + $newUser = $env:USERNAME.ToLower() + $password = "SecureP@ssw0rd" # Consider using a more secure password generation method + $escapedPassword = $password -replace '(["\\$`])', '\\$1' + # Create user account + wsl.exe -d Ubuntu -u root -- bash -c "sudo adduser --quiet --disabled-password --gecos '' $newUser" + # Set password + wsl.exe -d Ubuntu -u root -- bash -c "echo '${newUser}:${escapedPassword}' | sudo chpasswd" + # Add to sudo group + wsl.exe -d Ubuntu -u root -- bash -c "sudo usermod -aG sudo $newUser" + # Enable passwordless sudo for DevBox convenience + wsl.exe -d Ubuntu -u root -- bash -c "echo '$newUser ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/$newUser" + } catch { + Write-Error "Failed to add user as administrator: $_" + } + GetScript: return $false + TestScript: return $false + + # Update Ubuntu packages to ensure security patches and latest tool compatibility + # following Azure best practices for keeping development environments current + - resource: PSDscResources/Script + id: Ubuntu.Update + directives: + description: Update Ubuntu package repositories and install updates + securityContext: elevated # Requires admin rights to run WSL commands + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update && apt-get upgrade -y --quiet'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + } catch { + Write-Error "Failed to update Ubuntu packages: $_" + } + GetScript: return $false + TestScript: return $false + + #---------------------------------------------- + # Azure Development Tools for Ubuntu + #---------------------------------------------- + # Install Azure CLI tools in Ubuntu to provide cross-platform development capability + # and ensure consistent deployment behavior between local and cloud environments + - resource: PSDscResources/Script + id: Ubuntu.AzureCli + directives: + description: Install Azure CLI, Developer CLI and Bicep on Ubuntu + securityContext: elevated # Requires admin rights to run WSL commands + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + # Install curl as prerequisite + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install curl --quiet -y'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + # Install Azure CLI using official Microsoft script + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/InstallAzureCLIDeb | sudo bash'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + # Install Azure Developer CLI using official Microsoft script + $updateCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; curl -fsSL https://aka.ms/install-azd.sh | sudo bash'" + wsl.exe -d Ubuntu -u root -- bash -c "$updateCommand" + } catch { + Write-Error "Failed to install Azure CLI: $_" + } + GetScript: return $false + TestScript: return $false + + # Install .NET SDK and Runtime for cross-platform .NET development + # enabling development of Azure Functions, Web Apps, and microservices + - resource: PSDscResources/Script + id: Ubuntu.DotNetSDK + directives: + description: Install .NET 9 SDK and Runtime on Ubuntu + securityContext: elevated # Requires admin rights to run WSL commands + settings: + SetScript: | + try { + # Set execution policy for current process + Set-ExecutionPolicy Bypass -Scope Process -Force -ErrorAction SilentlyContinue + # Ensure path is properly set + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + # Add Microsoft's PPA for .NET + $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; sudo add-apt-repository ppa:dotnet/backports -y'" + wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" + # Install .NET SDK for development + $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update --quiet && apt-get install --quiet -y dotnet-sdk-9.0'" + wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" + # Install ASP.NET Core Runtime for web applications + $installDotnetCommand = "sudo bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get install --quiet -y aspnetcore-runtime-9.0'" + wsl.exe -d Ubuntu -u root -- bash -c "$installDotnetCommand" + } catch { + Write-Error "Failed to install .NET SDK and Runtime: $_" + } + GetScript: return $false + TestScript: return $false + + #---------------------------------------------- + # Container and API Development Tools + #---------------------------------------------- + + # Resource: Docker Desktop + # Docker Desktop provides container management with Kubernetes integration + # essential for microservices development and container-based Azure services. + # + # This tool enables: + # - Local testing of containerized applications before Azure deployment + # - Integration with Azure Container Registry for image management + # - Local Kubernetes testing before AKS deployment + # - Development of container-based solutions for Azure App Service, ACI, and AKS + # + # Following Azure best practices by using the official package through WinGet. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Docker.DockerDesktop + directives: + description: Install Docker Desktop for container development and testing + allowPrerelease: true + securityContext: elevated # Requires admin rights to install system services + settings: + id: Docker.DockerDesktop + + # Resource: Helm Package Manager + # Helm simplifies Kubernetes application management with package-based deployments + # commonly used with Azure Kubernetes Service (AKS). + # + # This tool enables: + # - Consistent deployment of applications to AKS + # - Management of application release cycles + # - Simplified configuration of complex Kubernetes applications + # - Integration with Azure DevOps pipelines for CI/CD + # + # WinGet installation ensures the official package is used. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Helm.Helm + directives: + description: Install Helm for Kubernetes application deployment management + allowPrerelease: true + settings: + id: Helm.Helm + + # Resource: Postman API Testing Platform + # Postman enables API testing and documentation, ensuring reliable integrations + # between backend services and Azure APIs. + # + # This tool enables: + # - Testing of Azure Functions, Logic Apps, and API Management endpoints + # - Creation of comprehensive API test suites + # - Documentation of APIs for development teams + # - Automation of API testing for Azure integration scenarios + # + # WinGet installation follows Azure security best practices. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Postman.Postman + directives: + description: Install Postman API platform for designing, testing and documenting APIs + allowPrerelease: true + settings: + id: Postman.Postman + + # Resource: Visual Studio Code Extensions + # This resource installs essential VS Code extensions for Azure backend development. + # The extensions provide IntelliSense, debugging, and other features specific to + # Azure development workflows, ensuring a complete development environment. + # + # These extensions follow the Microsoft-recommended developer tooling for + # Azure cloud-native application development and DevOps practices. + - resource: PSDscResources/Script + id: Microsoft.VisualStudioCode.Extensions + directives: + description: Install VS Code Extensions for Azure backend development + securityContext: elevated # Required for system-wide extension installation + allowPrerelease: true # Allows latest preview features + settings: + SetScript: | + try { + # Set execution policy to bypass for automation purposes + # This is required for PowerShell script execution in restricted environments + Set-ExecutionPolicy Bypass -Scope Process -Force + + # Ensure VS Code is in the PATH for the code command + # Combines machine and user paths to ensure the command is found + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + + Write-Verbose "Installing VS Code extensions for Azure backend development..." -Verbose + + # PowerShell extension for scripting and Azure automation tasks + code --install-extension ms-vscode.powershell + + # WSL integration for cross-platform development between Windows/Linux + code --install-extension ms-vscode-remote.remote-wsl + + # C# development kit for .NET applications and Azure Functions + code --install-extension ms-dotnettools.csdevkit + + # TypeScript with latest features for modern API development + code --install-extension ms-vscode.vscode-typescript-next + + # YAML support for Kubernetes, ARM templates, and pipelines + code --install-extension redhat.vscode-yaml + + # Bicep for Azure infrastructure as code (recommended over ARM templates) + code --install-extension ms-azuretools.vscode-bicep + + # Azure Tools extension pack for comprehensive Azure development + code --install-extension ms-vscode.vscode-node-azure-pack + + # Azure CLI tools for command-line management of Azure resources + code --install-extension ms-vscode.azurecli + + # GitHub remote repositories for cloud-based development + code --install-extension GitHub.remotehub + + # GitHub Pull Requests for collaborative development + code --install-extension GitHub.vscode-pull-request-github + + # Docker extension for container management and development + code --install-extension ms-azuretools.vscode-docker + + # Kubernetes extension for managing Kubernetes clusters and resources + code --install-extension ms-kubernetes-tools.vscode-kubernetes-tools + + # Postman extension for API testing and documentation + code --install-extension Postman.postman-for-vscode + + Write-Verbose "VS Code extensions installed successfully" -Verbose + } catch { + Write-Error "Failed to install VS Code extensions: $_" + } + # In a fully idempotent implementation, this would return the current state + # of installed extensions for reporting and validation + GetScript: return $false + # In production environments, TestScript would check if specific + # extensions are already installed to avoid unnecessary reinstallation + TestScript: return $false diff --git a/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml index a897ca8b..ac95889a 100644 --- a/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-config.dsc.yaml @@ -1,160 +1,160 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Common Configuration Sample for .NET Engineers -# ================================= -# -# Purpose: -# This DSC configuration sets up a standard development environment with: -# - Development storage using Dev Drive -# - Source control tools (Git, GitHub CLI) -# - Development runtimes (.NET 9 SDK and Runtime) -# - Development tools (VS Code, Node.js) -# -# Prerequisites: -# - Windows 10/11 with admin privileges -# - Internet connectivity for package downloads -# -# Maintainers: DevExp Team - -properties: - configurationVersion: "0.2.0" - resources: - #---------------------------------------------- - # Source Control Tools - #---------------------------------------------- - # Resource: Git - # Git is essential for workflows, enabling developers to work with - # source code repositories, CI/CD pipelines, and infrastructure as code. - # - # Azure-specific considerations: - # - Supports Azure DevOps repositories and GitHub integration - # - Required for Azure Bicep template development and versioning - # - Enables GitOps workflows with Azure Arc and Azure Kubernetes Service - # - Optimized when used with Dev Drive for performance - # - # Will be used by: - # - Azure CLI when working with Azure Repos - # - GitHub CLI for GitHub-hosted Azure projects - # - Azure Developer CLI (azd) for project templates - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Git.Git - directives: - allowPrerelease: true - description: Install Git version control system - settings: - id: Git.Git - - # Resource: GitHub CLI - # GitHub CLI streamlines GitHub workflow automation directly from the terminal, - # enabling efficient Azure DevOps integration and GitHub Actions management. - # - # Azure-specific benefits: - # - Manage GitHub repositories that host Azure infrastructure code - # - Create and manage GitHub Actions workflows for Azure deployments - # - Work with GitHub issues and pull requests for Azure service development - # - Authenticate to GitHub Container Registry for Azure container deployments - # - # Security note: - # - Authentication tokens are stored securely in Windows Credential Manager - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: GitHub.cli - directives: - allowPrerelease: true - description: Install GitHub command-line interface - settings: - id: GitHub.cli - dependsOn: - - Git.Git # Requires Git for full functionality - - #---------------------------------------------- - # Development Runtimes - #---------------------------------------------- - # Resource: .NET SDK 9 - # The .NET SDK is core to Azure development, providing the tools needed to build, - # test, and deploy applications targeting Azure services. - # - # Azure-specific features: - # - Azure SDK integration for Azure services - # - Built-in templates for Azure Functions and Web Apps - # - Support for containerized applications on Azure Container Apps - # - Tools for Azure Active Directory integration - # - Azure-optimized middleware components - # - # Performance note: - # - Benefits from Dev Drive configuration for large solution files - # - Includes .NET CLI tools for Azure Functions development - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.DotNet.SDK.9 - directives: - allowPrerelease: true - description: Install .NET 9 SDK for application development - settings: - id: Microsoft.DotNet.SDK.9 - - # Resource: .NET Runtime 9 - # The .NET Runtime enables execution of .NET applications and is required - # for running Azure tooling that depends on .NET. - # - # Azure-specific considerations: - # - Required by many Azure command-line tools (Azure PowerShell, etc.) - # - Supports running Azure Functions core tools locally - # - Needed for Azure Storage Emulator and other local emulators - # - Enables testing of containerized .NET applications before Azure deployment - # - # Note: While included in the SDK, explicit installation ensures availability - # for tools that may require the runtime directly - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.DotNet.Runtime.9 - directives: - allowPrerelease: true - description: Install .NET 9 Runtime - settings: - id: Microsoft.DotNet.Runtime.9 - dependsOn: - - Microsoft.DotNet.SDK.9 # Runtime is included in SDK, but explicit dependency ensures correct order - - # Resource: Node.js - # Node.js is a JavaScript runtime environment essential for modern web development - # and crucial for many Azure development scenarios. - # - # Azure-specific benefits: - # - Required for Azure Static Web Apps local development - # - Used by Azure Functions for JavaScript/TypeScript functions - # - Powers npm packages for Azure SDK for JavaScript - # - Enables local development of Azure App Service Node.js applications - # - Required for many Azure DevOps build pipelines - # - # Security note: - # - WinGet installation ensures secure, verified package source - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: OpenJS.NodeJS - directives: - allowPrerelease: true - description: Install Node.js JavaScript runtime - settings: - id: OpenJS.NodeJS - - #---------------------------------------------- - # Development Tools - #---------------------------------------------- - # Resource: Visual Studio Code - # VS Code is Microsoft's recommended editor for Azure development with - # extensive Azure service integration through extensions. - # - # Azure-specific capabilities: - # - Direct Azure resource management through Azure extensions - # - Integrated terminal for Azure CLI and PowerShell commands - # - Azure Functions local development and debugging - # - Azure App Service deployment integration - # - Cosmos DB explorer and Storage explorer integrations - # - ARM template and Bicep authoring and validation - # - # Note: Additional Azure extensions will be installed in separate configurations - # to provide targeted functionality for specific development scenarios - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Microsoft.VisualStudioCode - directives: - allowPrerelease: true - description: Install Visual Studio Code editor - settings: - id: Microsoft.VisualStudioCode +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Common Configuration Sample for .NET Engineers +# ================================= +# +# Purpose: +# This DSC configuration sets up a standard development environment with: +# - Development storage using Dev Drive +# - Source control tools (Git, GitHub CLI) +# - Development runtimes (.NET 9 SDK and Runtime) +# - Development tools (VS Code, Node.js) +# +# Prerequisites: +# - Windows 10/11 with admin privileges +# - Internet connectivity for package downloads +# +# Maintainers: DevExp Team + +properties: + configurationVersion: "0.2.0" + resources: + #---------------------------------------------- + # Source Control Tools + #---------------------------------------------- + # Resource: Git + # Git is essential for workflows, enabling developers to work with + # source code repositories, CI/CD pipelines, and infrastructure as code. + # + # Azure-specific considerations: + # - Supports Azure DevOps repositories and GitHub integration + # - Required for Azure Bicep template development and versioning + # - Enables GitOps workflows with Azure Arc and Azure Kubernetes Service + # - Optimized when used with Dev Drive for performance + # + # Will be used by: + # - Azure CLI when working with Azure Repos + # - GitHub CLI for GitHub-hosted Azure projects + # - Azure Developer CLI (azd) for project templates + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Git.Git + directives: + allowPrerelease: true + description: Install Git version control system + settings: + id: Git.Git + + # Resource: GitHub CLI + # GitHub CLI streamlines GitHub workflow automation directly from the terminal, + # enabling efficient Azure DevOps integration and GitHub Actions management. + # + # Azure-specific benefits: + # - Manage GitHub repositories that host Azure infrastructure code + # - Create and manage GitHub Actions workflows for Azure deployments + # - Work with GitHub issues and pull requests for Azure service development + # - Authenticate to GitHub Container Registry for Azure container deployments + # + # Security note: + # - Authentication tokens are stored securely in Windows Credential Manager + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: GitHub.cli + directives: + allowPrerelease: true + description: Install GitHub command-line interface + settings: + id: GitHub.cli + dependsOn: + - Git.Git # Requires Git for full functionality + + #---------------------------------------------- + # Development Runtimes + #---------------------------------------------- + # Resource: .NET SDK 9 + # The .NET SDK is core to Azure development, providing the tools needed to build, + # test, and deploy applications targeting Azure services. + # + # Azure-specific features: + # - Azure SDK integration for Azure services + # - Built-in templates for Azure Functions and Web Apps + # - Support for containerized applications on Azure Container Apps + # - Tools for Azure Active Directory integration + # - Azure-optimized middleware components + # + # Performance note: + # - Benefits from Dev Drive configuration for large solution files + # - Includes .NET CLI tools for Azure Functions development + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.DotNet.SDK.9 + directives: + allowPrerelease: true + description: Install .NET 9 SDK for application development + settings: + id: Microsoft.DotNet.SDK.9 + + # Resource: .NET Runtime 9 + # The .NET Runtime enables execution of .NET applications and is required + # for running Azure tooling that depends on .NET. + # + # Azure-specific considerations: + # - Required by many Azure command-line tools (Azure PowerShell, etc.) + # - Supports running Azure Functions core tools locally + # - Needed for Azure Storage Emulator and other local emulators + # - Enables testing of containerized .NET applications before Azure deployment + # + # Note: While included in the SDK, explicit installation ensures availability + # for tools that may require the runtime directly + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.DotNet.Runtime.9 + directives: + allowPrerelease: true + description: Install .NET 9 Runtime + settings: + id: Microsoft.DotNet.Runtime.9 + dependsOn: + - Microsoft.DotNet.SDK.9 # Runtime is included in SDK, but explicit dependency ensures correct order + + # Resource: Node.js + # Node.js is a JavaScript runtime environment essential for modern web development + # and crucial for many Azure development scenarios. + # + # Azure-specific benefits: + # - Required for Azure Static Web Apps local development + # - Used by Azure Functions for JavaScript/TypeScript functions + # - Powers npm packages for Azure SDK for JavaScript + # - Enables local development of Azure App Service Node.js applications + # - Required for many Azure DevOps build pipelines + # + # Security note: + # - WinGet installation ensures secure, verified package source + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: OpenJS.NodeJS + directives: + allowPrerelease: true + description: Install Node.js JavaScript runtime + settings: + id: OpenJS.NodeJS + + #---------------------------------------------- + # Development Tools + #---------------------------------------------- + # Resource: Visual Studio Code + # VS Code is Microsoft's recommended editor for Azure development with + # extensive Azure service integration through extensions. + # + # Azure-specific capabilities: + # - Direct Azure resource management through Azure extensions + # - Integrated terminal for Azure CLI and PowerShell commands + # - Azure Functions local development and debugging + # - Azure App Service deployment integration + # - Cosmos DB explorer and Storage explorer integrations + # - ARM template and Bicep authoring and validation + # + # Note: Additional Azure extensions will be installed in separate configurations + # to provide targeted functionality for specific development scenarios + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Microsoft.VisualStudioCode + directives: + allowPrerelease: true + description: Install Visual Studio Code editor + settings: + id: Microsoft.VisualStudioCode diff --git a/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml b/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml index 9b5851d4..f4e12a7f 100644 --- a/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml +++ b/.configuration/devcenter/workloads/ADO/common-frontend-usertasks-config.dsc.yaml @@ -1,114 +1,114 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Frontend Development Configuration Sample for .NET Engineers -# ============================================= -# -# Purpose: -# This DSC configuration creates a complete Azure Frontend development environment with: -# - .NET development tools in both environments -# - API testing tools (Postman) -# -# Requirements: -# - with Windows 11 -# - Administrator access for tool installation -# - Internet connectivity for package downloads -# -# Best Practices: -# - Uses Windows and WSL2 for a complete cross-platform experience -# - Applies principle of least privilege where possible -# - Maintains consistent environment between Windows and Linux -# - Uses official Microsoft installation methods for all tools -# -# Maintainer: DevExp Team -# -properties: - configurationVersion: 0.2.0 - - resources: - # Resource: Postman API Platform - # Postman enables API testing and documentation, ensuring reliable integrations - # between Frontend services and Azure APIs. This tool is essential for: - # - Testing REST API endpoints from Azure services - # - Creating and managing API collections for consistent testing - # - Generating API documentation for team collaboration - # - Setting up automated test suites for frontend-to-backend integration - # - # Using WinGet ensures we install the official, verified package. - - resource: Microsoft.WinGet.DSC/WinGetPackage - id: Postman.Postman - directives: - description: Install Postman API platform for designing, testing and documenting APIs - allowPrerelease: true - settings: - id: Postman.Postman - - # Resource: Visual Studio Code Extensions - # Installs essential extensions for frontend development with Azure integration. - # These extensions provide syntax highlighting, IntelliSense, debugging, and other - # features specific to Azure and web development technologies. - # - # Following best practices for developer productivity and - # standardized tooling across development teams. - - resource: PSDscResources/Script - id: Microsoft.VisualStudioCode.Extensions - directives: - description: Install VS Code Extensions for Azure frontend development - securityContext: elevated # Requires admin rights to install extensions for all users - allowPrerelease: true # Allow prerelease versions when needed for latest features - settings: - SetScript: | - try { - # Set execution policy to bypass for automation - # Required for running scripts in restricted environments - Set-ExecutionPolicy Bypass -Scope Process -Force - - # Ensure VS Code CLI is in PATH - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - - Write-Verbose "Installing VS Code extensions for Azure frontend development..." -Verbose - - # PowerShell extension for Azure automation scripts and DevOps - code --install-extension ms-vscode.powershell - - # WSL integration for cross-platform development targeting Linux environments - code --install-extension ms-vscode-remote.remote-wsl - - # C# extension for backend integration and full-stack development - code --install-extension ms-dotnettools.csdevkit - - # TypeScript with latest features for modern web development - code --install-extension ms-vscode.vscode-typescript-next - - # YAML support for deployment configurations and GitHub Actions - code --install-extension redhat.vscode-yaml - - # Bicep for Azure infrastructure as code deployments - # Follows Azure best practice for IaC with native Azure syntax - code --install-extension ms-azuretools.vscode-bicep - - # Azure Tools pack for comprehensive Azure service integration - code --install-extension ms-vscode.vscode-node-azure-pack - - # Azure CLI tools for command-line Azure management - code --install-extension ms-vscode.azurecli - - # GitHub integration for repository management - # Essential for modern DevOps workflows with Azure - code --install-extension GitHub.remotehub - - # GitHub Pull Request integration for code reviews - code --install-extension GitHub.vscode-pull-request-github - - # Postman extension for API testing and documentation - code --install-extension Postman.postman-for-vscode - - Write-Verbose "VS Code extensions installed successfully" -Verbose - } catch { - Write-Error "Failed to install VS Code extensions: $_" - } - # Returns information about installed extensions - # In a fully idempotent implementation, this would check for specific extensions - GetScript: return $false - # Check if VSCode extensions need to be installed - # In a fully idempotent implementation, this would check for specific extensions - TestScript: return $false +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Frontend Development Configuration Sample for .NET Engineers +# ============================================= +# +# Purpose: +# This DSC configuration creates a complete Azure Frontend development environment with: +# - .NET development tools in both environments +# - API testing tools (Postman) +# +# Requirements: +# - with Windows 11 +# - Administrator access for tool installation +# - Internet connectivity for package downloads +# +# Best Practices: +# - Uses Windows and WSL2 for a complete cross-platform experience +# - Applies principle of least privilege where possible +# - Maintains consistent environment between Windows and Linux +# - Uses official Microsoft installation methods for all tools +# +# Maintainer: DevExp Team +# +properties: + configurationVersion: 0.2.0 + + resources: + # Resource: Postman API Platform + # Postman enables API testing and documentation, ensuring reliable integrations + # between Frontend services and Azure APIs. This tool is essential for: + # - Testing REST API endpoints from Azure services + # - Creating and managing API collections for consistent testing + # - Generating API documentation for team collaboration + # - Setting up automated test suites for frontend-to-backend integration + # + # Using WinGet ensures we install the official, verified package. + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: Postman.Postman + directives: + description: Install Postman API platform for designing, testing and documenting APIs + allowPrerelease: true + settings: + id: Postman.Postman + + # Resource: Visual Studio Code Extensions + # Installs essential extensions for frontend development with Azure integration. + # These extensions provide syntax highlighting, IntelliSense, debugging, and other + # features specific to Azure and web development technologies. + # + # Following best practices for developer productivity and + # standardized tooling across development teams. + - resource: PSDscResources/Script + id: Microsoft.VisualStudioCode.Extensions + directives: + description: Install VS Code Extensions for Azure frontend development + securityContext: elevated # Requires admin rights to install extensions for all users + allowPrerelease: true # Allow prerelease versions when needed for latest features + settings: + SetScript: | + try { + # Set execution policy to bypass for automation + # Required for running scripts in restricted environments + Set-ExecutionPolicy Bypass -Scope Process -Force + + # Ensure VS Code CLI is in PATH + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + + Write-Verbose "Installing VS Code extensions for Azure frontend development..." -Verbose + + # PowerShell extension for Azure automation scripts and DevOps + code --install-extension ms-vscode.powershell + + # WSL integration for cross-platform development targeting Linux environments + code --install-extension ms-vscode-remote.remote-wsl + + # C# extension for backend integration and full-stack development + code --install-extension ms-dotnettools.csdevkit + + # TypeScript with latest features for modern web development + code --install-extension ms-vscode.vscode-typescript-next + + # YAML support for deployment configurations and GitHub Actions + code --install-extension redhat.vscode-yaml + + # Bicep for Azure infrastructure as code deployments + # Follows Azure best practice for IaC with native Azure syntax + code --install-extension ms-azuretools.vscode-bicep + + # Azure Tools pack for comprehensive Azure service integration + code --install-extension ms-vscode.vscode-node-azure-pack + + # Azure CLI tools for command-line Azure management + code --install-extension ms-vscode.azurecli + + # GitHub integration for repository management + # Essential for modern DevOps workflows with Azure + code --install-extension GitHub.remotehub + + # GitHub Pull Request integration for code reviews + code --install-extension GitHub.vscode-pull-request-github + + # Postman extension for API testing and documentation + code --install-extension Postman.postman-for-vscode + + Write-Verbose "VS Code extensions installed successfully" -Verbose + } catch { + Write-Error "Failed to install VS Code extensions: $_" + } + # Returns information about installed extensions + # In a fully idempotent implementation, this would check for specific extensions + GetScript: return $false + # Check if VSCode extensions need to be installed + # In a fully idempotent implementation, this would check for specific extensions + TestScript: return $false diff --git a/.configuration/devcenter/workloads/winget-update.ps1 b/.configuration/devcenter/workloads/winget-update.ps1 index 8af7b78c..1c4f5db3 100644 --- a/.configuration/devcenter/workloads/winget-update.ps1 +++ b/.configuration/devcenter/workloads/winget-update.ps1 @@ -1,147 +1,147 @@ -Set-ExecutionPolicy Bypass -Scope Process -Force -Clear-Host - -<# -.SYNOPSIS - Quietly updates all Microsoft Store apps using winget (v1.11.x compatible). - -.DESCRIPTION - - Runs fully non-interactive (no prompts). - - Properly orders command + flags for winget 1.11.x. - - Accepts msstore/package agreements on upgrade/install only. - - Uses include-unknown and a forced second pass to catch stubborn Store apps. - - Detects if winget/App Installer updated itself mid-run and retries once. - - Executes winget by absolute path (no App Execution Alias quirks). - - Logs to C:\ProgramData\Winget-StoreUpgrade\upgrade-YYYYMMDD-HHMMSS.log - -.NOTES - Recommended to run in an elevated session to service machine-wide apps. -#> - -# ===== Global non-interactive settings ===== -$ErrorActionPreference = 'Stop' -$ProgressPreference = 'SilentlyContinue' -$env:WINGET_DISABLE_INTERACTIVITY = '1' # environment-based; no CLI side effects - -# ===== Logging ===== -$LogRoot = Join-Path $env:ProgramData 'Winget-StoreUpgrade' -if (-not (Test-Path $LogRoot)) { New-Item -Path $LogRoot -ItemType Directory -Force | Out-Null } -$Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' -$LogFile = Join-Path $LogRoot "upgrade-$Timestamp.log" - -function Write-Info { param([string]$m) "[INFO ] $m" | Tee-Object -FilePath $LogFile -Append } -function Write-Warn { param([string]$m) "[WARN ] $m" | Tee-Object -FilePath $LogFile -Append } -function Write-Err { param([string]$m) "[ERROR] $m" | Tee-Object -FilePath $LogFile -Append } - -Write-Info "Log file: $LogFile" -Write-Info "Starting Microsoft Store updates..." - -# ===== Robust winget resolution and invoker ===== -function Resolve-WinGetExe { - # Prefer packaged App Installer location (more stable than user alias) - $pkg = Get-AppxPackage -Name Microsoft.DesktopAppInstaller -ErrorAction SilentlyContinue - if ($pkg) { - $candidate = Join-Path $pkg.InstallLocation 'winget.exe' - if (Test-Path $candidate) { return $candidate } - } - # Fallback to whatever PowerShell resolves - return (Get-Command winget.exe -ErrorAction Stop).Source -} - -$script:WinGetExe = Resolve-WinGetExe - -function Invoke-WinGet { - param( - [Parameter(Mandatory)][string[]]$Args, # e.g. @('upgrade','--all',...) - [switch]$RetryOnSelfUpdate # retry once if winget self-updated - ) - # Execute the EXE directly — do NOT call 'winget' then pass a path - $output = & $script:WinGetExe @Args 2>&1 - $text = $output | Out-String - $text | Tee-Object -FilePath $LogFile -Append | Out-Null - - if ($RetryOnSelfUpdate -and $text -match 'Restart the application to complete the upgrade') { - Write-Info "winget/App Installer updated itself; re-resolving path and retrying once..." - Start-Sleep -Milliseconds 500 - $script:WinGetExe = Resolve-WinGetExe - $output = & $script:WinGetExe @Args 2>&1 - $text = $output | Out-String - $text | Tee-Object -FilePath $LogFile -Append | Out-Null - } - return $text -} - -# ===== Preflight: confirm winget exists ===== -try { - Invoke-WinGet -Args @('--version') | Out-Null -} catch { - Write-Err "winget (App Installer) not found. Install/update 'App Installer' from Microsoft Store and re-run." - exit 1 -} - -# ===== Ensure Microsoft Store Install Service is running (helps Store updates) ===== -try { - $svc = Get-Service -Name InstallService -ErrorAction SilentlyContinue - if ($svc -and $svc.Status -ne 'Running') { - Write-Info "Starting Microsoft Store Install Service (InstallService)..." - Start-Service -Name InstallService -ErrorAction SilentlyContinue - } -} catch { Write-Warn "Could not verify/start InstallService. Continuing..." } - -# ===== Ensure msstore source exists and refresh (NO accept flags here) ===== -try { - $sources = Invoke-WinGet -Args @('source','list','--disable-interactivity') - if ($sources -notmatch '(?im)^\s*msstore\b') { - Write-Info "msstore source not found; resetting..." - Invoke-WinGet -Args @('source','reset','--force','msstore','--disable-interactivity') - } - Invoke-WinGet -Args @('source','update','--disable-interactivity') | Out-Null -} catch { - Write-Warn "Winget source operations reported issues; continuing..." -} - -# ===== Optional: prevent winget self-upgrade during the session (pin App Installer) ===== -# (Uncomment if you want to avoid mid-run self-update entirely) -# Invoke-WinGet -Args @('pin','add','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null - -# ===== PASS 1: Accept msstore terms & upgrade what winget can detect ===== -Write-Info "Pass 1: upgrading Microsoft Store apps (include-unknown)..." -Invoke-WinGet -Args @( - 'upgrade','--all', - '--source','msstore', - '--include-unknown', - '--silent', - '--accept-source-agreements','--accept-package-agreements', - '--disable-interactivity' -) -RetryOnSelfUpdate | Out-Null - -# ===== PASS 2: Force re-install latest for stragglers where version compare is unknown ===== -Write-Info "Pass 2: forced upgrade (msstore) for remaining/unknown version apps..." -Invoke-WinGet -Args @( - 'upgrade','--all', - '--source','msstore', - '--include-unknown', - '--force', - '--silent', - '--accept-source-agreements','--accept-package-agreements', - '--disable-interactivity' -) -RetryOnSelfUpdate | Out-Null - -# ===== OPTIONAL Safety net pass: catch apps mapped under other sources ===== -Write-Info "Safety net: unfiltered pass to catch any remaining packages..." -Invoke-WinGet -Args @( - 'upgrade','--all', - '--include-unknown', - '--silent', - '--accept-source-agreements','--accept-package-agreements', - '--disable-interactivity' -) -RetryOnSelfUpdate | Out-Null - -# ===== Summary: show any remaining Store upgrades (no accept flags here) ===== -Write-Info "Summary check for remaining Microsoft Store upgrades..." -Invoke-WinGet -Args @('upgrade','--source','msstore','--disable-interactivity') | Out-Null - -# ===== Optional: unpin App Installer after run ===== -# Invoke-WinGet -Args @('pin','remove','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null - -Write-Info "Completed. Full log: $LogFile" +Set-ExecutionPolicy Bypass -Scope Process -Force +Clear-Host + +<# +.SYNOPSIS + Quietly updates all Microsoft Store apps using winget (v1.11.x compatible). + +.DESCRIPTION + - Runs fully non-interactive (no prompts). + - Properly orders command + flags for winget 1.11.x. + - Accepts msstore/package agreements on upgrade/install only. + - Uses include-unknown and a forced second pass to catch stubborn Store apps. + - Detects if winget/App Installer updated itself mid-run and retries once. + - Executes winget by absolute path (no App Execution Alias quirks). + - Logs to C:\ProgramData\Winget-StoreUpgrade\upgrade-YYYYMMDD-HHMMSS.log + +.NOTES + Recommended to run in an elevated session to service machine-wide apps. +#> + +# ===== Global non-interactive settings ===== +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' +$env:WINGET_DISABLE_INTERACTIVITY = '1' # environment-based; no CLI side effects + +# ===== Logging ===== +$LogRoot = Join-Path $env:ProgramData 'Winget-StoreUpgrade' +if (-not (Test-Path $LogRoot)) { New-Item -Path $LogRoot -ItemType Directory -Force | Out-Null } +$Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' +$LogFile = Join-Path $LogRoot "upgrade-$Timestamp.log" + +function Write-Info { param([string]$m) "[INFO ] $m" | Tee-Object -FilePath $LogFile -Append } +function Write-Warn { param([string]$m) "[WARN ] $m" | Tee-Object -FilePath $LogFile -Append } +function Write-Err { param([string]$m) "[ERROR] $m" | Tee-Object -FilePath $LogFile -Append } + +Write-Info "Log file: $LogFile" +Write-Info "Starting Microsoft Store updates..." + +# ===== Robust winget resolution and invoker ===== +function Resolve-WinGetExe { + # Prefer packaged App Installer location (more stable than user alias) + $pkg = Get-AppxPackage -Name Microsoft.DesktopAppInstaller -ErrorAction SilentlyContinue + if ($pkg) { + $candidate = Join-Path $pkg.InstallLocation 'winget.exe' + if (Test-Path $candidate) { return $candidate } + } + # Fallback to whatever PowerShell resolves + return (Get-Command winget.exe -ErrorAction Stop).Source +} + +$script:WinGetExe = Resolve-WinGetExe + +function Invoke-WinGet { + param( + [Parameter(Mandatory)][string[]]$Args, # e.g. @('upgrade','--all',...) + [switch]$RetryOnSelfUpdate # retry once if winget self-updated + ) + # Execute the EXE directly — do NOT call 'winget' then pass a path + $output = & $script:WinGetExe @Args 2>&1 + $text = $output | Out-String + $text | Tee-Object -FilePath $LogFile -Append | Out-Null + + if ($RetryOnSelfUpdate -and $text -match 'Restart the application to complete the upgrade') { + Write-Info "winget/App Installer updated itself; re-resolving path and retrying once..." + Start-Sleep -Milliseconds 500 + $script:WinGetExe = Resolve-WinGetExe + $output = & $script:WinGetExe @Args 2>&1 + $text = $output | Out-String + $text | Tee-Object -FilePath $LogFile -Append | Out-Null + } + return $text +} + +# ===== Preflight: confirm winget exists ===== +try { + Invoke-WinGet -Args @('--version') | Out-Null +} catch { + Write-Err "winget (App Installer) not found. Install/update 'App Installer' from Microsoft Store and re-run." + exit 1 +} + +# ===== Ensure Microsoft Store Install Service is running (helps Store updates) ===== +try { + $svc = Get-Service -Name InstallService -ErrorAction SilentlyContinue + if ($svc -and $svc.Status -ne 'Running') { + Write-Info "Starting Microsoft Store Install Service (InstallService)..." + Start-Service -Name InstallService -ErrorAction SilentlyContinue + } +} catch { Write-Warn "Could not verify/start InstallService. Continuing..." } + +# ===== Ensure msstore source exists and refresh (NO accept flags here) ===== +try { + $sources = Invoke-WinGet -Args @('source','list','--disable-interactivity') + if ($sources -notmatch '(?im)^\s*msstore\b') { + Write-Info "msstore source not found; resetting..." + Invoke-WinGet -Args @('source','reset','--force','msstore','--disable-interactivity') + } + Invoke-WinGet -Args @('source','update','--disable-interactivity') | Out-Null +} catch { + Write-Warn "Winget source operations reported issues; continuing..." +} + +# ===== Optional: prevent winget self-upgrade during the session (pin App Installer) ===== +# (Uncomment if you want to avoid mid-run self-update entirely) +# Invoke-WinGet -Args @('pin','add','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null + +# ===== PASS 1: Accept msstore terms & upgrade what winget can detect ===== +Write-Info "Pass 1: upgrading Microsoft Store apps (include-unknown)..." +Invoke-WinGet -Args @( + 'upgrade','--all', + '--source','msstore', + '--include-unknown', + '--silent', + '--accept-source-agreements','--accept-package-agreements', + '--disable-interactivity' +) -RetryOnSelfUpdate | Out-Null + +# ===== PASS 2: Force re-install latest for stragglers where version compare is unknown ===== +Write-Info "Pass 2: forced upgrade (msstore) for remaining/unknown version apps..." +Invoke-WinGet -Args @( + 'upgrade','--all', + '--source','msstore', + '--include-unknown', + '--force', + '--silent', + '--accept-source-agreements','--accept-package-agreements', + '--disable-interactivity' +) -RetryOnSelfUpdate | Out-Null + +# ===== OPTIONAL Safety net pass: catch apps mapped under other sources ===== +Write-Info "Safety net: unfiltered pass to catch any remaining packages..." +Invoke-WinGet -Args @( + 'upgrade','--all', + '--include-unknown', + '--silent', + '--accept-source-agreements','--accept-package-agreements', + '--disable-interactivity' +) -RetryOnSelfUpdate | Out-Null + +# ===== Summary: show any remaining Store upgrades (no accept flags here) ===== +Write-Info "Summary check for remaining Microsoft Store upgrades..." +Invoke-WinGet -Args @('upgrade','--source','msstore','--disable-interactivity') | Out-Null + +# ===== Optional: unpin App Installer after run ===== +# Invoke-WinGet -Args @('pin','remove','--id','Microsoft.AppInstaller','--disable-interactivity') | Out-Null + +Write-Info "Completed. Full log: $LogFile" diff --git a/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml b/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml index 111c9758..35ea99d3 100644 --- a/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml +++ b/.configuration/devcenter/workloads/winget-upgrade-packages.dsc.yaml @@ -1,76 +1,76 @@ -# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 -# -# Common Configuration Sample for .NET Engineers -# ================================= -# -# Purpose: -# This DSC configuration sets up a standard development environment with: -# - Development storage using Dev Drive -# - Source control tools (Git, GitHub CLI) -# - Development runtimes (.NET 9 SDK and Runtime) -# - Development tools (VS Code, Node.js) -# -# Prerequisites: -# - Windows 10/11 with admin privileges -# - Internet connectivity for package downloads -# -# Maintainers: DevExp Team - -properties: - configurationVersion: "0.2.0" - resources: - - resource: PSDscResources/Script - id: Winget-Upgrade-Packages - directives: - description: Upgrade all Microsoft Store apps using winget - securityContext: elevated # Requires admin rights to install apps - settings: - SetScript: | - $ErrorActionPreference = 'Stop' - $ProgressPreference = 'SilentlyContinue' - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force | Out-Null - - $scriptUrl = 'https://raw.githubusercontent.com/Evilazaro/DevExp-DevBox/main/.configuration/devcenter/workloads/winget-update.ps1' - $downloadDir = Join-Path $env:TEMP 'DevExp-DevBox-Setup' - $localScript = Join-Path $downloadDir 'winget-update.ps1' - - # Ensure TLS 1.2 for older hosts - try { - [System.Net.ServicePointManager]::SecurityProtocol = ` - [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12 - } catch { } - - # Create temp folder if needed - if (-not (Test-Path -LiteralPath $downloadDir)) { - New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null - } - - # Download script quietly (retry up to 3 times) - $maxAttempts = 3 - for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { - try { - Invoke-WebRequest -Uri $scriptUrl -OutFile $localScript -UseBasicParsing -TimeoutSec 60 -ErrorAction Stop - break - } catch { - if ($attempt -eq $maxAttempts) { - Write-Error "Failed to download script after $maxAttempts attempts. $_" - exit 1 - } - Start-Sleep -Seconds ([int][Math]::Pow(2, $attempt)) - } - } - - # Validate and unblock - if (-not (Test-Path -LiteralPath $localScript) -or (Get-Item -LiteralPath $localScript).Length -le 0) { - Write-Error "Downloaded script is missing or empty: $localScript" - exit 1 - } - try { Unblock-File -LiteralPath $localScript } catch { } - - # Run the script in a separate quiet PowerShell process - Start-Process powershell.exe -ArgumentList @( - '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $localScript - ) -WindowStyle Hidden -Wait - - GetScript: return $false +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# +# Common Configuration Sample for .NET Engineers +# ================================= +# +# Purpose: +# This DSC configuration sets up a standard development environment with: +# - Development storage using Dev Drive +# - Source control tools (Git, GitHub CLI) +# - Development runtimes (.NET 9 SDK and Runtime) +# - Development tools (VS Code, Node.js) +# +# Prerequisites: +# - Windows 10/11 with admin privileges +# - Internet connectivity for package downloads +# +# Maintainers: DevExp Team + +properties: + configurationVersion: "0.2.0" + resources: + - resource: PSDscResources/Script + id: Winget-Upgrade-Packages + directives: + description: Upgrade all Microsoft Store apps using winget + securityContext: elevated # Requires admin rights to install apps + settings: + SetScript: | + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force | Out-Null + + $scriptUrl = 'https://raw.githubusercontent.com/Evilazaro/DevExp-DevBox/main/.configuration/devcenter/workloads/winget-update.ps1' + $downloadDir = Join-Path $env:TEMP 'DevExp-DevBox-Setup' + $localScript = Join-Path $downloadDir 'winget-update.ps1' + + # Ensure TLS 1.2 for older hosts + try { + [System.Net.ServicePointManager]::SecurityProtocol = ` + [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12 + } catch { } + + # Create temp folder if needed + if (-not (Test-Path -LiteralPath $downloadDir)) { + New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null + } + + # Download script quietly (retry up to 3 times) + $maxAttempts = 3 + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + try { + Invoke-WebRequest -Uri $scriptUrl -OutFile $localScript -UseBasicParsing -TimeoutSec 60 -ErrorAction Stop + break + } catch { + if ($attempt -eq $maxAttempts) { + Write-Error "Failed to download script after $maxAttempts attempts. $_" + exit 1 + } + Start-Sleep -Seconds ([int][Math]::Pow(2, $attempt)) + } + } + + # Validate and unblock + if (-not (Test-Path -LiteralPath $localScript) -or (Get-Item -LiteralPath $localScript).Length -le 0) { + Write-Error "Downloaded script is missing or empty: $localScript" + exit 1 + } + try { Unblock-File -LiteralPath $localScript } catch { } + + # Run the script in a separate quiet PowerShell process + Start-Process powershell.exe -ArgumentList @( + '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $localScript + ) -WindowStyle Hidden -Wait + + GetScript: return $false TestScript: return $false \ No newline at end of file diff --git a/src/workload/core/catalog.bicep b/src/workload/core/catalog.bicep index abb3f193..34f4fbbb 100644 --- a/src/workload/core/catalog.bicep +++ b/src/workload/core/catalog.bicep @@ -16,6 +16,9 @@ type Catalog = { @description('Type of repository (GitHub or Azure DevOps Git)') type: CatalogType + @description('Visibility of the catalog') + visibility: 'public' | 'private' + @description('URI of the repository') uri: string @@ -48,7 +51,7 @@ resource catalog 'Microsoft.DevCenter/devcenters/catalogs@2025-04-01-preview' = uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - secretIdentifier: secretIdentifier + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } } : { @@ -56,7 +59,7 @@ resource catalog 'Microsoft.DevCenter/devcenters/catalogs@2025-04-01-preview' = uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - secretIdentifier: secretIdentifier + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } } ) diff --git a/src/workload/project/projectCatalog.bicep b/src/workload/project/projectCatalog.bicep index 42f327ac..e113948a 100644 --- a/src/workload/project/projectCatalog.bicep +++ b/src/workload/project/projectCatalog.bicep @@ -48,7 +48,7 @@ resource catalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null + // secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null adoGit: catalogConfig.sourceControl == 'adoGit' @@ -56,7 +56,7 @@ resource catalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null + // secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null } From 160ead65ce84df4c45c623d2e9794bd20215ad18 Mon Sep 17 00:00:00 2001 From: Evilazaro Date: Fri, 22 Aug 2025 11:16:02 -0500 Subject: [PATCH 4/5] Remove tasks folder --- .../devcenter/tasks/choco/Choco.ps1 | 151 --- .../devcenter/tasks/choco/task.yaml | 49 - .../devcenter/tasks/git-clone/cleanup.ps1 | 12 - .../devcenter/tasks/git-clone/main.ps1 | 620 ----------- .../devcenter/tasks/git-clone/runAsUser.ps1 | 121 --- .../devcenter/tasks/git-clone/task.yaml | 52 - .../Install-VSIXExtension.ps1 | 195 ---- .../tasks/install-vs-extension/task.yaml | 22 - .../tasks/powershell/Run-Command.ps1 | 35 - .../devcenter/tasks/powershell/task.yaml | 30 - .../devcenter/tasks/winget/cleanup.ps1 | 13 - .../devcenter/tasks/winget/main.ps1 | 460 -------- .../devcenter/tasks/winget/runAsUser.ps1 | 124 --- .../devcenter/tasks/winget/task.yaml | 64 -- CODE_OF_CONDUCT.md | 256 ++--- CONTRIBUTING.md | 2 +- README.md | 130 +-- RELEASE_STRATEGY.md | 276 ++--- SECURITY.md | 4 +- azure.yaml | 44 +- cleanSetUp.ps1 | 250 ++--- package.json | 104 +- setUp.sh | 984 +++++++++--------- src/workload/project/projectCatalog.bicep | 4 +- 24 files changed, 1027 insertions(+), 2975 deletions(-) delete mode 100644 .configuration/devcenter/tasks/choco/Choco.ps1 delete mode 100644 .configuration/devcenter/tasks/choco/task.yaml delete mode 100644 .configuration/devcenter/tasks/git-clone/cleanup.ps1 delete mode 100644 .configuration/devcenter/tasks/git-clone/main.ps1 delete mode 100644 .configuration/devcenter/tasks/git-clone/runAsUser.ps1 delete mode 100644 .configuration/devcenter/tasks/git-clone/task.yaml delete mode 100644 .configuration/devcenter/tasks/install-vs-extension/Install-VSIXExtension.ps1 delete mode 100644 .configuration/devcenter/tasks/install-vs-extension/task.yaml delete mode 100644 .configuration/devcenter/tasks/powershell/Run-Command.ps1 delete mode 100644 .configuration/devcenter/tasks/powershell/task.yaml delete mode 100644 .configuration/devcenter/tasks/winget/cleanup.ps1 delete mode 100644 .configuration/devcenter/tasks/winget/main.ps1 delete mode 100644 .configuration/devcenter/tasks/winget/runAsUser.ps1 delete mode 100644 .configuration/devcenter/tasks/winget/task.yaml diff --git a/.configuration/devcenter/tasks/choco/Choco.ps1 b/.configuration/devcenter/tasks/choco/Choco.ps1 deleted file mode 100644 index c10aba8f..00000000 --- a/.configuration/devcenter/tasks/choco/Choco.ps1 +++ /dev/null @@ -1,151 +0,0 @@ -[CmdletBinding()] -param( - [Parameter(Mandatory=$true)] - [string] $Package, - - [Parameter()] - [string] $Version, - - [Parameter()] - [string] $Switches, - - [Parameter()] - [string] $IgnoreChecksums -) - -if (-not $Package) { - throw "Package parameter is mandatory. Please provide a value for the Package parameter." -} - -################################################################################################### -# -# PowerShell configurations -# - -# Ensure we force use of TLS 1.2 for all downloads. -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - -# Expected path of the choco.exe file. -$Choco = "$Env:ProgramData/chocolatey/choco.exe" - -################################################################################################### -# -# Functions used in this script. -# - -function Ensure-Chocolatey -{ - [CmdletBinding()] - param( - [string] $ChocoExePath - ) - - if (-not (Test-Path "$ChocoExePath")) - { - Set-ExecutionPolicy Bypass -Scope Process -Force - $installScriptPath = [System.IO.Path]::GetTempFileName() + ".ps1" - Invoke-WebRequest -Uri 'https://chocolatey.org/install.ps1' -OutFile $installScriptPath - - try { - Execute -File $installScriptPath - } finally { - Remove-Item $installScriptPath - } - - if ($LastExitCode -eq 3010) - { - Write-Host 'The recent changes indicate a reboot is necessary. Please reboot at your earliest convenience.' - } - } -} - -function Install-Package -{ - [CmdletBinding()] - param( - [string] $ChocoExePath, - [string] $Package, - [string] $Version, - [string] $Switches, - [string] $IgnoreChecksums - ) - - $expression = "$ChocoExePath install $Package" - - if ($Version){ - $expression = "$expression --version $Version" - } - - if ($Switches){ - $expression = "$expression --params ""'$Switches'"" " - } - - $expression = "$expression -y -f --acceptlicense --no-progress --stoponfirstfailure" - - if ($IgnoreChecksums -eq "true") { - $expression = "$expression --ignorechecksums" - } - - $expression = "$expression `nexit `$LASTEXITCODE" - - Set-ExecutionPolicy Bypass -Scope Process -Force - $packageScriptPath = [System.IO.Path]::GetTempFileName() + ".ps1" - Set-Content -Value $expression -Path $packageScriptPath - Write-Host "File path $packageScriptPath" - - Execute -File $packageScriptPath - Remove-Item $packageScriptPath -} - -function Execute -{ - [CmdletBinding()] - param( - $File - ) - - # Note we're calling powershell.exe directly, instead - # of running Invoke-Expression, as suggested by - # https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/avoid-using-invoke-expression?view=powershell-7.3 - # Note that this will run powershell.exe - # even if the system has pwsh.exe. - powershell.exe -File $File - - # capture the exit code from the process - $processExitCode = $LASTEXITCODE - - # This check allows us to capture cases where the command we execute exits with an error code. - # In that case, we do want to throw an exception with whatever is in stderr. Normally, when - # Invoke-Expression throws, the error will come the normal way (i.e. $Error) and pass via the - # catch below. - if ($processExitCode -or $expError) - { - if ($processExitCode -eq 3010) - { - # Expected condition. The recent changes indicate a reboot is necessary. Please reboot at your earliest convenience. - } - elseif ($expError) - { - throw $expError - } - else - { - throw "Installation failed with exit code: $processExitCode. Please see the Chocolatey logs in %ALLUSERSPROFILE%\chocolatey\logs folder for details." - break - } - } -} - -################################################################################################### -# -# Main execution block. -# - -Write-Host 'Ensuring latest Chocolatey version is installed.' -Ensure-Chocolatey -ChocoExePath "$Choco" - -Write-Host "Preparing to install Chocolatey package: $Package." -Write-Host "Command: $Choco -Package $Package -Version $Version -Switches $Switches -IgnoreChecksums $IgnoreChecksums" -Install-Package -ChocoExePath "$Choco" -Package $Package -Version $Version -Switches $Switches -IgnoreChecksums $IgnoreChecksums - -Write-Host "`nThe artifact was applied successfully.`n" diff --git a/.configuration/devcenter/tasks/choco/task.yaml b/.configuration/devcenter/tasks/choco/task.yaml deleted file mode 100644 index 39b8e55b..00000000 --- a/.configuration/devcenter/tasks/choco/task.yaml +++ /dev/null @@ -1,49 +0,0 @@ - -# This is a Chocolatey package installation task for Dev Box. -$schema: "1.0" -name: choco -description: Installs a Chocolatey package. -author: Microsoft Corporation -command: "./Choco.ps1 -Package {{package}} -Version {{version}} -Switches {{switches}} -IgnoreChecksums {{ignoreChecksums}}" -parameters: - package: - default: "" - type: string - required: true - description: | - The name of the Chocolatey package to install. - For example, "git". - Visit https://chocolatey.org/packages to learn - more about Chocolatey packages. - version: - default: "" - type: string - required: false - description: The version of the Chocolatey package to install. - switches: - default: "" - type: string - required: false - description: Additional params required for the package to install. - ignoreChecksums: - default: false - type: boolean - required: false - description: Whether to ignore checksums when installing the package. -documentation: - notes: This task is used to install a Chocolatey package. - examples: - - name: choco - description: install OracleJDK 17.0.2 - parameters: - package: oraclejdk - version: 17.0.2 - - name: choco - description: install azure functions core tools - parameters: - package: azure-functions-core-tools - switches: /x64 - - name: choco - description: install notepad++ - parameters: - package: notepadplusplus diff --git a/.configuration/devcenter/tasks/git-clone/cleanup.ps1 b/.configuration/devcenter/tasks/git-clone/cleanup.ps1 deleted file mode 100644 index 9132e24c..00000000 --- a/.configuration/devcenter/tasks/git-clone/cleanup.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -if (!(Test-Path "$($CustomizationScriptsDir)\$($LockFile)")) { - Unregister-ScheduledTask -TaskName $RunAsUserTask -Confirm:$false - Unregister-ScheduledTask -TaskName $CleanupTask -Confirm:$false - Remove-Item $CustomizationScriptsDir -Force -Recurse -} \ No newline at end of file diff --git a/.configuration/devcenter/tasks/git-clone/main.ps1 b/.configuration/devcenter/tasks/git-clone/main.ps1 deleted file mode 100644 index 5c8d0cd7..00000000 --- a/.configuration/devcenter/tasks/git-clone/main.ps1 +++ /dev/null @@ -1,620 +0,0 @@ -param ( - [Parameter()] - [string]$RepositoryUrl, - [Parameter()] - [string]$Directory, - [Parameter()] - [string]$Branch, - [Parameter()] - [string]$Pat -) - - -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" -$PsInstallScope = "CurrentUser" -if ($(whoami.exe) -eq "nt authority\system") { - $PsInstallScope = "AllUsers" -} -$TargetRepoDirectory = Join-Path -Path $Directory -ChildPath "$($RepositoryUrl -replace "^.+\/([^\/]+?)(?:\.git)?$", '$1')" - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -function SetupScheduledTasks { - Write-Host "Setting up scheduled tasks" - if (!(Test-Path -PathType Container $CustomizationScriptsDir)) { - New-Item -Path $CustomizationScriptsDir -ItemType Directory - New-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" -ItemType File - Copy-Item "./$($RunAsUserScript)" -Destination $CustomizationScriptsDir - Copy-Item "./$($CleanupScript)" -Destination $CustomizationScriptsDir - } - - # Reference: https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-objects - $SchedService = New-Object -comobject "Schedule.Service" - $SchedService.Connect() - - # Schedule the cleanup script to run every minute as SYSTEM - $Task = $SchedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations Cleanup" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - $Trigger.Repetition.Interval="PT1M" - - $Action = $Task.Actions.Create(0) - $Action.Path = "PowerShell.exe" - $Action.Arguments = "Set-ExecutionPolicy Bypass -Scope Process -Force; $($CustomizationScriptsDir)\$($CleanupScript)" - - $TaskFolder = $SchedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($CleanupTask)", $Task , 6, "NT AUTHORITY\SYSTEM", $null, 5) - - # Schedule the script to be run in the user context on login - $Task = $SchedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - $Task.Principal.RunLevel = 1 - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - - $Action = $Task.Actions.Create(0) - $Action.Path = "C:\Program Files\PowerShell\7\pwsh.exe" - $Action.Arguments = "-MTA -Command $($CustomizationScriptsDir)\$($RunAsUserScript)" - - $TaskFolder = $SchedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($RunAsUserTask)", $Task , 6, "Users", $null, 4) - Write-Host "Done setting up scheduled tasks" -} - -function WithRetry { - Param( - [Parameter(Position=0, Mandatory=$true)] - [scriptblock]$ScriptBlock, - - [Parameter(Position=1, Mandatory=$false)] - [int]$Maximum = 5, - - [Parameter(Position=2, Mandatory=$false)] - [int]$Delay = 100 - ) - - $iterationCount = 0 - $lastException = $null - do { - $iterationCount++ - try { - Invoke-Command -Command $ScriptBlock - return - } catch { - $lastException = $_ - Write-Error $_ - - # Sleep for a random amount of time with exponential backoff - $randomDouble = Get-Random -Minimum 0.0 -Maximum 1.0 - $k = $randomDouble * ([Math]::Pow(2.0, $iterationCount) - 1.0) - Start-Sleep -Milliseconds ($k * $Delay) - } - } while ($iterationCount -lt $Maximum) - - throw $lastException -} - -function InstallPS7 { - if (!(Get-Command pwsh -ErrorAction SilentlyContinue)) { - Write-Host "Installing PowerShell 7" - $code = Invoke-RestMethod -Uri https://aka.ms/install-powershell.ps1 - $null = New-Item -Path function:Install-PowerShell -Value $code - WithRetry -ScriptBlock { - if ("$($PsInstallScope)" -eq "CurrentUser") { - Install-PowerShell -UseMSI - } - else { - # The -Quiet flag requires admin permissions - Install-PowerShell -UseMSI -Quiet - } - } -Maximum 5 -Delay 100 - # Need to update the path post install - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "Done Installing PowerShell 7" - } - else { - Write-Host "PowerShell 7 is already installed" - } -} - -function InstallWinGet { - Write-Host "Installing powershell modules in scope: $PsInstallScope" - - # ensure NuGet provider is installed - if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope $PsInstallScope - Write-Host "Done Installing NuGet provider" - } - else { - Write-Host "NuGet provider is already installed" - } - - # Set PSGallery installation policy to trusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Trusted" - - # check if the Microsoft.Winget.Client module is installed - $wingetClientPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Client | Where-Object { `$_.Version -ge '1.9.2411' }" - if (!($wingetClientPackage)) { - Write-Host "Installing Microsoft.Winget.Client" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Client -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.Winget.Client" - } - else { - Write-Host "Microsoft.Winget.Client is already installed" - } - - # check if the Microsoft.WinGet.Configuration module is installed - $wingetConfigurationPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration | Where-Object { `$_.Version -ge '1.8.1911' }" - if (!($wingetConfigurationPackage)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -AllowPrerelease -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.WinGet.Configuration" - } - else { - Write-Host "Microsoft.WinGet.Configuration is already installed" - } - - Write-Host "Updating WinGet" - try { - Write-Host "Attempting to repair WinGet Package Manager" - pwsh.exe -MTA -Command "Repair-WinGetPackageManager -Latest -Force -Verbose" - Write-Host "Done Reparing WinGet Package Manager" - } - catch { - Write-Host "Failed to repair WinGet Package Manager" - Write-Error $_ - } - - if ($PsInstallScope -eq "CurrentUser") { - # Under a user account, the way to materialize winget.exe and make it work is by installing DesktopAppInstaller appx, - # which in turn may have Xaml and VC++ redistributable requirements. - - $architecture = "x64" - if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" - } - - $msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.30035.0" } - if (!($msVCLibsPackage)) { - # Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } - } - - $msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } - if (!($msUiXamlPackage)) { - # install Microsoft.UI.Xaml - try { - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } - } - - $desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" - if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } - } - - Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "WinGet version: $(winget -v)" - } - - # Revert PSGallery installation policy to untrusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted" -} - - -function InstallPackage{ - param ( - [string]$PackageId, - [string]$PackageCommand, - [string]$PackagePath - ) - - # install package if it's not already installed - if (!(Get-Command $PackageCommand -ErrorAction SilentlyContinue)) { - # if winget is available, use it to install package - if (Get-Command winget -ErrorAction SilentlyContinue) { - Write-Host "Installing $PackageId with winget" - winget install --id $PackageId -e --source winget --silent - $installExitCode = $LASTEXITCODE - Write-Host "'winget install --id $PackageId -e --source winget --silent' exited with code: $($installExitCode)" - if ($installExitCode -eq 0) { - # add package path to path - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";" + $PackagePath - } - } - - # If we reached here without being able to install the package, try with Install-WinGetPackage - if (!(Get-Command $PackageCommand -ErrorAction SilentlyContinue)) { - # install winget and use that to install packages - - # winget is not available for system context, so we need to leverage WingGet cmdlets for system context. - # The WinGet cmdlets aren't supported in PowerShell 5, so we need to install Powershell7 here. - InstallPS7 - InstallWinGet - - # install package via winget client - Write-Host "Installing $PackageId with Install-WinGetPackage" - $mtaFlag = "-MTA" - $scopeFlagValue = "SystemOrUnknown" - if ($PsInstallScope -eq "CurrentUser") { - $mtaFlag = "" - $scopeFlagValue = "UserOrUnknown" - } - - $tempOutFile = [System.IO.Path]::GetTempFileName() + ".out.json" - - $installCommandBlock = { - $installPackageCommand = "Install-WinGetPackage -Scope $($scopeFlagValue) -Mode Silent -Source winget -Id $($PackageId) | ConvertTo-Json -Depth 10 | Tee-Object -FilePath '$($tempOutFile)'" - $processCreation = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine="C:\Program Files\PowerShell\7\pwsh.exe $($mtaFlag) -Command `"$($installPackageCommand)`""} - if (!($processCreation) -or !($processCreation.ProcessId)) { - Write-Error "Failed to install $PackageId package. Process creation failed." - exit 1 - } - - $process = Get-Process -Id $processCreation.ProcessId - $handle = $process.Handle # cache process.Handle so ExitCode isn't null when we need it below - $process.WaitForExit() - $installExitCode = $process.ExitCode - if ($installExitCode -ne 0) { - Write-Error "Failed to install $PackageId with Install-WinGetPackage, error code $($installExitCode)." - # this was the last try, so exit with the install exit code - exit $installExitCode - } - - # read the output file and write it to the console - if (Test-Path -Path $tempOutFile) { - $unitResults = Get-Content -Path $tempOutFile -Raw | Out-String - Write-Host $unitResults - Remove-Item -Path $tempOutFile -Force - # If there are any errors in the package installation, we need to exit with a non-zero code - $unitResultsObject = $unitResults | ConvertFrom-Json - - # If the initial scope didn't produce an installer, retry with an "Any" scope - if (($unitResultsObject.Status -eq "NoApplicableInstallers") -and ($scopeFlagValue -ne "Any")) { - ([ref]$scopeFlagValue).Value = "Any" - .$installCommandBlock - } - - # If there are any errors in the package installation, we need to exit with a non-zero code - if ($unitResultsObject.Status -ne "Ok") { - Write-Error "There were errors installing the package." - exit 1 - } - } - else { - Write-Host "Couldn't find output file for $PackageId installation, assuming fail." - exit 1 - } - } - .$installCommandBlock - - # add git to path - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";" + $PackagePath - } - } -} - -InstallPackage -PackageId "Git.Git" -PackagePath "C:\Program Files\Git\cmd" -PackageCommand "git" - -Write-Host "git version: $(git --version)" - -$repoCloned = $false - -# Disable credential prompting when running under system context to make sure the clone operation doesn't hang -$credentialInteractiveFlag = @('-c', 'credential.interactive=Never') -if ($PsInstallScope -eq "CurrentUser") { - $credentialInteractiveFlag = @() -} - -if ($Pat) { - # When a PAT is provided, we'll attempt to clone the repository during provisioning time. - # If this fails, we'll try again when the user logs in. - Write-Host "Cloning repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - if ($Branch) { - Write-Host "Using branch: $($Branch)" - } - - # First we'll try to clone the repository using the provided PAT. - try { - # ensure the target directory doesn't exist - if (Test-Path -PathType Container $TargetRepoDirectory) { - Remove-Item -Recurse -Force $TargetRepoDirectory - } - - if (!(Test-Path -PathType Container $TargetRepoDirectory)) { - New-Item -Path $TargetRepoDirectory -ItemType Directory - } - - # Attempt to clone - Push-Location $TargetRepoDirectory - $b64pat = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("user:$Pat")) - - # Disable credential prompting here to make sure the clone operation doesn't hang - if ($Branch) { - git $credentialInteractiveFlag -c http.extraHeader="Authorization: Basic $b64pat" clone -b $Branch $RepositoryUrl . 3>&1 2>&1 - } - else { - git $credentialInteractiveFlag -c http.extraHeader="Authorization: Basic $b64pat" clone $RepositoryUrl . 3>&1 2>&1 - } - - if ($LASTEXITCODE -ne 0) { - throw "git clone exited with code: $($LASTEXITCODE)" - } - - # Mark the directory as safe for git operations - $TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' - if ($PsInstallScope -eq "CurrentUser") { - git config --global --add safe.directory "$($TargetRepoDirectorySafe)" - } - else { - git config --system --add safe.directory "$($TargetRepoDirectorySafe)" - } - - # If the code reaches this point, we've successfully cloned the repository. - Write-Host "Successfully cloned repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - $repoCloned = $true - } - catch { - Write-Error $_ - Write-Host "Failed to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory), we'll try again assuming it's an access token." - } - finally { - Pop-Location - } - - # If the repo wasn't cloned successfully, it may be that the PAT is actually an access token, which requires - # a different approach. We'll try to clone the repository using the provided PAT as an access token. - if (!$repoCloned) { - try { - - # ===== Normalize the repository clone link - - # Sample repo clone links: - # https://organization@dev.azure.com/organization/project-name/_git/sample-repo.name - # https://dev.azure.com/organization/project-name/_git/Sample-repo.name - # https://organization.visualstudio.com/project-name/_git/sample-repo.name - # https://organization.visualstudio.com/DefaultCollection/project-name/_git/sample-repo.name - - $Pattern1 = '^https://(?[a-zA-Z0-9]+)@dev.azure.com/(?[a-zA-Z0-9]+)/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - $Pattern2 = '^https://dev.azure.com/(?[a-zA-Z0-9]+)/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - $Pattern3 = '^https://(?[a-zA-Z0-9]+).visualstudio.com/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - $Pattern4 = '^https://(?[a-zA-Z0-9]+).visualstudio.com/[Dd]efault[Cc]ollection/(?[\.\-a-zA-Z0-9]+)/_git/(?[\.\-a-zA-Z0-9]+)/?$' - - $RepositoryUrl = $RepositoryUrl.ToLower() - if ($RepositoryUrl -match $Pattern1) { - Write-Output "Match Pattern1" - } - elseif ($RepositoryUrl -match $Pattern2) { - Write-Output "Match Pattern2" - } - elseif ($RepositoryUrl -match $Pattern3) { - Write-Output "Match Pattern3" - } - elseif ($RepositoryUrl -match $Pattern4) { - Write-Output "Match Pattern4" - } - else { - throw "RepositoryUrl doesnot match any known pattern" - } - - $NormalizedRepositoryUrl = 'https://{org}:{at}@dev.azure.com/{org}/{project}/_git/{reponame}' - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{org}', $Matches.org) - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{project}', $Matches.project) - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{reponame}', $Matches.reponame) - $NormalizedRepositoryUrl = $NormalizedRepositoryUrl.Replace('{at}', $Pat) - - # ensure the target directory doesn't exist - if (Test-Path -PathType Container $TargetRepoDirectory) { - Remove-Item -Recurse -Force $TargetRepoDirectory - } - - if (!(Test-Path -PathType Container $TargetRepoDirectory)) { - New-Item -Path $TargetRepoDirectory -ItemType Directory - } - - # Attempt to clone - Push-Location $TargetRepoDirectory - if ($Branch) { - git clone -b $Branch $NormalizedRepositoryUrl . 3>&1 2>&1 - } - else { - git clone $NormalizedRepositoryUrl . 3>&1 2>&1 - } - - if ($LASTEXITCODE -ne 0) { - throw "git clone exited with code: $($LASTEXITCODE)" - } - - # Mark the directory as safe for git operations - $TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' - if ($PsInstallScope -eq "CurrentUser") { - git config --global --add safe.directory "$($TargetRepoDirectorySafe)" - } - else { - git config --system --add safe.directory "$($TargetRepoDirectorySafe)" - } - - # If the code reaches this point, we've successfully cloned the repository. - Write-Host "Successfully cloned repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - $repoCloned = $true - } - catch { - Write-Error $_ - Write-Host "Failed to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory), cloning attempt will be queued for user login" - } - finally { - Pop-Location - } - } -} - -# Check if the repository is hosted in GitHub -if (!$repoCloned -and ($RepositoryUrl -match "github.com")) { - # attempt to clone without credentials - Write-Host "Attempting to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory) without credentials" - if ($Branch) { - Write-Host "Using branch: $($Branch)" - } - - try { - # ensure the target directory doesn't exist - if (Test-Path -PathType Container $TargetRepoDirectory) { - Remove-Item -Recurse -Force $TargetRepoDirectory - } - - if (!(Test-Path -PathType Container $TargetRepoDirectory)) { - New-Item -Path $TargetRepoDirectory -ItemType Directory - } - - # Attempt to clone - Push-Location $TargetRepoDirectory - - # Disable credential prompting here to make sure the clone operation doesn't hang - if ($Branch) { - git $credentialInteractiveFlag clone -b $Branch $RepositoryUrl . 3>&1 2>&1 - } - else { - git $credentialInteractiveFlag clone $RepositoryUrl . 3>&1 2>&1 - } - - if ($LASTEXITCODE -ne 0) { - throw "git clone exited with code: $($LASTEXITCODE)" - } - - # Mark the directory as safe for git operations - $TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' - if ($PsInstallScope -eq "CurrentUser") { - git config --global --add safe.directory "$($TargetRepoDirectorySafe)" - } - else { - git config --system --add safe.directory "$($TargetRepoDirectorySafe)" - } - - # If the code reaches this point, we've successfully cloned the repository. - Write-Host "Successfully cloned repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)" - $repoCloned = $true - } - catch { - Write-Error $_ - Write-Host "Failed to clone repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory), cloning attempt will be queued for user login" - } - finally { - Pop-Location - } -} - - -if ($repoCloned) -{ - exit 0 #Success! -} - -# If the code reaches this point, we failed to clone the repository during provisioning time or -# a PAT was not provided. We'll queue the clone attempt for user login. - -# install powershell 7 -InstallPS7 - -if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($LockFile)")) { - SetupScheduledTasks -} - -Write-Host "Writing commands to user script" - -function AppendToUserScript { - Param( - [Parameter(Position=0, Mandatory=$true)] - [string]$Content - ) - - Add-Content -Path "$($CustomizationScriptsDir)\$($RunAsUserScript)" -Value $Content -} - -# Work from C:\ -AppendToUserScript "Push-Location C:\" - -# Write intent to output stream -AppendToUserScript "Write-Host 'Cloning repository: $($RepositoryUrl) to directory: $($TargetRepoDirectory)'" -if ($Branch) { - AppendToUserScript "Write-Host 'Using branch: $($Branch)'" -} - -# make directory if it doesn't exist -AppendToUserScript "if (Test-Path -PathType Container '$($TargetRepoDirectory)') {" -AppendToUserScript " Remove-Item -Recurse -Force '$($TargetRepoDirectory)'" -AppendToUserScript "}" -AppendToUserScript "if (!(Test-Path -PathType Container '$($TargetRepoDirectory)')) {" -AppendToUserScript " New-Item -Path '$($TargetRepoDirectory)' -ItemType Directory" -AppendToUserScript "}" - -# Attempt to clone the repository -AppendToUserScript "Push-Location $($TargetRepoDirectory)" -if ($Branch) { - AppendToUserScript "git clone -b $($Branch) $($RepositoryUrl) ." -} -else { - AppendToUserScript "git clone $($RepositoryUrl) ." -} - -AppendToUserScript "Pop-Location" - -# Mark the directory as safe for git operations -$TargetRepoDirectorySafe = $TargetRepoDirectory -replace '\\', '/' -AppendToUserScript "git config --global --add safe.directory '$($TargetRepoDirectorySafe)'" - -AppendToUserScript "Pop-Location" - -Write-Host "Done writing commands to user script" - -exit 0 #Success! \ No newline at end of file diff --git a/.configuration/devcenter/tasks/git-clone/runAsUser.ps1 b/.configuration/devcenter/tasks/git-clone/runAsUser.ps1 deleted file mode 100644 index 9fda5f85..00000000 --- a/.configuration/devcenter/tasks/git-clone/runAsUser.ps1 +++ /dev/null @@ -1,121 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -Start-Transcript -Path $env:TEMP\scheduled-task-customization.log -Append -IncludeInvocationHeader - -Write-Host "Microsoft Dev Box - Customizations" -Write-Host "----------------------------------" -Write-Host "Setting up scheduled tasks..." - -Remove-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" - -Write-Host "Updating WinGet" -# ensure NuGet provider is installed -if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser - Write-Host "Done Installing NuGet provider" -} -else { - Write-Host "NuGet provider is already installed" -} - -# Set PSGallery installation policy to trusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - -# check if the Microsoft.Winget.Client module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.Winget.Client)) { - Write-Host "Installing Microsoft.Winget.Client" - Install-Module Microsoft.WinGet.Client -Scope CurrentUser - Write-Host "Done Installing Microsoft.Winget.Client" -} -else { - Write-Host "Microsoft.Winget.Client is already installed" -} - -# check if the Microsoft.WinGet.Configuration module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -AllowPrerelease -Scope CurrentUser" - Write-Host "Done Installing Microsoft.WinGet.Configuration" -} -else { - Write-Host "Microsoft.WinGet.Configuration is already installed" -} - -$architecture = "x64" -if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" -} - -$msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.33728.0" } -if (!($msVCLibsPackage)) { -# Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } -} - -$msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } -if (!($msUiXamlPackage)) { - # install Microsoft.UI.Xaml - try{ - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } -} - -$desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" -if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - - # install the DesktopAppInstaller appx package - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } -} - -Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe -Write-Host "WinGet version: $(winget -v)" - -$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") -Write-Host "Done Updating WinGet" - -# Revert PSGallery installation policy to untrusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted \ No newline at end of file diff --git a/.configuration/devcenter/tasks/git-clone/task.yaml b/.configuration/devcenter/tasks/git-clone/task.yaml deleted file mode 100644 index efa04e05..00000000 --- a/.configuration/devcenter/tasks/git-clone/task.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# This is a git repository cloning task for Dev Box - -$schema: 1.0 -name: git-clone -description: Clones a git repository. -author: Microsoft Corporation -command: .\main.ps1 -RepositoryUrl {{ repositoryUrl }} -Directory {{ directory }} -Branch {{ branch }} -Pat {{ pat }} -parameters: - repositoryUrl: - default: '' - type: string - required: true - description: The URL of the repository to clone. - directory: - default: C:\Workspaces - type: string - required: false - description: | - The directory under which the repository will be cloned. This is the - equivalent of the working directory under which the git command will - execute. - branch: - default: '' - type: string - required: false - description: The branch to clone. - pat: - default: '' - type: string - required: false - description: The PAT (personal access token) to use when cloning the repository. -documentation: - notes: | - This task allows you to clone a repository. When the "pat" parameter is - not specified, the task will queue the cloning operation to happen on the - first user login. Otherwise the task will clone the repository during the - dev box provisioning stage. - We encourage you to pass a reference uri to a KeyVault secret as the value - for the "pat" parameter instead of including the Raw secret value in yamls. - Note that Powershell7, Git will be installed during this task. - examples: - - name: git-clone - description: Clone this repository into C:\Workspaces - parameters: - repositoryUrl: https://github.com/microsoft/calculator - directory: C:\Workspaces - - name: git-clone - description: Clone this repository's feature/winui2.6 branch into C:\Workspaces - parameters: - repositoryUrl: https://github.com/microsoft/calculator - directory: C:\Workspaces - branch: feature/winui2.6 \ No newline at end of file diff --git a/.configuration/devcenter/tasks/install-vs-extension/Install-VSIXExtension.ps1 b/.configuration/devcenter/tasks/install-vs-extension/Install-VSIXExtension.ps1 deleted file mode 100644 index 240e8ca1..00000000 --- a/.configuration/devcenter/tasks/install-vs-extension/Install-VSIXExtension.ps1 +++ /dev/null @@ -1,195 +0,0 @@ -<# -.SYNOPSIS - Install an extension defined by the given item name to the latest Visual Studio instance -.PARAMETER MarketplaceItemName - Markplace Item Name (as used in the URI of a given Visual Studio Extension Maketplace entry) -#> -param ( - [Parameter(Mandatory)] - [string]$MarketplaceItemName - ) - -<# -.SYNOPSIS - Download an extension defined by the given item name -.PARAMETER MarketplaceItemName - Markplace Item Name (as used in the URI of a given Visual Studio Extension Maketplace entry) -#> - -function Get-VSIXFromMarketplace -{ - param ( - [Parameter(Mandatory)] - [string]$MarketplaceItemName - ) - - # Declare exit code. Default to failure and set to 0 when operation succeeds. - $exitCode = 1 - - # Turn off progress to fix speed bug in Invoke-WebRequest - $ProgressPreference = 'SilentlyContinue' - - $vswhereResult = Invoke-VsWhere "-prerelease -latest -format json" | ConvertFrom-Json - - $instanceVersion = $vswhereResult.InstallationVersion - - $isolationInfo = ConvertFrom-StringData((Get-Content "$($vswhereResult.ProductPath -replace ".exe",".isolation.ini")" | Select-Object -Skip 2) -replace "\\","\\\\" -join "`n") - $arch = If ($isolationInfo.ProductArch -eq "x64") { "amd64" } else { $isolationInfo.ProductArch } - - $marketplaceQueryBody = -@" -{"flags":"673","filters":[{"criteria":[{"filterType":"7","value":"$MarketplaceItemName"},{"filterType":"15","value":"$instanceVersion"},{"filterType":"22","value":"$arch"},{"filterType":"8","value":"Microsoft.VisualStudio.Enterprise"},{"filterType":"8","value":"Microsoft.VisualStudio.Ultimate"},{"filterType":"8","value":"Microsoft.VisualStudio.Pro"},{"filterType":"8","value":"Microsoft.VisualStudio.Community"},{"filterType":"8","value":"Microsoft.VisualStudio.IntegratedShell"}],"sortBy":"4","sortOrder":"2","pageSize":"2","pageNumber":"1"}]} -"@ - - $requestParams = @{ - Uri = "https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery" - Method = "Post" - Headers = @{"Accept" = 'application/json;api-version=3.2-preview.1'} - ContentType = "application/json" - Body = $marketplaceQueryBody - UseBasicParsing = $true - } - - $jsonResponse = (Invoke-WebRequest @requestParams).Content | ConvertFrom-Json - $extensions = $jsonResponse.results.extensions - - if(-not $extensions -or ($extensions -is [array] -and $extensions.length -le 0)) { - Write-Warning "No extension was found for item: $MarketplaceItemName. Specify a valid extension." - exit $exitcode - } - - if($extensions -is [array] -and $extensions.length -gt 1) { - Write-Warning "Multiple extensions ($($extensions.length)) found for the given item name $MarketplaceItemName" - exit $exitcode - } - - $cdnUrl = "$($extensions.versions[0].fallbackAssetUri)/Microsoft.VisualStudio.IDE.Payload?redirect=true&install=true" - $downloadFilePath = Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::ChangeExtension("VSIX$([IO.Path]::GetRandomFileName())", ".vsix")) - $extensionName = $extensions.displayName - Write-Host "Downloading $extensionName" - - Invoke-WebRequest $cdnUrl -UseBasicParsing -OutFile $downloadFilePath - - # Turn progress back on - $ProgressPreference = 'Continue' - - return $downloadFilePath -} - -<# -.SYNOPSIS - Invokes Visual Studio Locator, if it exists, with the provided arguments. -.DESCRIPTION - Invokes Visual Studio Locator (vswhere.exe) with the provided arguments. - If this script is run without the locator present, it will fail. -.PARAMETER Arguments - Arguments to pass onwards to Visual Studio Locator. -.LINK - https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances#using-vswhereexe -#> -function Invoke-VsWhere -{ - param - ( - [Parameter(Mandatory)] - [string]$Arguments - ) - - Assert-VsWherePresent - - return Invoke-Expression -Command "&'$(Get-VsWherePath)' $Arguments" -} - -<# -.SYNOPSIS - Returns the default path of Visual Studio Locator (vswhere.exe). -#> -function Get-VsWherePath -{ - return Join-Path -Path "${env:ProgramFiles(x86)}" -ChildPath "Microsoft Visual Studio\Installer\vswhere.exe" -} - -<# -.SYNOPSIS - Throws an exception if Visual Studio Locator (vswhere.exe) is not present in the default location. -#> -function Assert-VsWherePresent -{ - if(-not (Test-Path (Get-VsWherePath))) - { - throw "Visual Studio Locator not found." - exit $exitcode - } -} - -<# -.SYNOPSIS - Invokes VSIX Installer, if it exists, with the provided arguments. -.DESCRIPTION - Invokes VSIX Installer with the provided arguments. - If this script is run without VSIX Installer present, it will fail. -.PARAMETER Arguments - Arguments to pass onwards to VSIX Installer. -#> -function Invoke-VsixInstaller -{ - param - ( - [Parameter(Mandatory)] - [string]$Arguments - ) - - Assert-VsixInstallerPresent - - return Invoke-Expression -Command "&'$(Get-VsixInstallerPath)' $Arguments" -} - -<# -.SYNOPSIS - Returns the default path of VSIX Installer. -#> -function Get-VsixInstallerPath -{ - return Join-Path -Path "${env:ProgramFiles(x86)}" -ChildPath "Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service\VSIXInstaller.exe" -} - -<# -.SYNOPSIS - Throws an exception if VSIX Installer is not present in the default location. -#> -function Assert-VsixInstallerPresent -{ - if(-not (Test-Path (Get-VsixInstallerPath))) - { - throw "VSIX Installer not found." - exit $exitcode - } -} - -# ---- Main Script Start ---- - -$pathToVSIX = Get-VSIXFromMarketplace $MarketplaceItemName - -if(-not $pathToVsix -or -not (Test-Path $pathToVSIX -PathType Leaf)) { - Write-Warning "Issue with VSIX path for item $MarketplaceItemName. No extension downloaded; skipping install." - exit $exitcode -} - -Write-Host "Invoking VSIX Installer for downloaded VSIX at $pathToVsix..." - -try { - Invoke-VsixInstaller "/a /enableUpdate /q /f /sp $pathToVSIX" | Out-Null - Wait-Process (Get-Process VsixInstaller).id -Timeout 600 -} -catch { - Write-Warning "VSIX Installer failed with error: $_" - exit $exitcode -} - -Write-Host "VSIX Installer Completed." -Write-Host "$MarketplaceItemName Successfully installed." - -$exitcode = 0 -exit $exitCode - -# ---- Main Script End ---- diff --git a/.configuration/devcenter/tasks/install-vs-extension/task.yaml b/.configuration/devcenter/tasks/install-vs-extension/task.yaml deleted file mode 100644 index 3b5c67be..00000000 --- a/.configuration/devcenter/tasks/install-vs-extension/task.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# This is a Visual Studio extension installation task for Dev Box. - -$schema: 1.0 -name: install-vs-extension -description: Installs a Visual Studio extension -author: Microsoft Corporation -command: ".\\Install-VSIXExtension.ps1 -MarketplaceItemName {{marketplaceItemName}}" -parameters: - marketplaceItemName: - type: string - required: true - description: | - The name of the Visual Studio extension to install. - Visit https://marketplace.visualstudio.com to learn - more about Visual Studio extensions. -documentation: - notes: This task is used to install a Visual Studio extension. - examples: - - name: install-vs-extension - description: Install GitHub Copilot - parameters: - marketplaceItemName: GitHub.copilotvs diff --git a/.configuration/devcenter/tasks/powershell/Run-Command.ps1 b/.configuration/devcenter/tasks/powershell/Run-Command.ps1 deleted file mode 100644 index c29d6200..00000000 --- a/.configuration/devcenter/tasks/powershell/Run-Command.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -param( - [Parameter()] - [string]$Command, - [Parameter()] - [string]$WorkingDirectory - ) - -# Check if workingDirectory is set and not empty and if so, change to it. -if ($WorkingDirectory -and $WorkingDirectory -ne "") { - # Check if the working directory exists. - if (-not (Test-Path $WorkingDirectory)) { - # Create the working directory if it does not exist. - Write-Output "Creating working directory $WorkingDirectory" - New-Item -ItemType Directory -Force -Path $WorkingDirectory - } - - Write-Output "Changing to working directory $WorkingDirectory" - Set-Location $WorkingDirectory -} - -# Note we're calling powershell.exe directly, instead -# of running Invoke-Expression, as suggested by -# https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/avoid-using-invoke-expression?view=powershell-7.3 -# Note that this will run powershell.exe -# even if the system has pwsh.exe. -Write-Output "Running command $Command" -powershell.exe -Command $Command -$CommandExitCode = $LASTEXITCODE -Write-Output "Command exited with code $CommandExitCode" - -# Task powershell scripts should always end with an -# exit code reported up to the runner agent. -# This is how the runner agent knows whether the -# command succeeded or failed. -exit $CommandExitCode diff --git a/.configuration/devcenter/tasks/powershell/task.yaml b/.configuration/devcenter/tasks/powershell/task.yaml deleted file mode 100644 index 90ae9ba3..00000000 --- a/.configuration/devcenter/tasks/powershell/task.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This is a simple powershell command execution task for Dev Box. - -$schema: 1.0 -name: powershell -description: Execute a powershell command. -author: Microsoft Corporation -command: ".\\Run-Command.ps1 -Command {{command}} -WorkingDirectory {{workingDirectory}}" -parameters: - command: - type: string - default: "" - required: true - description: The command to execute. - workingDirectory: - type: string - default: "" - required: false - description: The working directory to execute the command in. -documentation: - notes: This task is used to execute a powershell command. - examples: - - name: powershell - description: print hello world - parameters: - command: "Write-Host 'hello, world!'" - - name: powershell - description: run script - parameters: - command: "./script.ps1" - workingDirectory: C:\\provisioning\\temp diff --git a/.configuration/devcenter/tasks/winget/cleanup.ps1 b/.configuration/devcenter/tasks/winget/cleanup.ps1 deleted file mode 100644 index 1e156f99..00000000 --- a/.configuration/devcenter/tasks/winget/cleanup.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$SetVariablesScript = "setVariables.ps1" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -if (!(Test-Path "$($CustomizationScriptsDir)\$($LockFile)")) { - Unregister-ScheduledTask -TaskName $RunAsUserTask -Confirm:$false - Unregister-ScheduledTask -TaskName $CleanupTask -Confirm:$false - Remove-Item $CustomizationScriptsDir -Force -Recurse -} diff --git a/.configuration/devcenter/tasks/winget/main.ps1 b/.configuration/devcenter/tasks/winget/main.ps1 deleted file mode 100644 index cddcf65b..00000000 --- a/.configuration/devcenter/tasks/winget/main.ps1 +++ /dev/null @@ -1,460 +0,0 @@ -param ( - [Parameter()] - [string]$ConfigurationFile, - [Parameter()] - [string]$DownloadUrl, - [Parameter()] - [string]$InlineConfigurationBase64, - [Parameter()] - [string]$Package, - [Parameter()] - [string]$Version = '', - [Parameter()] - [string]$RunAsUser -) - -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" -$PsInstallScope = "CurrentUser" -if ($(whoami.exe) -eq "nt authority\system") { - $PsInstallScope = "AllUsers" -} - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -function SetupScheduledTasks { - Write-Host "Setting up scheduled tasks" - if (!(Test-Path -PathType Container $CustomizationScriptsDir)) { - New-Item -Path $CustomizationScriptsDir -ItemType Directory - } - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($LockFile)")) { - New-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" -ItemType File - } - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($RunAsUserScript)")) { - Copy-Item "./$($RunAsUserScript)" -Destination $CustomizationScriptsDir - } - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($CleanupScript)")) { - Copy-Item "./$($CleanupScript)" -Destination $CustomizationScriptsDir - } - - # Reference: https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-objects - $ShedService = New-Object -comobject "Schedule.Service" - $ShedService.Connect() - - # Schedule the cleanup script to run every minute as SYSTEM - $Task = $ShedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations Cleanup" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - $Trigger.Repetition.Interval="PT1M" - - $Action = $Task.Actions.Create(0) - $Action.Path = "PowerShell.exe" - $Action.Arguments = "Set-ExecutionPolicy Bypass -Scope Process -Force; $($CustomizationScriptsDir)\$($CleanupScript)" - - $TaskFolder = $ShedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($CleanupTask)", $Task , 6, "NT AUTHORITY\SYSTEM", $null, 5) - - # Schedule the script to be run in the user context on login - $Task = $ShedService.NewTask(0) - $Task.RegistrationInfo.Description = "Dev Box Customizations" - $Task.Settings.Enabled = $true - $Task.Settings.AllowDemandStart = $false - $Task.Principal.RunLevel = 1 - - $Trigger = $Task.Triggers.Create(9) - $Trigger.Enabled = $true - - $Action = $Task.Actions.Create(0) - $Action.Path = "C:\Program Files\PowerShell\7\pwsh.exe" - $Action.Arguments = "-MTA -Command $($CustomizationScriptsDir)\$($RunAsUserScript)" - - $TaskFolder = $ShedService.GetFolder("\") - $TaskFolder.RegisterTaskDefinition("$($RunAsUserTask)", $Task , 6, "Users", $null, 4) - Write-Host "Done setting up scheduled tasks" -} - -function WithRetry { - Param( - [Parameter(Position=0, Mandatory=$true)] - [scriptblock]$ScriptBlock, - - [Parameter(Position=1, Mandatory=$false)] - [int]$Maximum = 5, - - [Parameter(Position=2, Mandatory=$false)] - [int]$Delay = 100 - ) - - $iterationCount = 0 - $lastException = $null - do { - $iterationCount++ - try { - Invoke-Command -Command $ScriptBlock - return - } catch { - $lastException = $_ - Write-Error $_ - - # Sleep for a random amount of time with exponential backoff - $randomDouble = Get-Random -Minimum 0.0 -Maximum 1.0 - $k = $randomDouble * ([Math]::Pow(2.0, $iterationCount) - 1.0) - Start-Sleep -Milliseconds ($k * $Delay) - } - } while ($iterationCount -lt $Maximum) - - throw $lastException -} - -function InstallPS7 { - if (!(Get-Command pwsh -ErrorAction SilentlyContinue)) { - Write-Host "Installing PowerShell 7" - $code = Invoke-RestMethod -Uri https://aka.ms/install-powershell.ps1 - $null = New-Item -Path function:Install-PowerShell -Value $code - WithRetry -ScriptBlock { - if ("$($PsInstallScope)" -eq "CurrentUser") { - Install-PowerShell -UseMSI - } - else { - # The -Quiet flag requires admin permissions - Install-PowerShell -UseMSI -Quiet - } - } -Maximum 5 -Delay 100 - # Need to update the path post install - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "Done Installing PowerShell 7" - } - else { - Write-Host "PowerShell 7 is already installed" - } -} - -function InstallWinGet { - Write-Host "Installing powershell modules in scope: $PsInstallScope" - - # ensure NuGet provider is installed - if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope $PsInstallScope - Write-Host "Done Installing NuGet provider" - } - else { - Write-Host "NuGet provider is already installed" - } - - # Set PSGallery installation policy to trusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Trusted" - - # check if the Microsoft.Winget.Client module is installed - $wingetClientPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Client | Where-Object { `$_.Version -ge '1.9.2411' }" - if (!($wingetClientPackage)) { - Write-Host "Installing Microsoft.Winget.Client" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Client -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.Winget.Client" - } - else { - Write-Host "Microsoft.Winget.Client is already installed" - } - - # check if the Microsoft.WinGet.Configuration module is installed - $wingetConfigurationPackage = pwsh.exe -Command "Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration | Where-Object { `$_.Version -ge '1.8.1911' }" - if (!($wingetConfigurationPackage)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -Scope $PsInstallScope" - Write-Host "Done Installing Microsoft.WinGet.Configuration" - } - else { - Write-Host "Microsoft.WinGet.Configuration is already installed" - } - - Write-Host "Updating WinGet" - try { - Write-Host "Attempting to repair WinGet Package Manager" - pwsh.exe -MTA -Command "Repair-WinGetPackageManager -Latest -Force -Verbose" - Write-Host "Done Reparing WinGet Package Manager" - } - catch { - Write-Host "Failed to repair WinGet Package Manager" - Write-Error $_ - } - - if ($PsInstallScope -eq "CurrentUser") { - - $architecture = "x64" - if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" - } - - $msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.30035.0" } - if (!($msVCLibsPackage)) { - # Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } - } - - $msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } - if (!($msUiXamlPackage)) { - # instal Microsoft.UI.Xaml - try { - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } - } - - $desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" - if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } - } - - Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + ";C:\Program Files\PowerShell\7" - Write-Host "WinGet version: $(winget -v)" - } - - # Revert PSGallery installation policy to untrusted - Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted - pwsh.exe -MTA -Command "Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted" -} - -InstallPS7 -InstallWinGet - -function AppendToUserScript { - Param( - [Parameter(Position=0, Mandatory=$true)] - [string]$Content - ) - - Add-Content -Path "$($CustomizationScriptsDir)\$($RunAsUserScript)" -Value $Content -} - -function EnsureConfigurationFileIsSet ($ConfigurationFile) { - # if $ConfigurationFile is not specified, we need to write the configuration to a temporary file - if (-not $ConfigurationFile) { - if ($RunAsUser -eq "true") { - # when running as user, we need to write the configuration to a file in the customization scripts directory - $ConfigurationFile = "$($CustomizationScriptsDir)\$([System.IO.Path]::GetRandomFileName()).yaml" - } - else { - # when running in the provisioning context, we need to write the configuration to a temporary file - # when this is run as system, it will end up somewhere under C:\Windows\system32\config\systemprofile\AppData\Local\Temp\ - # when running as a user, it will end up somewhere under C:\Users\\AppData\Local\Temp\ - $ConfigurationFile = [System.IO.Path]::GetTempFileName() + ".yaml" - } - } - - # Ensure the directory exists - $ConfigurationFileDir = Split-Path -Path $ConfigurationFile - if(-Not (Test-Path -Path $ConfigurationFileDir)) { - $null = New-Item -ItemType Directory -Path $ConfigurationFileDir - } - - return $ConfigurationFile -} - -# If an inline base64 configuration is specified, we need to write the decoded version to the file -if ($InlineConfigurationBase64) { - Write-Host "Decoding base64 inline configuration and writing to file" - - $ConfigurationFile = EnsureConfigurationFileIsSet($ConfigurationFile) - $InlineConfiguration = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($InlineConfigurationBase64)) - $InlineConfiguration | Out-File -FilePath $ConfigurationFile -Encoding utf8 - - Write-Host "Wrote configuration to file: $($ConfigurationFile)" -} -# If a download URL is specified, we need to download the contents and write them to the file -elseif ($DownloadUrl) { - Write-Host "Downloading configuration file from: $($DownloadUrl)" - - $ConfigurationFile = EnsureConfigurationFileIsSet($ConfigurationFile) - Invoke-WebRequest -Uri $DownloadUrl -OutFile $ConfigurationFile - - Write-Host "Downloaded configuration to: $($ConfigurationFile)" -} - -$versionFlag = "" -# We're running as user via scheduled task: -if ($RunAsUser -eq "true") { - Write-Host "Running as user via scheduled task" - - if (!(Test-Path -PathType Leaf "$($CustomizationScriptsDir)\$($LockFile)")) { - SetupScheduledTasks - } - - Write-Host "Writing commands to user script" - - # We're running in package mode: - if ($Package) { - # If there's a version passed, add the version flag for CLI - if ($Version -ne '') { - Write-Host "Specifying version: $($Version)" - $versionFlag = "--version `"$($Version)`"" - } - Write-Host "Appending package install: $($Package)" - AppendToUserScript "winget install --id `"$($Package)`" $($versionFlag) --accept-source-agreements --accept-package-agreements --silent" - AppendToUserScript "Write-Host `"winget exit code: `$LASTEXITCODE`"" - } - # We're running in configuration file mode: - elseif ($ConfigurationFile) { - Write-Host "Appending installation of configuration file: $($ConfigurationFile)" - AppendToUserScript "winget configure --file `"$($ConfigurationFile)`" --accept-configuration-agreements" - AppendToUserScript "Write-Host `"winget exit code: `$LASTEXITCODE`"" - } - else { - Write-Error "No package or configuration file specified" - exit 1 - } -} -# We're running in the provisioning context: -else { - Write-Host "Running in the provisioning context" - $tempOutFile = [System.IO.Path]::GetTempFileName() + ".out.json" - - $mtaFlag = "-MTA" - $scopeFlagValue = "SystemOrUnknown" - if ($PsInstallScope -eq "CurrentUser") { - $mtaFlag = "" - $scopeFlagValue = "UserOrUnknown" - } - - # We're running in package mode: - if ($Package) { - Write-Host "Running package install: $($Package)" - # If there's a version passed, add the version flag for PS - if ($Version -ne '') { - Write-Host "Specifying version: $($Version)" - $versionFlag = "-Version '$($Version)'" - } - - $installCommandBlock = { - $installPackageCommand = "Install-WinGetPackage -Scope $($scopeFlagValue) -Mode Silent -Source winget -Id '$($Package)' $($versionFlag) | ConvertTo-Json -Depth 10 | Tee-Object -FilePath '$($tempOutFile)'" - $processCreation = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine="C:\Program Files\PowerShell\7\pwsh.exe $($mtaFlag) -Command `"$($installPackageCommand)`""} - if (!($processCreation) -or !($processCreation.ProcessId)) { - Write-Error "Failed to install package. Process creation failed." - exit 1 - } - - $process = Get-Process -Id $processCreation.ProcessId - $handle = $process.Handle # cache process.Handle so ExitCode isn't null when we need it below - $process.WaitForExit() - $installExitCode = $process.ExitCode - if ($installExitCode -ne 0) { - Write-Error "Failed to install package. Exit code: $($installExitCode)." - exit 1 - } - - # read the output file and write it to the console - if (Test-Path -Path $tempOutFile) { - $unitResults = Get-Content -Path $tempOutFile -Raw | Out-String - Write-Host $unitResults - Remove-Item -Path $tempOutFile -Force - $unitResultsObject = $unitResults | ConvertFrom-Json - - # If the initial scope didn't produce an installer, retry with an "Any" scope - if (($unitResultsObject.Status -eq "NoApplicableInstallers") -and ($scopeFlagValue -ne "Any")) { - ([ref]$scopeFlagValue).Value = "Any" - .$installCommandBlock - } - - # If there are any errors in the package installation, we need to exit with a non-zero code - if ($unitResultsObject.Status -ne "Ok") { - Write-Error "There were errors installing the package." - exit 1 - } - } - else { - Write-Host "Couldn't find output file for package installation, assuming fail." - exit 1 - } - } - .$installCommandBlock - } - # We're running in configuration file mode: - elseif ($ConfigurationFile) { - Write-Host "Running installation of configuration file: $($ConfigurationFile)" - $applyConfigCommand = "Get-WinGetConfiguration -File '$($ConfigurationFile)' | Invoke-WinGetConfiguration -AcceptConfigurationAgreements | Select-Object -ExpandProperty UnitResults | ConvertTo-Json -Depth 10 | Tee-Object -FilePath '$($tempOutFile)'" - $processCreation = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine="C:\Program Files\PowerShell\7\pwsh.exe -Command `"$($applyConfigCommand)`""} - if (!($processCreation) -or !($processCreation.ProcessId)) { - Write-Error "Failed to run configuration file installation. Process creation failed." - exit 1 - } - - $process = Get-Process -Id $processCreation.ProcessId - $handle = $process.Handle # cache process.Handle so ExitCode isn't null when we need it below - $process.WaitForExit() - $installExitCode = $process.ExitCode - if ($installExitCode -ne 0) { - Write-Error "Failed to run configuration file installation. Exit code: $($installExitCode)." - exit 1 - } - - # read the output file and write it to the console - if (Test-Path -Path $tempOutFile) { - $unitResults = Get-Content -Path $tempOutFile -Raw | Out-String - Write-Host $unitResults - Remove-Item -Path $tempOutFile -Force - # If there are any errors in the unit results, we need to exit with a non-zero code - $unitResultsObject = $unitResults | ConvertFrom-Json - $errors = $unitResultsObject | Where-Object { $_.ResultCode -ne "0" } - if ($errors) { - Write-Error "There were errors applying the configuration." - exit 1 - } - } - else { - Write-Host "Couldn't find output file for configuration application, assuming fail." - exit 1 - } - } - else { - Write-Error "No package or configuration file specified" - exit 1 - } -} - -exit 0 \ No newline at end of file diff --git a/.configuration/devcenter/tasks/winget/runAsUser.ps1 b/.configuration/devcenter/tasks/winget/runAsUser.ps1 deleted file mode 100644 index dc6c3599..00000000 --- a/.configuration/devcenter/tasks/winget/runAsUser.ps1 +++ /dev/null @@ -1,124 +0,0 @@ -$CustomizationScriptsDir = "C:\DevBoxCustomizations" -$LockFile = "lockfile" -$SetVariablesScript = "setVariables.ps1" -$RunAsUserScript = "runAsUser.ps1" -$CleanupScript = "cleanup.ps1" -$RunAsUserTask = "DevBoxCustomizations" -$CleanupTask = "DevBoxCustomizationsCleanup" - -# Set the progress preference to silently continue -# in order to avoid progress bars in the output -# as this makes web requests very slow -# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables -$ProgressPreference = 'SilentlyContinue' - -Start-Transcript -Path $env:TEMP\scheduled-task-customization.log -Append -IncludeInvocationHeader - -Write-Host "Microsoft Dev Box - Customizations" -Write-Host "----------------------------------" -Write-Host "Setting up scheduled tasks..." - -Remove-Item -Path "$($CustomizationScriptsDir)\$($LockFile)" - -Write-Host "Updating WinGet" -# ensure NuGet provider is installed -if (!(Get-PackageProvider | Where-Object { $_.Name -eq "NuGet" -and $_.Version -gt "2.8.5.201" })) { - Write-Host "Installing NuGet provider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser - Write-Host "Done Installing NuGet provider" -} -else { - Write-Host "NuGet provider is already installed" -} - -# Set PSGallery installation policy to trusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted - -# check if the Microsoft.Winget.Client module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.Winget.Client)) { - Write-Host "Installing Microsoft.Winget.Client" - Install-Module Microsoft.WinGet.Client -Scope CurrentUser - Write-Host "Done Installing Microsoft.Winget.Client" -} -else { - Write-Host "Microsoft.Winget.Client is already installed" -} - -# check if the Microsoft.WinGet.Configuration module is installed -if (!(Get-Module -ListAvailable -Name Microsoft.WinGet.Configuration)) { - Write-Host "Installing Microsoft.WinGet.Configuration" - pwsh.exe -MTA -Command "Install-Module Microsoft.WinGet.Configuration -AllowPrerelease -Scope CurrentUser" - Write-Host "Done Installing Microsoft.WinGet.Configuration" -} -else { - Write-Host "Microsoft.WinGet.Configuration is already installed" -} - -$architecture = "x64" -if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - $architecture = "arm64" -} - -$msVCLibsPackage = Get-AppxPackage -Name "Microsoft.VCLibs.140.00.UWPDesktop" | Where-Object { $_.Version -ge "14.0.30035.0" } -if (!($msVCLibsPackage)) { -# Install Microsoft.VCLibs.140.00.UWPDesktop - try { - Write-Host "Installing Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibs = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.VCLibs.140.00.UWPDesktop" - $MsVCLibsAppx = "$($MsVCLibs).appx" - - Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.$($architecture).14.00.Desktop.appx" -OutFile $MsVCLibsAppx - Add-AppxPackage -Path $MsVCLibsAppx -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.VCLibs.140.00.UWPDesktop" - } catch { - Write-Host "Failed to install Microsoft.VCLibs.140.00.UWPDesktop" - Write-Error $_ - } -} - -$msUiXamlPackage = Get-AppxPackage -Name "Microsoft.UI.Xaml.2.8" | Where-Object { $_.Version -ge "8.2310.30001.0" } -if (!($msUiXamlPackage)) { - # instal Microsoft.UI.Xaml - try{ - Write-Host "Installing Microsoft.UI.Xaml" - $MsUiXaml = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-Microsoft.UI.Xaml.2.8.6" - $MsUiXamlZip = "$($MsUiXaml).zip" - Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6" -OutFile $MsUiXamlZip - Expand-Archive $MsUiXamlZip -DestinationPath $MsUiXaml - Add-AppxPackage -Path "$($MsUiXaml)\tools\AppX\$($architecture)\Release\Microsoft.UI.Xaml.2.8.appx" -ForceApplicationShutdown - Write-Host "Done Installing Microsoft.UI.Xaml" - } catch { - Write-Host "Failed to install Microsoft.UI.Xaml" - Write-Error $_ - } -} - -$desktopAppInstallerPackage = Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" -if (!($desktopAppInstallerPackage) -or ($desktopAppInstallerPackage.Version -lt "1.22.0.0")) { - # install Microsoft.DesktopAppInstaller - try { - Write-Host "Installing Microsoft.DesktopAppInstaller" - $DesktopAppInstallerAppx = "$env:TEMP\$([System.IO.Path]::GetRandomFileName())-DesktopAppInstaller.appx" - Invoke-WebRequest -Uri "https://aka.ms/getwinget" -OutFile $DesktopAppInstallerAppx - - # install the DesktopAppInstaller appx package - Add-AppxPackage -Path $DesktopAppInstallerAppx -ForceApplicationShutdown - - Write-Host "Done Installing Microsoft.DesktopAppInstaller" - } - catch { - Write-Host "Failed to install DesktopAppInstaller appx package" - Write-Error $_ - } -} - -Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe -Write-Host "WinGet version: $(winget -v)" - -$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") -Write-Host "Done Updating WinGet" - -# Revert PSGallery installation policy to untrusted -Set-PSRepository -Name "PSGallery" -InstallationPolicy Untrusted - - diff --git a/.configuration/devcenter/tasks/winget/task.yaml b/.configuration/devcenter/tasks/winget/task.yaml deleted file mode 100644 index 3412c284..00000000 --- a/.configuration/devcenter/tasks/winget/task.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# This is a winget package installation task for Dev Box. - -$schema: 1.0 -name: winget -description: Applies a winget configuration to the Dev Box. -author: Microsoft Corporation -command: .\main.ps1 -ConfigurationFile {{configurationFile}} -DownloadUrl {{downloadUrl}} -InlineConfigurationBase64 {{inlineConfigurationBase64}} -Package {{package}} -Version {{version}} -RunAsUser {{runAsUser}} -parameters: - configurationFile: - default: '' - type: 'string' - required: false - description: | - The path to the winget config yaml file. The file must be located in the local machine. - downloadUrl: - default: '' - type: 'string' - required: false - description: | - A publicly accessible URL where the config yaml file is stored. This file will be downloaded to the path given in 'configurationFile'. - inlineConfigurationBase64: - default: '' - type: 'string' - required: false - description: | - A base64 encoded string of the winget config yaml file. This file will be decoded and saved to the path given in 'configurationFile' or to a temporary file if not specified. - package: - default: '' - type: 'string' - required: false - description: | - The name of the package to install. This is an alternative way. - If a config yaml file is provided under other parameters, there is no need for the package name. - version: - default: '' - type: 'string' - required: false - description: | - The version of the package to install. - If a config yaml file is provided under other parameters, there is no need for the package version. - runAsUser: - default: false - type: 'boolean' - required: false - description: | - Whether to run the installation as the current user or as an administrator. - If set to true, the installation will run during the user's first login to the machine. -documentation: - notes: This task allows installing packages via winget, or applying a winget configuration file. - examples: - - name: winget - description: Install a package via winget - parameters: - package: git - - name: winget - description: Apply a winget configuration file that's already present on the machine - parameters: - configurationFile: 'C:\WinGetConfig\winget.yaml' - - name: winget - description: Apply a winget configuration file, downloading from a public URL to the specified path, running as the user - parameters: - downloadUrl: 'https://raw.githubusercontent.com/microsoft/devhome/main/sampleConfigurations/microsoft/vscode/configuration.dsc.yaml' - configurationFile: 'C:\WinGetConfig\configuration.dsc.yaml' - runAsUser: true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 637fa493..db644629 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,128 +1,128 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -evalvesd@microsoft.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +evalvesd@microsoft.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb1ae458..72e451ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1 @@ -... +... diff --git a/README.md b/README.md index 8251069d..35e21547 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,65 @@ -# Build - -[![Accelerator CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml) -[![Documentation CI/CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml) - -# Overview -The [**Dev Box accelerator**](https://evilazaro.github.io/DevExp-DevBox/) is an open-source, reference implementation designed to help you quickly establish a landing zone subscription optimized for Microsoft Dev Box deployments. Built on the principles and best practices of the [**Azure Cloud Adoption Framework (CAF) enterprise-scale landing zones**](https://docs.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/enterprise-scale), it provides a strategic design path and a target technical state that: - -- Establishes foundational services (network, monitoring, security, and workload) required for a secure, scalable, and multi-tenant Dev Box environment. -- Aligns to CAF guidance for subscription structure, resource groups, and role-based access control (RBAC). -- Is fully modular, parameterized, and ready to be adapted to your organization’s existing landing zone or to provision new platform services from scratch. -- Is open source—feel free to fork, extend, or customize the Bicep modules, policies, and scripts to meet your unique requirements. - -## Resources Visualization - -![Resources Visualization](https://evilazaro.github.io/DevExp-DevBox/docs/overview/whatis/mainbicepvisualization.png) - -## What the Microsoft Dev Box accelerator Provides - -The Microsoft Dev Box Accelerator delivers a comprehensive set of Bicep modules, automation scripts, and **YAML configuration files with accompanying JSON schema definitions**, designed to streamline the deployment of a production-ready Microsoft Dev Box landing zone. These artifacts empower infrastructure professionals with a **configuration-as-code** approach, enabling repeatable, scalable, and policy-compliant environments. - -### Key Components - -- **Networking**: Virtual networks, subnets, network connections and optional hub connectivity. -- **Identity & Access**: Microsoft Entra integration, service principals, managed identities, and RBAC assignments. -- **Security & Governance**: Policy assignments (tagging, security baseline, resource consistency), Azure Monitor and Log Analytics integration. -- **Platform Services**: DevCenter, Projects, and supporting components. - -## Cloud Adoption Framework Alignment - -All artifacts align with CAF’s enterprise-scale landing zone patterns: - -- **Management Group Hierarchy**: Clear separation of concerns (Connectivity, Monitoring, Security, Workload). -- **Modularity**: Deploy only the foundational services you need. - -## Enterprise-Scale Design Principles - -- **Scalability**: Supports hundreds of developers and multiple Dev Box SKUs. -- **Security**: Zero-trust networking, least-privilege access, continuous monitoring, and compliance. -- **Cost Management**: Tagging, budget alerts, and automated Dev Box lifecycle management. - -## Design Areas - -When implementing a scalable Microsoft Dev Box landing zone, consider the following design areas: - -| Design Area | Considerations | -|----------------------------|--------------------------------------------------------------------------------------------------------------------------| -| **Subscription Topology** | Placement under a dedicated **“Dev Box”** subscription; isolation from production workloads; environment-dependent naming. | -| **Resource Organization** | Resource group structure (e.g., `connectivity-rg`, `monitoring-rg`, `security-rg`, and `workload-rg`); consistent naming & tagging policies. | -| **Networking** | Hub-and-spoke or standalone VNet; subnet segmentation; Azure Firewall or NVA integration; optional VPN/ExpressRoute. | -| **Identity & Access** | Microsoft Entra security groups for platform engineering teams, dev team leads, and developers; managed identities for automation, and DevCenter integration. | | -| **Security & Governance** | Key Vault for secrets; Log Analytics workspace for logs, and telemetry. | -| **Platform Services** | Configuration of Dev Center, Custom Tasks Catalogs, Networking Connections, Projects, Environments and Image Definitions, and environments types; assignment of Dev Center roles via RBAC. | - -## Journey Paths -> - **Greenfield**: Deploy the accelerator’s Bicep modules to create platform foundational services, then launch your Dev Box environment. -> - **Brownfield**: Import existing landing zone services by disabling and parameterizing connections (e.g., pointing to an existing VNet, Subnet, Resource Group or Key Vault). - -**Learn more** how to configure the Accelerator in the [Accelerator Configuration](https://evilazaro.github.io/DevExp-DevBox/docs/configureresources/) session. - -## Release Strategy - -## Overview - -The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. [Learn more...](RELEASE_STRATEGY.md) +# Build + +[![Accelerator CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml) +[![Documentation CI/CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml) + +# Overview +The [**Dev Box accelerator**](https://evilazaro.github.io/DevExp-DevBox/) is an open-source, reference implementation designed to help you quickly establish a landing zone subscription optimized for Microsoft Dev Box deployments. Built on the principles and best practices of the [**Azure Cloud Adoption Framework (CAF) enterprise-scale landing zones**](https://docs.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/enterprise-scale), it provides a strategic design path and a target technical state that: + +- Establishes foundational services (network, monitoring, security, and workload) required for a secure, scalable, and multi-tenant Dev Box environment. +- Aligns to CAF guidance for subscription structure, resource groups, and role-based access control (RBAC). +- Is fully modular, parameterized, and ready to be adapted to your organization’s existing landing zone or to provision new platform services from scratch. +- Is open source—feel free to fork, extend, or customize the Bicep modules, policies, and scripts to meet your unique requirements. + +## Resources Visualization + +![Resources Visualization](https://evilazaro.github.io/DevExp-DevBox/docs/overview/whatis/mainbicepvisualization.png) + +## What the Microsoft Dev Box accelerator Provides + +The Microsoft Dev Box Accelerator delivers a comprehensive set of Bicep modules, automation scripts, and **YAML configuration files with accompanying JSON schema definitions**, designed to streamline the deployment of a production-ready Microsoft Dev Box landing zone. These artifacts empower infrastructure professionals with a **configuration-as-code** approach, enabling repeatable, scalable, and policy-compliant environments. + +### Key Components + +- **Networking**: Virtual networks, subnets, network connections and optional hub connectivity. +- **Identity & Access**: Microsoft Entra integration, service principals, managed identities, and RBAC assignments. +- **Security & Governance**: Policy assignments (tagging, security baseline, resource consistency), Azure Monitor and Log Analytics integration. +- **Platform Services**: DevCenter, Projects, and supporting components. + +## Cloud Adoption Framework Alignment + +All artifacts align with CAF’s enterprise-scale landing zone patterns: + +- **Management Group Hierarchy**: Clear separation of concerns (Connectivity, Monitoring, Security, Workload). +- **Modularity**: Deploy only the foundational services you need. + +## Enterprise-Scale Design Principles + +- **Scalability**: Supports hundreds of developers and multiple Dev Box SKUs. +- **Security**: Zero-trust networking, least-privilege access, continuous monitoring, and compliance. +- **Cost Management**: Tagging, budget alerts, and automated Dev Box lifecycle management. + +## Design Areas + +When implementing a scalable Microsoft Dev Box landing zone, consider the following design areas: + +| Design Area | Considerations | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------| +| **Subscription Topology** | Placement under a dedicated **“Dev Box”** subscription; isolation from production workloads; environment-dependent naming. | +| **Resource Organization** | Resource group structure (e.g., `connectivity-rg`, `monitoring-rg`, `security-rg`, and `workload-rg`); consistent naming & tagging policies. | +| **Networking** | Hub-and-spoke or standalone VNet; subnet segmentation; Azure Firewall or NVA integration; optional VPN/ExpressRoute. | +| **Identity & Access** | Microsoft Entra security groups for platform engineering teams, dev team leads, and developers; managed identities for automation, and DevCenter integration. | | +| **Security & Governance** | Key Vault for secrets; Log Analytics workspace for logs, and telemetry. | +| **Platform Services** | Configuration of Dev Center, Custom Tasks Catalogs, Networking Connections, Projects, Environments and Image Definitions, and environments types; assignment of Dev Center roles via RBAC. | + +## Journey Paths +> - **Greenfield**: Deploy the accelerator’s Bicep modules to create platform foundational services, then launch your Dev Box environment. +> - **Brownfield**: Import existing landing zone services by disabling and parameterizing connections (e.g., pointing to an existing VNet, Subnet, Resource Group or Key Vault). + +**Learn more** how to configure the Accelerator in the [Accelerator Configuration](https://evilazaro.github.io/DevExp-DevBox/docs/configureresources/) session. + +## Release Strategy + +## Overview + +The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. [Learn more...](RELEASE_STRATEGY.md) diff --git a/RELEASE_STRATEGY.md b/RELEASE_STRATEGY.md index 19a9c72f..849c6602 100644 --- a/RELEASE_STRATEGY.md +++ b/RELEASE_STRATEGY.md @@ -1,139 +1,139 @@ -# Dev Box Landing Zone Accelerator - Release Strategy - -This document outlines the comprehensive release strategy for the Dev Box landing zone accelerator, detailing branch-based versioning, automated workflows, and deployment processes. - -## Overview - -The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. - -## Release Strategy Summary - -| Branch Pattern | Version Strategy | Release Publication | Tag Creation | Artifacts | -|----------------|------------------|-------------------|--------------|-----------| -| `main` | Conditional major increment | ✅ Published | ✅ Created | ✅ Built & Uploaded | -| `feature/**` | Patch increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | -| `fix/**` | Minor increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | -| `pull_request` | Based on source branch | ❌ Not published | ✅ Created | ✅ Built & Uploaded | - -## Branch-Specific Versioning Rules - -### 🎯 Main Branch (`main`) - -**New Conditional Major Increment Rule:** - -- **If `minor = 0` AND `patch = 0`**: Increment major version - - Example: `v1.0.0` → `v2.0.0` -- **If `minor ≠ 0` OR `patch ≠ 0`**: Keep major version, increment patch - - Example: `v1.5.0` → `v1.5.1` - - Example: `v1.0.3` → `v1.0.4` - - Example: `v1.5.3` → `v1.5.4` - -**Overflow Handling:** -- If patch exceeds 99: Reset patch to 0, increment minor - - Example: `v1.5.99` → `v1.6.0` -- If minor exceeds 99: Reset minor to 0, increment major - - Example: `v1.99.99` → `v2.0.0` - -### ✨ Feature Branches (`feature/**`) - -**Patch Increment Strategy:** -- Increments the patch version by the number of commits in the branch -- Format: `vX.Y.(Z+commits)-feature.branch-name` - -**Examples:** -- Current: `v1.2.5`, Branch: `feature/user-authentication`, Commits: 3 -- Result: `v1.2.8-feature.user-authentication` - -**Overflow Logic:** -- If `patch + commits > 99`: Reset patch to 0, increment minor -- If minor overflow occurs: Reset minor to 0, increment major - -### 🔧 Fix Branches (`fix/**`) - -**Minor Increment Strategy:** -- Increments the minor version by the number of commits in the branch -- Format: `vX.(Y+commits).Z-fix.branch-name` - -**Examples:** -- Current: `v1.2.5`, Branch: `fix/login-bug`, Commits: 2 -- Result: `v1.4.5-fix.login-bug` - -**Overflow Logic:** -- If `minor + commits > 99`: Reset minor to 0, increment major - -## Version Examples - -### Main Branch Scenarios - -| Current Version | Condition | Action | Result | Reasoning | -|----------------|-----------|---------|---------|-----------| -| `v1.0.0` | minor=0, patch=0 | Major increment | `v2.0.0` | Clean state allows major bump | -| `v1.5.0` | minor≠0, patch=0 | Patch increment | `v1.5.1` | Development continues on current major | -| `v1.0.3` | minor=0, patch≠0 | Patch increment | `v1.0.4` | Development continues on current major | -| `v1.5.99` | Patch overflow | Minor increment | `v1.6.0` | Patch overflow triggers minor bump | -| `v1.99.99` | Cascading overflow | Major increment | `v2.0.0` | Full overflow resets to new major | - -### Feature Branch Scenarios - -| Current Version | Branch | Commits | Calculation | Result | -|----------------|---------|---------|-------------|---------| -| `v1.2.5` | `feature/auth` | 3 | 5 + 3 = 8 | `v1.2.8-feature.auth` | -| `v1.2.97` | `feature/ui` | 5 | 97 + 5 = 102 > 99 | `v1.3.0-feature.ui` | -| `v1.99.95` | `feature/api` | 8 | Cascading overflow | `v2.0.0-feature.api` | - -### Fix Branch Scenarios - -| Current Version | Branch | Commits | Calculation | Result | -|----------------|---------|---------|-------------|---------| -| `v1.5.3` | `fix/bug-123` | 2 | 5 + 2 = 7 | `v1.7.3-fix.bug-123` | -| `v1.98.3` | `fix/critical` | 3 | 98 + 3 = 101 > 99 | `v2.0.3-fix.critical` | - -## Release Notes Structure - -Each release includes comprehensive documentation: - -```markdown -🌟 **Branch-Based Release Strategy with Conditional Major Increment** - -🔀 **Branch**: `main` -🏷️ **Version**: `v2.0.0` -📦 **Previous Version**: `v1.0.0` -🚀 **Release Type**: `main` -🤖 **Trigger**: `Push` -📝 **Commit**: `abc123...` - -## Release Strategy Applied -🎯 **Main Branch**: Conditional major increment (only if minor=0 AND patch=0) - -## Main Branch Logic -- **If minor=0 AND patch=0**: Increment major → `major+1.0.0` -- **If minor≠0 OR patch≠0**: Keep major, increment patch → `major.minor.(patch+1)` -- **Overflow handling**: If patch > 99 → `minor+1, patch=0` - -## Artifacts -- 📄 Bicep templates compiled to ARM templates -- 🏗️ Infrastructure deployment files -- 📋 Release metadata and documentation -``` - -## Best Practices - -### For Developers - -1. **Branch Naming**: Use descriptive branch names following the patterns: - - `feature/descriptive-name` - - `fix/issue-description` - -2. **Commit Strategy**: Keep commits atomic and meaningful as they influence version calculations - -3. **Testing**: Ensure all changes are tested before merging to main - -### For Release Management - -1. **Main Branch Protection**: Only merge tested, reviewed code to main -2. **Version Monitoring**: Monitor version progression to prevent unexpected major increments -3. **Release Planning**: Use the conditional major increment rule for planned major releases - -This release strategy provides a robust, automated approach to version management while maintaining flexibility for different development workflows and ensuring consistent, trackable releases for the Dev Box landing zone accelerator. -``` +# Dev Box Landing Zone Accelerator - Release Strategy + +This document outlines the comprehensive release strategy for the Dev Box landing zone accelerator, detailing branch-based versioning, automated workflows, and deployment processes. + +## Overview + +The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. + +## Release Strategy Summary + +| Branch Pattern | Version Strategy | Release Publication | Tag Creation | Artifacts | +|----------------|------------------|-------------------|--------------|-----------| +| `main` | Conditional major increment | ✅ Published | ✅ Created | ✅ Built & Uploaded | +| `feature/**` | Patch increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | +| `fix/**` | Minor increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | +| `pull_request` | Based on source branch | ❌ Not published | ✅ Created | ✅ Built & Uploaded | + +## Branch-Specific Versioning Rules + +### 🎯 Main Branch (`main`) + +**New Conditional Major Increment Rule:** + +- **If `minor = 0` AND `patch = 0`**: Increment major version + - Example: `v1.0.0` → `v2.0.0` +- **If `minor ≠ 0` OR `patch ≠ 0`**: Keep major version, increment patch + - Example: `v1.5.0` → `v1.5.1` + - Example: `v1.0.3` → `v1.0.4` + - Example: `v1.5.3` → `v1.5.4` + +**Overflow Handling:** +- If patch exceeds 99: Reset patch to 0, increment minor + - Example: `v1.5.99` → `v1.6.0` +- If minor exceeds 99: Reset minor to 0, increment major + - Example: `v1.99.99` → `v2.0.0` + +### ✨ Feature Branches (`feature/**`) + +**Patch Increment Strategy:** +- Increments the patch version by the number of commits in the branch +- Format: `vX.Y.(Z+commits)-feature.branch-name` + +**Examples:** +- Current: `v1.2.5`, Branch: `feature/user-authentication`, Commits: 3 +- Result: `v1.2.8-feature.user-authentication` + +**Overflow Logic:** +- If `patch + commits > 99`: Reset patch to 0, increment minor +- If minor overflow occurs: Reset minor to 0, increment major + +### 🔧 Fix Branches (`fix/**`) + +**Minor Increment Strategy:** +- Increments the minor version by the number of commits in the branch +- Format: `vX.(Y+commits).Z-fix.branch-name` + +**Examples:** +- Current: `v1.2.5`, Branch: `fix/login-bug`, Commits: 2 +- Result: `v1.4.5-fix.login-bug` + +**Overflow Logic:** +- If `minor + commits > 99`: Reset minor to 0, increment major + +## Version Examples + +### Main Branch Scenarios + +| Current Version | Condition | Action | Result | Reasoning | +|----------------|-----------|---------|---------|-----------| +| `v1.0.0` | minor=0, patch=0 | Major increment | `v2.0.0` | Clean state allows major bump | +| `v1.5.0` | minor≠0, patch=0 | Patch increment | `v1.5.1` | Development continues on current major | +| `v1.0.3` | minor=0, patch≠0 | Patch increment | `v1.0.4` | Development continues on current major | +| `v1.5.99` | Patch overflow | Minor increment | `v1.6.0` | Patch overflow triggers minor bump | +| `v1.99.99` | Cascading overflow | Major increment | `v2.0.0` | Full overflow resets to new major | + +### Feature Branch Scenarios + +| Current Version | Branch | Commits | Calculation | Result | +|----------------|---------|---------|-------------|---------| +| `v1.2.5` | `feature/auth` | 3 | 5 + 3 = 8 | `v1.2.8-feature.auth` | +| `v1.2.97` | `feature/ui` | 5 | 97 + 5 = 102 > 99 | `v1.3.0-feature.ui` | +| `v1.99.95` | `feature/api` | 8 | Cascading overflow | `v2.0.0-feature.api` | + +### Fix Branch Scenarios + +| Current Version | Branch | Commits | Calculation | Result | +|----------------|---------|---------|-------------|---------| +| `v1.5.3` | `fix/bug-123` | 2 | 5 + 2 = 7 | `v1.7.3-fix.bug-123` | +| `v1.98.3` | `fix/critical` | 3 | 98 + 3 = 101 > 99 | `v2.0.3-fix.critical` | + +## Release Notes Structure + +Each release includes comprehensive documentation: + +```markdown +🌟 **Branch-Based Release Strategy with Conditional Major Increment** + +🔀 **Branch**: `main` +🏷️ **Version**: `v2.0.0` +📦 **Previous Version**: `v1.0.0` +🚀 **Release Type**: `main` +🤖 **Trigger**: `Push` +📝 **Commit**: `abc123...` + +## Release Strategy Applied +🎯 **Main Branch**: Conditional major increment (only if minor=0 AND patch=0) + +## Main Branch Logic +- **If minor=0 AND patch=0**: Increment major → `major+1.0.0` +- **If minor≠0 OR patch≠0**: Keep major, increment patch → `major.minor.(patch+1)` +- **Overflow handling**: If patch > 99 → `minor+1, patch=0` + +## Artifacts +- 📄 Bicep templates compiled to ARM templates +- 🏗️ Infrastructure deployment files +- 📋 Release metadata and documentation +``` + +## Best Practices + +### For Developers + +1. **Branch Naming**: Use descriptive branch names following the patterns: + - `feature/descriptive-name` + - `fix/issue-description` + +2. **Commit Strategy**: Keep commits atomic and meaningful as they influence version calculations + +3. **Testing**: Ensure all changes are tested before merging to main + +### For Release Management + +1. **Main Branch Protection**: Only merge tested, reviewed code to main +2. **Version Monitoring**: Monitor version progression to prevent unexpected major increments +3. **Release Planning**: Use the conditional major increment rule for planned major releases + +This release strategy provides a robust, automated approach to version management while maintaining flexibility for different development workflows and ensuring consistent, trackable releases for the Dev Box landing zone accelerator. +``` This comprehensive documentation explains the entire release strategy, providing clear examples, troubleshooting guidance, and best practices for the Dev Box landing zone accelerator project. \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index a4fbff2f..ab27f132 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,2 +1,2 @@ -# Security Policy - +# Security Policy + diff --git a/azure.yaml b/azure.yaml index aecccf88..6684f8ce 100644 --- a/azure.yaml +++ b/azure.yaml @@ -1,23 +1,23 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/refs/heads/main/schemas/v1.0/azure.yaml.json - -name: ContosoDevExp - -hooks: - preprovision: - shell: sh - continueOnError: false - interactive: true - run: | - #!/bin/bash - - set -e - defaultPlatform="github" - - # Add a if statement to check if the environment variable is set - if [ -z "${SOURCE_CONTROL_PLATFORM}" ]; then - echo "SOURCE_CONTROL_PLATFORM is not set. Setting it to '${defaultPlatform}' by default." - export SOURCE_CONTROL_PLATFORM="${SOURCE_CONTROL_PLATFORM:-${defaultPlatform}}" - else - echo "Existing SOURCE_CONTROL_PLATFORM is set to '${SOURCE_CONTROL_PLATFORM}'." - fi +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/refs/heads/main/schemas/v1.0/azure.yaml.json + +name: ContosoDevExp + +hooks: + preprovision: + shell: sh + continueOnError: false + interactive: true + run: | + #!/bin/bash + + set -e + defaultPlatform="github" + + # Add a if statement to check if the environment variable is set + if [ -z "${SOURCE_CONTROL_PLATFORM}" ]; then + echo "SOURCE_CONTROL_PLATFORM is not set. Setting it to '${defaultPlatform}' by default." + export SOURCE_CONTROL_PLATFORM="${SOURCE_CONTROL_PLATFORM:-${defaultPlatform}}" + else + echo "Existing SOURCE_CONTROL_PLATFORM is set to '${SOURCE_CONTROL_PLATFORM}'." + fi ./setup.sh -e ${AZURE_ENV_NAME} -s ${SOURCE_CONTROL_PLATFORM} \ No newline at end of file diff --git a/cleanSetUp.ps1 b/cleanSetUp.ps1 index 24bc55e4..a779ae5e 100644 --- a/cleanSetUp.ps1 +++ b/cleanSetUp.ps1 @@ -1,126 +1,126 @@ -# PowerShell script to clean up the setup by deleting users, credentials, and GitHub secrets - -[CmdletBinding()] -param( - [Parameter(Mandatory=$false)] - [string]$EnvName = "gitHub", - - [Parameter(Mandatory=$false)] - [ValidateSet("eastus", "eastus2", "westus", "westus2", "westus3", "northeurope", "westeurope")] - [string]$Location = "eastus2" -) - -# Exit immediately if a command exits with a non-zero status, treat unset variables as an error, and propagate errors in pipelines. -$ErrorActionPreference = "Stop" -$WarningPreference = "Stop" - -$appDisplayName = "ContosoDevEx GitHub Actions Enterprise App" -$ghSecretName = "AZURE_CREDENTIALS" - -# Function to delete deployments -function Remove-Deployments { - param ( - [string]$resourceGroupName - ) - - try { - $deployments = az deployment sub list --query "[].name" -o tsv - if ($LASTEXITCODE -ne 0) { - throw "Failed to list deployments." - } - - foreach ($deployment in $deployments) { - if (-not [string]::IsNullOrEmpty($deployment)) { - Write-Output "Deleting deployment: $deployment" - az deployment sub delete --name $deployment - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete deployment: $deployment" - } - Write-Output "Deployment $deployment deleted." - } - } - return $true - } - catch { - Write-Error "Error deleting deployments: $_" - return $false - } -} - -# Function to clean up the setup by deleting users, credentials, and GitHub secrets -function Remove-SetUp { - param ( - [Parameter(Mandatory = $true)] - [string]$appDisplayName, - - [Parameter(Mandatory = $true)] - [string]$ghSecretName - ) - - try { - # Check if required parameters are provided - if ([string]::IsNullOrEmpty($appDisplayName) -or [string]::IsNullOrEmpty($ghSecretName)) { - throw "Missing required parameters." - } - - Write-Output "Starting cleanup process for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" - - # Delete deployments - Write-Output "Deleting deployments..." - $deploymentResult = Remove-Deployments - if (-not $deploymentResult) { - throw "Failed to delete deployments." - } - - # Delete users and assigned roles - Write-Output "Deleting users and assigned roles..." - & ".\.configuration\setup\powershell\Azure\deleteUsersAndAssignedRoles.ps1" -appDisplayName $appDisplayName - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete users and assigned roles." - } - - # Delete deployment credentials - Write-Output "Deleting deployment credentials..." - & ".\.configuration\setup\powershell\Azure\deleteDeploymentCredentials.ps1" -appDisplayName $appDisplayName - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete deployment credentials." - } - - # Delete GitHub secret for Azure credentials - Write-Output "Deleting GitHub secret for Azure credentials..." - & ".\GitHub\deleteGitHubSecretAzureCredentials.ps1" -ghSecretName $ghSecretName - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete GitHub secret for Azure credentials." - } - - Write-Output "Cleanup process completed successfully for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" - return $true - } - catch { - Write-Error "Error during cleanup process: $_" - return $false - } -} - -# Main script execution -try { - Clear-Host - - # Call the cleanup function with the required parameters - Write-Output "Starting cleanup process with EnvName: $EnvName and Location: $Location" - - # Additional cleanup script if it exists - $cleanupScriptPath = ".\.configuration\powershell\cleanUp.ps1" - if (Test-Path $cleanupScriptPath) { - & $cleanupScriptPath $EnvName $Location - if ($LASTEXITCODE -ne 0) { - throw "Cleanup script failed." - } - } - - Write-Output "All cleanup operations completed successfully." -} -catch { - Write-Error "Script execution failed: $_" - exit 1 +# PowerShell script to clean up the setup by deleting users, credentials, and GitHub secrets + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$EnvName = "gitHub", + + [Parameter(Mandatory=$false)] + [ValidateSet("eastus", "eastus2", "westus", "westus2", "westus3", "northeurope", "westeurope")] + [string]$Location = "eastus2" +) + +# Exit immediately if a command exits with a non-zero status, treat unset variables as an error, and propagate errors in pipelines. +$ErrorActionPreference = "Stop" +$WarningPreference = "Stop" + +$appDisplayName = "ContosoDevEx GitHub Actions Enterprise App" +$ghSecretName = "AZURE_CREDENTIALS" + +# Function to delete deployments +function Remove-Deployments { + param ( + [string]$resourceGroupName + ) + + try { + $deployments = az deployment sub list --query "[].name" -o tsv + if ($LASTEXITCODE -ne 0) { + throw "Failed to list deployments." + } + + foreach ($deployment in $deployments) { + if (-not [string]::IsNullOrEmpty($deployment)) { + Write-Output "Deleting deployment: $deployment" + az deployment sub delete --name $deployment + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete deployment: $deployment" + } + Write-Output "Deployment $deployment deleted." + } + } + return $true + } + catch { + Write-Error "Error deleting deployments: $_" + return $false + } +} + +# Function to clean up the setup by deleting users, credentials, and GitHub secrets +function Remove-SetUp { + param ( + [Parameter(Mandatory = $true)] + [string]$appDisplayName, + + [Parameter(Mandatory = $true)] + [string]$ghSecretName + ) + + try { + # Check if required parameters are provided + if ([string]::IsNullOrEmpty($appDisplayName) -or [string]::IsNullOrEmpty($ghSecretName)) { + throw "Missing required parameters." + } + + Write-Output "Starting cleanup process for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" + + # Delete deployments + Write-Output "Deleting deployments..." + $deploymentResult = Remove-Deployments + if (-not $deploymentResult) { + throw "Failed to delete deployments." + } + + # Delete users and assigned roles + Write-Output "Deleting users and assigned roles..." + & ".\.configuration\setup\powershell\Azure\deleteUsersAndAssignedRoles.ps1" -appDisplayName $appDisplayName + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete users and assigned roles." + } + + # Delete deployment credentials + Write-Output "Deleting deployment credentials..." + & ".\.configuration\setup\powershell\Azure\deleteDeploymentCredentials.ps1" -appDisplayName $appDisplayName + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete deployment credentials." + } + + # Delete GitHub secret for Azure credentials + Write-Output "Deleting GitHub secret for Azure credentials..." + & ".\GitHub\deleteGitHubSecretAzureCredentials.ps1" -ghSecretName $ghSecretName + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete GitHub secret for Azure credentials." + } + + Write-Output "Cleanup process completed successfully for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" + return $true + } + catch { + Write-Error "Error during cleanup process: $_" + return $false + } +} + +# Main script execution +try { + Clear-Host + + # Call the cleanup function with the required parameters + Write-Output "Starting cleanup process with EnvName: $EnvName and Location: $Location" + + # Additional cleanup script if it exists + $cleanupScriptPath = ".\.configuration\powershell\cleanUp.ps1" + if (Test-Path $cleanupScriptPath) { + & $cleanupScriptPath $EnvName $Location + if ($LASTEXITCODE -ne 0) { + throw "Cleanup script failed." + } + } + + Write-Output "All cleanup operations completed successfully." +} +catch { + Write-Error "Script execution failed: $_" + exit 1 } \ No newline at end of file diff --git a/package.json b/package.json index 12cbc815..33d405ea 100644 --- a/package.json +++ b/package.json @@ -1,53 +1,53 @@ -{ - "name": "docsy-example-site", - "version": "0.10.0", - "version.next": "0.10.1-dev.0-unreleased", - "description": "Example site that uses Docsy theme for technical documentation.", - "repository": "github:google/docsy-example", - "homepage": "https://example.docsy.dev", - "author": "Docsy Authors", - "license": "Apache-2.0", - "bugs": "https://github.com/google/docsy-example/issues", - "spelling": "cSpell:ignore docsy hugo htmltest precheck postbuild rtlcss -", - "scripts": { - "_build": "npm run _hugo-dev --", - "_check:links": "echo IMPLEMENTATION PENDING for check-links; echo", - "_hugo": "hugo --cleanDestinationDir", - "_hugo-dev": "npm run _hugo -- -e dev -DFE", - "_local": "npx cross-env HUGO_MODULE_WORKSPACE=docsy.work", - "_serve": "npm run _hugo-dev -- --minify serve --renderToMemory", - "build:preview": "npm run _hugo-dev -- --minify --baseURL \"${DEPLOY_PRIME_URL:-/}\"", - "build:production": "npm run _hugo -- --minify", - "build": "npm run _build -- ", - "check:links:all": "HTMLTEST_ARGS= npm run _check:links", - "check:links": "npm run _check:links", - "clean": "rm -Rf public/* resources", - "local": "npm run _local -- npm run", - "make:public": "git init -b main public", - "precheck:links:all": "npm run build", - "precheck:links": "npm run build", - "postbuild:preview": "npm run _check:links", - "postbuild:production": "npm run _check:links", - "serve": "npm run _serve", - "test": "npm run check:links", - "update:dep": "npm install --save-dev autoprefixer@latest postcss-cli@latest", - "update:hugo": "npm install --save-dev --save-exact hugo-extended@latest", - "update:pkgs": "npx npm-check-updates -u" - }, - "devDependencies": { - "autoprefixer": "^10.4.20", - "cross-env": "^7.0.3", - "hugo-extended": "0.136.2", - "postcss-cli": "^11.0.0", - "rtlcss": "^4.3.0" - }, - "optionalDependencies": { - "npm-check-updates": "^17.1.4" - }, - "private": true, - "prettier": { - "proseWrap": "always", - "singleQuote": true - } - } +{ + "name": "docsy-example-site", + "version": "0.10.0", + "version.next": "0.10.1-dev.0-unreleased", + "description": "Example site that uses Docsy theme for technical documentation.", + "repository": "github:google/docsy-example", + "homepage": "https://example.docsy.dev", + "author": "Docsy Authors", + "license": "Apache-2.0", + "bugs": "https://github.com/google/docsy-example/issues", + "spelling": "cSpell:ignore docsy hugo htmltest precheck postbuild rtlcss -", + "scripts": { + "_build": "npm run _hugo-dev --", + "_check:links": "echo IMPLEMENTATION PENDING for check-links; echo", + "_hugo": "hugo --cleanDestinationDir", + "_hugo-dev": "npm run _hugo -- -e dev -DFE", + "_local": "npx cross-env HUGO_MODULE_WORKSPACE=docsy.work", + "_serve": "npm run _hugo-dev -- --minify serve --renderToMemory", + "build:preview": "npm run _hugo-dev -- --minify --baseURL \"${DEPLOY_PRIME_URL:-/}\"", + "build:production": "npm run _hugo -- --minify", + "build": "npm run _build -- ", + "check:links:all": "HTMLTEST_ARGS= npm run _check:links", + "check:links": "npm run _check:links", + "clean": "rm -Rf public/* resources", + "local": "npm run _local -- npm run", + "make:public": "git init -b main public", + "precheck:links:all": "npm run build", + "precheck:links": "npm run build", + "postbuild:preview": "npm run _check:links", + "postbuild:production": "npm run _check:links", + "serve": "npm run _serve", + "test": "npm run check:links", + "update:dep": "npm install --save-dev autoprefixer@latest postcss-cli@latest", + "update:hugo": "npm install --save-dev --save-exact hugo-extended@latest", + "update:pkgs": "npx npm-check-updates -u" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "cross-env": "^7.0.3", + "hugo-extended": "0.136.2", + "postcss-cli": "^11.0.0", + "rtlcss": "^4.3.0" + }, + "optionalDependencies": { + "npm-check-updates": "^17.1.4" + }, + "private": true, + "prettier": { + "proseWrap": "always", + "singleQuote": true + } + } \ No newline at end of file diff --git a/setUp.sh b/setUp.sh index 99e175ca..14dad946 100644 --- a/setUp.sh +++ b/setUp.sh @@ -1,492 +1,492 @@ -#!/bin/bash - -# setUp.sh - Sets up Azure Dev Box environment with GitHub integration -# -# DESCRIPTION -# Automates the setup of an Azure Developer CLI (azd) environment for Dev Box, -# handles GitHub authentication, and provisions required Azure resources. -# -# This script follows Azure best practices for security, error handling, -# and resource management. -# -# PARAMETERS -# -e, --env-name Name of the Azure environment to create -# -s, --source-control Source control platform (github or adogit) -# -h, --help Show this help message -# -# EXAMPLES -# ./setUp.sh -e "prod" -s "github" -# # Creates a "prod" environment with GitHub -# -# ./setUp.sh -e "dev" -s "adogit" -# # Creates a "dev" environment with Azure DevOps -# -# REQUIREMENTS -# - Azure CLI (az) -# - Azure Developer CLI (azd) -# - GitHub CLI (gh) [if using GitHub] -# - Valid authentication for chosen platform -# -# Author: DevExp Team -# Last Updated: 2023-05-15 - -# Script Configuration -set -euo pipefail # Exit on error, undefined vars, pipe failures -IFS=$'\n\t' # Secure Internal Field Separator - -# Global Variables -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -readonly TIMESTAMP_FORMAT="%Y-%m-%d %H:%M:%S" - -# Color codes for output -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly CYAN='\033[0;36m' -readonly NC='\033[0m' # No Color - -# Unicode icons -readonly INFO_ICON="ℹ️" -readonly WARNING_ICON="⚠️" -readonly ERROR_ICON="❌" -readonly SUCCESS_ICON="✅" - -# Global variables for script state -ENV_NAME="" -SOURCE_CONTROL_PLATFORM="" -GITHUB_TOKEN="" -ADO_TOKEN="" - -####################################### -# Helper Functions -####################################### - -# Logging function with different levels and colors -write_log_message() { - local message="$1" - local level="${2:-Info}" - local timestamp - timestamp=$(date +"$TIMESTAMP_FORMAT") - - case "$level" in - "Error") - echo -e "${ERROR_ICON} ${RED}[$timestamp] $message${NC}" >&2 - ;; - "Warning") - echo -e "${WARNING_ICON} ${YELLOW}[$timestamp] $message${NC}" - ;; - "Success") - echo -e "${SUCCESS_ICON} ${GREEN}[$timestamp] $message${NC}" - ;; - "Info"|*) - echo -e "${INFO_ICON} ${CYAN}[$timestamp] $message${NC}" - ;; - esac -} - -# Check if a command is available in PATH -test_command_availability() { - local command="$1" - - if ! command -v "$command" &> /dev/null; then - write_log_message "Required command '$command' was not found. Please install it before continuing." "Error" - return 1 - fi - return 0 -} - -# Show help message -show_help() { - cat << EOF -setUp.sh - Sets up Azure Dev Box environment with source control integration - -USAGE: - ./setUp.sh -e ENV_NAME -s SOURCE_CONTROL - -PARAMETERS: - -e, --env-name ENV_NAME Name of the Azure environment to create - -s, --source-control PLATFORM Source control platform (github or adogit) - -h, --help Show this help message - -EXAMPLES: - ./setUp.sh -e "prod" -s "github" - ./setUp.sh -e "dev" -s "adogit" - -REQUIREMENTS: - - Azure CLI (az) - - Azure Developer CLI (azd) - - GitHub CLI (gh) [if using GitHub] - - Valid authentication for chosen platform -EOF -} - -# Validate source control platform -validate_source_control() { - local platform="$1" - - if [[ "$platform" == "github" ]] || [[ "$platform" == "adogit" ]] || [[ -z "$platform" ]]; then - return 0 - else - write_log_message "Invalid source control platform: $platform" "Error" - write_log_message "Valid platforms: github, adogit" "Info" - return 1 - fi -} - -####################################### -# Authentication Functions -####################################### - -# Test Azure CLI authentication -test_azure_authentication() { - local az_context - - write_log_message "Verifying Azure authentication..." "Info" - - # Redirect stderr to /dev/null to prevent error messages from displaying - if ! az_context=$(az account show 2>/dev/null); then - write_log_message "Not logged into Azure. Please run 'az login' first." "Error" - return 1 - fi - - # Parse JSON output to check subscription state - local subscription_name subscription_id subscription_state - subscription_name=$(echo "$az_context" | jq -r '.name') - subscription_id=$(echo "$az_context" | jq -r '.id') - subscription_state=$(echo "$az_context" | jq -r '.state') - - # Check if subscription is enabled (Azure best practice) - if [[ "$subscription_state" != "Enabled" ]]; then - write_log_message "Current subscription '$subscription_name' is not in 'Enabled' state." "Error" - return 1 - fi - - # Output subscription details for verification - write_log_message "Using Azure subscription: $subscription_name (ID: $subscription_id)" "Info" - return 0 -} - -# Test Azure DevOps authentication -test_ado_authentication() { - write_log_message "Verifying Azure DevOps authentication..." "Info" - - # Check if Azure DevOps CLI is authenticated - if ! az devops configure --list &>/dev/null; then - write_log_message "Not logged into Azure DevOps. Please run 'az devops login' first." "Error" - return 1 - fi - - write_log_message "Azure DevOps authentication verified successfully" "Success" - return 0 -} - -# Test GitHub CLI authentication -test_github_authentication() { - write_log_message "Verifying GitHub authentication..." "Info" - - # Check if GitHub CLI is authenticated - if ! gh auth status &>/dev/null; then - write_log_message "Not logged into GitHub. Please run 'gh auth login' first." "Error" - return 1 - fi - - write_log_message "GitHub authentication verified successfully" "Success" - return 0 -} - -# Get GitHub token securely -get_secure_github_token() { - write_log_message "Retrieving GitHub token..." "Info" - - # Check if KEY_VAULT_SECRET environment variable is already set - if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then - write_log_message "Using existing KEY_VAULT_SECRET from environment" "Info" - GITHUB_TOKEN="${KEY_VAULT_SECRET}" - else - # Retrieve GitHub token using gh CLI - if ! GITHUB_TOKEN=$(gh auth token 2>/dev/null); then - write_log_message "Failed to retrieve GitHub token" "Error" - return 1 - fi - # Export as environment variable for future use - export KEY_VAULT_SECRET="${GITHUB_TOKEN}" - fi - - if [[ -z "$GITHUB_TOKEN" ]]; then - write_log_message "Failed to retrieve GitHub token" "Error" - return 1 - fi - - write_log_message "GitHub token retrieved and stored securely" "Success" - return 0 -} - -# Get Azure DevOps token securely -get_secure_ado_git_token() { - write_log_message "Retrieving Azure DevOps token..." "Info" - - # Try to get PAT from environment variable first - if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then - ADO_TOKEN="${KEY_VAULT_SECRET}" - write_log_message "Azure DevOps PAT retrieved from Key Vault" "Success" - else - write_log_message "Azure DevOps PAT not found in environment variables." "Warning" - write_log_message "Please enter your PAT securely." "Warning" - - # Prompt for PAT securely (no echo) - echo -n "Enter your Azure DevOps Personal Access Token: " - read -rs ADO_TOKEN - echo - - # Configure Azure DevOps defaults - if ! az devops configure --defaults organization=https://dev.azure.com/contososa2 project=DevExp-DevBox &>/dev/null; then - write_log_message "Azure DevOps organization and project not set. Please configure them first." "Error" - return 1 - fi - fi - - if [[ -z "$ADO_TOKEN" ]]; then - write_log_message "Failed to retrieve Azure DevOps PAT" "Error" - return 1 - fi - - # Export the token to environment variable - export AZURE_DEVOPS_EXT_PAT="$ADO_TOKEN" - - write_log_message "Azure DevOps PAT retrieved and stored securely" "Success" - return 0 -} - -####################################### -# Azure Configuration Functions -####################################### - -# Initialize Azure Developer CLI environment -initialize_azd_environment() { - local pat token_type masked_token - local env_dir env_file - - write_log_message "Initializing Azure Developer CLI environment..." "Info" - - # Get appropriate token based on source control platform - case "${SOURCE_CONTROL_PLATFORM}" in - "github") - write_log_message "Retrieving GitHub token for environment initialization..." "Info" - if ! get_secure_github_token; then - write_log_message "Unable to retrieve GitHub token. Aborting environment initialization." "Error" - return 1 - fi - pat="$GITHUB_TOKEN" - token_type="GitHub" - ;; - "adogit") - write_log_message "Retrieving Azure DevOps token for environment initialization..." "Info" - if ! get_secure_ado_git_token; then - write_log_message "Unable to retrieve Azure DevOps token. Aborting environment initialization." "Error" - return 1 - fi - pat="$ADO_TOKEN" - token_type="Azure DevOps" - ;; - *) - write_log_message "Unsupported source control platform: $SOURCE_CONTROL_PLATFORM" "Error" - return 1 - ;; - esac - - # Mask most of the token for security best practices - if [[ ${#pat} -ge 8 ]]; then - masked_token="${pat:0:4}****${pat: -2}" - else - masked_token="****" - fi - - write_log_message "🔐 $token_type token stored securely in memory. Masked: $masked_token" "Success" - - # Azure best practice: Verify environment exists or use existing - write_log_message "Using Azure Developer CLI environment: '$ENV_NAME'" "Info" - - # Prepare environment file path - env_dir="./.azure/$ENV_NAME" - env_file="$env_dir/.env" - - if [[ ! -d "$env_dir" ]]; then - mkdir -p "$env_dir" - fi - - # Azure best practice: Use environment-specific configuration - write_log_message "Configuring environment variables in $env_file" "Info" - - # Append to existing file or create if it doesn't exist - echo "KEY_VAULT_SECRET='$pat'" >> "$env_file" - echo "SOURCE_CONTROL_PLATFORM='$SOURCE_CONTROL_PLATFORM'" >> "$env_file" - - # Show current configuration for verification - write_log_message "Current Azure Developer CLI configuration:" "Info" - azd config show - - write_log_message "Azure Developer CLI environment '$ENV_NAME' initialized successfully." "Success" - return 0 -} - -# Start Azure resource provisioning -start_azure_provisioning() { - write_log_message "Starting Azure resource provisioning with azd..." "Info" - - # Run the provisioning process - if ! azd provision -e "$ENV_NAME"; then - local exit_code=$? - write_log_message "Azure provisioning failed with exit code $exit_code" "Error" - - # Provide guidance on common failures - write_log_message "This might be a quota or permissions issue. Check your Azure subscription limits and role assignments." "Warning" - - return 1 - fi - - write_log_message "Azure provisioning completed successfully" "Success" - return 0 -} - -# Interactive source control platform selection -select_source_control_platform() { - local selection valid_selection=false - - write_log_message "Please select your source control platform:" "Info" - echo "" - echo -e " ${YELLOW}1. Azure DevOps Git (adogit)${NC}" - echo -e " ${YELLOW}2. GitHub (github)${NC}" - echo "" - - while [[ "$valid_selection" == false ]]; do - echo -n "Enter your choice (1 or 2): " - read -r selection - - case "$selection" in - "1") - SOURCE_CONTROL_PLATFORM="adogit" - write_log_message "Selected: Azure DevOps Git" "Success" - valid_selection=true - ;; - "2") - SOURCE_CONTROL_PLATFORM="github" - write_log_message "Selected: GitHub" "Success" - valid_selection=true - ;; - *) - write_log_message "Invalid selection. Please enter 1 or 2." "Warning" - ;; - esac - done -} - -####################################### -# Main Script Logic -####################################### - -# Parse command line arguments -parse_arguments() { - while [[ $# -gt 0 ]]; do - case $1 in - -e|--env-name) - ENV_NAME="$2" - shift 2 - ;; - -s|--source-control) - SOURCE_CONTROL_PLATFORM="$2" - shift 2 - ;; - -h|--help) - show_help - exit 0 - ;; - *) - write_log_message "Unknown parameter: $1" "Error" - show_help - exit 1 - ;; - esac - done - - # Validate required parameters - if [[ -z "$ENV_NAME" ]]; then - write_log_message "Environment name is required. Use -e or --env-name parameter." "Error" - show_help - exit 1 - fi - - # If source control not provided, prompt for it - if [[ -z "$SOURCE_CONTROL_PLATFORM" ]]; then - select_source_control_platform - fi - - # Validate parameters - if ! validate_source_control "$SOURCE_CONTROL_PLATFORM"; then - exit 1 - fi -} - -# Main execution function -main() { - local required_tools=("az" "azd" "jq") - local tool - - # Parse command line arguments - parse_arguments "$@" - - # Script header with basic information - write_log_message "Starting Dev Box environment setup" "Info" - write_log_message "Environment name: $ENV_NAME" "Info" - write_log_message "Source control platform: $SOURCE_CONTROL_PLATFORM" "Info" - - # Add GitHub CLI to required tools if using GitHub - if [[ "$SOURCE_CONTROL_PLATFORM" == "github" ]]; then - required_tools+=("gh") - fi - - # Verify required tools - Azure best practice for dependency validation - write_log_message "Checking required tools..." "Info" - for tool in "${required_tools[@]}"; do - if ! test_command_availability "$tool"; then - write_log_message "Missing required tools. Please install them and retry." "Error" - exit 1 - fi - done - write_log_message "All required tools are available" "Success" - - # Verify Azure authentication - Azure security best practice - if ! test_azure_authentication; then - exit 1 - fi - - # Verify source control authentication - case "$SOURCE_CONTROL_PLATFORM" in - "github") - if ! test_github_authentication; then - exit 1 - fi - ;; - "adogit") - if ! test_ado_authentication; then - exit 1 - fi - ;; - esac - - # Initialize azd environment - if ! initialize_azd_environment; then - write_log_message "Failed to initialize Azure Developer CLI environment. Exiting." "Error" - exit 1 - fi - - # Success message with environment details - write_log_message "Dev Box environment '$ENV_NAME' setup successfully" "Success" - write_log_message "Access your Dev Center from the Azure portal" "Info" - write_log_message "Use 'azd env get-values' to view environment settings" "Info" -} - -# Set up error handling and cleanup -trap 'write_log_message "Script interrupted by user" "Warning"; exit 130' INT TERM - -# Execute main function with all arguments -main "$@" +#!/bin/bash + +# setUp.sh - Sets up Azure Dev Box environment with GitHub integration +# +# DESCRIPTION +# Automates the setup of an Azure Developer CLI (azd) environment for Dev Box, +# handles GitHub authentication, and provisions required Azure resources. +# +# This script follows Azure best practices for security, error handling, +# and resource management. +# +# PARAMETERS +# -e, --env-name Name of the Azure environment to create +# -s, --source-control Source control platform (github or adogit) +# -h, --help Show this help message +# +# EXAMPLES +# ./setUp.sh -e "prod" -s "github" +# # Creates a "prod" environment with GitHub +# +# ./setUp.sh -e "dev" -s "adogit" +# # Creates a "dev" environment with Azure DevOps +# +# REQUIREMENTS +# - Azure CLI (az) +# - Azure Developer CLI (azd) +# - GitHub CLI (gh) [if using GitHub] +# - Valid authentication for chosen platform +# +# Author: DevExp Team +# Last Updated: 2023-05-15 + +# Script Configuration +set -euo pipefail # Exit on error, undefined vars, pipe failures +IFS=$'\n\t' # Secure Internal Field Separator + +# Global Variables +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly TIMESTAMP_FORMAT="%Y-%m-%d %H:%M:%S" + +# Color codes for output +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly CYAN='\033[0;36m' +readonly NC='\033[0m' # No Color + +# Unicode icons +readonly INFO_ICON="ℹ️" +readonly WARNING_ICON="⚠️" +readonly ERROR_ICON="❌" +readonly SUCCESS_ICON="✅" + +# Global variables for script state +ENV_NAME="" +SOURCE_CONTROL_PLATFORM="" +GITHUB_TOKEN="" +ADO_TOKEN="" + +####################################### +# Helper Functions +####################################### + +# Logging function with different levels and colors +write_log_message() { + local message="$1" + local level="${2:-Info}" + local timestamp + timestamp=$(date +"$TIMESTAMP_FORMAT") + + case "$level" in + "Error") + echo -e "${ERROR_ICON} ${RED}[$timestamp] $message${NC}" >&2 + ;; + "Warning") + echo -e "${WARNING_ICON} ${YELLOW}[$timestamp] $message${NC}" + ;; + "Success") + echo -e "${SUCCESS_ICON} ${GREEN}[$timestamp] $message${NC}" + ;; + "Info"|*) + echo -e "${INFO_ICON} ${CYAN}[$timestamp] $message${NC}" + ;; + esac +} + +# Check if a command is available in PATH +test_command_availability() { + local command="$1" + + if ! command -v "$command" &> /dev/null; then + write_log_message "Required command '$command' was not found. Please install it before continuing." "Error" + return 1 + fi + return 0 +} + +# Show help message +show_help() { + cat << EOF +setUp.sh - Sets up Azure Dev Box environment with source control integration + +USAGE: + ./setUp.sh -e ENV_NAME -s SOURCE_CONTROL + +PARAMETERS: + -e, --env-name ENV_NAME Name of the Azure environment to create + -s, --source-control PLATFORM Source control platform (github or adogit) + -h, --help Show this help message + +EXAMPLES: + ./setUp.sh -e "prod" -s "github" + ./setUp.sh -e "dev" -s "adogit" + +REQUIREMENTS: + - Azure CLI (az) + - Azure Developer CLI (azd) + - GitHub CLI (gh) [if using GitHub] + - Valid authentication for chosen platform +EOF +} + +# Validate source control platform +validate_source_control() { + local platform="$1" + + if [[ "$platform" == "github" ]] || [[ "$platform" == "adogit" ]] || [[ -z "$platform" ]]; then + return 0 + else + write_log_message "Invalid source control platform: $platform" "Error" + write_log_message "Valid platforms: github, adogit" "Info" + return 1 + fi +} + +####################################### +# Authentication Functions +####################################### + +# Test Azure CLI authentication +test_azure_authentication() { + local az_context + + write_log_message "Verifying Azure authentication..." "Info" + + # Redirect stderr to /dev/null to prevent error messages from displaying + if ! az_context=$(az account show 2>/dev/null); then + write_log_message "Not logged into Azure. Please run 'az login' first." "Error" + return 1 + fi + + # Parse JSON output to check subscription state + local subscription_name subscription_id subscription_state + subscription_name=$(echo "$az_context" | jq -r '.name') + subscription_id=$(echo "$az_context" | jq -r '.id') + subscription_state=$(echo "$az_context" | jq -r '.state') + + # Check if subscription is enabled (Azure best practice) + if [[ "$subscription_state" != "Enabled" ]]; then + write_log_message "Current subscription '$subscription_name' is not in 'Enabled' state." "Error" + return 1 + fi + + # Output subscription details for verification + write_log_message "Using Azure subscription: $subscription_name (ID: $subscription_id)" "Info" + return 0 +} + +# Test Azure DevOps authentication +test_ado_authentication() { + write_log_message "Verifying Azure DevOps authentication..." "Info" + + # Check if Azure DevOps CLI is authenticated + if ! az devops configure --list &>/dev/null; then + write_log_message "Not logged into Azure DevOps. Please run 'az devops login' first." "Error" + return 1 + fi + + write_log_message "Azure DevOps authentication verified successfully" "Success" + return 0 +} + +# Test GitHub CLI authentication +test_github_authentication() { + write_log_message "Verifying GitHub authentication..." "Info" + + # Check if GitHub CLI is authenticated + if ! gh auth status &>/dev/null; then + write_log_message "Not logged into GitHub. Please run 'gh auth login' first." "Error" + return 1 + fi + + write_log_message "GitHub authentication verified successfully" "Success" + return 0 +} + +# Get GitHub token securely +get_secure_github_token() { + write_log_message "Retrieving GitHub token..." "Info" + + # Check if KEY_VAULT_SECRET environment variable is already set + if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then + write_log_message "Using existing KEY_VAULT_SECRET from environment" "Info" + GITHUB_TOKEN="${KEY_VAULT_SECRET}" + else + # Retrieve GitHub token using gh CLI + if ! GITHUB_TOKEN=$(gh auth token 2>/dev/null); then + write_log_message "Failed to retrieve GitHub token" "Error" + return 1 + fi + # Export as environment variable for future use + export KEY_VAULT_SECRET="${GITHUB_TOKEN}" + fi + + if [[ -z "$GITHUB_TOKEN" ]]; then + write_log_message "Failed to retrieve GitHub token" "Error" + return 1 + fi + + write_log_message "GitHub token retrieved and stored securely" "Success" + return 0 +} + +# Get Azure DevOps token securely +get_secure_ado_git_token() { + write_log_message "Retrieving Azure DevOps token..." "Info" + + # Try to get PAT from environment variable first + if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then + ADO_TOKEN="${KEY_VAULT_SECRET}" + write_log_message "Azure DevOps PAT retrieved from Key Vault" "Success" + else + write_log_message "Azure DevOps PAT not found in environment variables." "Warning" + write_log_message "Please enter your PAT securely." "Warning" + + # Prompt for PAT securely (no echo) + echo -n "Enter your Azure DevOps Personal Access Token: " + read -rs ADO_TOKEN + echo + + # Configure Azure DevOps defaults + if ! az devops configure --defaults organization=https://dev.azure.com/contososa2 project=DevExp-DevBox &>/dev/null; then + write_log_message "Azure DevOps organization and project not set. Please configure them first." "Error" + return 1 + fi + fi + + if [[ -z "$ADO_TOKEN" ]]; then + write_log_message "Failed to retrieve Azure DevOps PAT" "Error" + return 1 + fi + + # Export the token to environment variable + export AZURE_DEVOPS_EXT_PAT="$ADO_TOKEN" + + write_log_message "Azure DevOps PAT retrieved and stored securely" "Success" + return 0 +} + +####################################### +# Azure Configuration Functions +####################################### + +# Initialize Azure Developer CLI environment +initialize_azd_environment() { + local pat token_type masked_token + local env_dir env_file + + write_log_message "Initializing Azure Developer CLI environment..." "Info" + + # Get appropriate token based on source control platform + case "${SOURCE_CONTROL_PLATFORM}" in + "github") + write_log_message "Retrieving GitHub token for environment initialization..." "Info" + if ! get_secure_github_token; then + write_log_message "Unable to retrieve GitHub token. Aborting environment initialization." "Error" + return 1 + fi + pat="$GITHUB_TOKEN" + token_type="GitHub" + ;; + "adogit") + write_log_message "Retrieving Azure DevOps token for environment initialization..." "Info" + if ! get_secure_ado_git_token; then + write_log_message "Unable to retrieve Azure DevOps token. Aborting environment initialization." "Error" + return 1 + fi + pat="$ADO_TOKEN" + token_type="Azure DevOps" + ;; + *) + write_log_message "Unsupported source control platform: $SOURCE_CONTROL_PLATFORM" "Error" + return 1 + ;; + esac + + # Mask most of the token for security best practices + if [[ ${#pat} -ge 8 ]]; then + masked_token="${pat:0:4}****${pat: -2}" + else + masked_token="****" + fi + + write_log_message "🔐 $token_type token stored securely in memory. Masked: $masked_token" "Success" + + # Azure best practice: Verify environment exists or use existing + write_log_message "Using Azure Developer CLI environment: '$ENV_NAME'" "Info" + + # Prepare environment file path + env_dir="./.azure/$ENV_NAME" + env_file="$env_dir/.env" + + if [[ ! -d "$env_dir" ]]; then + mkdir -p "$env_dir" + fi + + # Azure best practice: Use environment-specific configuration + write_log_message "Configuring environment variables in $env_file" "Info" + + # Append to existing file or create if it doesn't exist + echo "KEY_VAULT_SECRET='$pat'" >> "$env_file" + echo "SOURCE_CONTROL_PLATFORM='$SOURCE_CONTROL_PLATFORM'" >> "$env_file" + + # Show current configuration for verification + write_log_message "Current Azure Developer CLI configuration:" "Info" + azd config show + + write_log_message "Azure Developer CLI environment '$ENV_NAME' initialized successfully." "Success" + return 0 +} + +# Start Azure resource provisioning +start_azure_provisioning() { + write_log_message "Starting Azure resource provisioning with azd..." "Info" + + # Run the provisioning process + if ! azd provision -e "$ENV_NAME"; then + local exit_code=$? + write_log_message "Azure provisioning failed with exit code $exit_code" "Error" + + # Provide guidance on common failures + write_log_message "This might be a quota or permissions issue. Check your Azure subscription limits and role assignments." "Warning" + + return 1 + fi + + write_log_message "Azure provisioning completed successfully" "Success" + return 0 +} + +# Interactive source control platform selection +select_source_control_platform() { + local selection valid_selection=false + + write_log_message "Please select your source control platform:" "Info" + echo "" + echo -e " ${YELLOW}1. Azure DevOps Git (adogit)${NC}" + echo -e " ${YELLOW}2. GitHub (github)${NC}" + echo "" + + while [[ "$valid_selection" == false ]]; do + echo -n "Enter your choice (1 or 2): " + read -r selection + + case "$selection" in + "1") + SOURCE_CONTROL_PLATFORM="adogit" + write_log_message "Selected: Azure DevOps Git" "Success" + valid_selection=true + ;; + "2") + SOURCE_CONTROL_PLATFORM="github" + write_log_message "Selected: GitHub" "Success" + valid_selection=true + ;; + *) + write_log_message "Invalid selection. Please enter 1 or 2." "Warning" + ;; + esac + done +} + +####################################### +# Main Script Logic +####################################### + +# Parse command line arguments +parse_arguments() { + while [[ $# -gt 0 ]]; do + case $1 in + -e|--env-name) + ENV_NAME="$2" + shift 2 + ;; + -s|--source-control) + SOURCE_CONTROL_PLATFORM="$2" + shift 2 + ;; + -h|--help) + show_help + exit 0 + ;; + *) + write_log_message "Unknown parameter: $1" "Error" + show_help + exit 1 + ;; + esac + done + + # Validate required parameters + if [[ -z "$ENV_NAME" ]]; then + write_log_message "Environment name is required. Use -e or --env-name parameter." "Error" + show_help + exit 1 + fi + + # If source control not provided, prompt for it + if [[ -z "$SOURCE_CONTROL_PLATFORM" ]]; then + select_source_control_platform + fi + + # Validate parameters + if ! validate_source_control "$SOURCE_CONTROL_PLATFORM"; then + exit 1 + fi +} + +# Main execution function +main() { + local required_tools=("az" "azd" "jq") + local tool + + # Parse command line arguments + parse_arguments "$@" + + # Script header with basic information + write_log_message "Starting Dev Box environment setup" "Info" + write_log_message "Environment name: $ENV_NAME" "Info" + write_log_message "Source control platform: $SOURCE_CONTROL_PLATFORM" "Info" + + # Add GitHub CLI to required tools if using GitHub + if [[ "$SOURCE_CONTROL_PLATFORM" == "github" ]]; then + required_tools+=("gh") + fi + + # Verify required tools - Azure best practice for dependency validation + write_log_message "Checking required tools..." "Info" + for tool in "${required_tools[@]}"; do + if ! test_command_availability "$tool"; then + write_log_message "Missing required tools. Please install them and retry." "Error" + exit 1 + fi + done + write_log_message "All required tools are available" "Success" + + # Verify Azure authentication - Azure security best practice + if ! test_azure_authentication; then + exit 1 + fi + + # Verify source control authentication + case "$SOURCE_CONTROL_PLATFORM" in + "github") + if ! test_github_authentication; then + exit 1 + fi + ;; + "adogit") + if ! test_ado_authentication; then + exit 1 + fi + ;; + esac + + # Initialize azd environment + if ! initialize_azd_environment; then + write_log_message "Failed to initialize Azure Developer CLI environment. Exiting." "Error" + exit 1 + fi + + # Success message with environment details + write_log_message "Dev Box environment '$ENV_NAME' setup successfully" "Success" + write_log_message "Access your Dev Center from the Azure portal" "Info" + write_log_message "Use 'azd env get-values' to view environment settings" "Info" +} + +# Set up error handling and cleanup +trap 'write_log_message "Script interrupted by user" "Warning"; exit 130' INT TERM + +# Execute main function with all arguments +main "$@" diff --git a/src/workload/project/projectCatalog.bicep b/src/workload/project/projectCatalog.bicep index e113948a..42f327ac 100644 --- a/src/workload/project/projectCatalog.bicep +++ b/src/workload/project/projectCatalog.bicep @@ -48,7 +48,7 @@ resource catalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - // secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null adoGit: catalogConfig.sourceControl == 'adoGit' @@ -56,7 +56,7 @@ resource catalog 'Microsoft.DevCenter/projects/catalogs@2025-04-01-preview' = { uri: catalogConfig.uri branch: catalogConfig.branch path: catalogConfig.path - // secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null + secretIdentifier: (catalogConfig.visibility == 'private') ? secretIdentifier : null } : null } From 35858233f62b9c2190218bed677332081813fe0a Mon Sep 17 00:00:00 2001 From: Evilazaro Date: Fri, 22 Aug 2025 11:44:17 -0500 Subject: [PATCH 5/5] Update image Definition --- CODE_OF_CONDUCT.md | 256 +++---- CONTRIBUTING.md | 2 +- README.md | 130 ++-- RELEASE_STRATEGY.md | 276 +++---- SECURITY.md | 4 +- azure.yaml | 44 +- cleanSetUp.ps1 | 250 +++---- infra/settings/workload/devcenter.yaml | 4 +- package.json | 104 +-- setUp.sh | 984 ++++++++++++------------- src/workload/project/projectPool.bicep | 26 +- 11 files changed, 1052 insertions(+), 1028 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index db644629..637fa493 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,128 +1,128 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -evalvesd@microsoft.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +evalvesd@microsoft.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72e451ca..eb1ae458 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1 @@ -... +... diff --git a/README.md b/README.md index 35e21547..8251069d 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,65 @@ -# Build - -[![Accelerator CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml) -[![Documentation CI/CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml) - -# Overview -The [**Dev Box accelerator**](https://evilazaro.github.io/DevExp-DevBox/) is an open-source, reference implementation designed to help you quickly establish a landing zone subscription optimized for Microsoft Dev Box deployments. Built on the principles and best practices of the [**Azure Cloud Adoption Framework (CAF) enterprise-scale landing zones**](https://docs.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/enterprise-scale), it provides a strategic design path and a target technical state that: - -- Establishes foundational services (network, monitoring, security, and workload) required for a secure, scalable, and multi-tenant Dev Box environment. -- Aligns to CAF guidance for subscription structure, resource groups, and role-based access control (RBAC). -- Is fully modular, parameterized, and ready to be adapted to your organization’s existing landing zone or to provision new platform services from scratch. -- Is open source—feel free to fork, extend, or customize the Bicep modules, policies, and scripts to meet your unique requirements. - -## Resources Visualization - -![Resources Visualization](https://evilazaro.github.io/DevExp-DevBox/docs/overview/whatis/mainbicepvisualization.png) - -## What the Microsoft Dev Box accelerator Provides - -The Microsoft Dev Box Accelerator delivers a comprehensive set of Bicep modules, automation scripts, and **YAML configuration files with accompanying JSON schema definitions**, designed to streamline the deployment of a production-ready Microsoft Dev Box landing zone. These artifacts empower infrastructure professionals with a **configuration-as-code** approach, enabling repeatable, scalable, and policy-compliant environments. - -### Key Components - -- **Networking**: Virtual networks, subnets, network connections and optional hub connectivity. -- **Identity & Access**: Microsoft Entra integration, service principals, managed identities, and RBAC assignments. -- **Security & Governance**: Policy assignments (tagging, security baseline, resource consistency), Azure Monitor and Log Analytics integration. -- **Platform Services**: DevCenter, Projects, and supporting components. - -## Cloud Adoption Framework Alignment - -All artifacts align with CAF’s enterprise-scale landing zone patterns: - -- **Management Group Hierarchy**: Clear separation of concerns (Connectivity, Monitoring, Security, Workload). -- **Modularity**: Deploy only the foundational services you need. - -## Enterprise-Scale Design Principles - -- **Scalability**: Supports hundreds of developers and multiple Dev Box SKUs. -- **Security**: Zero-trust networking, least-privilege access, continuous monitoring, and compliance. -- **Cost Management**: Tagging, budget alerts, and automated Dev Box lifecycle management. - -## Design Areas - -When implementing a scalable Microsoft Dev Box landing zone, consider the following design areas: - -| Design Area | Considerations | -|----------------------------|--------------------------------------------------------------------------------------------------------------------------| -| **Subscription Topology** | Placement under a dedicated **“Dev Box”** subscription; isolation from production workloads; environment-dependent naming. | -| **Resource Organization** | Resource group structure (e.g., `connectivity-rg`, `monitoring-rg`, `security-rg`, and `workload-rg`); consistent naming & tagging policies. | -| **Networking** | Hub-and-spoke or standalone VNet; subnet segmentation; Azure Firewall or NVA integration; optional VPN/ExpressRoute. | -| **Identity & Access** | Microsoft Entra security groups for platform engineering teams, dev team leads, and developers; managed identities for automation, and DevCenter integration. | | -| **Security & Governance** | Key Vault for secrets; Log Analytics workspace for logs, and telemetry. | -| **Platform Services** | Configuration of Dev Center, Custom Tasks Catalogs, Networking Connections, Projects, Environments and Image Definitions, and environments types; assignment of Dev Center roles via RBAC. | - -## Journey Paths -> - **Greenfield**: Deploy the accelerator’s Bicep modules to create platform foundational services, then launch your Dev Box environment. -> - **Brownfield**: Import existing landing zone services by disabling and parameterizing connections (e.g., pointing to an existing VNet, Subnet, Resource Group or Key Vault). - -**Learn more** how to configure the Accelerator in the [Accelerator Configuration](https://evilazaro.github.io/DevExp-DevBox/docs/configureresources/) session. - -## Release Strategy - -## Overview - -The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. [Learn more...](RELEASE_STRATEGY.md) +# Build + +[![Accelerator CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/azure-dev.yml) +[![Documentation CI/CD](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml/badge.svg)](https://github.com/Evilazaro/DevExp-DevBox/actions/workflows/hugo.yml) + +# Overview +The [**Dev Box accelerator**](https://evilazaro.github.io/DevExp-DevBox/) is an open-source, reference implementation designed to help you quickly establish a landing zone subscription optimized for Microsoft Dev Box deployments. Built on the principles and best practices of the [**Azure Cloud Adoption Framework (CAF) enterprise-scale landing zones**](https://docs.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/enterprise-scale), it provides a strategic design path and a target technical state that: + +- Establishes foundational services (network, monitoring, security, and workload) required for a secure, scalable, and multi-tenant Dev Box environment. +- Aligns to CAF guidance for subscription structure, resource groups, and role-based access control (RBAC). +- Is fully modular, parameterized, and ready to be adapted to your organization’s existing landing zone or to provision new platform services from scratch. +- Is open source—feel free to fork, extend, or customize the Bicep modules, policies, and scripts to meet your unique requirements. + +## Resources Visualization + +![Resources Visualization](https://evilazaro.github.io/DevExp-DevBox/docs/overview/whatis/mainbicepvisualization.png) + +## What the Microsoft Dev Box accelerator Provides + +The Microsoft Dev Box Accelerator delivers a comprehensive set of Bicep modules, automation scripts, and **YAML configuration files with accompanying JSON schema definitions**, designed to streamline the deployment of a production-ready Microsoft Dev Box landing zone. These artifacts empower infrastructure professionals with a **configuration-as-code** approach, enabling repeatable, scalable, and policy-compliant environments. + +### Key Components + +- **Networking**: Virtual networks, subnets, network connections and optional hub connectivity. +- **Identity & Access**: Microsoft Entra integration, service principals, managed identities, and RBAC assignments. +- **Security & Governance**: Policy assignments (tagging, security baseline, resource consistency), Azure Monitor and Log Analytics integration. +- **Platform Services**: DevCenter, Projects, and supporting components. + +## Cloud Adoption Framework Alignment + +All artifacts align with CAF’s enterprise-scale landing zone patterns: + +- **Management Group Hierarchy**: Clear separation of concerns (Connectivity, Monitoring, Security, Workload). +- **Modularity**: Deploy only the foundational services you need. + +## Enterprise-Scale Design Principles + +- **Scalability**: Supports hundreds of developers and multiple Dev Box SKUs. +- **Security**: Zero-trust networking, least-privilege access, continuous monitoring, and compliance. +- **Cost Management**: Tagging, budget alerts, and automated Dev Box lifecycle management. + +## Design Areas + +When implementing a scalable Microsoft Dev Box landing zone, consider the following design areas: + +| Design Area | Considerations | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------| +| **Subscription Topology** | Placement under a dedicated **“Dev Box”** subscription; isolation from production workloads; environment-dependent naming. | +| **Resource Organization** | Resource group structure (e.g., `connectivity-rg`, `monitoring-rg`, `security-rg`, and `workload-rg`); consistent naming & tagging policies. | +| **Networking** | Hub-and-spoke or standalone VNet; subnet segmentation; Azure Firewall or NVA integration; optional VPN/ExpressRoute. | +| **Identity & Access** | Microsoft Entra security groups for platform engineering teams, dev team leads, and developers; managed identities for automation, and DevCenter integration. | | +| **Security & Governance** | Key Vault for secrets; Log Analytics workspace for logs, and telemetry. | +| **Platform Services** | Configuration of Dev Center, Custom Tasks Catalogs, Networking Connections, Projects, Environments and Image Definitions, and environments types; assignment of Dev Center roles via RBAC. | + +## Journey Paths +> - **Greenfield**: Deploy the accelerator’s Bicep modules to create platform foundational services, then launch your Dev Box environment. +> - **Brownfield**: Import existing landing zone services by disabling and parameterizing connections (e.g., pointing to an existing VNet, Subnet, Resource Group or Key Vault). + +**Learn more** how to configure the Accelerator in the [Accelerator Configuration](https://evilazaro.github.io/DevExp-DevBox/docs/configureresources/) session. + +## Release Strategy + +## Overview + +The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. [Learn more...](RELEASE_STRATEGY.md) diff --git a/RELEASE_STRATEGY.md b/RELEASE_STRATEGY.md index 849c6602..19a9c72f 100644 --- a/RELEASE_STRATEGY.md +++ b/RELEASE_STRATEGY.md @@ -1,139 +1,139 @@ -# Dev Box Landing Zone Accelerator - Release Strategy - -This document outlines the comprehensive release strategy for the Dev Box landing zone accelerator, detailing branch-based versioning, automated workflows, and deployment processes. - -## Overview - -The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. - -## Release Strategy Summary - -| Branch Pattern | Version Strategy | Release Publication | Tag Creation | Artifacts | -|----------------|------------------|-------------------|--------------|-----------| -| `main` | Conditional major increment | ✅ Published | ✅ Created | ✅ Built & Uploaded | -| `feature/**` | Patch increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | -| `fix/**` | Minor increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | -| `pull_request` | Based on source branch | ❌ Not published | ✅ Created | ✅ Built & Uploaded | - -## Branch-Specific Versioning Rules - -### 🎯 Main Branch (`main`) - -**New Conditional Major Increment Rule:** - -- **If `minor = 0` AND `patch = 0`**: Increment major version - - Example: `v1.0.0` → `v2.0.0` -- **If `minor ≠ 0` OR `patch ≠ 0`**: Keep major version, increment patch - - Example: `v1.5.0` → `v1.5.1` - - Example: `v1.0.3` → `v1.0.4` - - Example: `v1.5.3` → `v1.5.4` - -**Overflow Handling:** -- If patch exceeds 99: Reset patch to 0, increment minor - - Example: `v1.5.99` → `v1.6.0` -- If minor exceeds 99: Reset minor to 0, increment major - - Example: `v1.99.99` → `v2.0.0` - -### ✨ Feature Branches (`feature/**`) - -**Patch Increment Strategy:** -- Increments the patch version by the number of commits in the branch -- Format: `vX.Y.(Z+commits)-feature.branch-name` - -**Examples:** -- Current: `v1.2.5`, Branch: `feature/user-authentication`, Commits: 3 -- Result: `v1.2.8-feature.user-authentication` - -**Overflow Logic:** -- If `patch + commits > 99`: Reset patch to 0, increment minor -- If minor overflow occurs: Reset minor to 0, increment major - -### 🔧 Fix Branches (`fix/**`) - -**Minor Increment Strategy:** -- Increments the minor version by the number of commits in the branch -- Format: `vX.(Y+commits).Z-fix.branch-name` - -**Examples:** -- Current: `v1.2.5`, Branch: `fix/login-bug`, Commits: 2 -- Result: `v1.4.5-fix.login-bug` - -**Overflow Logic:** -- If `minor + commits > 99`: Reset minor to 0, increment major - -## Version Examples - -### Main Branch Scenarios - -| Current Version | Condition | Action | Result | Reasoning | -|----------------|-----------|---------|---------|-----------| -| `v1.0.0` | minor=0, patch=0 | Major increment | `v2.0.0` | Clean state allows major bump | -| `v1.5.0` | minor≠0, patch=0 | Patch increment | `v1.5.1` | Development continues on current major | -| `v1.0.3` | minor=0, patch≠0 | Patch increment | `v1.0.4` | Development continues on current major | -| `v1.5.99` | Patch overflow | Minor increment | `v1.6.0` | Patch overflow triggers minor bump | -| `v1.99.99` | Cascading overflow | Major increment | `v2.0.0` | Full overflow resets to new major | - -### Feature Branch Scenarios - -| Current Version | Branch | Commits | Calculation | Result | -|----------------|---------|---------|-------------|---------| -| `v1.2.5` | `feature/auth` | 3 | 5 + 3 = 8 | `v1.2.8-feature.auth` | -| `v1.2.97` | `feature/ui` | 5 | 97 + 5 = 102 > 99 | `v1.3.0-feature.ui` | -| `v1.99.95` | `feature/api` | 8 | Cascading overflow | `v2.0.0-feature.api` | - -### Fix Branch Scenarios - -| Current Version | Branch | Commits | Calculation | Result | -|----------------|---------|---------|-------------|---------| -| `v1.5.3` | `fix/bug-123` | 2 | 5 + 2 = 7 | `v1.7.3-fix.bug-123` | -| `v1.98.3` | `fix/critical` | 3 | 98 + 3 = 101 > 99 | `v2.0.3-fix.critical` | - -## Release Notes Structure - -Each release includes comprehensive documentation: - -```markdown -🌟 **Branch-Based Release Strategy with Conditional Major Increment** - -🔀 **Branch**: `main` -🏷️ **Version**: `v2.0.0` -📦 **Previous Version**: `v1.0.0` -🚀 **Release Type**: `main` -🤖 **Trigger**: `Push` -📝 **Commit**: `abc123...` - -## Release Strategy Applied -🎯 **Main Branch**: Conditional major increment (only if minor=0 AND patch=0) - -## Main Branch Logic -- **If minor=0 AND patch=0**: Increment major → `major+1.0.0` -- **If minor≠0 OR patch≠0**: Keep major, increment patch → `major.minor.(patch+1)` -- **Overflow handling**: If patch > 99 → `minor+1, patch=0` - -## Artifacts -- 📄 Bicep templates compiled to ARM templates -- 🏗️ Infrastructure deployment files -- 📋 Release metadata and documentation -``` - -## Best Practices - -### For Developers - -1. **Branch Naming**: Use descriptive branch names following the patterns: - - `feature/descriptive-name` - - `fix/issue-description` - -2. **Commit Strategy**: Keep commits atomic and meaningful as they influence version calculations - -3. **Testing**: Ensure all changes are tested before merging to main - -### For Release Management - -1. **Main Branch Protection**: Only merge tested, reviewed code to main -2. **Version Monitoring**: Monitor version progression to prevent unexpected major increments -3. **Release Planning**: Use the conditional major increment rule for planned major releases - -This release strategy provides a robust, automated approach to version management while maintaining flexibility for different development workflows and ensuring consistent, trackable releases for the Dev Box landing zone accelerator. -``` +# Dev Box Landing Zone Accelerator - Release Strategy + +This document outlines the comprehensive release strategy for the Dev Box landing zone accelerator, detailing branch-based versioning, automated workflows, and deployment processes. + +## Overview + +The Dev Box landing zone accelerator uses a **branch-based semantic release strategy** with intelligent overflow handling and conditional versioning rules. This approach ensures consistent, predictable releases while maintaining development flexibility across different branch types. + +## Release Strategy Summary + +| Branch Pattern | Version Strategy | Release Publication | Tag Creation | Artifacts | +|----------------|------------------|-------------------|--------------|-----------| +| `main` | Conditional major increment | ✅ Published | ✅ Created | ✅ Built & Uploaded | +| `feature/**` | Patch increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | +| `fix/**` | Minor increment with overflow | ❌ Not published | ✅ Created | ✅ Built & Uploaded | +| `pull_request` | Based on source branch | ❌ Not published | ✅ Created | ✅ Built & Uploaded | + +## Branch-Specific Versioning Rules + +### 🎯 Main Branch (`main`) + +**New Conditional Major Increment Rule:** + +- **If `minor = 0` AND `patch = 0`**: Increment major version + - Example: `v1.0.0` → `v2.0.0` +- **If `minor ≠ 0` OR `patch ≠ 0`**: Keep major version, increment patch + - Example: `v1.5.0` → `v1.5.1` + - Example: `v1.0.3` → `v1.0.4` + - Example: `v1.5.3` → `v1.5.4` + +**Overflow Handling:** +- If patch exceeds 99: Reset patch to 0, increment minor + - Example: `v1.5.99` → `v1.6.0` +- If minor exceeds 99: Reset minor to 0, increment major + - Example: `v1.99.99` → `v2.0.0` + +### ✨ Feature Branches (`feature/**`) + +**Patch Increment Strategy:** +- Increments the patch version by the number of commits in the branch +- Format: `vX.Y.(Z+commits)-feature.branch-name` + +**Examples:** +- Current: `v1.2.5`, Branch: `feature/user-authentication`, Commits: 3 +- Result: `v1.2.8-feature.user-authentication` + +**Overflow Logic:** +- If `patch + commits > 99`: Reset patch to 0, increment minor +- If minor overflow occurs: Reset minor to 0, increment major + +### 🔧 Fix Branches (`fix/**`) + +**Minor Increment Strategy:** +- Increments the minor version by the number of commits in the branch +- Format: `vX.(Y+commits).Z-fix.branch-name` + +**Examples:** +- Current: `v1.2.5`, Branch: `fix/login-bug`, Commits: 2 +- Result: `v1.4.5-fix.login-bug` + +**Overflow Logic:** +- If `minor + commits > 99`: Reset minor to 0, increment major + +## Version Examples + +### Main Branch Scenarios + +| Current Version | Condition | Action | Result | Reasoning | +|----------------|-----------|---------|---------|-----------| +| `v1.0.0` | minor=0, patch=0 | Major increment | `v2.0.0` | Clean state allows major bump | +| `v1.5.0` | minor≠0, patch=0 | Patch increment | `v1.5.1` | Development continues on current major | +| `v1.0.3` | minor=0, patch≠0 | Patch increment | `v1.0.4` | Development continues on current major | +| `v1.5.99` | Patch overflow | Minor increment | `v1.6.0` | Patch overflow triggers minor bump | +| `v1.99.99` | Cascading overflow | Major increment | `v2.0.0` | Full overflow resets to new major | + +### Feature Branch Scenarios + +| Current Version | Branch | Commits | Calculation | Result | +|----------------|---------|---------|-------------|---------| +| `v1.2.5` | `feature/auth` | 3 | 5 + 3 = 8 | `v1.2.8-feature.auth` | +| `v1.2.97` | `feature/ui` | 5 | 97 + 5 = 102 > 99 | `v1.3.0-feature.ui` | +| `v1.99.95` | `feature/api` | 8 | Cascading overflow | `v2.0.0-feature.api` | + +### Fix Branch Scenarios + +| Current Version | Branch | Commits | Calculation | Result | +|----------------|---------|---------|-------------|---------| +| `v1.5.3` | `fix/bug-123` | 2 | 5 + 2 = 7 | `v1.7.3-fix.bug-123` | +| `v1.98.3` | `fix/critical` | 3 | 98 + 3 = 101 > 99 | `v2.0.3-fix.critical` | + +## Release Notes Structure + +Each release includes comprehensive documentation: + +```markdown +🌟 **Branch-Based Release Strategy with Conditional Major Increment** + +🔀 **Branch**: `main` +🏷️ **Version**: `v2.0.0` +📦 **Previous Version**: `v1.0.0` +🚀 **Release Type**: `main` +🤖 **Trigger**: `Push` +📝 **Commit**: `abc123...` + +## Release Strategy Applied +🎯 **Main Branch**: Conditional major increment (only if minor=0 AND patch=0) + +## Main Branch Logic +- **If minor=0 AND patch=0**: Increment major → `major+1.0.0` +- **If minor≠0 OR patch≠0**: Keep major, increment patch → `major.minor.(patch+1)` +- **Overflow handling**: If patch > 99 → `minor+1, patch=0` + +## Artifacts +- 📄 Bicep templates compiled to ARM templates +- 🏗️ Infrastructure deployment files +- 📋 Release metadata and documentation +``` + +## Best Practices + +### For Developers + +1. **Branch Naming**: Use descriptive branch names following the patterns: + - `feature/descriptive-name` + - `fix/issue-description` + +2. **Commit Strategy**: Keep commits atomic and meaningful as they influence version calculations + +3. **Testing**: Ensure all changes are tested before merging to main + +### For Release Management + +1. **Main Branch Protection**: Only merge tested, reviewed code to main +2. **Version Monitoring**: Monitor version progression to prevent unexpected major increments +3. **Release Planning**: Use the conditional major increment rule for planned major releases + +This release strategy provides a robust, automated approach to version management while maintaining flexibility for different development workflows and ensuring consistent, trackable releases for the Dev Box landing zone accelerator. +``` This comprehensive documentation explains the entire release strategy, providing clear examples, troubleshooting guidance, and best practices for the Dev Box landing zone accelerator project. \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index ab27f132..a4fbff2f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,2 +1,2 @@ -# Security Policy - +# Security Policy + diff --git a/azure.yaml b/azure.yaml index 6684f8ce..aecccf88 100644 --- a/azure.yaml +++ b/azure.yaml @@ -1,23 +1,23 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/refs/heads/main/schemas/v1.0/azure.yaml.json - -name: ContosoDevExp - -hooks: - preprovision: - shell: sh - continueOnError: false - interactive: true - run: | - #!/bin/bash - - set -e - defaultPlatform="github" - - # Add a if statement to check if the environment variable is set - if [ -z "${SOURCE_CONTROL_PLATFORM}" ]; then - echo "SOURCE_CONTROL_PLATFORM is not set. Setting it to '${defaultPlatform}' by default." - export SOURCE_CONTROL_PLATFORM="${SOURCE_CONTROL_PLATFORM:-${defaultPlatform}}" - else - echo "Existing SOURCE_CONTROL_PLATFORM is set to '${SOURCE_CONTROL_PLATFORM}'." - fi +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/refs/heads/main/schemas/v1.0/azure.yaml.json + +name: ContosoDevExp + +hooks: + preprovision: + shell: sh + continueOnError: false + interactive: true + run: | + #!/bin/bash + + set -e + defaultPlatform="github" + + # Add a if statement to check if the environment variable is set + if [ -z "${SOURCE_CONTROL_PLATFORM}" ]; then + echo "SOURCE_CONTROL_PLATFORM is not set. Setting it to '${defaultPlatform}' by default." + export SOURCE_CONTROL_PLATFORM="${SOURCE_CONTROL_PLATFORM:-${defaultPlatform}}" + else + echo "Existing SOURCE_CONTROL_PLATFORM is set to '${SOURCE_CONTROL_PLATFORM}'." + fi ./setup.sh -e ${AZURE_ENV_NAME} -s ${SOURCE_CONTROL_PLATFORM} \ No newline at end of file diff --git a/cleanSetUp.ps1 b/cleanSetUp.ps1 index a779ae5e..24bc55e4 100644 --- a/cleanSetUp.ps1 +++ b/cleanSetUp.ps1 @@ -1,126 +1,126 @@ -# PowerShell script to clean up the setup by deleting users, credentials, and GitHub secrets - -[CmdletBinding()] -param( - [Parameter(Mandatory=$false)] - [string]$EnvName = "gitHub", - - [Parameter(Mandatory=$false)] - [ValidateSet("eastus", "eastus2", "westus", "westus2", "westus3", "northeurope", "westeurope")] - [string]$Location = "eastus2" -) - -# Exit immediately if a command exits with a non-zero status, treat unset variables as an error, and propagate errors in pipelines. -$ErrorActionPreference = "Stop" -$WarningPreference = "Stop" - -$appDisplayName = "ContosoDevEx GitHub Actions Enterprise App" -$ghSecretName = "AZURE_CREDENTIALS" - -# Function to delete deployments -function Remove-Deployments { - param ( - [string]$resourceGroupName - ) - - try { - $deployments = az deployment sub list --query "[].name" -o tsv - if ($LASTEXITCODE -ne 0) { - throw "Failed to list deployments." - } - - foreach ($deployment in $deployments) { - if (-not [string]::IsNullOrEmpty($deployment)) { - Write-Output "Deleting deployment: $deployment" - az deployment sub delete --name $deployment - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete deployment: $deployment" - } - Write-Output "Deployment $deployment deleted." - } - } - return $true - } - catch { - Write-Error "Error deleting deployments: $_" - return $false - } -} - -# Function to clean up the setup by deleting users, credentials, and GitHub secrets -function Remove-SetUp { - param ( - [Parameter(Mandatory = $true)] - [string]$appDisplayName, - - [Parameter(Mandatory = $true)] - [string]$ghSecretName - ) - - try { - # Check if required parameters are provided - if ([string]::IsNullOrEmpty($appDisplayName) -or [string]::IsNullOrEmpty($ghSecretName)) { - throw "Missing required parameters." - } - - Write-Output "Starting cleanup process for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" - - # Delete deployments - Write-Output "Deleting deployments..." - $deploymentResult = Remove-Deployments - if (-not $deploymentResult) { - throw "Failed to delete deployments." - } - - # Delete users and assigned roles - Write-Output "Deleting users and assigned roles..." - & ".\.configuration\setup\powershell\Azure\deleteUsersAndAssignedRoles.ps1" -appDisplayName $appDisplayName - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete users and assigned roles." - } - - # Delete deployment credentials - Write-Output "Deleting deployment credentials..." - & ".\.configuration\setup\powershell\Azure\deleteDeploymentCredentials.ps1" -appDisplayName $appDisplayName - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete deployment credentials." - } - - # Delete GitHub secret for Azure credentials - Write-Output "Deleting GitHub secret for Azure credentials..." - & ".\GitHub\deleteGitHubSecretAzureCredentials.ps1" -ghSecretName $ghSecretName - if ($LASTEXITCODE -ne 0) { - throw "Failed to delete GitHub secret for Azure credentials." - } - - Write-Output "Cleanup process completed successfully for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" - return $true - } - catch { - Write-Error "Error during cleanup process: $_" - return $false - } -} - -# Main script execution -try { - Clear-Host - - # Call the cleanup function with the required parameters - Write-Output "Starting cleanup process with EnvName: $EnvName and Location: $Location" - - # Additional cleanup script if it exists - $cleanupScriptPath = ".\.configuration\powershell\cleanUp.ps1" - if (Test-Path $cleanupScriptPath) { - & $cleanupScriptPath $EnvName $Location - if ($LASTEXITCODE -ne 0) { - throw "Cleanup script failed." - } - } - - Write-Output "All cleanup operations completed successfully." -} -catch { - Write-Error "Script execution failed: $_" - exit 1 +# PowerShell script to clean up the setup by deleting users, credentials, and GitHub secrets + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$EnvName = "gitHub", + + [Parameter(Mandatory=$false)] + [ValidateSet("eastus", "eastus2", "westus", "westus2", "westus3", "northeurope", "westeurope")] + [string]$Location = "eastus2" +) + +# Exit immediately if a command exits with a non-zero status, treat unset variables as an error, and propagate errors in pipelines. +$ErrorActionPreference = "Stop" +$WarningPreference = "Stop" + +$appDisplayName = "ContosoDevEx GitHub Actions Enterprise App" +$ghSecretName = "AZURE_CREDENTIALS" + +# Function to delete deployments +function Remove-Deployments { + param ( + [string]$resourceGroupName + ) + + try { + $deployments = az deployment sub list --query "[].name" -o tsv + if ($LASTEXITCODE -ne 0) { + throw "Failed to list deployments." + } + + foreach ($deployment in $deployments) { + if (-not [string]::IsNullOrEmpty($deployment)) { + Write-Output "Deleting deployment: $deployment" + az deployment sub delete --name $deployment + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete deployment: $deployment" + } + Write-Output "Deployment $deployment deleted." + } + } + return $true + } + catch { + Write-Error "Error deleting deployments: $_" + return $false + } +} + +# Function to clean up the setup by deleting users, credentials, and GitHub secrets +function Remove-SetUp { + param ( + [Parameter(Mandatory = $true)] + [string]$appDisplayName, + + [Parameter(Mandatory = $true)] + [string]$ghSecretName + ) + + try { + # Check if required parameters are provided + if ([string]::IsNullOrEmpty($appDisplayName) -or [string]::IsNullOrEmpty($ghSecretName)) { + throw "Missing required parameters." + } + + Write-Output "Starting cleanup process for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" + + # Delete deployments + Write-Output "Deleting deployments..." + $deploymentResult = Remove-Deployments + if (-not $deploymentResult) { + throw "Failed to delete deployments." + } + + # Delete users and assigned roles + Write-Output "Deleting users and assigned roles..." + & ".\.configuration\setup\powershell\Azure\deleteUsersAndAssignedRoles.ps1" -appDisplayName $appDisplayName + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete users and assigned roles." + } + + # Delete deployment credentials + Write-Output "Deleting deployment credentials..." + & ".\.configuration\setup\powershell\Azure\deleteDeploymentCredentials.ps1" -appDisplayName $appDisplayName + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete deployment credentials." + } + + # Delete GitHub secret for Azure credentials + Write-Output "Deleting GitHub secret for Azure credentials..." + & ".\GitHub\deleteGitHubSecretAzureCredentials.ps1" -ghSecretName $ghSecretName + if ($LASTEXITCODE -ne 0) { + throw "Failed to delete GitHub secret for Azure credentials." + } + + Write-Output "Cleanup process completed successfully for appDisplayName: $appDisplayName and ghSecretName: $ghSecretName" + return $true + } + catch { + Write-Error "Error during cleanup process: $_" + return $false + } +} + +# Main script execution +try { + Clear-Host + + # Call the cleanup function with the required parameters + Write-Output "Starting cleanup process with EnvName: $EnvName and Location: $Location" + + # Additional cleanup script if it exists + $cleanupScriptPath = ".\.configuration\powershell\cleanUp.ps1" + if (Test-Path $cleanupScriptPath) { + & $cleanupScriptPath $EnvName $Location + if ($LASTEXITCODE -ne 0) { + throw "Cleanup script failed." + } + } + + Write-Output "All cleanup operations completed successfully." +} +catch { + Write-Error "Script execution failed: $_" + exit 1 } \ No newline at end of file diff --git a/infra/settings/workload/devcenter.yaml b/infra/settings/workload/devcenter.yaml index b4833053..10f27b63 100644 --- a/infra/settings/workload/devcenter.yaml +++ b/infra/settings/workload/devcenter.yaml @@ -154,14 +154,14 @@ projects: - name: "environments" type: environmentDefinition sourceControl: gitHub - visibility: public + visibility: private uri: "https://github.com/Evilazaro/eShop.git" branch: "main" path: "/.devcenter/environments" - name: "devboxImages" type: imageDefinition sourceControl: gitHub - visibility: public + visibility: private uri: "https://github.com/Evilazaro/eShop.git" branch: "main" path: "/.devcenter/imageDefinitions" diff --git a/package.json b/package.json index 33d405ea..12cbc815 100644 --- a/package.json +++ b/package.json @@ -1,53 +1,53 @@ -{ - "name": "docsy-example-site", - "version": "0.10.0", - "version.next": "0.10.1-dev.0-unreleased", - "description": "Example site that uses Docsy theme for technical documentation.", - "repository": "github:google/docsy-example", - "homepage": "https://example.docsy.dev", - "author": "Docsy Authors", - "license": "Apache-2.0", - "bugs": "https://github.com/google/docsy-example/issues", - "spelling": "cSpell:ignore docsy hugo htmltest precheck postbuild rtlcss -", - "scripts": { - "_build": "npm run _hugo-dev --", - "_check:links": "echo IMPLEMENTATION PENDING for check-links; echo", - "_hugo": "hugo --cleanDestinationDir", - "_hugo-dev": "npm run _hugo -- -e dev -DFE", - "_local": "npx cross-env HUGO_MODULE_WORKSPACE=docsy.work", - "_serve": "npm run _hugo-dev -- --minify serve --renderToMemory", - "build:preview": "npm run _hugo-dev -- --minify --baseURL \"${DEPLOY_PRIME_URL:-/}\"", - "build:production": "npm run _hugo -- --minify", - "build": "npm run _build -- ", - "check:links:all": "HTMLTEST_ARGS= npm run _check:links", - "check:links": "npm run _check:links", - "clean": "rm -Rf public/* resources", - "local": "npm run _local -- npm run", - "make:public": "git init -b main public", - "precheck:links:all": "npm run build", - "precheck:links": "npm run build", - "postbuild:preview": "npm run _check:links", - "postbuild:production": "npm run _check:links", - "serve": "npm run _serve", - "test": "npm run check:links", - "update:dep": "npm install --save-dev autoprefixer@latest postcss-cli@latest", - "update:hugo": "npm install --save-dev --save-exact hugo-extended@latest", - "update:pkgs": "npx npm-check-updates -u" - }, - "devDependencies": { - "autoprefixer": "^10.4.20", - "cross-env": "^7.0.3", - "hugo-extended": "0.136.2", - "postcss-cli": "^11.0.0", - "rtlcss": "^4.3.0" - }, - "optionalDependencies": { - "npm-check-updates": "^17.1.4" - }, - "private": true, - "prettier": { - "proseWrap": "always", - "singleQuote": true - } - } +{ + "name": "docsy-example-site", + "version": "0.10.0", + "version.next": "0.10.1-dev.0-unreleased", + "description": "Example site that uses Docsy theme for technical documentation.", + "repository": "github:google/docsy-example", + "homepage": "https://example.docsy.dev", + "author": "Docsy Authors", + "license": "Apache-2.0", + "bugs": "https://github.com/google/docsy-example/issues", + "spelling": "cSpell:ignore docsy hugo htmltest precheck postbuild rtlcss -", + "scripts": { + "_build": "npm run _hugo-dev --", + "_check:links": "echo IMPLEMENTATION PENDING for check-links; echo", + "_hugo": "hugo --cleanDestinationDir", + "_hugo-dev": "npm run _hugo -- -e dev -DFE", + "_local": "npx cross-env HUGO_MODULE_WORKSPACE=docsy.work", + "_serve": "npm run _hugo-dev -- --minify serve --renderToMemory", + "build:preview": "npm run _hugo-dev -- --minify --baseURL \"${DEPLOY_PRIME_URL:-/}\"", + "build:production": "npm run _hugo -- --minify", + "build": "npm run _build -- ", + "check:links:all": "HTMLTEST_ARGS= npm run _check:links", + "check:links": "npm run _check:links", + "clean": "rm -Rf public/* resources", + "local": "npm run _local -- npm run", + "make:public": "git init -b main public", + "precheck:links:all": "npm run build", + "precheck:links": "npm run build", + "postbuild:preview": "npm run _check:links", + "postbuild:production": "npm run _check:links", + "serve": "npm run _serve", + "test": "npm run check:links", + "update:dep": "npm install --save-dev autoprefixer@latest postcss-cli@latest", + "update:hugo": "npm install --save-dev --save-exact hugo-extended@latest", + "update:pkgs": "npx npm-check-updates -u" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "cross-env": "^7.0.3", + "hugo-extended": "0.136.2", + "postcss-cli": "^11.0.0", + "rtlcss": "^4.3.0" + }, + "optionalDependencies": { + "npm-check-updates": "^17.1.4" + }, + "private": true, + "prettier": { + "proseWrap": "always", + "singleQuote": true + } + } \ No newline at end of file diff --git a/setUp.sh b/setUp.sh index 14dad946..99e175ca 100644 --- a/setUp.sh +++ b/setUp.sh @@ -1,492 +1,492 @@ -#!/bin/bash - -# setUp.sh - Sets up Azure Dev Box environment with GitHub integration -# -# DESCRIPTION -# Automates the setup of an Azure Developer CLI (azd) environment for Dev Box, -# handles GitHub authentication, and provisions required Azure resources. -# -# This script follows Azure best practices for security, error handling, -# and resource management. -# -# PARAMETERS -# -e, --env-name Name of the Azure environment to create -# -s, --source-control Source control platform (github or adogit) -# -h, --help Show this help message -# -# EXAMPLES -# ./setUp.sh -e "prod" -s "github" -# # Creates a "prod" environment with GitHub -# -# ./setUp.sh -e "dev" -s "adogit" -# # Creates a "dev" environment with Azure DevOps -# -# REQUIREMENTS -# - Azure CLI (az) -# - Azure Developer CLI (azd) -# - GitHub CLI (gh) [if using GitHub] -# - Valid authentication for chosen platform -# -# Author: DevExp Team -# Last Updated: 2023-05-15 - -# Script Configuration -set -euo pipefail # Exit on error, undefined vars, pipe failures -IFS=$'\n\t' # Secure Internal Field Separator - -# Global Variables -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -readonly TIMESTAMP_FORMAT="%Y-%m-%d %H:%M:%S" - -# Color codes for output -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly CYAN='\033[0;36m' -readonly NC='\033[0m' # No Color - -# Unicode icons -readonly INFO_ICON="ℹ️" -readonly WARNING_ICON="⚠️" -readonly ERROR_ICON="❌" -readonly SUCCESS_ICON="✅" - -# Global variables for script state -ENV_NAME="" -SOURCE_CONTROL_PLATFORM="" -GITHUB_TOKEN="" -ADO_TOKEN="" - -####################################### -# Helper Functions -####################################### - -# Logging function with different levels and colors -write_log_message() { - local message="$1" - local level="${2:-Info}" - local timestamp - timestamp=$(date +"$TIMESTAMP_FORMAT") - - case "$level" in - "Error") - echo -e "${ERROR_ICON} ${RED}[$timestamp] $message${NC}" >&2 - ;; - "Warning") - echo -e "${WARNING_ICON} ${YELLOW}[$timestamp] $message${NC}" - ;; - "Success") - echo -e "${SUCCESS_ICON} ${GREEN}[$timestamp] $message${NC}" - ;; - "Info"|*) - echo -e "${INFO_ICON} ${CYAN}[$timestamp] $message${NC}" - ;; - esac -} - -# Check if a command is available in PATH -test_command_availability() { - local command="$1" - - if ! command -v "$command" &> /dev/null; then - write_log_message "Required command '$command' was not found. Please install it before continuing." "Error" - return 1 - fi - return 0 -} - -# Show help message -show_help() { - cat << EOF -setUp.sh - Sets up Azure Dev Box environment with source control integration - -USAGE: - ./setUp.sh -e ENV_NAME -s SOURCE_CONTROL - -PARAMETERS: - -e, --env-name ENV_NAME Name of the Azure environment to create - -s, --source-control PLATFORM Source control platform (github or adogit) - -h, --help Show this help message - -EXAMPLES: - ./setUp.sh -e "prod" -s "github" - ./setUp.sh -e "dev" -s "adogit" - -REQUIREMENTS: - - Azure CLI (az) - - Azure Developer CLI (azd) - - GitHub CLI (gh) [if using GitHub] - - Valid authentication for chosen platform -EOF -} - -# Validate source control platform -validate_source_control() { - local platform="$1" - - if [[ "$platform" == "github" ]] || [[ "$platform" == "adogit" ]] || [[ -z "$platform" ]]; then - return 0 - else - write_log_message "Invalid source control platform: $platform" "Error" - write_log_message "Valid platforms: github, adogit" "Info" - return 1 - fi -} - -####################################### -# Authentication Functions -####################################### - -# Test Azure CLI authentication -test_azure_authentication() { - local az_context - - write_log_message "Verifying Azure authentication..." "Info" - - # Redirect stderr to /dev/null to prevent error messages from displaying - if ! az_context=$(az account show 2>/dev/null); then - write_log_message "Not logged into Azure. Please run 'az login' first." "Error" - return 1 - fi - - # Parse JSON output to check subscription state - local subscription_name subscription_id subscription_state - subscription_name=$(echo "$az_context" | jq -r '.name') - subscription_id=$(echo "$az_context" | jq -r '.id') - subscription_state=$(echo "$az_context" | jq -r '.state') - - # Check if subscription is enabled (Azure best practice) - if [[ "$subscription_state" != "Enabled" ]]; then - write_log_message "Current subscription '$subscription_name' is not in 'Enabled' state." "Error" - return 1 - fi - - # Output subscription details for verification - write_log_message "Using Azure subscription: $subscription_name (ID: $subscription_id)" "Info" - return 0 -} - -# Test Azure DevOps authentication -test_ado_authentication() { - write_log_message "Verifying Azure DevOps authentication..." "Info" - - # Check if Azure DevOps CLI is authenticated - if ! az devops configure --list &>/dev/null; then - write_log_message "Not logged into Azure DevOps. Please run 'az devops login' first." "Error" - return 1 - fi - - write_log_message "Azure DevOps authentication verified successfully" "Success" - return 0 -} - -# Test GitHub CLI authentication -test_github_authentication() { - write_log_message "Verifying GitHub authentication..." "Info" - - # Check if GitHub CLI is authenticated - if ! gh auth status &>/dev/null; then - write_log_message "Not logged into GitHub. Please run 'gh auth login' first." "Error" - return 1 - fi - - write_log_message "GitHub authentication verified successfully" "Success" - return 0 -} - -# Get GitHub token securely -get_secure_github_token() { - write_log_message "Retrieving GitHub token..." "Info" - - # Check if KEY_VAULT_SECRET environment variable is already set - if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then - write_log_message "Using existing KEY_VAULT_SECRET from environment" "Info" - GITHUB_TOKEN="${KEY_VAULT_SECRET}" - else - # Retrieve GitHub token using gh CLI - if ! GITHUB_TOKEN=$(gh auth token 2>/dev/null); then - write_log_message "Failed to retrieve GitHub token" "Error" - return 1 - fi - # Export as environment variable for future use - export KEY_VAULT_SECRET="${GITHUB_TOKEN}" - fi - - if [[ -z "$GITHUB_TOKEN" ]]; then - write_log_message "Failed to retrieve GitHub token" "Error" - return 1 - fi - - write_log_message "GitHub token retrieved and stored securely" "Success" - return 0 -} - -# Get Azure DevOps token securely -get_secure_ado_git_token() { - write_log_message "Retrieving Azure DevOps token..." "Info" - - # Try to get PAT from environment variable first - if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then - ADO_TOKEN="${KEY_VAULT_SECRET}" - write_log_message "Azure DevOps PAT retrieved from Key Vault" "Success" - else - write_log_message "Azure DevOps PAT not found in environment variables." "Warning" - write_log_message "Please enter your PAT securely." "Warning" - - # Prompt for PAT securely (no echo) - echo -n "Enter your Azure DevOps Personal Access Token: " - read -rs ADO_TOKEN - echo - - # Configure Azure DevOps defaults - if ! az devops configure --defaults organization=https://dev.azure.com/contososa2 project=DevExp-DevBox &>/dev/null; then - write_log_message "Azure DevOps organization and project not set. Please configure them first." "Error" - return 1 - fi - fi - - if [[ -z "$ADO_TOKEN" ]]; then - write_log_message "Failed to retrieve Azure DevOps PAT" "Error" - return 1 - fi - - # Export the token to environment variable - export AZURE_DEVOPS_EXT_PAT="$ADO_TOKEN" - - write_log_message "Azure DevOps PAT retrieved and stored securely" "Success" - return 0 -} - -####################################### -# Azure Configuration Functions -####################################### - -# Initialize Azure Developer CLI environment -initialize_azd_environment() { - local pat token_type masked_token - local env_dir env_file - - write_log_message "Initializing Azure Developer CLI environment..." "Info" - - # Get appropriate token based on source control platform - case "${SOURCE_CONTROL_PLATFORM}" in - "github") - write_log_message "Retrieving GitHub token for environment initialization..." "Info" - if ! get_secure_github_token; then - write_log_message "Unable to retrieve GitHub token. Aborting environment initialization." "Error" - return 1 - fi - pat="$GITHUB_TOKEN" - token_type="GitHub" - ;; - "adogit") - write_log_message "Retrieving Azure DevOps token for environment initialization..." "Info" - if ! get_secure_ado_git_token; then - write_log_message "Unable to retrieve Azure DevOps token. Aborting environment initialization." "Error" - return 1 - fi - pat="$ADO_TOKEN" - token_type="Azure DevOps" - ;; - *) - write_log_message "Unsupported source control platform: $SOURCE_CONTROL_PLATFORM" "Error" - return 1 - ;; - esac - - # Mask most of the token for security best practices - if [[ ${#pat} -ge 8 ]]; then - masked_token="${pat:0:4}****${pat: -2}" - else - masked_token="****" - fi - - write_log_message "🔐 $token_type token stored securely in memory. Masked: $masked_token" "Success" - - # Azure best practice: Verify environment exists or use existing - write_log_message "Using Azure Developer CLI environment: '$ENV_NAME'" "Info" - - # Prepare environment file path - env_dir="./.azure/$ENV_NAME" - env_file="$env_dir/.env" - - if [[ ! -d "$env_dir" ]]; then - mkdir -p "$env_dir" - fi - - # Azure best practice: Use environment-specific configuration - write_log_message "Configuring environment variables in $env_file" "Info" - - # Append to existing file or create if it doesn't exist - echo "KEY_VAULT_SECRET='$pat'" >> "$env_file" - echo "SOURCE_CONTROL_PLATFORM='$SOURCE_CONTROL_PLATFORM'" >> "$env_file" - - # Show current configuration for verification - write_log_message "Current Azure Developer CLI configuration:" "Info" - azd config show - - write_log_message "Azure Developer CLI environment '$ENV_NAME' initialized successfully." "Success" - return 0 -} - -# Start Azure resource provisioning -start_azure_provisioning() { - write_log_message "Starting Azure resource provisioning with azd..." "Info" - - # Run the provisioning process - if ! azd provision -e "$ENV_NAME"; then - local exit_code=$? - write_log_message "Azure provisioning failed with exit code $exit_code" "Error" - - # Provide guidance on common failures - write_log_message "This might be a quota or permissions issue. Check your Azure subscription limits and role assignments." "Warning" - - return 1 - fi - - write_log_message "Azure provisioning completed successfully" "Success" - return 0 -} - -# Interactive source control platform selection -select_source_control_platform() { - local selection valid_selection=false - - write_log_message "Please select your source control platform:" "Info" - echo "" - echo -e " ${YELLOW}1. Azure DevOps Git (adogit)${NC}" - echo -e " ${YELLOW}2. GitHub (github)${NC}" - echo "" - - while [[ "$valid_selection" == false ]]; do - echo -n "Enter your choice (1 or 2): " - read -r selection - - case "$selection" in - "1") - SOURCE_CONTROL_PLATFORM="adogit" - write_log_message "Selected: Azure DevOps Git" "Success" - valid_selection=true - ;; - "2") - SOURCE_CONTROL_PLATFORM="github" - write_log_message "Selected: GitHub" "Success" - valid_selection=true - ;; - *) - write_log_message "Invalid selection. Please enter 1 or 2." "Warning" - ;; - esac - done -} - -####################################### -# Main Script Logic -####################################### - -# Parse command line arguments -parse_arguments() { - while [[ $# -gt 0 ]]; do - case $1 in - -e|--env-name) - ENV_NAME="$2" - shift 2 - ;; - -s|--source-control) - SOURCE_CONTROL_PLATFORM="$2" - shift 2 - ;; - -h|--help) - show_help - exit 0 - ;; - *) - write_log_message "Unknown parameter: $1" "Error" - show_help - exit 1 - ;; - esac - done - - # Validate required parameters - if [[ -z "$ENV_NAME" ]]; then - write_log_message "Environment name is required. Use -e or --env-name parameter." "Error" - show_help - exit 1 - fi - - # If source control not provided, prompt for it - if [[ -z "$SOURCE_CONTROL_PLATFORM" ]]; then - select_source_control_platform - fi - - # Validate parameters - if ! validate_source_control "$SOURCE_CONTROL_PLATFORM"; then - exit 1 - fi -} - -# Main execution function -main() { - local required_tools=("az" "azd" "jq") - local tool - - # Parse command line arguments - parse_arguments "$@" - - # Script header with basic information - write_log_message "Starting Dev Box environment setup" "Info" - write_log_message "Environment name: $ENV_NAME" "Info" - write_log_message "Source control platform: $SOURCE_CONTROL_PLATFORM" "Info" - - # Add GitHub CLI to required tools if using GitHub - if [[ "$SOURCE_CONTROL_PLATFORM" == "github" ]]; then - required_tools+=("gh") - fi - - # Verify required tools - Azure best practice for dependency validation - write_log_message "Checking required tools..." "Info" - for tool in "${required_tools[@]}"; do - if ! test_command_availability "$tool"; then - write_log_message "Missing required tools. Please install them and retry." "Error" - exit 1 - fi - done - write_log_message "All required tools are available" "Success" - - # Verify Azure authentication - Azure security best practice - if ! test_azure_authentication; then - exit 1 - fi - - # Verify source control authentication - case "$SOURCE_CONTROL_PLATFORM" in - "github") - if ! test_github_authentication; then - exit 1 - fi - ;; - "adogit") - if ! test_ado_authentication; then - exit 1 - fi - ;; - esac - - # Initialize azd environment - if ! initialize_azd_environment; then - write_log_message "Failed to initialize Azure Developer CLI environment. Exiting." "Error" - exit 1 - fi - - # Success message with environment details - write_log_message "Dev Box environment '$ENV_NAME' setup successfully" "Success" - write_log_message "Access your Dev Center from the Azure portal" "Info" - write_log_message "Use 'azd env get-values' to view environment settings" "Info" -} - -# Set up error handling and cleanup -trap 'write_log_message "Script interrupted by user" "Warning"; exit 130' INT TERM - -# Execute main function with all arguments -main "$@" +#!/bin/bash + +# setUp.sh - Sets up Azure Dev Box environment with GitHub integration +# +# DESCRIPTION +# Automates the setup of an Azure Developer CLI (azd) environment for Dev Box, +# handles GitHub authentication, and provisions required Azure resources. +# +# This script follows Azure best practices for security, error handling, +# and resource management. +# +# PARAMETERS +# -e, --env-name Name of the Azure environment to create +# -s, --source-control Source control platform (github or adogit) +# -h, --help Show this help message +# +# EXAMPLES +# ./setUp.sh -e "prod" -s "github" +# # Creates a "prod" environment with GitHub +# +# ./setUp.sh -e "dev" -s "adogit" +# # Creates a "dev" environment with Azure DevOps +# +# REQUIREMENTS +# - Azure CLI (az) +# - Azure Developer CLI (azd) +# - GitHub CLI (gh) [if using GitHub] +# - Valid authentication for chosen platform +# +# Author: DevExp Team +# Last Updated: 2023-05-15 + +# Script Configuration +set -euo pipefail # Exit on error, undefined vars, pipe failures +IFS=$'\n\t' # Secure Internal Field Separator + +# Global Variables +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly TIMESTAMP_FORMAT="%Y-%m-%d %H:%M:%S" + +# Color codes for output +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly CYAN='\033[0;36m' +readonly NC='\033[0m' # No Color + +# Unicode icons +readonly INFO_ICON="ℹ️" +readonly WARNING_ICON="⚠️" +readonly ERROR_ICON="❌" +readonly SUCCESS_ICON="✅" + +# Global variables for script state +ENV_NAME="" +SOURCE_CONTROL_PLATFORM="" +GITHUB_TOKEN="" +ADO_TOKEN="" + +####################################### +# Helper Functions +####################################### + +# Logging function with different levels and colors +write_log_message() { + local message="$1" + local level="${2:-Info}" + local timestamp + timestamp=$(date +"$TIMESTAMP_FORMAT") + + case "$level" in + "Error") + echo -e "${ERROR_ICON} ${RED}[$timestamp] $message${NC}" >&2 + ;; + "Warning") + echo -e "${WARNING_ICON} ${YELLOW}[$timestamp] $message${NC}" + ;; + "Success") + echo -e "${SUCCESS_ICON} ${GREEN}[$timestamp] $message${NC}" + ;; + "Info"|*) + echo -e "${INFO_ICON} ${CYAN}[$timestamp] $message${NC}" + ;; + esac +} + +# Check if a command is available in PATH +test_command_availability() { + local command="$1" + + if ! command -v "$command" &> /dev/null; then + write_log_message "Required command '$command' was not found. Please install it before continuing." "Error" + return 1 + fi + return 0 +} + +# Show help message +show_help() { + cat << EOF +setUp.sh - Sets up Azure Dev Box environment with source control integration + +USAGE: + ./setUp.sh -e ENV_NAME -s SOURCE_CONTROL + +PARAMETERS: + -e, --env-name ENV_NAME Name of the Azure environment to create + -s, --source-control PLATFORM Source control platform (github or adogit) + -h, --help Show this help message + +EXAMPLES: + ./setUp.sh -e "prod" -s "github" + ./setUp.sh -e "dev" -s "adogit" + +REQUIREMENTS: + - Azure CLI (az) + - Azure Developer CLI (azd) + - GitHub CLI (gh) [if using GitHub] + - Valid authentication for chosen platform +EOF +} + +# Validate source control platform +validate_source_control() { + local platform="$1" + + if [[ "$platform" == "github" ]] || [[ "$platform" == "adogit" ]] || [[ -z "$platform" ]]; then + return 0 + else + write_log_message "Invalid source control platform: $platform" "Error" + write_log_message "Valid platforms: github, adogit" "Info" + return 1 + fi +} + +####################################### +# Authentication Functions +####################################### + +# Test Azure CLI authentication +test_azure_authentication() { + local az_context + + write_log_message "Verifying Azure authentication..." "Info" + + # Redirect stderr to /dev/null to prevent error messages from displaying + if ! az_context=$(az account show 2>/dev/null); then + write_log_message "Not logged into Azure. Please run 'az login' first." "Error" + return 1 + fi + + # Parse JSON output to check subscription state + local subscription_name subscription_id subscription_state + subscription_name=$(echo "$az_context" | jq -r '.name') + subscription_id=$(echo "$az_context" | jq -r '.id') + subscription_state=$(echo "$az_context" | jq -r '.state') + + # Check if subscription is enabled (Azure best practice) + if [[ "$subscription_state" != "Enabled" ]]; then + write_log_message "Current subscription '$subscription_name' is not in 'Enabled' state." "Error" + return 1 + fi + + # Output subscription details for verification + write_log_message "Using Azure subscription: $subscription_name (ID: $subscription_id)" "Info" + return 0 +} + +# Test Azure DevOps authentication +test_ado_authentication() { + write_log_message "Verifying Azure DevOps authentication..." "Info" + + # Check if Azure DevOps CLI is authenticated + if ! az devops configure --list &>/dev/null; then + write_log_message "Not logged into Azure DevOps. Please run 'az devops login' first." "Error" + return 1 + fi + + write_log_message "Azure DevOps authentication verified successfully" "Success" + return 0 +} + +# Test GitHub CLI authentication +test_github_authentication() { + write_log_message "Verifying GitHub authentication..." "Info" + + # Check if GitHub CLI is authenticated + if ! gh auth status &>/dev/null; then + write_log_message "Not logged into GitHub. Please run 'gh auth login' first." "Error" + return 1 + fi + + write_log_message "GitHub authentication verified successfully" "Success" + return 0 +} + +# Get GitHub token securely +get_secure_github_token() { + write_log_message "Retrieving GitHub token..." "Info" + + # Check if KEY_VAULT_SECRET environment variable is already set + if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then + write_log_message "Using existing KEY_VAULT_SECRET from environment" "Info" + GITHUB_TOKEN="${KEY_VAULT_SECRET}" + else + # Retrieve GitHub token using gh CLI + if ! GITHUB_TOKEN=$(gh auth token 2>/dev/null); then + write_log_message "Failed to retrieve GitHub token" "Error" + return 1 + fi + # Export as environment variable for future use + export KEY_VAULT_SECRET="${GITHUB_TOKEN}" + fi + + if [[ -z "$GITHUB_TOKEN" ]]; then + write_log_message "Failed to retrieve GitHub token" "Error" + return 1 + fi + + write_log_message "GitHub token retrieved and stored securely" "Success" + return 0 +} + +# Get Azure DevOps token securely +get_secure_ado_git_token() { + write_log_message "Retrieving Azure DevOps token..." "Info" + + # Try to get PAT from environment variable first + if [[ -n "${KEY_VAULT_SECRET:-}" ]]; then + ADO_TOKEN="${KEY_VAULT_SECRET}" + write_log_message "Azure DevOps PAT retrieved from Key Vault" "Success" + else + write_log_message "Azure DevOps PAT not found in environment variables." "Warning" + write_log_message "Please enter your PAT securely." "Warning" + + # Prompt for PAT securely (no echo) + echo -n "Enter your Azure DevOps Personal Access Token: " + read -rs ADO_TOKEN + echo + + # Configure Azure DevOps defaults + if ! az devops configure --defaults organization=https://dev.azure.com/contososa2 project=DevExp-DevBox &>/dev/null; then + write_log_message "Azure DevOps organization and project not set. Please configure them first." "Error" + return 1 + fi + fi + + if [[ -z "$ADO_TOKEN" ]]; then + write_log_message "Failed to retrieve Azure DevOps PAT" "Error" + return 1 + fi + + # Export the token to environment variable + export AZURE_DEVOPS_EXT_PAT="$ADO_TOKEN" + + write_log_message "Azure DevOps PAT retrieved and stored securely" "Success" + return 0 +} + +####################################### +# Azure Configuration Functions +####################################### + +# Initialize Azure Developer CLI environment +initialize_azd_environment() { + local pat token_type masked_token + local env_dir env_file + + write_log_message "Initializing Azure Developer CLI environment..." "Info" + + # Get appropriate token based on source control platform + case "${SOURCE_CONTROL_PLATFORM}" in + "github") + write_log_message "Retrieving GitHub token for environment initialization..." "Info" + if ! get_secure_github_token; then + write_log_message "Unable to retrieve GitHub token. Aborting environment initialization." "Error" + return 1 + fi + pat="$GITHUB_TOKEN" + token_type="GitHub" + ;; + "adogit") + write_log_message "Retrieving Azure DevOps token for environment initialization..." "Info" + if ! get_secure_ado_git_token; then + write_log_message "Unable to retrieve Azure DevOps token. Aborting environment initialization." "Error" + return 1 + fi + pat="$ADO_TOKEN" + token_type="Azure DevOps" + ;; + *) + write_log_message "Unsupported source control platform: $SOURCE_CONTROL_PLATFORM" "Error" + return 1 + ;; + esac + + # Mask most of the token for security best practices + if [[ ${#pat} -ge 8 ]]; then + masked_token="${pat:0:4}****${pat: -2}" + else + masked_token="****" + fi + + write_log_message "🔐 $token_type token stored securely in memory. Masked: $masked_token" "Success" + + # Azure best practice: Verify environment exists or use existing + write_log_message "Using Azure Developer CLI environment: '$ENV_NAME'" "Info" + + # Prepare environment file path + env_dir="./.azure/$ENV_NAME" + env_file="$env_dir/.env" + + if [[ ! -d "$env_dir" ]]; then + mkdir -p "$env_dir" + fi + + # Azure best practice: Use environment-specific configuration + write_log_message "Configuring environment variables in $env_file" "Info" + + # Append to existing file or create if it doesn't exist + echo "KEY_VAULT_SECRET='$pat'" >> "$env_file" + echo "SOURCE_CONTROL_PLATFORM='$SOURCE_CONTROL_PLATFORM'" >> "$env_file" + + # Show current configuration for verification + write_log_message "Current Azure Developer CLI configuration:" "Info" + azd config show + + write_log_message "Azure Developer CLI environment '$ENV_NAME' initialized successfully." "Success" + return 0 +} + +# Start Azure resource provisioning +start_azure_provisioning() { + write_log_message "Starting Azure resource provisioning with azd..." "Info" + + # Run the provisioning process + if ! azd provision -e "$ENV_NAME"; then + local exit_code=$? + write_log_message "Azure provisioning failed with exit code $exit_code" "Error" + + # Provide guidance on common failures + write_log_message "This might be a quota or permissions issue. Check your Azure subscription limits and role assignments." "Warning" + + return 1 + fi + + write_log_message "Azure provisioning completed successfully" "Success" + return 0 +} + +# Interactive source control platform selection +select_source_control_platform() { + local selection valid_selection=false + + write_log_message "Please select your source control platform:" "Info" + echo "" + echo -e " ${YELLOW}1. Azure DevOps Git (adogit)${NC}" + echo -e " ${YELLOW}2. GitHub (github)${NC}" + echo "" + + while [[ "$valid_selection" == false ]]; do + echo -n "Enter your choice (1 or 2): " + read -r selection + + case "$selection" in + "1") + SOURCE_CONTROL_PLATFORM="adogit" + write_log_message "Selected: Azure DevOps Git" "Success" + valid_selection=true + ;; + "2") + SOURCE_CONTROL_PLATFORM="github" + write_log_message "Selected: GitHub" "Success" + valid_selection=true + ;; + *) + write_log_message "Invalid selection. Please enter 1 or 2." "Warning" + ;; + esac + done +} + +####################################### +# Main Script Logic +####################################### + +# Parse command line arguments +parse_arguments() { + while [[ $# -gt 0 ]]; do + case $1 in + -e|--env-name) + ENV_NAME="$2" + shift 2 + ;; + -s|--source-control) + SOURCE_CONTROL_PLATFORM="$2" + shift 2 + ;; + -h|--help) + show_help + exit 0 + ;; + *) + write_log_message "Unknown parameter: $1" "Error" + show_help + exit 1 + ;; + esac + done + + # Validate required parameters + if [[ -z "$ENV_NAME" ]]; then + write_log_message "Environment name is required. Use -e or --env-name parameter." "Error" + show_help + exit 1 + fi + + # If source control not provided, prompt for it + if [[ -z "$SOURCE_CONTROL_PLATFORM" ]]; then + select_source_control_platform + fi + + # Validate parameters + if ! validate_source_control "$SOURCE_CONTROL_PLATFORM"; then + exit 1 + fi +} + +# Main execution function +main() { + local required_tools=("az" "azd" "jq") + local tool + + # Parse command line arguments + parse_arguments "$@" + + # Script header with basic information + write_log_message "Starting Dev Box environment setup" "Info" + write_log_message "Environment name: $ENV_NAME" "Info" + write_log_message "Source control platform: $SOURCE_CONTROL_PLATFORM" "Info" + + # Add GitHub CLI to required tools if using GitHub + if [[ "$SOURCE_CONTROL_PLATFORM" == "github" ]]; then + required_tools+=("gh") + fi + + # Verify required tools - Azure best practice for dependency validation + write_log_message "Checking required tools..." "Info" + for tool in "${required_tools[@]}"; do + if ! test_command_availability "$tool"; then + write_log_message "Missing required tools. Please install them and retry." "Error" + exit 1 + fi + done + write_log_message "All required tools are available" "Success" + + # Verify Azure authentication - Azure security best practice + if ! test_azure_authentication; then + exit 1 + fi + + # Verify source control authentication + case "$SOURCE_CONTROL_PLATFORM" in + "github") + if ! test_github_authentication; then + exit 1 + fi + ;; + "adogit") + if ! test_ado_authentication; then + exit 1 + fi + ;; + esac + + # Initialize azd environment + if ! initialize_azd_environment; then + write_log_message "Failed to initialize Azure Developer CLI environment. Exiting." "Error" + exit 1 + fi + + # Success message with environment details + write_log_message "Dev Box environment '$ENV_NAME' setup successfully" "Success" + write_log_message "Access your Dev Center from the Azure portal" "Info" + write_log_message "Use 'azd env get-values' to view environment settings" "Info" +} + +# Set up error handling and cleanup +trap 'write_log_message "Script interrupted by user" "Warning"; exit 130' INT TERM + +# Execute main function with all arguments +main "$@" diff --git a/src/workload/project/projectPool.bicep b/src/workload/project/projectPool.bicep index b0cb2f04..3f6b3834 100644 --- a/src/workload/project/projectPool.bicep +++ b/src/workload/project/projectPool.bicep @@ -5,7 +5,7 @@ param name string param location string = resourceGroup().location @description('The name of the catalog to use for the pool') -param catalogs object[] +param catalogs Catalog[] @description('The name of the dev box definition to use for the pool') param imageDefinitionName string @@ -22,6 +22,30 @@ param networkType string @description('The name of the project to which the pool belongs') param projectName string +@description('Catalog definition') +type Catalog = { + @description('Name of the catalog') + name: string + + @description('Type of catalog (environment or image)') + type: 'environmentDefinition' | 'imageDefinition' + + @description('Source control type') + sourceControl: 'gitHub' | 'adoGit' + + @description('Visibility of the catalog') + visibility: 'public' | 'private' + + @description('URI of the repository') + uri: string + + @description('Branch to sync from') + branch: string + + @description('Path within the repository to sync') + path: string +} + @description('Project') resource project 'Microsoft.DevCenter/projects@2025-04-01-preview' existing = { name: projectName