wiki.getshifting.com

--- Sjoerd Hooft's InFormation Technology ---

User Tools

Site Tools


start

SHIFT-WIKI - Sjoerd Hooft's InFormation Technology

This WIKI is my personal documentation blog. Please enjoy it and feel free to reach out through blue sky if you have a question, remark, improvement or observation.


Chaetsheet Databases

Summary: Database hints, tips, oneliners and best practices.
Date: 8 December 2024

MS SQL

Start MS SQL Management Studio as a user from a different domain to a different database server
runas /netonly /user:getshifting.local\admin-sjoerd "c:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Ssms.exe -S dbdev01.getshifting.local"


See all logged in users
CREATE TABLE #sp_who2 (SPID INT,STATUS VARCHAR(255),
      Login  VARCHAR(255),HostName  VARCHAR(255),
      BlkBy  VARCHAR(255),DBName  VARCHAR(255),
      Command VARCHAR(255),CPUTime INT,
      DiskIO INT,LastBatch VARCHAR(255),
      ProgramName VARCHAR(255),SPID2 INT,
      REQUESTID INT)
INSERT INTO #sp_who2 EXEC sp_who2
SELECT      *
FROM        #sp_who2
-- Add any filtering of the results here :
WHERE       Login = 'SqlAdmin'
-- Add any sorting of the results here :
-- ORDER BY    DBName ASC
 
DROP TABLE #sp_who2


See all sessions
SELECT s.session_id
    ,r.STATUS
    ,r.blocking_session_id 'blocked by'
    ,r.wait_type
    ,wait_resource
    ,r.wait_time / (1000.0) 'Wait Time (in Sec)'
    ,r.cpu_time
    ,r.logical_reads
    ,r.reads
    ,r.writes
    ,r.total_elapsed_time / (1000.0) 'Elapsed Time (in Sec)'
    ,SUBSTRING(st.TEXT, (r.statement_start_offset / 2) + 1, (
            (
                CASE r.statement_end_offset
                    WHEN - 1
                        THEN Datalength(st.TEXT)
                    ELSE r.statement_end_offset
                    END - r.statement_start_offset
                ) / 2
            ) + 1) AS statement_text
    ,COALESCE(Quotename(Db_name(st.dbid)) + N'.' + Quotename(Object_schema_name(st.objectid, st.dbid)) + N'.' +
     Quotename(Object_name(st.objectid, st.dbid)), '') AS command_text
    ,r.command
    ,s.login_name
    ,s.host_name
    ,s.program_name
    ,s.host_process_id
    ,s.last_request_end_time
    ,s.login_time
    ,r.open_transaction_count
FROM sys.dm_exec_sessions AS s
INNER JOIN sys.dm_exec_requests AS r ON r.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS st
WHERE r.session_id != @@SPID
ORDER BY r.cpu_time DESC
    ,r.STATUS
    ,r.blocking_session_id
    ,s.session_id

PostgreSQL

# get postgresql user and password
cd /data/docker
cat .env
docker container ps
docker exec -it container_db /bin/bash
psql -U <user> -W


See connections
SHOW max_connections;
SHOW shared_buffers;


See current activity
SELECT pid AS process_id,
       usename AS username,
       datname AS database_name,
       client_hostname,
       application_name,
       backend_start,
       state,
       wait_event,
       state_change
FROM pg_stat_activity;


See all databases
\l
-- See all databases with more information
\l+
2025/06/01 11:59

Cheatsheet Azure DevOps

Summary: Azure DevOps hints, tips, oneliners and best practices.
Date: 8 December 2024

Output in Azure DevOps

Logging output
# Logging output in azure devops
##[group]Beginning of a group - white
##[warning]Warning message - orange
##[error]Error message - red
##[section]Start of a section - green
##[debug]Debug text - purple
##[command]Command-line being run - blue
##[endgroup] - green
Write-Host "`n##[section]Function: $($MyInvocation.MyCommand).`n"


Log error or warning
Write-Host "##vso[task.logissue type=error]Something went very wrong."
Write-Host "##vso[task.logissue type=warning]Found something that could be a problem."


