Powershell to Federate Office 365 domain - powershell

I am attempting to Federate office 365 and use it as a Service Provider, but I can't seem to change the SigningCertificate information in the FederationSettings. I am trying to use this powershell command:
Set-MsolDomainAuthentication -IssuerUri $entity -LogOffUri $logout -PassiveLogOnUri $url -PreferredAuthenticationProtocol SAMLP -SigningCertificate $cert
This command only works if I remove -SigningCertificate $cert. And I can't change the SigningCertificate once I've set up federation either. Also I am not using ADFS. I really appreciate any help anyone can provide for why this won't work.
Error Message:
Set-MsolDomainAuthentication : Invalid value for parameter. Parameter Name:
.
At line:1 char:1
+ Set-MsolDomainAuthentication -IssuerUri $entity -LogOffUri $logout
-PassiveLogOn ...
CategoryInfo:OperationStopped: (:) [Set-MsolDomainAuthenticaion], MicrosoftOnlineException + FullyQualifiedErrorId : Microsoft.Online.Administration.Automation.ProprtyValidationException,Microsoft.Online.Administration.Automation.SetDomainAuthentication

In case anyone else finds themselves in this situation the answer is to make sure all the spaces are removed from the certificate string.

Related

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:

PowerShell Script with Clixml and Taskscheduler

Here's the problem:
When I run my PowerShell script manually, everything works fine. But it doesn't work via task scheduling.
The reason is probably my stored credentials for Active Directory and Mobile Device Management.I stored them with "Export-Clixml" but the xml files cant be read when i open the script with task scheduler. Without it works perfect.
The task scheduler is executed with the same user who saved the credentials in the XML before.
I hope you understand what I mean.
Edit 1:
I've tried the Solution from "Bender the Greatest".
Unfortunately it still doesn't work. I exported my credentials with "Export-Clixml" and import them with "Import-Clixml".
It doesn't matter if I generate them manually via my admin account or via the system account (with psexec). If I then execute it via the corresponding account with which the credentials were generated, it still doesn't work.
But it seems to find the credentials (I saw that in the log). But The import doesn't seem to work.
If I manually include the credentials in the script it works fine, but I would have liked to store them encrypted.
[Lizenzauswertung] Überprüfe Zugangsdaten
[Lizenzauswertung] MDM Zugangsdaten gefunden - Credentials found
[Lizenzauswertung] MDM AccessToken gefunden - Credentials found
[Lizenzauswertung] AD Zugangsdaten gefunden - Credentials found
Here im trying to decrypt the Password from the XML File:
It is not possible to call a method for an expression that has NULL.(Translated)
In C:\Scripts\ADAuswertung\AD_Auswertung_GKZ.ps1:171 Zeichen:1
+ $PSCPW = $CredsMDM.GetNetworkCredential().Password
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
It is not possible to call a method for an expression that has NULL.(Translated)
In C:\Scripts\ADAuswertung\AD_Auswertung_GKZ.ps1:171 Zeichen:1
+ $PSCPW = $CredsMDM.GetNetworkCredential().Password
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
This is a replacement for the username to have the right spelling for a webrequest:
It is not possible to call a method for an expression that has NULL.(Translated)
In C:\Scripts\ADAuswertung\AD_Auswertung_GKZ.ps1:172 Zeichen:1
+ $RPSCUser = $PSCUser.Replace("INTRA","intra.lan")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
It is not possible to call a method for an expression that has NULL.(Translated)
In C:\Scripts\ADAuswertung\AD_Auswertung_GKZ.ps1:172 Zeichen:1
+ $RPSCUser = $PSCUser.Replace("INTRA","intra.lan")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Here you can see the errors that occur when importing the MDM credentials. The reason for the errors is described above. I edit the credentials after importing them before they are passed to the script.
The MDM Access Token is not encrypted and can be read.
The AD credentials are not processed beforehand, but passed directly to the AD commands. Therefore, no error is displayed here. Since it does not finish the AD report, which I saw in the log, these are not imported either.
Here is a little Codesnippet of the Import/Export. Just got the code here for the MDM credentials. The other queries are the same.
If (Test-Path $CredentialsMDM){
$CredsMDM = Import-Clixml -Path $CredentialsMDM
Write-Host "[Lizenzauswertung] MDM Zugangsdaten gefunden" -ForegroundColor Green
} else {
Write-Host "[Lizenzauswertung] MDM Zugangsdaten nicht gefunden" -ForegroundColor Yellow
Get-Credential -Message "Zugangsdaten für MDM / Airwatch" | export-clixml -path $CredentialsMDM
$CredsMDM = Import-Clixml -Path $CredentialsMDM
}
Write-Host "[Lizenzauswertung] Zugangsdaten überprüft" -ForegroundColor Green
$PSCUser = $CredsMDM.UserName
$PSCPW = $CredsMDM.GetNetworkCredential().Password
$RPSCUser = $PSCUser.Replace("INTRA","intra.lan")
$AccessToken = $CredsMDMAT.accesstoken
$Auth = $CredsAD
(I am sorry for the spaghetti code.)
You need to provide the error you're getting but I'm going to take a shot in the dark here. I'm guessing you're either exporting secretAD.xml as one user and running your script as another user from Task Scheduler, or exporting secretAD.xml on another computer/server. By default, credentials are able to be decrypted only by the user that built it on that specific server. This is how Windows' Data Protection API (DPAPI) works.
The easy way to fix this is to generate secretAD.xml as the user you intend to run the script as. You can specify your own key for decryption, which can be used by any user on any box, but you will need to figure out a method of secure delivery of the decryption key to the runtime. If you want/need to run the script as NT Authority\SYSTEM, I recommend using psexec to open an interactive SYSTEM Powershell session, generate the credential, and then use Export-CliXml to serialize the credential to secretAD.xml.
Note that the default DPAPI key gets changed when an account password is rotated, so you'll need to remember to rebuild the credential when that happens.
I've found the solution to my problem.
The reason was that the path to the credentials was ".\secretMDM.xml".
The task scheduler executes the script at a different location.
Therefore I had to specify the whole path of the file.
Thanks for the help anyway.
Edit:
Out of interest I created the "Credential" - XML files with the Task Scheduler. Have a look where it stores the files ;)

