Office 365 Graph API 400 Bad Request - powershell

I'm using the following module
https://psmsgraph.readthedocs.io/en/latest/
When attempting to pass in the following $AuthCode
#{AuthCodeCredential=System.Management.Automation.PSCredential; ResultURL=https://localhost/?code=AQABAAIAAADX8GCi6Js6SK82TsD2Pb7rsheCqji8MFS19OlJ8dFqrxjfk9
TTz9sPIyErZzaOD45niqpvZz5vnViz6tAU2BoKGQfX1-tW781HweG4jHoznO09NQpJDTiDl8i8yv6O_xT7RqUzitK59gssyyOPjc-4k5BEVa0hPJpXJCFHwUokCRzRBo4tS6Frv5XbxQkR8huM2Y1pK8o6Mq
PxKMPxGSfcrzS7vRjh-99qeD-DsXbc1eGGh1AQsWfTl1wqUlUcLJnMstF6ePnxIuM2XpRbFo7kYZ-5md7xrSD4Fw9L02NQjA-7TuOFM_4xXeM6gKL9SG8iW9Hxow0aNUm8ZtsLPBvAPJWJVrzhglVqz9pmmV
c9nAD1ujn2au2J9OlT_zwlXsJyHb3Gf4GqjQraYm91dV_7HzRj92LlCwjAwPypXFAhllYXPflUCgUtUYvvJfxSVS5Nc1meRUXlk-qedwv1RpbbT-pNHMPUuJmbEEM-sxAY1Oxg9GC93oH3S-rSMF5kZydr40
UOFwlnRIp10Vti6VA3IAA&session_state=25644a38-645f-4c8b-a6d9-be01838fa932; Application=Guid: 91ba65d8-2aa9-4771-9f9c-d05756da6931 Name: PowerShell Module; Au
thCodeBaseURL=https://login.microsoftonline.com/<mytenantid>/oauth2/authorize; Response=System.Collections.Hashtable; Issued=19/06/2
018 6:46:38 PM; Success=System.Management.Automation.PSScriptProperty; Expires=System.Management.Automation.PSScriptProperty; IsExpired=System.Management.Au
tomation.PSScriptProperty}
I get the following error:
Get-GraphOauthAccessToken : The remote server returned an error: (400) Bad Request.
At line:24 char:21
+ ... cessToken = Get-GraphOauthAccessToken -BaseURL 'https://login.microso ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WebException
+ FullyQualifiedErrorId : System.Net.WebException,Get-GraphOauthAccessToken
Here is the full code:
Import-Module -name 'PSMSGraph'
#In the credential prompt, provide your application's Client ID as the username and Client Secret as the password
$ClientCredential = Get-Credential
$GraphAppParams = #{
Name = 'PowerShell Module'
ClientCredential = $ClientCredential
RedirectUri = 'https://localhost/'
Tenant = '<mytenantid>'
}
$GraphApp = New-GraphApplication #GraphAppParams
Write-Host "Application"
Write-Host $GraphApp
Write-Host "-------------"
# This will prompt you to log in with your O365/Azure credentials.
# This is required at least once to authorize the application to act on behalf of your account
# The username and password is not passed back to or stored by PowerShell.
$AuthCode = Get-GraphOauthAuthorizationCode -BaseURL 'https://login.microsoftonline.com/<mytenantid>/oauth2/authorize' -Application $GraphApp
Write-Host 'Authorization Code'
Write-Host $AuthCode
Write-Host '----------------'
# see the following help for what resource to use.
# get-help Get-GraphOauthAccessToken -Parameter Resource
$GraphAccessToken = Get-GraphOauthAccessToken -BaseURL 'https://login.microsoftonline.com/<mytenantid>/oauth2/token' -AuthenticationCode $AuthCode -Resource "https://graph.microsoft.com"
$GraphAccessToken | Export-GraphOAuthAccessToken -Path 'c:\Temp\AccessToken.XML'
My problem is that there are numerous URLS that I could connect to and conflicting ideas of what my tenant id is so I'm not sure if I have either correct. This is for a company not a single user.
Edit - at the end of the day, all I want to be able to do is pull reports that are visible in https://portal.office.com/adminportal/home#/reportsUsage

Related

