Accessing Key Vault secrets securely - powershell

I wrote a program in Powershell which runs on a schedule in an Azure Functions app. To avoid hard-coded credentials, I created an Azure Key Vault to store the secrets. I created a managed identity in the Azure Function, created the secrets in Azure Key Vault and then created application settings in Azure Function with the URL to point at the secrets in Azure Key Vault. The program references the application secrets (APPSETTING) and behaves as expected:
$uSecret = $ENV:APPSETTING_SecretUsername
$pSecret = $ENV:APPSETTING_SecretPassword
$sasSecret = $ENV:APPSETTING_SecretSAS
$securePassword = ConvertTo-SecureString -String $pSecret -AsPlainText -Force
$UserCredential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $uSecret, $securePassword
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
However I noticed that if I run the same program on my local computer via Windows Powershell (run as Administrator) and with the above lines amended as follows, the program runs fine - meaning it can access Office 365 and the data lake storage:
$uSecret = (Get-AzKeyVaultSecret -VaultName 'auditkeyvault' -Name 'SecretUsername').SecretValueText
$pSecret = (Get-AzKeyVaultSecret -VaultName 'auditkeyvault' -Name 'SecretPassword').SecretValueText
$sasSecret = (Get-AzKeyVaultSecret -VaultName 'auditkeyvault' -Name 'SecretSAS').SecretValueText
$securePassword = ConvertTo-SecureString -String $pSecret -AsPlainText -Force
$UserCredential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $uSecret, $securePassword
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Why am I able to run this locally on my computer? I would have expected only the Azure Functions app to be able to retrieve the secrets from Azure Key Vault and that any other resource such as my local computer would be prevented?
Isn't the whole purpose of creating a managed identity for the Azure Function with the specific URL, so that it could identify itself as the authenticated/authorised resource to access the keys? Yet when I run the program locally above with (Get-AzKeyVaultSecret -VaultName 'auditkeyvault' -Name 'SecretUsername').SecretValueText, my program is still able to retrieve the keys and run!
Can someone please shed some light on why this is happening or if I have misunderstood something?
Many thanks!
(PS. This is all running on a trial instance with sample data, so no real data is compromised at the moment)

The purpose of the keyvault is keep your secrets securely.
Any authorized credentials (through the Keyvault access policies) can access those secrets through the REST api.
To access the secrets, you need:
An access policy in the keyvault that allow you sufficient access
To be authenticated with an authorized account
Get-AzKeyVaultSecret is just another way to retrieve secret.
It work on your computer because your session is still authenticated and your AzureAd account have read access to that keyvault secret.
You can effectively use any Az command without re-authenticating everytime.
Call Get-AzContext to get the current context details.
Connect-AzAccount do save your access tokens and other relevant informations when used automatically at the following location: C:\Users\MAK\.Azure\AzureRmContext.json
If you were to disconnect first Disconnect-AzAccount and trying to get the secret again without re-authenticating, then it would fail.
Note
If you are not comfortable with the Az module saving your tokens on disk, you can disable the default behavior through Disable-AzContextAutosave

Related

Remote powershell sessions can only be established with interactively entered credentials?