Generate extra output if debug mode is set
if ($env:SYSTEM_DEBUG -eq "True"){$headers | Out-Host}

Variables in Azure DevOps

Pipeline variable in yaml - without PowerShell
variables:
  - name: BuildConfiguration
    value: 'Release'
- task: DotNetCoreCLI@2
  displayName: "Build Solutions"
  inputs:
    command: 'build'
    projects: '**/*.sln'
    arguments: '--no-restore --configuration $(BuildConfiguration)'


Pipeline parameter in yaml
if ( "${{ parameters.alllogs }}" -eq "True" ){write-Host "Parameter from pipeline"}


System variable in yaml
Variables can be used like system environment variables

if (Test-Path "$env:Build_SourcesDirectory\$env:Build_Repository_Name\appsettings.Development.json"){Write-Host "System / Environment Variable"}
$sites = get-childitem -path env:* | Where-Object {$_.name -like "*-SiteName"} | Select-Object -ExpandProperty value # Added through Variable Group


Variables can also be used like pipeline variables

get-childitem $(Pipeline.Workspace)
$dbserver = "$(serverDb)" + "." + "$(dnsZone)" # from Azure App Configuration


Variables in PS scriptfile
$adserver = $env:adServer + "." + $env:domain # from Azure App Configuration
$projects = Get-ChildItem -Path "$env:Build_SourcesDirectory\$env:Build_Repository_Name\deploy\" -Directory | Select-Object Name

Starter Azure Pipeline

name: $(Build.DefinitionName)-$(Build.BuildId)
appendCommitMessageToRunName: false

variables:
  buildConfiguration: 'Release'
  deploymentSuffix: '$(Build.DefinitionName)-$(Build.BuildId).$(System.StageAttempt).$(System.JobAttempt)'

parameters:
  - name: alllogs
    displayName: "Enable all logging"
    type: boolean
    default: true
 
# https://learn.microsoft.com/en-us/azure/devops/pipelines/process/scheduled-triggers?view=azure-devops&tabs=yaml
schedules:
- cron: "05 23 * * *" # 23.05h
  displayName: Nightly build on master
  branches:
    include:
    - master
  always: true

pool:
  name: ReleasePool # Self hosted
  vmImage: ubuntu-latest # MS Hosted
  vmImage: windows-latest # MS Hosted