PowerShell authenticate and read google calendar events

Using PowerShell, I am trying to authenticate to Google calendar using a service account, and read events. With the help of stack overflow, I was able to install and import the required packages in my PowerShell session.
Newtonsoft.Json
Google.Apis.Core
Google.Apis
Google.Apis.Auth
Google.Apis.Calendar.v3
I then tried to read Google calendar events
# Set the credentials and calendar ID
$credentials = Get-Content "C:\Users\Windows\Desktop\powershell-376318-70973daa61d9.json" | ConvertFrom-Json
$calendarId = "primary"
# Build the calendar service
$service = New-Object Google.Apis.Calendar.v3.CalendarService
$service.Credentials = New-Object Google.Apis.Auth.OAuth2.GoogleCredential($credentials)
# Get the current time
$now = Get-Date
# Get the events for the next hour
$events = $service.Events.List($calendarId)
$events.TimeMin = $now
$events.TimeMax = $now.AddHours(1)
$events.SingleEvents = $true
$events.OrderBy = "startTime"
$events = $events.Execute()
# Print the events
foreach ($event in $events.Items) {
Write-Host "Event: $($event.Summary)"
Write-Host "Start Time: $($event.Start.DateTime)"
Write-Host "End Time: $($event.End.DateTime)"
Write-Host ""
}
But I get this error
New-Object : A constructor was not found. Cannot find an appropriate constructor for type
Google.Apis.Auth.OAuth2.GoogleCredential.
At line:7 char:24
+ ... edentials = New-Object Google.Apis.Auth.OAuth2.GoogleCredential($cred ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : CannotFindAppropriateCtor,Microsoft.PowerShell.Commands.NewObjectCommand
Exception calling "Execute" with "0" argument(s): "The service calendar has thrown an exception. HttpStatusCode is
Forbidden. The request is missing a valid API key."
At line:18 char:1
+ $events = $events.Execute()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : GoogleApiException
Obviously, there's an issue with "Google.Apis.Auth.OAuth2.GoogleCredential". I'm not sure it's even possible to authenticate and read events using a service account, but right now I don't know how to proceed with this.
I want to avoid using OAuth2 client id and client secret for authentication if possible, because I've had issues in the past with token expiries, whereas a service account doesn't expire.
The error message tells you exactly what the problem is:
Cannot find an appropriate constructor for type Google.Apis.Auth.OAuth2.GoogleCredential.
Having a look at the docs shows: There really is no constructor like GoogleCredential(string). But you can use different static methods to create a GoogleCredential object. E. g. FromJson(String):
# Set the credentials and calendar ID
$credentials = Get-Content -Raw -Path "C:\Users\Windows\Desktop\powershell-376318-70973daa61d9.json"
# Build the calendar service
$service = New-Object Google.Apis.Calendar.v3.CalendarService
$service.Credentials = [Google.Apis.Auth.OAuth2.GoogleCredential]::FromJson($credentials)
You can also pass the file directly to GoogleCredential via FromFile:
# Build the calendar service
$service = New-Object Google.Apis.Calendar.v3.CalendarService
$service.Credentials = [Google.Apis.Auth.OAuth2.GoogleCredential]::FromFile('C:\Users\Windows\Desktop\powershell-376318-70973daa61d9.json')

Sharepoint Timezone Change Powershell

I need to change the timezone for all sites within sharepoint. I run this code that i found online.
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
#function to change Timezone regional settings of a SharePoint Online site
Function Set-SPOnlineTimeZone([String]$SiteURL,[String]$TimezoneName,[PSCredential]$Cred)
{
Try
{
#Setup Credentials to connect
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)
#Set up the context
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Ctx.Credentials = $credentials
#Get the Root web from given URL
$Web = $Ctx.web
$Ctx.Load($Web)
#Get the Time zone to update
$Timezones = $Web.RegionalSettings.TimeZones
$Ctx.Load($Timezones)
$Ctx.ExecuteQuery()
$NewTimezone = $Timezones | Where {$_.Description -eq $TimezoneName}
#Update the timezone of the site
$Web.RegionalSettings.TimeZone = $NewTimezone
$Web.Update()
$Ctx.ExecuteQuery()
Write-host -f Green "Timezone has been updated for "$Web.Url
#Get all subsites of the web
$Ctx.Load($Web.Webs)
$Ctx.executeQuery()
#Iterate through each subsites and call the function recursively
Foreach ($Subweb in $Web.Webs)
{
#Call the function to set Timezone for the web
Set-SPOnlineTimeZone -SiteURL $Subweb.URL -TimezoneName $TimezoneName -Cred $AdminCredentials
}
}
Catch [System.Exception]
{
Write-Host -f Red $_.Exception.Message
}
}
#Config parameters for SharePoint Online Admin Center and Timezone description
$AdminSiteURL = "https://crescent-admin.sharepoint.com/"
$TimezoneName ="(UTC+04:00) Abu Dhabi, Muscat"
#Get credentials to connect to SharePoint Online Admin Center
$AdminCredentials = Get-Credential
#Connect to SharePoint Online Tenant Admin
Connect-SPOService -URL $AdminSiteURL -Credential $AdminCredentials
#Get all Site Collections
$SitesCollection = Get-SPOSite -Limit ALL
#Iterate through each site collection
ForEach($Site in $SitesCollection)
{
Write-host -f Yellow "Setting Timezone for Site Collection:"$Site.URL
#Call the function to set Timezone for the site
Set-SPOnlineTimeZone -SiteURL $Site.URL -TimezoneName $TimezoneName -cred $AdminCredentials
}
#Read more: https://www.sharepointdiary.com/2017/06/sharepoint-online-change-time-zone-using-powershell.html#ixzz68Mt3Xrom
I gett the following error
At line:10 char:16
+ ... edentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
Exception calling "ExecuteQuery" with "0" argument(s): "The sign-in name or password does not match one in the Microsoft account system."
At line:19 char:1
+ $Ctx.ExecuteQuery()
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IdcrlException
An error occurred while enumerating through a collection: The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested..
At line:22 char:1
+ $Timezones | Select ID, Description
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Share...lient.TimeZone]:<GetEnumerator>d__0) [], RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
The account that i'm using is admin account for office365 (globle admin)however it has 2fa. I'm assumuing there needs to be some other method for this but no idea. Any help will be appreciated
According the Microsofts documentation on the Connect-SP* command, if your account is using 2FA you'll want to omit the credentials from your connect statement and let it prompt you for credentials manually.
https://learn.microsoft.com/en-us/powershell/module/sharepoint-online/connect-sposervice?view=sharepoint-ps
See example 3
If you are using MFA in your environment, I would suggest you use sharepoint pnp powesrhsell.
You could refer to the below articles for more:
https://techcommunity.microsoft.com/t5/SharePoint/Using-SharePoint-Client-Side-Object-Model-with-PowerShell-and/m-p/92734
https://www.c-sharpcorner.com/blogs/using-csom-to-connect-to-a-sharepoint-site-with-multi-factor-authentication-enabled

