Error Get-AzureKeyVaultSecret : Unable to retrieve service key for ServicePrincipal account - powershell

Using PowerShell, I can access my Azure KeyVault locally, provide the secret and return stored passwords. I can also do this from a VM I spin up. Additionally, I can access a single password from the KeyVault and use it to impersonate a user on a VSTS build agent, but I cannot access the remaining stored passwords. What gives?
Here's the message I receive:
[error]Get-AzureKeyVaultSecret : Unable to retrieve service key for
ServicePrincipal account
[hiddenaccountname]#[companyaccount].com. Please log in
again to supply the credentials for this service principal. In
PowerShell, execute Login-AzureRMAccount for Azure Resource Manager
cmdlets or Add-AzureAccount for service management cmdlets.
...
CategoryInfo : CloseError: (:) [Get-AzureKeyVaultSecret], KeyNotFoundException
FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.GetAzureKeyVaultSecret
Here's what my code looks like at this section:
Login-AzureRmAccount -Credential $AzureCredential -ServicePrincipal -TenantId [abunchofnumbers]
$Password = (Get-AzureKeyVaultSecret -VaultName "[nameOfVault]" -Name "[nameOfSecret]").SecretValueText
The login accepts the provided credentials and logs me in, but the Get-AzureKeyVaultSecret cmdlet fails, and only on VSTS during a build.
Any ideas, you strange and wonderful devs?

[Resolved - Follow up] My process involved one Powershell script to impersonate a user by using Login-AzureRmAccount and Get-AzureKeyVaultSecret to get the password for a user, then a 2nd Powershell script to log back in to AzureRmAccount to get more passwords from the KeyVault. I swapped the order of these two scripts and problem went away.

Related

MicrosoftTeams cmdlets not working with AccessToken