I'm trying to automate a powershell script which gathers data from O365. I've got a special limited user setup with the privileges required on O365 and also with local logon allowed on the server so that I can "run-as" that user (which I do for all the scripts below. I have verified different, expected errors when running as other users).
The script works fine interactively when credentials are set like this and the session opened:
$cred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $cred -Authentication Basic –AllowRedirection
However, if I create the credentials file for automation with:
Get-Credential | Export-Clixml -Path C:\batch\${env:USERNAME}_cred.xml
And then access from the script via:
$cred = Import-Clixml -Path C:\batch\${env:USERNAME}_cred.xml
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $cred -Authentication Basic –AllowRedirection
The credential file load appears to succeed. I then get "Access Denied" on the session open, and then of course the rest of the script fails due to the session being null. I'm cutting and pasting the password in all cases (plus have tried many, MANY times including hand typing) so I don't think it's a simple typo issue. Seems more like something I'm fundamentally misunderstanding about powershell. Ultimately I'd like to not just have the credentials automated, but also have it run from task scheduler if there's any special settings above and beyond that I also need.
I don't see anything wrong from your code from PowerShell perspective. I have tested the way you are creating credentials within a company domain and I was able to create new session by importing credential XML file that was created by exporting the credentials the way you did. I then assume it might be MS Exchange related.
I can suggest alternatives for you to try:
# First we need to get the encrypted password:
$TempCred = Get-Credential
# provide credentials to the prompt
# now the encryption to be saved in a file
$TempCred.Password | ConvertFrom-SecureString | Set-Content C:\mypass.txt
This was the encrypted version of your password is saved as a text.
In your automation script you can now do this:
$username = "yourusername"
$password = Get-Content C:\mypass.txt | ConvertTo-SecureString
$cred = New-Object System.Management.Automation.PsCredential($username, $password)
$session = New-PSSession -Credential $cred .....
I am not sure if this works in your case, it worked in my company domain. Once again it worked for me the XML version too. I am just providing alternatives to try if you are not keen to find out as to why the XML way did not work.
I was able to get this working, in my environment at least, by including a call to Import-PSSession:
$Credential = Import-Clixml -Path D:\Modules\O365Credentials.xml
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Credential -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking
Get-Mailbox
Does the account in question have MFA enabled? If so, you might try this.
This script:
Downloads Exchange Online Remote PowerShell Module
Installs Exchange Online PowerShell Module
Connects Exchange Online PowerShell using MFA
Or, you can perform these manually. More information, including a detailed walk-through, is available here:
https://o365reports.com/2019/04/17/connect-exchange-online-using-mfa/

How to use powershell to deploy model into Analysis services without configure it to be http?

Hi I have been trying to deploy model to analysis service by using XMLA script. I used deployment wizard and it worked fine. However when I tried to use command Invoke-ASCmd to deploy model to my analysis server. It got a error of targetinovation which I figured out to be my credential argument error.
The command I used:
$user = "myemail#outlook.com"
$PWord = ConvertTo-SecureString -String "password" -AsPlainText -Force
$Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $user, $PWord
Invoke-ASCmd -InputFile AW.XMLA -Server asazure://southeastasia.asazure.windows.net/azurejenkins -Credential $Credential
But when I not use the -Credential argument it prompt the window email login which work fine. How do I use this command without prompting the user to enter email and password?
Solution to this problem.
I need to create webapp/api in active directory of azure. Copy its application id, tenant id and authentication key which you need to go to keys section in your app.
Go to SQL management server program which connected to your analysis server. Go to security and add app:# in manual entry (in case you don't see your app in the list).
Then use the same command I used in the question but change some components. Change user email to your app id and password to your authentication key. Add tenant id.
Add-AzureAnalysisServicesAccount -Credential $Credential -ServicePrincipal -TenantId $TenantId -RolloutEnvironment "xxxxxx.asazure.windows.net"
Invoke-ASCmd -InputFile xx.XMLA -Server asazure://southeastasia.asazure.windows.net/xxxxxx -Credential $Credential
Hope this script help someone who has the same problem as mine.

Azure Credentials for build server