Retrieved token gets WordWrapped in PowerShell

Trying to retrieve a token via PowerShell to PowerBI.
I manage to recieve the token but I guess it's being WordWrapped because using the variable gives me an error. If I copy the string and removes row cuts It works.
How can I prevent the token from being WordWrapped (It prints the value in rows not in one line)?
My Code - Version 1
#Username for login in Azure
$username = "name#domain.com"
#Password for login in Azure encrypted
$password = "0100000011100000000020000000000000..."
#Takes the username and convert the encrypted password and stores them in $cred variable
$cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,($password | ConvertTo-SecureString)
#Uses the credentials to login to PowerBI
Login-PowerBI -Credential $cred
#Retrieves the login token that was created at login
$PowerBIToken = Get-PowerBIAccessToken
#The token is stored in $PowerBIToken.values but has "Bearer MYTOKEN" format, remove "Bearer "
$PowerBIToken = $PowerBIToken.values -replace 'Bearer '
#Adds " before and after the token since it is required I think to use the token
$PowerBIToken = "`"$PowerBIToken`""
#Writes to host the information formatted to verify If it is correct
Write-Host $PowerBIToken
#Disconnect the access to PowerBI
Disconnect-PowerBIServiceAccount
Clear-PBITableRows -authToken $PowerBIToken -DataSetId "11111111-7777-ffff-6666-dddddddddddd" -TableName "RealTimeData"
My Code - Version 2
#Username for login in Azure
$username = "name#domain.com"
#Password for login in Azure encrypted
$password = "0100000011100000000020000000000000..."
#Takes the username and convert the encrypted password and stores them in $cred variable
$cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,($password | ConvertTo-SecureString)
#Uses the credentials to login to PowerBI
Login-PowerBI -Credential $cred
#Retrieves the login token that was created at login
$PowerBIToken = Get-PowerBIAccessToken
#Writes to host the information formatted to verify If it is correct
Write-Host $PowerBIToken.values
#Disconnect the access to PowerBI
Disconnect-PowerBIServiceAccount
Clear-PBITableRows -authToken $PowerBIToken.values -DataSetId "11111111-7777-ffff-6666-dddddddddddd" -TableName "RealTimeData"
Both versions above gives this error
Invoke-PBIRequest : The remote server returned an error: (403) Forbidden. - ''
At C:\Program Files\WindowsPowerShell\Modules\PowerBIPS\PowerBIPS.psm1:1410 char:5
+ Invoke-PBIRequest -authToken $authToken -method Delete -resource ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WebException
+ FullyQualifiedErrorId : System.Net.WebException,Invoke-PBIRequest
Without adding " to the token gives this error
Clear-PBITableRows : Cannot process argument transformation on parameter 'authToken'. Cannot convert value to type System.String.
At C:\Temp\PowerBI\PowerBI-AccessToken.ps1:12 char:35
+ Clear-PBITableRows -authToken $PowerBIToken -DataSetId "f866e111- ...
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Clear-PBITableRows], ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Clear-PBITableRows
But if I copy the token string printed on the Write-Host command and put it instead of the $PowerBIToken variable in the final row Clear-PBITableRows... and removes all row cuts (wordWrappes) that command works as intended.

ExecuteCrmOrganizationRequest fails with PublishThemeRequest

I'm trying to write a powershell script to publish a theme in my on-premise installation of Dynamics CRM.
According to this page it should be really straight forward, I create an object of type PublishThemeRequest which derives from OrganizationRequest and call the method ExecuteCrmOrganizationRequest.
This is the code I'm running:
Import-Module Microsoft.Xrm.Data.Powershell
Add-PSSnapin Microsoft.Xrm.Tooling.Connector
$orgName = "<my organization name>";
$serverUrl = "http://server_url";
$Cred = Get-Credential -UserName "<my username>" -Message "Please Enter admin credentials for CRM"
$conn = Get-CrmConnection -Credential $Cred -OrganizationName $orgName -ServerUrl $serverUrl
$req = New-Object Microsoft.Crm.Sdk.Messages.PublishThemeRequest
$req.Target = New-CrmEntityReference -EntityLogicalName "theme" -Id "DB80D57A-6410-4D11-B784-0093122802AC"
$result = [Microsoft.Crm.Sdk.Messages.PublishThemeResponse]$conn.ExecuteCrmOrganizationRequest($req, $null)
This is what I get when I execute the code above:
Cannot convert argument "req", with value: "Microsoft.Crm.Sdk.Messages.PublishThemeRequest", for "ExecuteCrmOrganizationRequest" to type
"Microsoft.Xrm.Sdk.OrganizationRequest": "Cannot convert the "Microsoft.Crm.Sdk.Messages.PublishThemeRequest" value of type
"Microsoft.Crm.Sdk.Messages.PublishThemeRequest" to type "Microsoft.Xrm.Sdk.OrganizationRequest"."
At C:\Users\xxxxxxxxxx\Desktop\PublishTheme.ps1:21 char:1
+ $result = [Microsoft.Crm.Sdk.Messages.PublishThemeResponse]$conn.Exec ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
I have been reading the documentation and other websites for a couple of hours now but seem to have hit a wall.
Any ideas of what my problem might be?

Binding to a different active directory ldap instance in Powershell

I am trying to connect to some independent LDAP stores (ADAM - Active Directory Application Mode) using a specific set of credentials to bind with, but having trouble working out the best way to do this. Here is an example which I had hoped would work:
$ldapHost = New-Object System.DirectoryServices.DirectoryEntry("LDAP://{serverip}:{port}/dc=acme,dc=com","cn=myuser,dc=acme,dc=com","myPassw0rd")
$ldapQuery = New-Object System.DirectoryServices.DirectorySearcher
$ldapQuery.SearchRoot = $ldapHost
$ldapQuery.Filter = "(objectclass=*)"
$ldapQuery.SearchScope = "Base"
$ldapQuery.FindAll()
This will get me:
Exception calling "FindAll" with "0" argument(s): "A local error has occurred.
"
At line:1 char:19
+ $ldapQuery.FindAll <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I also tried:
$ldapHost = New-Object System.DirectoryServices.DirectoryEntry("LDAP://{myip}:{port}/dc=acme,dc=com")
$ldapHost.Username = "cn=myuser,dc=acme,dc=com"
which results:
The following exception occurred while retrieving member "Username": "The specified directory service attribute or valu
e does not exist.
"
At line:1 char:11
+ $ldapHost. <<<< Username = "cn=myuser,DC=acme,dc=com"
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
I've tried a few variations with filter etc. Most of the documentation I can find on this just assumes that I'm connecting to ldap from within the same directory/am connecting with the correct user for the query.
If you're familiar with Python's ldap module, this is how I do it in that:
import ldap
ld = ldap.initialize("ldap://{myip}:{port}")
ld.bind_s("cn=myuser,dc=acme,dc=com","Passw0rd")
ld.search_s("dc=acme,dc=com",ldap.SCOPE_BASE,"objectclass=*")
Any pointers on how to approach this? I can definitely connect via the various LDAP clients out there. I might need to explicitly specify authentication, but I'm not sure because there is so little information on querying from outside the domain.
You can try this...I use it to connect to an OpenLDAP instance and it works well. Works against AD also so it should fit your needs. You'll need to update the $basedn variable and the host/username ones.
$hostname = ''
$username = ''
$Null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
#Connects to LDAP
$LDAPConnect = New-Object System.DirectoryServices.Protocols.LdapConnection "$HostName"
#Set session options (SSL + LDAP V3)
$LDAPConnect.SessionOptions.SecureSocketLayer = $true
$LDAPConnect.SessionOptions.ProtocolVersion = 3
# Pick Authentication type:
# Anonymous, Basic, Digest, DPA (Distributed Password Authentication),
# External, Kerberos, Msn, Negotiate, Ntlm, Sicily
$LDAPConnect.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
# Gets username and password.
$credentials = new-object "System.Net.NetworkCredential" -ArgumentList $UserName,(Read-Host "Password" -AsSecureString)
# Bind with the network credentials. Depending on the type of server,
# the username will take different forms.
Try {
$ErrorActionPreference = 'Stop'
$LDAPConnect.Bind($credentials)
$ErrorActionPreference = 'Continue'
}
Catch {
Throw "Error binding to ldap - $($_.Exception.Message)"
}
Write-Verbose "Successfully bound to LDAP!" -Verbose
$basedn = "OU=Users and Groups,DC=TEST,DC=NET"
$scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
#Null returns all available attributes
$attrlist = $null
$filter = "(objectClass=*)"
$ModelQuery = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $basedn,$filter,$scope,$attrlist
#$ModelRequest is a System.DirectoryServices.Protocols.SearchResponse
Try {
$ErrorActionPreference = 'Stop'
$ModelRequest = $LDAPConnect.SendRequest($ModelQuery)
$ErrorActionPreference = 'Continue'
}
Catch {
Throw "Problem looking up model account - $($_.Exception.Message)"
}
$ModelRequest
Credit for most of this goes here..
http://mikemstech.blogspot.com/2013/03/searching-non-microsoft-ldap.html
This worked for me. Only use this for testing purposes since password is not secured at all.
Add-Type -AssemblyName System.DirectoryServices.Protocols
$server = test.com
$username = "CN=username,OU=users,DC=test,DC=com"
$password = "userpassword"
$Credentials = new-object System.Net.NetworkCredential($username, $password)
$LdapConnection = New-Object System.DirectoryServices.Protocols.LdapConnection $server
# Basic auth, cleartext password using port 389
$LdapConnection.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
$LdapConnection.Bind($Credentials)
$LdapConnection.Dispose()