Add-AzureRmServiceFabricNodeType -> 'accountName' cannot be null

I'm trying to use the 'Add-AzureRmServiceFabricNodeType' command to add a new nodeType to an existing service fabric cluster. This is my command:
Add-AzureRmServiceFabricNodeType -ResourceGroupName "$ResourceGroupName$" -Name "$ClusterName$" -NodeType "$TypeName$" -VmSku "Standard_H8" -Capacity 3 -VmUserName "$UserName$" -VmPassword $pwd
Having already logged in and set the subscription using 'Login-AzureRmAccount' and 'Set-AzureRmContext'
The call runs for ~1hr and then returns the following error:
WARNING: Rolling back the changes to the cluster
Add-AzureRmServiceFabricNodeType : 'accountName' cannot be null.
At line:1 char:1
+ Add-AzureRmServiceFabricNodeType -ResourceGroupName "%ResourceGroupName% ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Add-AzureRmServiceFabricNodeType], ValidationException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.ServiceFabric.Commands.AddAzureRmServiceFabricNodeType
I have successfully added a nodetype to this cluster in the past, but then i didn't set the -VmSku option. As well as that difference, the subscription has since been upgraded from a pay as you go sub to an Enterprise Agreement. Based on the error received I guess it might have something to do with that, but i can't seem to find what exactly.
Any ideas?
I will assume you used the secure encoding to provide the password
$password = ConvertTo-SecureString -String 'Password$123456' -AsPlainText -Force
I also suggest you add the -Tier as part of your command, because as part of provisioning process it requires the sku, tier and capacity. If you not provide one it will use the default, and the sku might not be compatible with the default tier or your account availability.
You can also check in Azure if the VMSS are created once you run the command.
If you want to investigate further, I would recommend reading the source code for the command operation executed for adding node types.

Windows Azure Powershell command Get-AzureWebsite results in error String was not recognized as a valid Boolean

I'm trying to manage some of my Azure Services via Powershell, using http://windowsazurewebsitescheatsheet.info/#powershell as a starting point. I was able to download and import my subscription settings just fine (Import-AzurePublishSettingsFile and Select-AzureSubscription)
After choosing my subscription, I'm attempting to run the Get-AzureWebsite command to view all of my websites, and continue onward from there. When I run that command, however, I get the following:
Import-AzurePublishSettingsFile "C:\AzureCredentials.publishsettings"
Select-AzureSubscription "My Azure Subscription"
Get-AzureWebsite
Get-AzureWebsite : String was not recognized as a valid Boolean.
At line:1 char:1
+ Get-AzureWebsite
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Get-AzureWebsite], FormatException
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Commands.Websites.GetAzureWebsiteCommand
Anyone have similar issues? I get this error if I run the command as stated above, or if I specify the name of my website. TIA for any help!
This might be related to a known issue enumerating a sites with SSL bindings
There was new release of Windows Azure Powershell released on 02/12. Please try with the new version.
Please run the command again with -Debug turned on, this will display request and response messages and make it easier to pinpoint the issue.

How to deploy to Azure with powershell?

I want to deploy my application to azure with powershell. So far I have created a certificate in the localmachine store, I'm not going to run the deploy script as me, uploaded the script to azure. The next step is to get access to the service on azure in powershell but there it fails. The script I have so far is:
$cert = Get-Item Cert:\LocalMachine\deploy\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Set-AzureSubscription -SubscriptionName $subscriptionName -SubscriptionId $subscriptionId -Certificate $cert
Select-AzureSubscription $subscriptionName
$service = Get-AzureService $azureId
It fails on the last row with the following message:
Get-AzureService : Communication could not be established. This could be due to an invalid subscription ID. Note that subscription IDs are case sensitive.
At F:\DeployTest\deploy.ps1:9 char:12
+ $service = Get-AzureService $azureId
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-AzureService], Exception
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Management.ServiceManagement.HostedServices.GetAzureServiceCommand
Get-AzureService : HTTP Status Code: AuthenticationFailed - HTTP Error Message: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
Operation ID:
At F:\DeployTest\deploy.ps1:9 char:12
+ $service = Get-AzureService $azureId
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Get-AzureService], CommunicationException
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Management.ServiceManagement.HostedServices.GetAzureServiceCommand
I really don't know what the problem is, the certificate I'm trying to use is uploaded so it feels like there is something fundamental I've missed.
Update: I did get it to work after downloading the .publishsettings-file and importing that instead of trying to use Set-AzureSubscription. I'm still a little bit confused though, shouldn't it be possible to use the method I tried above?
I finally found the problem, and of course it was a user problem. First when I was in the azure portal I didn't find where to upload the certificate, so I uploaded it to first place I found mentioning certificates. What I did found out was that this area was the wrong one, I uploaded the certificate to the certificate area under the cloud service I wanted to administrate, which is the wrong place.
The correct place to upload the certificate to is under settings in the admin portal of azure. So the code above works if the certificate is uploaded to the correct location.