I'm having authentication headaches with Azure. I have a continuous build server running powershell scripts, and I'm getting messages like:
Your Azure credentials have not been set up or have expired, please run Login-AzureRMAccount to set up your Azure credentials.
I don't like having to login with my account on the build server. I suppose I could create another account just for building, but that will expire as well. Is there a better way of handling this?
You should not use Login-AzureRmAccount in your build/deploy scripts because this is an interactive login, but rather Add-AzureRmAccount instead.
Add-AzureRmAccount requires you create a service principal (application) in Azure AD and use its client id and secret/key to authenticate.
Here is a code snippet that you can use:
$clientID = "<the client id of your AD Application>"
$key = "<the key of your AD Application>"
$SecurePassword = $key | ConvertTo-SecureString -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential `
-argumentlist $clientID, $SecurePassword
Add-AzureRmAccount -Credential $cred -Tenant "xxxx-xxxx-xxxx-xxxx" -ServicePrincipal
To find out you tenant id (single subscription)
$tenant = (Get-AzureRmSubscription).TenantId
Find tenant id (multiple subscriptions)
$tenant = (Get-AzureRmSubscription -SubscriptionName "My Subscription").TenantId

Azure Powershell programmatic login

I am working with Azure (HDInsight in particular) using a personal account (no work/school acocunt).
I would create a script that automatically login on azure and perform some actions.
I found a solutions saving an azure publishsetting json file after logging with our credentials but this settings file contains token that expires.
How can I deal with this issue? What is the best way to accomplish this automatico logon?
Thanks
Roberto
You need to create a service principal. Once you've created the service principal you can assign it permissions on specific resources using Role-Based Access Control. From there your script can login as the service principal without requiring you to login interactively.
The main concern with this approach is securing access to your script since it contains credentials that allow access to your Azure resources.
This article has a good walkthrough:
#First, login as yourself so you can setup the service principal
Login-AzureRmAccount
#Password doesn't have to be *your* password, but the password the script will use
$app = New-AzureRmADApplication –DisplayName "<Your script name>" –HomePage "http://localhost" –IdentifierUris "http://localhost/YourAppName" –Password "<Password>"
#Create the service principal
New-AzureRmADServicePrincipal –ApplicationId $app.ApplicationId
#Assign the Reader role to your new service principal. Other roles listed at
#https://azure.microsoft.com/en-us/documentation/articles/role-based-access-built-in-roles/
New-AzureRmRoleAssignment –RoleDefinitionName Reader –ServicePrincipalName $app.ApplicationId
$pass = ConvertTo-SecureString "<Password>" -AsPlainText –Force
#Servce principal username looks like 92c22f1f-d1d4-46a1-b025-edb47fc03809#something.onmicrosoft.com
#the GUID part is $app.ApplicationId and the domain part is found in the Azure portal
$cred = New-Object -TypeName pscredential –ArgumentList "<Service Principal UserName>", $pass
Login-AzureRmAccount -Credential $cred -ServicePrincipal –TenantId <TenantId>
If it is not a production/shared setup and more a developer setup you can also do, careful, the password is plain text here:
$SubscriptionName = 'MySubscription'
$pswd = 'MyPassword' | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Credential -UserName 'MyEmail#something.com' -Password $pswd
Add-AzureRmAccount -Credential $creds
Set-AzureRmContext -SubscriptionName $SubscriptionName
Login-AzureRmAccount -Credential $creds -SubscriptionName $SubscriptionName
Below information might help you
Create an Automation Account in Azure
Add your credentials in Automation Account as a variable ( e.g
variablename = loginazure) Below Script will automatically login into azure (use Powershell workflow runbook).
$AzureLogin = Get-AutomationPSCredential -Name 'loginazure'
$AzurePortalLogin = New-Object -TypeName System.Management.Automation.PSCredential$AzureLogin
Add-AzureRmAccount -Credential $AzurePortalLogin
Get-AzureRmSubscription -SubscriptionName "your subscription name" | Set-AzureRmContext
use the above script within Inline Script {}
Regards
Thamarai Selvan S
Here are a couple of commands that you can fire up to get started.
$credentials = Get-Credential
Login-AzureRmAcoount -Credential $credentials
$SubscriptionName
Select-AzureRmSubscription -SubscriptionName "The name of your subscription"
Select-AzureRmSubscription -SubscriptionName $SubscriptionName

Powershell Special characters / Automating build - PSRemoting

I am using Teamcity and Powershell to automate few builds. Teamcity runs under lets say account A. There are few executable that needs to be run under account B. Account B has access to DB (windows auth integrated security access) and needs to go and create few tables on install. Passwords for account B is saved in the text file. I am having few issues with this.
1) Account B can have any special characters. I am reading XML file (as this file can be saved in a location where only Account A has access) and new up PSCredential to do the PSRemoting. I am using something like
$secpasswd = ConvertTo-SecureString $passwordFromXMLFile -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("username", $secpasswd)
$PasswordFromXMLFile can have any special characters. How should I deal with this?
2) When I try to run the executable in context of account B (after I somehow escaped and connected using this account), I am seeing the executable runs under anonymous account instead of domainname\B account. Is there some setting that I need to make sure it runs under account B.
My code looks something like - this is not the exact code I am just extracting some relevant portion.
$secpasswd = ConvertTo-SecureString $passwordFromXMLFile -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("B", $secpasswd)
$session = New-PSSession -ComputerName $serverName -Credential $mycreds
I see the session connects fine. to the computer
$scriptblock = {
Start-Process $exeName $arguments - wait
}
invoke-command -session $session -scriptblock $scriptblock
Thoughts? Start-Process runs but it executes on anonymous authority instead of account B.