Skip to content

Instantly share code, notes, and snippets.

@ShridharParameshwarBhat
Last active February 23, 2022 22:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ShridharParameshwarBhat/5a4f439afdb99ec4730e481b91bcb795 to your computer and use it in GitHub Desktop.
Save ShridharParameshwarBhat/5a4f439afdb99ec4730e481b91bcb795 to your computer and use it in GitHub Desktop.
This is the script to generate the BEK file for unlocking the encrypted disk
Param(
[Parameter(Mandatory = $true,
HelpMessage="URL of the secret stored in the keyvault")]
[ValidateNotNullOrEmpty()]
[string]$secretUrl,
[Parameter(Mandatory = $true,
HelpMessage="Resource group of keyvault")]
[ValidateNotNullOrEmpty()]
[string]$keyVaultResourceGroup,
[Parameter(Mandatory = $true,
HelpMessage="URL of the KEK")]
[ValidateNotNullOrEmpty()]
[string]$kekUrl,
[Parameter(Mandatory = $true,
HelpMessage="Location where the retrieved secret should be written to")]
[ValidateNotNullOrEmpty()]
[string]$secretFilePath
)
#Login-AzAccount;
#Install Active directory module
Install-Module -Name MSOnline;
#Get current logged in user and active directory tenant details
$ctx = Get-AzContext;
$adTenant = $ctx.Tenant.Id;
$currentUser = $ctx.Account.Id
#Parse the secret URL
$secretUri = [System.Uri] $secretUrl;
#Retrieve keyvault name, secret name and secret version from secret URL
$keyVaultName = $secretUri.Host.Split('.')[0];
$secretName = $secretUri.Segments[2].TrimEnd('/');
$secretVersion = $secretUri.Segments[3].TrimEnd('/');
#Set permissions for the current user to unwrap keys and retrieve secrets from KeyVault
$KeyVault = Get-AzKeyVault -VaultName $keyVaultName -ResourceGroupName $keyVaultResourceGroup;
$acl = $KeyVault.AccessPolicies;
$currentacl = $acl | Where-Object { $_.DisplayName -match $currentUser };
$aclp2k = $currentacl.PermissionsToKeys + "unwrapKey"
$aclp2s = $currentacl.PermissionsToSecrets + "get"
Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName -PermissionsToKeys $aclp2k -PermissionsToSecrets $aclp2s -UserPrincipalName $currentUser;
#Retrieve secret from KeyVault secretUrl
$keyVaultSecret = Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName -Version $secretVersion;
$secretBase64 = $keyVaultSecret.SecretValue;
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secretBase64)
$secretBase64 = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
#Unwrap secret if the secret is wrapped with KEK
if($kekUrl)
{
########################################################################################################################
# Initialize ADAL libraries and get authentication context required to make REST API called against KeyVault REST APIs.
########################################################################################################################
# Install the ADAL Module
Install-Module -Name Az.Accounts -Scope AllUsers -RequiredVersion "1.9.4" -Repository PSGallery -Force -AllowClobber
# Load ADAL Assemblies. If the ADAL Assemblies cannot be found, please see the "Install Az PowerShell module" section.
$adal = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
If ((Test-Path -Path $adal) -and (Test-Path -Path $adalforms)) {
[System.Reflection.Assembly]::LoadFrom($adal)
[System.Reflection.Assembly]::LoadFrom($adalforms)
}
else
{
Write-output "ADAL Assemblies files cannot be found. Please set the correct path for `$adal` and `$adalforms`, then run the script again."
exit
}
# Set well-known client ID for AzurePowerShell
$clientId = "1950a258-227b-4e31-a9cf-717495945fc2"
# Set redirect URI for Azure PowerShell
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
# Set Resource URI to Azure Service Management API
$resourceAppIdURI = "https://vault.azure.net"
# Set Authority to Azure AD Tenant
$authority = "https://login.windows.net/$adTenant"
# Create Authentication Context tied to Azure AD Tenant
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
# Acquire token
$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters).result
# Generate auth header
$authHeader = $authResult.CreateAuthorizationHeader()
# Set HTTP request headers to include Authorization header
$headers = @{'x-ms-version'='2014-08-01';"Authorization" = $authHeader}
# Place wrapped BEK in JSON object to send to KeyVault REST API
########################################################################################################################
# 1. Retrieve the secret from KeyVault
# 2. If Kek is not NULL, unwrap the secret with Kek by making KeyVault REST API call
# 3. Convert Base64 string to bytes and write to the BEK file
########################################################################################################################
#Call KeyVault REST API to Unwrap
$jsonObject = @"
{
"alg": "RSA-OAEP",
"value" : "$secretBase64"
}
"@
$unwrapKeyRequestUrl = $kekUrl+ "/unwrapkey?api-version=2015-06-01";
$result = Invoke-RestMethod -Method POST -Uri $unwrapKeyRequestUrl -Headers $headers -Body $jsonObject -ContentType "application/json";
#Convert Base64Url string returned by KeyVault unwrap to Base64 string
$secretBase64 = $result.value;
}
$secretBase64 = $secretBase64.Replace('-', '+');
$secretBase64 = $secretBase64.Replace('_', '/');
if($secretBase64.Length %4 -eq 2)
{
$secretBase64+= '==';
}
elseif($secretBase64.Length %4 -eq 3)
{
$secretBase64+= '=';
}
if($secretFilePath)
{
$bekFileBytes = [System.Convert]::FromBase64String($secretBase64);
[System.IO.File]::WriteAllBytes($secretFilePath,$bekFileBytes);
}
#Delete the key from the memory
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
clear-variable -name secretBase64
@ishepherd
Copy link

