Business Central Admin API - can't delete/stop sessions - azure-active-directory

Using powershell, I can connect to the business central admin API with a registered app on Azure as per the following setup Business Central Admin API. I am able to retrieve the list of environments and also the list of sessions etc. One of the key requirements when using the API, is to be able to delete/stop sessions. However when I attempt to do this, it is giving me an error which is basically a null response for the delete method.
# This sample authenticates to Azure Active Directory (AAD) an obtains an access token.
# The access token can be used for authenticating to Business Central APIs.
Get-Module
import-Module AzureAD
# Parameters
$aadAppId = "ea954743-####-4025-bc90-############" # AAD app id
$aadAppRedirectUri = "http://localhost" # AAD app redirect URI
$aadTenantId = "46fe5ca5-####-4e42-92e9-############" # tenant id
# Load Microsoft.Identity.Client.dll
Add-Type -Path "C:\Program Files\WindowsPowerShell\Modules\AzureAD\2.0.2.135\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
# Get access token
$ctx = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new("https://login.microsoftonline.com/$aadTenantId")
$redirectUri = New-Object -TypeName System.Uri -ArgumentList $aadAppRedirectUri
$platformParameters = New-Object -TypeName Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters -ArgumentList ([Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always)
$accessToken = $ctx.AcquireTokenAsync("https://api.businesscentral.dynamics.com", $aadAppId, $redirectUri, $platformParameters).GetAwaiter().GetResult().AccessToken
Write-Host -ForegroundColor Cyan 'Authentication complete - we have an access token for Business Central, and it is stored in the $accessToken variable.'
#Get list of environments
$response = Invoke-WebRequest `
-Method Get `
-Uri "https://api.businesscentral.dynamics.com/admin/v2.11/applications/businesscentral/environments" `
-Headers #{Authorization=("Bearer $accessToken")}
Write-Host (ConvertTo-Json (ConvertFrom-Json $response.Content))
#List user sessions
$environmentName = "TEST"
$response = Invoke-WebRequest `
-Method Get `
-Uri "https://api.businesscentral.dynamics.com/admin/v2.11/applications/businesscentral/environments/$environmentName/sessions" `
-Headers #{Authorization=("Bearer $accessToken")}
Write-Host (ConvertTo-Json (ConvertFrom-Json $response.Content))
#Get Session Details
$sessionId = "145197"
$response = Invoke-WebRequest `
-Method Get `
-Uri "https://api.businesscentral.dynamics.com/admin/v2.11/applications/businesscentral/environments/$environmentName/sessions/$sessionId" `
-Headers #{Authorization=("Bearer $accessToken")}
Write-Host (ConvertTo-Json (ConvertFrom-Json $response.Content))
#Stop/Delete Session
$response = Invoke-WebRequest `
-Method Delete `
-Uri "https://api.businesscentral.dynamics.com/admin/v2.11/applications/businesscentral/environments/$environmentName/sessions/$sessionId" `
-Headers #{Authorization=("Bearer $accessToken")}
Write-Host (ConvertTo-Json (ConvertFrom-Json $response.Content))
$response = Invoke-WebRequest `
-Method Delete `
-Uri "https://api.businesscentral.dynamics.com/admin/v2.11/applications/businesscentral/environments/$environmentName/sessions/$sessionId" `
-Headers #{Authorization=("Bearer $accessToken")}
Write-Host "Responded with: $($response.StatusCode) $($response.StatusDescription)"
When I try to delete a session, it gives the following error.
PS C:\WINDOWS\system32> #Stop/Delete Session
$response = Invoke-WebRequest `
-Method Delete `
-Uri "https://api.businesscentral.dynamics.com/admin/v2.11/applications/businesscentral/environments/$environmentName/sessions/$sessionId" `
-Headers #{Authorization=("Bearer $accessToken")}
Write-Host (ConvertTo-Json (ConvertFrom-Json $response.Content))
ConvertFrom-Json : Cannot convert 'System.Byte[]' to the type 'System.String' required by parameter
'InputObject'. Specified method is not supported.
At line:6 char:46
+ Write-Host (ConvertTo-Json (ConvertFrom-Json $response.Content))
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [ConvertFrom-Json], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.ConvertFromJsonComm
and
PS C:\WINDOWS\system32> $response = Invoke-WebRequest `
-Method Delete `
-Uri "https://api.businesscentral.dynamics.com/admin/v2.11/applications/businesscentral/environments/$environmentName/sessions/$sessionId" `
-Headers #{Authorization=("Bearer $accessToken")}
Write-Host "Responded with: $($response.StatusCode) $($response.StatusDescription)"
Responded with: 204 No Content
The scope of the token looks alright, or at least the same as what I have setup in the Azure app, which is delegated permissions. Assuming these are the correct permissions.
"scp": "Financials.ReadWrite.All user_impersonation",
Any help greatly appreciated!

Apologies, it turns out it is working. A '204 No Content' indicates a successful completion for the Delete method. My confusion stemmed from expecting a response from the delete method when there was none.
Delete Items - Business Central API

Related

Http Request or cURL in Windows 10

How to request HTTP in Windows 10 by default without installing any plugin?
curl -X POST http://you-url.com/api/search
We can use the PowerShell
$Uri = 'http://you-url.com/api/search'
$Fields = #{
properties='{"color": "red"}'
maxitems='30'
skipcount='0'
location='country'
userName='testing'
password='testing'
}
Invoke-RestMethod -Uri $Uri -Method Post -Body $Fields | ConvertTo-Json -Depth 10;

Get-SqlErrorLog Email Backup Failure

I have a PowerShell script where I want to have it email me when a backup for SQL Server fails. I am trying to use Get-SqlErrorLog. The Script works, but no email sent. Here is the script:
$event = Get-SQLErrorLog -ServerInstance $env:COMPUTERNAME -Since Yesterday | Where-Object { $_.Text -match 'failed to complete the command BACKUP DATABASE' }
if ($event.EntryType -eq "Error")
{
$PCName = $env:COMPUTERNAME
$EmailBody = $event | format-list -property * | out-string
$EmailFrom = "$PCName me#mycompany.org"
$EmailTo = "me#mycompany.org"
$EmailSubject = "SQL Server Backup failure for $env:COMPUTERNAME"
$SMTPServer = "smtp.org"
Write-host "Sending Email"
Send-MailMessage -SMTPServer smtp.org -To me#mycompany.org -From me#mycompany.org -Subject "SQL Server Backup Failure for $env:COMPUTERNAME" -Body $EmailBody
}
else
{
write-host "No error found"
write-host "Here is the log entry that was inspected:"
$event
}

Powershell Script to Start Service if it is Stopped and wait for minute and send an email notification

I am very new to Powershell and in learning stage, I have tried to create an script to do automate below task. This script is not working as i am expected. Could you please review it and give me some help on it.
My task is,
I am trying to find out SQL Services (more than one SQL services in multiple servers) which are in stopped state and trying to start it, Waiting for an minute to complete the service start and verifying the service status again. If still it is stopped state i am trying to sending an email to setup of people for an action.
Could you please review the below code and correct the mistake, i tried to find it but unable to do
#Define servers & Services Variables
$Servers = GC "E:\Bhanu\SQLServer.txt"
$Services = GC "E:\Bhanu\SQLService.txt"
#Function Call
Function ServiceStatus ($Servers, $Services)
{
foreach ($Server in $Servers)
{
foreach ($Service in $Services)
{
$Servicestatus = get-service -ComputerName $Server -Name $Service
if ($Servicestatus.Status -eq "Stopped")
{
Start-service $Service
Start-Sleep -Seconds 60
$ServiceStatus1 = Get-Service -ComputerName $Server -Name $Service
if ($Servicestatus1.Status -eq "Stopped")
{
FuncMail -To “abc#gmail.com” -From “abc#gmail.com” -Subject $Server + $Service "fails to Start, Take immediate Action to avoid Impact” -Body $ServiceName "Service fails to Start, Take immediate Action to avoid Impact” -smtpServer “servername”
}
}
}
}
}
function FuncMail
{
#param($strTo, $strFrom, $strSubject, $strBody, $smtpServer)
param($To, $From, $Subject, $Body, $smtpServer)
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = $From
$msg.To.Add($To)
$msg.Subject = $Subject
$msg.IsBodyHtml = 1
$msg.Body = $Body
$smtp.Send($msg)
}
servicestatus $Servers $Services
Please let me know if you need anything here from my end
Hi this isn't the best approach and i'm doing it in quick way.
note %=foreach-object; ?=Where-Object.
You have to save your password on one file if your smtp-server require authentication otherwise don't run it using read-host -assecurestring | convertfrom-securestring | out-file "C:\Secure\Password.txt"
I'm also assuming you have your servers saved on one file.
My solution is to start all sql server service if you want to start specific just save the service name on one file on separate line.
The code to execute bellow.
#Loading Server and service details
$Services=Get-content C:\PS\Service.txt
$servidores=get-content C:\PS\Servers\Servers.txt
#Loading Mail credential
$Mailpasswordpath="C:\PS\Securestring.txt"
$Mailusername="DOmain\User"
$password=cat $Mailpasswordpath |ConvertTo-Securestring
$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Mailusername,$password
$servidores|Foreach-Object{Get-Service -ComputerName $_ -Name $Services }| #Get the services running on all servers
Where-Object{$_.Status -eq "Stopped"}| #Which status is equal stopped
Foreach-Object{
$_.Start(); #try to start
Start-Sleep -Seconds 60; #wait one minute
$_.Refresh(); #refresh then service to update status
#validate is status still stopped
if($_.Status -eq "Stopped")
{
#LOADING Mail details
$To="user#domain.com"
$subject="$($_.MachineName) $($_.Name) fails to Start, Take immediate Action to avoid Impact"
$From="ServiceStatus#domain.com"
$smtp="Server.domain.com"
$body="$($_.Name) Service fails to Start, Take immediate Action to avoid Impact"
#Sending email to notify
Send-MailMessage -To $To -Subject $subject -From $From -SmtpServer $smtp -Body $body -Credential $Cred
}
}
P.S: It's not the best approach I only decide to solve this problem. if you want we can create a function together later just test it first.

How to populate other attributes when using powershell 2.0 new-aduser?

When trying to create users from a file I get an error when -otherAttributes #{} is added to ps script. I am new to powershell and can not get it to work. Any guidance is appreciated. This is My code:
Import-Module ActiveDirectory
$OU = 'ou=Staging OU, dc=Domain, dc=net'
$usrfile = "\\servername\testuser.csv"
Import-Csv $usrfile |
ForEach-Object {
New-ADUser -SamAccountName $_.samaccountname -UserPrincipalName $_.Userprincipalname -Name $_.Name -DisplayName $_.displayname -GivenName $_.givenname -Surname $_.sn -Initials $_.initials -Title $_.title -OtherName $_.middlename `
-Description $_.description -Department $_.department -EmployeeID $_.employeeid -Path $OU -Country $_.c `
-OtherAttributes #{departmentNumber=$user.departmentnumber;localeID=$user.localid;extensionAttribute1=$user.extensionAttribute1;employeeType=$user.employeetype} `
-AccountPassword (ConvertTo-SecureString "Password" -AsPlainText -Force) -Enabled $true `
-PasswordNeverExpires $false -ChangePasswordAtLogon $true -PassThru
} > Import.log
The parameter "-otherAttributes #{}" fails in my AD. I found other way change this value in AD.
Try with this:
PS> Set-ADuser -identity joseiba -Clear 'msRTCSIP-PrimaryUserAddress'
PS> Set-ADuser -identity joseiba -Add #{'msRTCSIP-PrimaryUserAddress'='4213#grupofibratel.com'}
BR
Make sure you have all your $'s as $_... when accessing the object in the current loop iteration. I see some aren't.
you left out your ' around the ldapdisplayname for the otherattributes
#{departmentNumber=$user.departmentnumber;
should be: #{'departmentNumber'=$user.departmentnumber;
you need the ' around each ldapdisplayname in otherattributes..
just fought this myself dealing with the notes field

Unable to query [adsisearcher] for trusted domains (PowerShell)

I wanted to query [adsisearcher] to get me the OU info for a few servers for servers in trusted & non trusted domains.
$session = New-PSSession -ComputerName icvmm02
Invoke-Command -Session $session -ScriptBlock {
$compname= (Get-WmiObject "Win32_Computersystem" -namespace "root\CIMV2" -computername $comp).name
$searcher = [adsisearcher]"(&(ObjectCategory=computer)(Name=$compname))"
$ou= $searcher.FindOne() | Select-Object path
$serverou = $ou.path
}
$adou= (Invoke-Command -Session $session -ScriptBlock { $serverou })
Get-PSSession | Remove-PSSession
for servers in trusted domains im passing a $cred = get credentials while creating a pssession, but when i run
$compname= (Get-WmiObject "Win32_Computersystem" -namespace "root\CIMV2" -computername $comp).name
$searcher = [adsisearcher]"(&(ObjectCategory=computer)(Name=$compname))"
$ou= $searcher.FindOne() | Select-Object path
it gives me an error as
Exception calling "FindOne" with "0" argument(s): "An operations error occurred.
"
At line:1 char:27
+ $ou= $searcher.FindOne <<<< () | Select-Object path
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
$serverou = $ou.path
Could some one please help me in sorting this out.
I don't the cause but the ADSI interface just doesn't work in remote sessions. I can't even get the DN of the domain of a computer in my domain. I can get this to run locally but not remotely:
icm { ([adsi]"").distinguishedName } #works
icm -Session $s -ScriptBlock { ([adsi]"").distinguishedName } #doesn't work
Looks like there's a problem with the FindOne method call - you can find out more about the $searcher object with this;
$searcher | gm
the findOne method should be there with a list of the parameters it takes.
Though, I've just tried it on the type:
[adsisearcher] | gm | sort name
and there's no FindOne method - are you sure it's a method of adsisearcher?

Resources