I am trying to run cmdlets from powershell module MicrosoftTeams (version 2.0.0) in a C# web application. I am using Authorization code flow and code from the answer provided in this post to acquire token: Acquire AAD token using ASP.Net web forms. Note: I had changed resource in the code to graph.windows.net to acquire AAD token. Token is acquired by using AuthenticationContext.AcquireTokenByAuthorizationCodeAsync Method.
Once the token is acquired, I run the following lines to create a powershell instance in C# and to import MicrosoftTeams Module.
PowerShell pshell
InitialSessionState iss;
iss = InitialSessionState.CreateDefault2();
iss.ImportPSModule(new[] { "MicrosoftTeams" });
pshell = PowerShell.Create(iss);
Then to connect with MicrosoftTeams, I run the following code:
var connectCmd = new Command("Connect-MicrosoftTeams");
connectCmd.Parameters.Add("AadAccessToken", AccessToken);
connectCmd.Parameters.Add("AccountId", "xxxxxxx#xxxxxx.onmicrosoft.com");
pshell.Commands.AddCommand(connectCmd);
var result1 = pshell.Invoke();
Code works fine till here.
After this I clear the shell commands and invoke the Get-CsTeamsCallingPolicy cmdlet:
pshell.Commands.Clear();
pshell.Streams.Error.Clear();
pshell.AddScript("Get-CsTeamsCallingPolicy");
var result2 = pshell.Invoke();
After Invoke, I get an exception and this dialog pops up:
Pressing 'Continue' brings back the same dialogue a couple of times.
Exception details from this screen are:
System.Collections.Generic.KeyNotFoundException was unhandled by user code
HResult=-2146232969
Message=The given key was not present in the dictionary.
Source=mscorlib
StackTrace:
at System.Collections.Concurrent.ConcurrentDictionary`2.get_Item(TKey key)
at Microsoft.TeamsCmdlets.Powershell.Connect.Models.AzureSessionProvider.GetAccessToken(String resource, IEnumerable`1 scopes) in D:\a\1\s\src\Microsoft.TeamsCmdlets.PowerShell.Connect\Models\AzureSession.cs:line 80
at Microsoft.TeamsCmdlets.Powershell.Connect.TeamsPowerShellSession.GetAccessToken(String resource, IEnumerable`1 scopes) in D:\a\1\s\src\Microsoft.TeamsCmdlets.PowerShell.Connect\TeamsPowerShellSession.cs:line 82
at Microsoft.TeamsCmdlets.PowerShell.Connect.GetCsInternalAccessToken.ProcessRecord() in D:\a\1\s\src\Microsoft.TeamsCmdlets.PowerShell.Connect\GetCsInternalAccessToken.cs:line 61
at System.Management.Automation.CommandProcessor.ProcessRecord()
After pressing continue for the 3rd time, control goes back to C# code, and I receive the following runtime exception:
Exception calling "GetSteppablePipeline" with "1" argument(s):
"Exception calling "GetRemoteNewCsOnlineSession" with "1" argument(s):
"Run either Connect-MicrosoftTeams or new-csonlinesession before running cmdlets.""
Trying to run this logic from the powershell editor shows similar behavior:
Running the following two lines:
$AccessToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Connect-MicrosoftTeams -AadAccessToken $AccessToken -AccountId 'xxxxxxx#xxxxxx.onmicrosoft.com'
gives this result:
Account Environment Tenant TenantId
------- ----------- ------ --------
xxxxxxx#xxxxxx.onmicrosoft.com AzureCloud xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
I then run Get-Team cmdlet:
Get-Team -User xxxxxxx#xxxxxxx.onmicrosoft.com
which results in this message:
Get-Team : The given key was not present in the dictionary.
At line:1 char:1
+ Get-Team -User xxxxxxx#xxxxxxx.onmicrosoft.com
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-Team], KeyNotFoundException
+ FullyQualifiedErrorId : System.Collections.Generic.KeyNotFoundException,Microsoft.TeamsCmdlets.PowerShell.Custom.GetTeam
Running cmdlet Get-CsTeamsCallingPolicy yields this
Exception calling "GetSteppablePipeline" with "1" argument(s):
"Exception calling "GetRemoteNewCsOnlineSession" with "1" argument(s):
"Run either Connect-MicrosoftTeams or new-csonlinesession before running cmdlets.""
At C:\Program Files\WindowsPowerShell\Modules\MicrosoftTeams\2.0.0\net472\SfBORemotePowershellModule.psm1:11369 char:13
+ $steppablePipeline = $scriptCmd.GetSteppablePipeline($myI ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CmdletInvocationException
If I run Connect-MicrosoftTeams directly without providing access token and accountid, I get the login screens and after login everything works fine but not happening with AadAccessToken.
Same code works fine if connecting to AzureAD module via Connect-AzureAD cmdlet like this both in web application and powershell editor:
Connect-AzureAD -AadAccessToken $AccessToken -AccountId 'xxxxxxx#xxxxxxx.onmicrosoft.com'
If someone has faced and successfully resolved this issue or have some tips on how to resolve this, please help.
I have already tried a lot of things including searching for the specific exception messages and any possible solutions but found nothing that could help in this particular scenario, installed the latest version of MSTeams module, the previous version was old and did not have all the cmdlets that I am looking to work with. I actually installed the preview version of MSTeams module also to see if this issue is fixed in the upcoming release. Uninstalled the deprecated SkypeForBuisnessOnline Connector module, updated windows and so on. If you look at Example 4 in the Microsoft documentation for Connect-MicrosoftTeams, this is what I am following.
There are several problems in your implementation, I will explain them one by one below.
The answer in this post generates an Microsoft Graph access token rather than an AAD Graph access token. But you put it as the -AadAccessToken. You should put it as -MsAccessToken here because Get-Team is calling Microsoft Graph. But we cannot simply use Connect-MicrosoftTeams -MsAccessToken $AadAccessToken-AccountId 'xxxxxxx#xxxxxx.onmicrosoft.com' because the -AadAccessToken is necessary. So for testing purpose, we could set it as the same as -MsAccessToken.
So you can use this cmd to connect to Microsoft Teams:
Connect-MicrosoftTeams -AadAccessToken $AadAccessToken -MsAccessToken $AadAccessToken -AccountId 'xxxxxxx#xxxxxx.onmicrosoft.com'
Then you can run Get-Team -User xxxxxxx#xxxxxxx.onmicrosoft.com successfully.
But there will be another issue here. Get-CsTeamsCallingPolicy is a cmd under Skype For Business Powershell module. We have to run either Connect-MicrosoftTeams (in Teams module) or new-csonlinesession (in SFB module) before running cmdlets.
It means that Get-CsTeamsCallingPolicy is NOT calling Microsoft Graph or AAD Graph.
So the connection method using -AadAccessToken and -MsAccessToken is NOT enough for you to run this command from the SFB module.
I know you don't want to perform login interactively again here for running Powershell cmd. However, since your account has already enabled MFA, static login will no longer apply.
So next I tried to connect with a service principal by following Example 3 here.
Connect-MicrosoftTeams -TenantId c3eac90d-eb4b-48ef-ac86-7acac472d3cd -CertificateThumbprint 9b6ac64bfb8b48dbb53cca75fb33ce2d -applicationid daaaf729-aaff-45ba-8055-a39dd618fe24
Then the error Run either Connect-MicrosoftTeams or new-csonlinesession before running cmdlets. is bypassed BUT I got a new error when I run `Get-CsTeamsCallingPolicy:
Exception calling "GetRemoteNewCsOnlineSession" with "1" argument(s): "Tenant Domain is empty"
But I clearly specified the tenant id when I logged in with the service principal.
So I think that the Microsoft Teams module is not well integrated with SFB module currently.
Your design cannot be implemented in this way.
Hope all my findings are helpful.

Automated way to purge SPO User Profiles

As part of a clean up task, I'm looking for a way to programmatically purge deleted AAD accounts from the User Profile Manager in Sharepoint Online.
I was using the Sharepoint Powershell module (Microsoft.Online.SharePoint.PowerShell) to manually do it, using the Remove-SPOUserProfile commandlet, which worked perfectly if I was using it in an interactive session. But as soon as I tried implementing my script into Azure Automation I found that particular module falls back to Basic Authentication when using a PSCredential object in the Connect-SPOService statement. And Basic Auth is blocked at my Organisation (I can't see them allowing it just for me!)
I found the PnP Module (PnP.PowerShell), which does allow authentication via stored credentials. But it doesn't have an equivalent User Profile Remove cmdlet.
Finally, I tried resorting to pure REST API, and while I can get an existing user profile, I can't get a profile for an account that has been deleted (marked as 'Profiles Missing from Import' in the SPO ProfMngr.aspx page). This is because the SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=#v) API needs to have an exact match (eg i:0#.f|membership|vardhaman#siteurl.onmicrosoft.com), and when an AAD account is deleted the profile username gets DELETED-<GUID> appended to it.
So my questions are:
Am I right or wrong about the sharepoint module and stored creds? (IE, so the module can be used from Azure Automation with a service principal or service account)
Am I right or wrong about the PnP module and it is missing the similar Remove-SPOUserProfile?
With the REST API, how do you search for profiles, especially profiles "missing from import"?
Is there some way to predict what the DELETED-<GUID> will be for a given user? Because I was able to get a user profile if I looked up the full deleted name and supply that to my REST call.
The official documentation on this is light - the old traditional sharepoint APIs aren't being developed any more, in favour of MS Graph, but the Graph Documentation doesn't seem to cover my particular use case.
Any pointers appreciated
Update 1
Thanks #Michael Han_MSFT.
I was using a pre-release/nightly build (0.3.32) but looking at Release documentation so didn't realise remove profile was in there.
I'm still getting problems though:
Connect-PnPOnline `
-url "https://<tenantname>.sharepoint.com" `
-ClientId $ClientId `
-ClientSecret $ClientSecret
# $guest1 = Guest account's email address
$azureEmail = ($guest1 -replace "#", "_") + "#ext##<tenantname>.onmicrosoft.com"
Remove-PnPUserProfile `
-LoginName $azureEmail
Remove-PnPUserProfile : The remote server returned an error: (401) Unauthorized.
At line:11 char:1
+ Remove-PnPUserProfile `
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Remove-PnPUserProfile], WebException
+ FullyQualifiedErrorId : System.Net.WebException,PnP.PowerShell.Commands.UserProfiles.RemoveUserProf
ile
So I tweaked the URL:
Connect-PnPOnline `
-url "https://<tenantname>-admin.sharepoint.com" `
-ClientId $ClientId `
-ClientSecret $ClientSecret
$azureEmail = ($guest1 -replace "#", "_") + "#ext##azurediagovt.onmicrosoft.com"
Remove-PnPUserProfile `
-LoginName $azureEmail
Remove-PnPUserProfile :
At line:11 char:1
+ Remove-PnPUserProfile `
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (:) [Remove-PnPUserProfile], HttpRequestException
+ FullyQualifiedErrorId : EXCEPTION,PnP.PowerShell.Commands.UserProfiles.RemoveUserProfile
So you can see if I go to <tenantname> I get a 401, but if I go to <tenantname>-name the response is simply blank.
I was certain I had given my App the right permissions (Is there some way to review what permissions have been assigned?)
In AppInv.aspx I think had this permissions code (I was following a couple of blogs):
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/Tenant" Right="FullControl"/>
<AppPermissionRequest Scope="http://sharepoint/social/Tenant" Right="FullControl"/>
</AppPermissionRequests>
As a further test, I tried the PnP version of what I was doing in REST (Get-PnpUserProfileProperty) and got
Get-PnPUserProfileProperty : Current user is not a tenant administrator.
At line:1 char:1
+ Get-PnPUserProfileProperty -Connection $pnpctx -Account "scottdu#data ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (:) [Get-PnPUserProfileProperty], ServerException
+ FullyQualifiedErrorId : EXCEPTION,PnP.PowerShell.Commands.UserProfiles.GetUserProfileProperty
Which is strange, because REST would give me a results.d response.
At this stage, I could look at making the App Id a Sharepoint Service Admin (I already have approval to allow Azure Automation to have whatever rights it needs to solve this).
(Update 1a: Made no difference, unless there is a delay between assigning the role and the permissions taking affect).
AAD registered app can be used to connect PnpOnline and delete user profile. Please see my below steps:
(Main refer article: https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread)
Step1:Registering an Azure AD application in the Azure Active Directory tenant that is linked to your Office 365 tenant and grant permission
Use admin account to access https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps. Click “New registration” and create a name of your app.
Go to “API permissions” and click on the "Add a permission" button and grant SharePoint API permission.
Select needed permissions.
Admin need to consent for those permissions, after that in status column will show green.
Step2: Create a self signed certificate and connect with app
Go to this link(https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread#setting-up-an-azure-ad-app-for-app-only-access), copy the scripts and save as “Create-SelfSignedCertificate.ps1”.
Run below command with PowerShell. You will be asked to give a password to encrypt your private key, and both the .PFX file and .CER file will be exported to the current folder.
.\Create-SelfSignedCertificate.ps1 -CommonName "YourCompanyName"
-StartDate 2020-1-09 -EndDate 2022-10-01
Go to AAD app, click on "Certificates & secrets" in the left menu bar. Click on the "Upload certificate" button, select the .CER file you generated and upload it.
Step3: Connect pnp online and perform delete profile
$ Connect-PnPOnline -ClientId <$application client id as copied over
from the AAD app registration above> -CertificatePath '<$path to the
PFX file generated by the PowerShell script above>'
-CertificatePassword (ConvertTo-SecureString -AsPlainText "<$password assigned to the generated certificate pair above>" -Force) -Url
https://<$yourtenant>.sharepoint.com -Tenant
"<$tenantname>.onmicrosoft.com"
$Remove-PnPUserProfile -LoginName $UPN
For REST api way, I found this article noted REST for delete user profile is not implemented.
(https://learn.microsoft.com/en-us/sharepoint/dev/general-development/work-with-user-profiles-in-sharepoint)
You could try the command : Remove-PnPUserProfile
https://pnp.github.io/powershell/cmdlets/Remove-PnPUserProfile.html
You should install the prerelease version of PnP.PowerShell:
https://www.powershellgallery.com/packages/PnP.PowerShell/0.3.8-nightly
Update:
You could try to use tenant administrator account to connect the sharepoint admin site, then run the command Remove-PnPUserProfile. This works for me:

Authenticating with Azure Active Directory on powershell

I am attempting to explore the features of the Azure Active Directory V2 PowerShell Module
I have an Azure Account, and I have set up an Active Directory with multiple users.
My first goal is simple: show me the list of users.
So I type:
Connect-AzureAD
I am presented with a dialog and type in my user account and password. It returns on object of type Microsoft.Open.Azure.AD.CommonLibrary.PSAzureContext.
I then type
Get-AzureADUser
And the error is:
Get-AzureADUser : Error occurred while executing GetUsers
Code: Authentication_Unauthorized
Message: User was not found
HttpStatusCode: Forbidden
I am still able to list the users using the Azure RM Powershell module. The following code works:
Add-AzureRmAccount
Get-AzureRmADUser
What do I do to get Get-AzureADUser to work?
The cmdlet Connect-AzureAD establishes connection to ADD domian, after we login successed a confirmation will display:
PS C:\windows\system32> connect-azuread
Account Environment Tenant
------- ----------- ------
jasontest1#xxxxxx.onmicrosoft.com AzureCloud xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
The connection can be vaildated with this cmdlet Get-AzureADDomain, if the user is connected to AAD domain, where he has management privileges - the information about the domain will be displayed:
PS C:\windows\system32> get-azureaddomain
Name AvailabilityStatus AuthenticationType
---- ------------------ ------------------
hcl.com Managed
msgamestudios.com Managed
foobar.local Managed
multimap.com Managed
skypestaytogether.com Managed
insightsquarterly.com.au Managed
calanit.onmicrosoft.com Federated
msft.ccsctp.net Managed
ruffiangames.com Managed
xn--m1bg0b0byewac1j8b.com Managed
VoicesforInnovation.org Managed
shaanximic.com Managed
www.yunnanmic.com Managed
wsmbela.pss.com Managed
fornax.off Managed
api.staging.yammer.com Managed
codenauts.net Managed
acompli.com Managed
testdomains.co Managed
microsoft.hr Managed
Bayportali.mmdservice.com Managed
contoso.com Managed
api.swrepository.com Managed
Equivio.com Managed
sunshine.am Managed
microsoftaffiliates.com Managed
If user has no admin privileges, we will get the error same as you.
Get-AzureADDomain : Error occurred while executing GetDomains
Code: Authentication_Unauthorized
Message: User was not found
HttpStatusCode: Forbidden
The reason is that, the cmdlet GetAzureADDomian has no tenant specified, so the connection was established to a domian, where user has no admin privileges.
To ensure connection to expected AAD domian, the tenant ID must specified in call to Connect-AzureAD cmdlet.
PS C:\windows\system32> Connect-AzureAD -TenantId
As already answered here, please use:
PS C:\windows\system32> Connect-AzureAD -TenantId {YOUR_TENANT_ID}
Example:
PS C:\windows\system32> Connect-AzureAD -TenantId ce1af0ab-ae35-4f60-8f2d-944444444444
It's a common mistake to use TenantId we get when executing Connect-AzureAd, like following:
But use the TenantId from Azure Portal --> Azure Active Directory --> Properties --> Directory Id.
The Directory Id = TenantId.

ArgumentNullException - Get-AzureService

I'm trying to use the Windows Azure PowerShell module to manage a subscription.
I have downloaded my certificate (the .publishsettings file) and imported it with Import-AzurePublishSettingsFile and then I've selected my subscription with Select-AzureSubscription neither of which gave errors.
I've also set my subscription using Set-AzureSubscription -SubscriptionName "Blah"
Still, I get a
Get-AzureService : Value cannot be null.
Parameter name: subscriptionId
when running Get-AzureService
I've read getting started guides and various documentation but I can't work out what I'm doing wrong. Which in my mind, makes this a UX problem that Microsoft should address.
Update
I got a bit further, I used
Set-AzureSubscription -SubscriptionName "Blah" -SubscriptionId 0123
which changed the error from Get-AzureService to:
Get-AzureService : Value cannot be null.
Parameter name: managementCertificate
But now I cannot set my certificate since the argument wants an X509Certificate type.
There is a better way to authenticate when using the Azure Powershell cmdlets --- Add-AzureAccount. This will prompt you for your login credentials instead of using the service management certificate.
You may still run into some issues because Azure powershell caches your subscriptions in XML files in %appdata%\Windows Azure Powershell.
I would recommend:
Close the Azure Powershell window
Delete the XML files in %appdata%\Windows Azure Powershell.
Open Azure Powershell and run Add-AzureAccount.
This should ensure that you have the correct subscriptions configured.
I hope this might help you-
Add-AzureAccount
Get-AzurePublishSettingsFile
Import-AzurePublishSettingsFile filenamewithpath
filenamewithpath is the publishsetting file with path saved on your pc

Security Token Validation Error with JWT token -- HDInsight

I created a new storage account and provisioned an HDInsight cluster on Windows Azure. However, when I try to run any command on Windows Azure Powershell related to the cluster, I get the following error:
PS C:\> Get-AzureHDInsightCluster -Name $clusterName
Get-AzureHDInsightCluster : Request failed with code:Unauthorized
Content:<Error xmlns="http://schemas.microsoft.com/windowsazure"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Code>AuthenticationFailed</Code><Message>A security token
validation error occured for the received JWT token.</Message></Error>
At line:1 char:1
+ Get-AzureHDInsightCluster -Name $clusterName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-AzureHDInsightCluster], HttpLayerException
+ FullyQualifiedErrorId : Microsoft.Hadoop.Client.HttpLayerException,Microsoft.WindowsAzure.Management.HDInsight.C
mdlet.PSCmdlets.GetAzureHDInsightClusterCmdlet
I'm completely new to HDInsight, but I ran a script before this command to add the Azure account, create variables for the subscription name, storage account name, and container name from this page.
Do you know what this error means? And how to fix it? I'm trying to run a script with Pig commands after following this website.
It looks like the PowerShell environment is not properly setup. You should add your Azure subscription first with GetAzureSubscription. See: http://www.windowsazure.com/en-us/documentation/articles/install-configure-powershell/#Connect