I could not get the ADAL way working.
Fortunately since Az.Accounts 2.2.0, there is Get-AzAccessToken which seems to make ADAL entirely unnecessary.

if($kekUrl)
{
-
-    ########################################################################################################################
-    # Initialize ADAL libraries and get authentication context required to make REST API called against KeyVault REST APIs. 
-    ########################################################################################################################
-	
-	# Install the ADAL Module
-	Install-Module -Name Az.Accounts -Scope AllUsers -RequiredVersion "1.9.4" -Repository PSGallery -Force -AllowClobber
-
-	# Load ADAL Assemblies. If the ADAL Assemblies cannot be found, please see the "Install Az PowerShell module" section. 
-	$adal = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
-	$adalforms = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"  
-
-	If ((Test-Path -Path $adal) -and (Test-Path -Path $adalforms)) { 
-
-	[System.Reflection.Assembly]::LoadFrom($adal)
-	[System.Reflection.Assembly]::LoadFrom($adalforms)
-	}
-	else
-	{
-	 Write-output "ADAL Assemblies files cannot be found. Please set the correct path for `$adal` and `$adalforms`, then run the script again."
-	 exit    
-	}  
-
-    # Set well-known client ID for AzurePowerShell
-    $clientId = "1950a258-227b-4e31-a9cf-717495945fc2" 
-    # Set redirect URI for Azure PowerShell
-    $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
-    # Set Resource URI to Azure Service Management API
-    $resourceAppIdURI = "https://vault.azure.net"
-    # Set Authority to Azure AD Tenant
-    $authority = "https://login.windows.net/$adTenant"
-    # Create Authentication Context tied to Azure AD Tenant
-    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
	# Acquire token
-	$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
-	$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters).result
+    $token = Get-AzAccessToken -ResourceUrl 'https://vault.azure.net'
    # Generate auth header 
-    $authHeader = $authResult.CreateAuthorizationHeader()
+    $authHeader = "$($token.Type) $($token.Token)"
    # Set HTTP request headers to include Authorization header
    $headers = @{'x-ms-version'='2014-08-01';"Authorization" = $authHeader}

@ishepherd
Copy link

An Invoke-AzKeyVaultKeyOperation cmdlet is going to land soon, which would simplify the above even further.
Azure/azure-powershell#15816

@evanzhang89
Copy link

ADAL way not working either from multiple customer environments. Try below fork with Get-AzAccessToken
https://gist.github.com/evanzhang89/22a5f73aaf238e0a712a6d753e1a4e0e

Param(
[Parameter(Mandatory = $true,
HelpMessage="URL of the secret stored in the keyvault")]
[ValidateNotNullOrEmpty()]
[string]$secretUrl,

[Parameter(Mandatory = $true,
HelpMessage="Resource group of keyvault")]
[ValidateNotNullOrEmpty()]
[string]$keyVaultResourceGroup,

[Parameter(Mandatory = $true,
HelpMessage="URL of the KEK")]
[ValidateNotNullOrEmpty()]
[string]$kekUrl,

[Parameter(Mandatory = $true,
HelpMessage="Location where the retrieved secret should be written to")]
[ValidateNotNullOrEmpty()]
[string]$secretFilePath
)

Login-AzAccount;

#Install Active directory module
Install-Module -Name MSOnline;

#Get current logged in user and active directory tenant details
$ctx = Get-AzContext;
$adTenant = $ctx.Tenant.Id;
$currentUser = $ctx.Account.Id

#Parse the secret URL
$secretUri = [System.Uri] $secretUrl;

#Retrieve keyvault name, secret name and secret version from secret URL
$keyVaultName = $secretUri.Host.Split('.')[0];
$secretName = $secretUri.Segments[2].TrimEnd('/');
$secretVersion = $secretUri.Segments[3].TrimEnd('/');

#Set permissions for the current user to unwrap keys and retrieve secrets from KeyVault
$KeyVault = Get-AzKeyVault -VaultName $keyVaultName -ResourceGroupName $keyVaultResourceGroup;
$acl = $KeyVault.AccessPolicies;
$currentacl = $acl | Where-Object { $_.DisplayName -match $currentUser };
$aclp2k = $currentacl.PermissionsToKeys + "unwrapKey"
$aclp2s = $currentacl.PermissionsToSecrets + "get"
Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName -PermissionsToKeys $aclp2k -PermissionsToSecrets $aclp2s -UserPrincipalName $currentUser;