trigger:
  branches:
    include:
      - release/*
      - master
trigger: none

resources:
  repositories:
  - repository: templates
    type: git
    name: DevOps/templates
  - repository: self
  pipelines:
  - pipeline: TA
    source: TestAutomation # Pipeline name that produces the artifact (must be unique)

stages:
- stage: build
  displayName: "Stage: Build"
  pool:
    name: BuildPoolDotNet

  jobs:
  - job: build
    displayName: "Job: Build & Test"
    steps:
    - checkout: self
      clean: true # If true, run git clean -ffdx followed by git reset --hard HEAD before fetching.
    - checkout: templatses
      clean: true

    - ${{ if not(eq(variables['Build.Reason'], 'PullRequest')) }}:
      - template: /pipelines/log-information.yml@operations
        parameters:
          environment: "environment: build"
          alllogs: ${{ parameters.alllogs }}
 
# Stage with template that has steps (see deploy.yml)
- stage: release
  displayName: "Stage: release"
  condition: and(succeeded(), ne(variables['Build.SourceBranchName'], 'master'))

  variables:
    environment: 'tst'

  jobs:
  - deployment: Deploy
    workspace:
      clean: all
    environment: ${{ variables.environment }}

  - template: /pipelines/deploy.yml@templates
    parameters:
      environment: ${{ variables.environment }}
      alllogs: ${{ parameters.alllogs }}
 
# Stage with template as step (see prod.yml)
- stage: production
  displayName: "Stage: prod"
  dependsOn: release

  variables:
    tag: 'Production'

  jobs:
  - job: prd
    displayName: "production"
    condition: and(succeeded(), eq( ${{ parameters.deployProduction }}, true))
    variables:
      environment: 'prd'

    steps:
    - template: /deploy/production.yml
      parameters:
        environment: ${{ variables.environment }}
        alllogs: ${{ parameters.alllogs }}

Template deploy.yml

parameters:
  environment: ""
  alllogs: ""
  dependsOn1: []
  dependsOn2: []
  dependsOn3: []
  dependsOn4: []

jobs:
- job: deploy
  displayName: "Deploy steps"
  dependsOn:
    - ${{ parameters.dependsOn1 }}
    - ${{ parameters.dependsOn2 }}
    - ${{ parameters.dependsOn3 }}
    - ${{ parameters.dependsOn4 }}
  workspace:
    clean: all
  steps:

  - checkout: operations
    clean: true # If true, run git clean -ffdx followed by git reset --hard HEAD before fetching.
 
  #- ${{ if eq(parameters.alllogs, true) }}:
  - template: /pipelines/log-information.yml@operations
    parameters:
      environment: ${{ parameters.environment }}
      alllogs: ${{ parameters.alllogs }}
      input1: "adGroups: ${{ parameters.adGroups }}"

  - task: PowerShell@2
    displayName: "PowerShell task"
    inputs:
      pwsh: true
      targetType: 'inline'
      script: |
        Write-Host "`n##[section]PowerShell task `n"

Template prod.yml - Most Common Tasks

parameters:
  environment: ""
  alllogs: ""
  spokeEntity: ""

steps:
- task: PowerShell@2
  displayName: "Powershell Core task"
  inputs:
    pwsh: true
    targetType: 'inline'
    script: |
      Write-Host "`n##[section]Start PS Core task`n"
      Get-ChildItem -path env:* | Sort Name

- task: Bash@3
  displayName: "Bash task"
  inputs:
    targetType: 'inline'
    failOnStderr: true
    script: |
      echo '##[Section]Start Bash task'
      printenv | sort

- task: AzureCLI@2
  displayName: "Azure CLI task"
  condition: and(succeeded(), or(eq(variables.runAzureTask, true), eq(variables.runAzureAll, true)))
  inputs:
    azureSubscription: $(azureServiceConnection)
    scriptType: bash
    scriptLocation: inlineScript
    inlineScript: |
      set -e
      echo '##[Section]Start Azure CLI task'
      az account set --subscription $(subId)

- task: AzurePowerShell@5
  displayName: "Azure Powershell task"
  inputs:
    azureSubscription: $(azureServiceConnection)
    azurePowerShellVersion: 'LatestVersion'
    pwsh: true
    ScriptType: 'InlineScript'
    Inline: |
      if ( "${{ parameters.alllogs }}" -eq "True" ){Write-Host "`n##[section]Start Azure Powershell task`n"}
      # Set Azure Context for environment subscription
      Set-AzContext -Name ${{ parameters.environment }} -Subscription $env:azureSubscriptionName
2025/06/01 11:59

Cheatsheet Azure CLI

Summary: AzureCLI hints, tips, oneliners and best practices. I prefer working with the Azure CLI when setting up test environments as it's fast, it's easy to see which settings you're deploying and the use of variables makes different snippets work very good together. When looking for such snippets, look for the deploy keyword.
Date: 8 December 2024

Login to Azure

az login
az account set --subscription xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
az account show

Get Objects

# Check if a resourcegroup exists and set a variable
RgExists=$(az group exists -n $(rgName))
echo "$(rgName) exists: $RgExists"
 
# Get firewall Id
firewallId=$(az network firewall show --name $(azureFirewallName) --resource-group $(rgName) --query id --output tsv)
 
# Get Log Analytics Workspace Id
workspaceId=$(az monitor log-analytics workspace show --workspace-name $(lawName) --resource-group $(rgName) --subscription $(subId) --query id -o tsv)
 
# Get Virtual Network Gateway (VPN) Id
vngId=$(az network vnet-gateway show --name $(virtualNetworkGatewayName) --resource-group $(rgName) --query id --output tsv)
 
# Get Keyvault Id
keyvaultId=$(az keyvault show --name $(keyVaultName) --resource-group $(rgName) --query id --output tsv)

Tags

resourceGroup=$(az group show -n poc-$(team)-$(pocnaam) --query id --output tsv)
echo 'The resource group has the following tags'
az tag list --resource-id $resourceGroup
echo 'add a tag'
az tag update --resource-id $resourceGroup --operation Merge --tags DateOne="$(firstdate)" DateTwo="$(seconddate)"

Azure Resource Graph Explorer Query

# check if a resource has a specific tag set
tag=$(az graph query -q "resourcecontainers | where type == 'microsoft.resources/subscriptions/resourcegroups' and name == 'poc-$(team)-$(pocnaam)' | where tags contains 'startdate'" | jq '.data[].name')
 
echo "Tag date: $tag"
 
# do something if the tag is empty
if [ -z "$tag" ];
then
  echo "do something, the tag is not set"
else
  echo "do something else, the tag is set"
fi
 
# do something if the tag is not empty
if [ -n "$bedrijf" ];
then
  echo "do something, the tag is set"
else
  echo "do something else, the tag is not set"
fi

BICEP deployment

# deploy resourcegroup in a subscription
az deployment sub create \
  --location $(location) \
  --name "hub-$(deploymentSuffix)" \
  --template-file ./modules/resourcegroup.bicep \
  --parameters ./hub/hub-rg-parameterfile.jsonc \
  --parameters location=$(location)
 
# deploy resources in resourcegroup
az deployment group create \
  --resource-group $(rgName) \
  --name "hub-vnet-$(deploymentSuffix)" \
  --template-file ./hub/hub-vnet.bicep \
  --parameters deploymentSuffix=$(deploymentSuffix) \
  --parameters subId=$(subId) \
  --parameters vnetName=$(vnetName)

Assign Roles

Assign a role to as group on a storage account (Storage Blob Data Owner). Note that this was used in a azure devops pipeline with parameters as variables.
az role assignment create \
  --role "Storage Blob Data Owner" \
  --assignee-object-id "cd96f93g-3973-4d94-e2c9-adacd18c2312" \
  --assignee-principal-type "Group" \
  --scope "/subscriptions/$(subscriptionId)/resourceGroups/$(ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$(StorageAccountName)"

Keyvaults

Show all secrets in a keyvault and make it easy to read
# All secrets
az keyvault secret list --vault-name kv-euw-dev-001 --output table
# One secret
az keyvault secret show --vault-name kv-euw-dev-001 --name app-id-clientid -o table
az keyvault secret show --vault-name kv-euw-dev-001 --name app-id-secret -o table
# Just list all of the names
az keyvault secret list --vault-name kv-euw-vtx-dev-001 --query [].name --output table

Bastion

Get rdp file from bastion
# variables
tenantId="c2a75ggc-b442-5678-9234-1e906d30ede2"
subscriptionId="c2a75ggc-b442-5678-9234-48a7fab3b688"
rg="rg-euw-bastion"
bastionName="bas-euw-global-001"
vmId="/subscriptions/c2a75ggc-b442-5678-9234-aebcd3d45789/resourceGroups/rg-euw-app01/providers/Microsoft.Compute/virtualMachines/vm-app1-001"
 
# Authenticate to Azure PIM groups
Start-Process "https://portal.azure.com/#view/Microsoft_Azure_PIMCommon/ActivationMenuBlade/~/aadgroup"
# Configure the azure cli client
az logout
az config set core.login_experience_v2=off
az login --tenant $tenantId
az account set --subscription $subscriptionId
az network bastion rdp --name $bastionName --resource-group $rg --target-resource-id $vmId --configure

Deploy

In this section you'll find snippets to deploy resources in Azure. The code is written, unless otherwise stated, in PowerShell and can be deployed using the cloud shell.


Use the –no-wait parameter to deploy resources in the background. This is useful when you're deploying multiple resources and don't want to wait for each deployment to finish. You can check the status of the deployment in the deployment section of the resource group in the Azure portal.


After some deployments additional commands are shown, for example to retrieve the id of the resource. This is done when the id is needed in a subsequent deployment.

Use the following command to get the status of the last deployment in a resource group:
az deployment group show --resource-group $rg --name $((az deployment group list --resource-group $rg --query "[0].name").trim('"')) --output table

Use the following command to delete a resource group and all resources in it:
az group delete --name $rg --yes --no-wait

Variables

This is a collection of variables used in the snippets below.
# Define a identifier which will be used in all resource names, for example: "tst", "poc", "shift"
$project = "tst"
# Define the resource group and location
$rg = "rg-$project"
$loc = "westeurope"
# Define the default username and password
$admin = "azadmin"
$password = "P@ssword1234"
# Define the virtual network and subnets
$vnet = "vnet-$project"
$vnetAddressSpace = "10.0.0.0/8"
$subnet = "snet-$project"
$subnetAddressSpace00 = "10.0.0.0/24"
$subnetAddressSpace01 = "10.0.1.0/24"
$subnetAddressSpace02 = "10.0.2.0/24"
$subnetAddressSpace03 = "10.0.3.0/24"
$subnetAddressSpace04 = "10.0.4.0/24"
$subnetAddressSpace05 = "10.0.5.0/24"
$subnetAddressSpace06 = "10.0.6.0/24"
$subnetAddressSpace07 = "10.0.7.0/24"
$subnetAddressSpace08 = "10.0.8.0/24"
$subnetAddressSpace09 = "10.0.9.0/24"
$subnetAddressSpace10 = "10.0.10.0/24"
$vmname = "vm-$($project)01"
# Bastion
$bastion = "bas-$project"
# Storage account must be globally unique, lowercase, numbers and between 3 and 24 characters
$sa = "st$project"
# Managed identity
$id = "id-$project"
# Keyvault
$kv = "kv-$project"
$kvSecret = "secret-$project"
# Azure SQL Database server and database
$sqlServer = "sql-$project"
$dbName = "sqldb-$project"

Resourcegroup

az group create `
  --name $rg `
  --location $loc

Virtual Network

# Create a virtual network
az network vnet create `
  --name $vnet `
  --resource-group $rg `
  --location $loc `
  --address-prefixes $vnetAddressSpace
 
# Create a subnet
az network vnet subnet create `
  --name $subnet `
  --resource-group $rg `
  --vnet-name $vnet `
  --address-prefix $subnetAddressSpace00
$subnetId = (az network vnet subnet show `
  --resource-group $rg `
  --vnet-name $vnet `
  --name $subnet `
  --query "id").Trim('"')
Write-Host $subnetId

Virtual Machine

# Create a Windows Server 2022 virtual machine, without a public IP address, with a system-assigned managed identity, in the vnet and subnet created above, with a 64GB data disk, secure boot and trusted launch enabled. All resources will be deleted when the VM is deleted.
# Create data disk
az disk create `
  --resource-group $rg `
  --name "disk-$vmname" `
  --location $loc `
  --size-gb 64 `
  --public-network-access Disabled `
  --sku Standard_LRS
# Create VM
# Note: when creating a domain controller set the --data-disk-caching to None
az vm create `
  --resource-group $rg `
  --name $vmname `
  --location $loc `
  --image Win2022Datacenter `
  --size Standard_DS1_v2 `
  --assign-identity [system] `
  --nsg "" `
  --admin-username $admin `
  --admin-password $password `
  --public-ip-address "" `
  --accelerated-networking false `
  --nic-delete-option delete `
  --vnet-name $vnet `
  --subnet $subnet `
  --storage-sku Standard_LRS `
  --os-disk-name "osdisk-$vmname" `
  --os-disk-delete-option delete `
  --attach-data-disks "disk-$vmname" `
  --data-disk-caching ReadWrite `
  --data-disk-delete-option delete `
  --enable-hibernation false `
  --enable-secure-boot true `
  --security-type TrustedLaunch `
  --accept-term `
  --no-wait
# Set auto shutdown at 16:00 UTC (17:00 CET)
az vm auto-shutdown -g $rg -n $vmname --time 16:00

Bastion

# Create bastion subnet
az network vnet subnet create `
  --name AzureBastionSubnet `
  --resource-group $rg `
  --vnet-name $vnet `
  --address-prefix $subnetAddressSpace01 `
  --no-wait
# Create public ip address
az network public-ip create `
  --resource-group $rg `
  --name "pip-$bastion" `
  --sku Standard `
  --location $loc
# Create bastion
az network bastion create `
  --name $bastion `
  --public-ip-address "pip-$bastion" `
  --resource-group $rg `
  --vnet-name $vnet `
  --location $loc `
  --sku Basic `
  --no-wait

Storage Account

# Storage account
az storage account create `
  --resource-group $rg `
  --name $sa `
  --location $loc `
  --kind StorageV2 `
  --sku Standard_LRS `
  --enable-large-file-share `
  --min-tls-version TLS1_2 `
  --https-only true `
  --allow-blob-public-access false
# Get storage account Id
$saId = (az storage account show `
  --resource-group $rg `
  --name $sa `
  --query "id").trim('"')
Write-Host $saId
# Storage container
az storage container create `
  --name "cont-$project" `
  --account-name $sa
# File share
az storage share-rm create `
  --resource-group $rg `
  --storage-account $sa `
  --name "share$sa" `
  --quota 1024 `
  --enabled-protocols SMB
# Create a directory for user profiles
az storage directory create `
  --account-name $sa `
  --share-name "share$sa" `
  --name "userprofiles"

Enable Data Protection and Lock

As this is mostly used for test environments I've also included the commands to disable the data protection.
# Enabling container soft delete
az storage account blob-service-properties update `
  --enable-container-delete-retention true `
  --container-delete-retention-days 5 `
  --account-name $sa `
  --resource-group $rg
# az storage account blob-service-properties update --enable-container-delete-retention false --account-name $sa --resource-group $rg
# Enabling blob soft delete
az extension add -n storage-preview
az storage fs service-properties update `
  --account-name $sa `
  --delete-retention true `
  --delete-retention-period 5
# az storage fs service-properties update --account-name $sa --delete-retention false
# Set a resource lock on the storage account
az lock create `
  --name "LockStorageAccount" `
  --lock-type CanNotDelete `
  --resource-group $rg `
  --resource $sa `
  --resource-type "Microsoft.Storage/storageAccounts" `
  --notes "This lock is to prevent accidental deletion of the storage account"
# az lock delete --name LockStorageAccount --resource-group $rg --resource $sa --resource-type "Microsoft.Storage/storageAccounts"

Private Endpoint

# Get group Id and private dns zone
$privateLinkGroupId = (Get-AzPrivateLinkResource -PrivateLinkResourceId $saId).GroupId
Write-Host $privateLinkGroupId
# Multiple options are returned, using blob for now
$privateLinkGroupId = "blob"
$privateLinkZoneName = ((Get-AzPrivateLinkResource -PrivateLinkResourceId $saId).RequiredZoneNames).trim('{}')
Write-Host $privateLinkZoneName
# Multiple options are returned, using blob for now
$privateLinkZoneName = "privatelink.blob.core.windows.net"
 
# Create private endpoint
az network private-endpoint create `
  --resource-group $rg `
  --name "pep-$sa" `
  --location $loc `
  --subnet $subnetId `
  --private-connection-resource-id $saId `
  --group-id $privateLinkGroupId `
  --connection-name "con-$sa"
# Get private endpoint Id
$pepId = (az network private-endpoint show `
  --resource-group $rg `
  --name "pep-$sa" `
  --query "id").Trim('"')
Write-Host $pepId
# Get nic from pep
$nicPepId = (az network private-endpoint show `
  --ids $pepId `
  --query "networkInterfaces[0].id").Trim('"')
Write-Host $nicPepId
# Get IP address from nic
$nicIp = (az network nic show `
  --ids $nicPepId `
  --query "ipConfigurations[0].privateIPAddress").Trim('"')
Write-Host $nicIp

Private DNS Zone

az network private-dns zone create `
  --resource-group $rg `
  --name $privateLinkZoneName
# Create a link between the virtual network and the private DNS zone
az network private-dns link vnet create `
  --resource-group $rg `
  --zone-name $privateLinkZoneName `
  --name $vnet-DnsLink `
  --virtual-network $vnet `
  --registration-enabled false

Private DNS Record

# create recordset
az network private-dns record-set a create `
  --resource-group $rg `
  --zone-name $privateLinkZoneName `
  --name $sa
 
# Create a record
az network private-dns record-set a add-record `
  --resource-group $rg `
  --zone-name $privateLinkZoneName `
  --record-set-name $sa `
  --ipv4-address $nicIp

Managed Identity

# Managed identity
az identity create `
  --name $id `
  --resource-group $rg `
  --location $loc
 
# Get the managed identity client id (Client Id (Application Id) is used to authenticate workloads)
$idId = az identity show `
  --name $id `
  --resource-group $rg `
  --query clientId `
  --output tsv
Write-Host $idId
 
# Get the managed identity object id (Object (Principal) Id is used to authorize workloads, aka, assign permissions in Azure)
$idPrincipalId = az identity show `
  --name $id `
  --resource-group $rg `
  --query principalId `
  --output tsv
Write-Host $idPrincipalId

Keyvault

az keyvault create `
  --name $kv `
  --resource-group $rg `
  --location $loc `
  --enable-purge-protection `
  --enable-rbac-authorization `
  --retention-days 7
 
# Get the keyvault id
$kvId = az keyvault show `
  --resource-group $rg `
  --name $kv `
  --query id `
  --output tsv
Write-Host $kvId
Note that enabling purge protection is required for workload identity to work with Azure Key Vault. Because this is a test environment the retention days are set to 7.

Assign Permissions

# Assign permissions to the current user
$userId = az account show `
  --query user.name `
  --output tsv
 
# Set permissions for the user
az role assignment create `
  --assignee $userId `
  --role "Key Vault Secrets Officer" `
  --scope $kvId
 
# Set permissions for the workload identity
az role assignment create `
  --assignee-object-id $idPrincipalId `
  --role "Key Vault Secrets User" `
  --scope $kvId `
  --assignee-principal-type ServicePrincipal

Create Secret

# Create a secret in the key vault
az keyvault secret set `
  --vault-name $kv `
  --name $kvSecret `
  --value $password

Azure SQL Server

# create db server
az sql server create `
  --name $sqlServer `
  --resource-group $rg `
  --location $loc `
  --admin-user $admin `
  --admin-password $password
# Get Sql Server Id
$sqlServerId = (az sql server show `
  --name $sqlServer `
  --resource-group $rg `
  --query "id").trim('"')
Write-Host $sqlServerId

Private Endpoint

# Get group Id and private dns zone
$privateLinkGroupId = (Get-AzPrivateLinkResource -PrivateLinkResourceId $sqlServerId).GroupId
Write-Host $privateLinkGroupId
# Note, sometimes the command below can return multiple domains
$privateLinkZoneName = ((Get-AzPrivateLinkResource -PrivateLinkResourceId $sqlServerId).RequiredZoneNames).trim('{}')
Write-Host $privateLinkZoneName
 
# Create private endpoint
az network private-endpoint create `
  --resource-group $rg `
  --name "pep-$sqlServer" `
  --location $loc `
  --subnet $subnetId `
  --private-connection-resource-id $sqlServerId `
  --group-id $privateLinkGroupId `
  --connection-name "con-$sqlServer"
 
# Get private endpoint Id
$pepId = (az network private-endpoint show `
  --resource-group $rg `
  --name "pep-$sqlServer" `
  --query "id").Trim('"')
Write-Host $pepId
# Get nic from pep
$nicPepId = (az network private-endpoint show `
  --ids $pepId `
  --query "networkInterfaces[0].id").Trim('"')
Write-Host $nicPepId
# Get IP address from nic
$nicIp = (az network nic show `
  --ids $nicPepId `
  --query "ipConfigurations[0].privateIPAddress").Trim('"')
Write-Host $nicIp

Private DNS Zone

# Private DNS zone
az network private-dns zone create `
  --resource-group $rg `
  --name $privateLinkZoneName
# Create a link between the virtual network and the private DNS zone
az network private-dns link vnet create `
  --resource-group $rg `
  --zone-name $privateLinkZoneName `
  --name $vnet-DnsLink `
  --virtual-network $vnet `
  --registration-enabled false

Private DNS Record

# private dns record
# create recordset
az network private-dns record-set a create `
  --resource-group $rg `
  --zone-name $privateLinkZoneName `
  --name $sqlServer
 
# Create a record
az network private-dns record-set a add-record `
  --resource-group $rg `
  --zone-name $privateLinkZoneName `
  --record-set-name $sqlServer `
  --ipv4-address $nicIp

Azure SQL Hyperscale Database

When creating a Hyperscale database, carefully consider the setting for backup-storage-redundancy. Storage redundancy can only be specified during the database creation process for Hyperscale databases. You can choose locally redundant, zone-redundant, or geo-redundant storage. The selected storage redundancy option will be used for the lifetime of the database for both data storage redundancy and backup storage redundancy. Allowed values for the backup-storage-redundancy parameter are: Local, Zone, Geo. Unless explicitly specified, databases will be configured to use geo-redundant backup storage.
# create db
az sql db create `
  --resource-group $rg `
  --server $sqlServer `
  --name $dbname `
  --edition Hyperscale `
  --compute-model Serverless `
  --family Gen5 `
  --capacity 2 `
  --backup-storage-redundancy Local `
  --ha-replicas 0 `
  --yes
2025/06/01 11:59

Cheatsheet Active Directory

Summary: Active Directory hints, tips, oneliners and best practices.
Date: 8 December 2024

Active Directory Users

Count all users in all departments
Get-ADUser -SearchBase "OU=Users,DC=GetShifting,DC=local" -filter * -Properties name,department,enabled  | where {$_.enabled -eq "true"} | group department | select name, count


All enabled users that have never logged in
Get-ADUser -Filter {(lastlogondate -notlike "*" -AND enabled -eq "True")} -SearchBase "OU=Users,DC=GetShifting,DC=local"


All enabled users that have not logged on for more than 90 days
$90Days = (get-date).adddays(-90)
Get-ADUser -Filter {(lastlogondate -le $90days -AND enabled -eq "True")} -SearchBase "OU=Users,DC=GetShifting,DC=local"


Get the most common reasons on a user for not being able to logon
Get-ADUser User123 -Properties * | Select-Object enabled,passwordexpired,lockedout

Active Directory Groups

Get all groups in a specific OU
Get-ADGroup -SearchBase "OU=Groups,DC=GetShifting,DC=local" -Filter * | Select-Object name


Get all groupmembers from a specific group
Get-ADGroupMember "RDP Desktop Users" -recursive | Select-Object Name


Active Directory Servers

All servers
Get-ADComputer -Filter {(operatingSystem -like "*windows*Server*")}

Note: Does not select Windows 2000 Servers.

Select servers on properties
Get-ADComputer -filter {(description -like "*financial*" -AND name -like "SQL*")} -Properties * | select name, description

Active Directory DNS

Get all AD DNS zones with all properties
Get-ChildItem "AD:CN=MicrosoftDNS,DC=DomainDNSZones,DC=getshifting,DC=local" | Select-Object *


Get all records in a DNS zone with all properties
Get-ChildItem "AD:DC=getshifting.local,CN=MicrosoftDNS,DC=DomainDNSZones,DC=getshifting,DC=local" | Select-Object *


Get creation and modify date of AD DNS records
Get-ChildItem "AD:DC=getshifting.local,CN=MicrosoftDNS,DC=DomainDNSZones,DC=getshifting,DC=local" | Where-Object {$_.name -like "build*"} | Get-ADObject -Properties Created,Modified | Select-Object Name,Created,Modified

Active Directory Policies

Get the max password age from the policy
(Get-ADDefaultDomainPasswordPolicy -ErrorAction Stop).MaxPasswordAge.Days

Service Principal Names

Create a service principle name:

  • Open a command prompt and use the following command:
setspn -A BOBJCentralMS/crystalsrv.company.local BOservice

Check:

C:\Windows\system32>setspn -Q BOBJCentralMS/crystalsrv.company.local
CN=BusinessObjects Service,OU=ServiceAccounts,DC=company,DC=local
        BOBJCentralMS/crystalsrv.company.local
        BOBJCentralMS/crystal01srv.company.local

Existing SPN found!
2025/06/01 11:59
start.txt · Last modified: by 127.0.0.1