#Retrieve secret from KeyVault secretUrl
$keyVaultSecret = Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName -Version $secretVersion;
$secretBase64 = $keyVaultSecret.SecretValue;
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secretBase64)
$secretBase64 = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)

#Unwrap secret if the secret is wrapped with KEK
if($kekUrl)
{

########################################################################################################################
# Initialize ADAL libraries and get authentication context required to make REST API called against KeyVault REST APIs. 
########################################################################################################################

# Install the ADAL Module
Install-Module -Name Az.Accounts -Scope AllUsers -Repository PSGallery -Force -AllowClobber

$Token = "Bearer {0}" -f (Get-AzAccessToken -Resource "https://vault.azure.net").Token

$headers = @{
'Authorization' = $token
"x-ms-version"  = '2014-08-01'

}

# Place wrapped BEK in JSON object to send to KeyVault REST API

########################################################################################################################
# 1. Retrieve the secret from KeyVault
# 2. If Kek is not NULL, unwrap the secret with Kek by making KeyVault REST API call
# 3. Convert Base64 string to bytes and write to the BEK file
########################################################################################################################

#Call KeyVault REST API to Unwrap 
$jsonObject = @"
{
    "alg": "RSA-OAEP",
    "value" : "$secretBase64"
}

"@

$unwrapKeyRequestUrl = $kekUrl+ "/unwrapkey?api-version=2015-06-01";
$result = Invoke-RestMethod -Method POST -Uri $unwrapKeyRequestUrl -Headers $headers -Body $jsonObject -ContentType "application/json";

#Convert Base64Url string returned by KeyVault unwrap to Base64 string
$secretBase64 = $result.value;

}

$secretBase64 = $secretBase64.Replace('-', '+');
$secretBase64 = $secretBase64.Replace('_', '/');
if($secretBase64.Length %4 -eq 2)
{
$secretBase64+= '==';
}
elseif($secretBase64.Length %4 -eq 3)
{
$secretBase64+= '=';
}

if($secretFilePath)
{
$bekFileBytes = [System.Convert]::FromBase64String($secretBase64);
[System.IO.File]::WriteAllBytes($secretFilePath,$bekFileBytes);
}

#Delete the key from the memory
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
clear-variable -name secretBase64

@wangken82
Copy link

I could not get the ADAL way working.
Fortunately since Az.Accounts 2.2.0, there is Get-AzAccessToken which seems to make ADAL entirely unnecessary.

if($kekUrl)
{
-
-    ########################################################################################################################
-    # Initialize ADAL libraries and get authentication context required to make REST API called against KeyVault REST APIs. 
-    ########################################################################################################################
-	
-	# Install the ADAL Module
-	Install-Module -Name Az.Accounts -Scope AllUsers -RequiredVersion "1.9.4" -Repository PSGallery -Force -AllowClobber
-
-	# Load ADAL Assemblies. If the ADAL Assemblies cannot be found, please see the "Install Az PowerShell module" section. 
-	$adal = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
-	$adalforms = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"  
-
-	If ((Test-Path -Path $adal) -and (Test-Path -Path $adalforms)) { 
-
-	[System.Reflection.Assembly]::LoadFrom($adal)
-	[System.Reflection.Assembly]::LoadFrom($adalforms)
-	}
-	else
-	{
-	 Write-output "ADAL Assemblies files cannot be found. Please set the correct path for `$adal` and `$adalforms`, then run the script again."
-	 exit    
-	}  
-
-    # Set well-known client ID for AzurePowerShell
-    $clientId = "1950a258-227b-4e31-a9cf-717495945fc2" 
-    # Set redirect URI for Azure PowerShell
-    $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
-    # Set Resource URI to Azure Service Management API
-    $resourceAppIdURI = "https://vault.azure.net"
-    # Set Authority to Azure AD Tenant
-    $authority = "https://login.windows.net/$adTenant"
-    # Create Authentication Context tied to Azure AD Tenant
-    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
	# Acquire token
-	$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
-	$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters).result
+    $token = Get-AzAccessToken -ResourceUrl 'https://vault.azure.net'
    # Generate auth header 
-    $authHeader = $authResult.CreateAuthorizationHeader()
+    $authHeader = "$($token.Type) $($token.Token)"
    # Set HTTP request headers to include Authorization header
    $headers = @{'x-ms-version'='2014-08-01';"Authorization" = $authHeader}

Great finding from ishepherd, some VMs need enable TLS12 before installing the Az.Acccounts.

Modify the installing ADAL module to Azaccount module

#########################################################################################################
# Install Az.Accounts 2.1.0,  only replace the ADAL part, other lines no need to change
#########################################################################################################
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-Module -Name Az.Accounts -RequiredVersion 2.2.3

# Generate auth header 
$token = Get-AzAccessToken -ResourceUrl 'https://vault.azure.net'
$authHeader = "$($token.Type) $($token.Token)"

# Set HTTP request headers to include Authorization header
$headers = @{'x-ms-version'='2014-08-01';"Authorization" = $authHeader}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment