Application Programming Interface (API) is a set of protocols, routines, and tools used to build software applications. The main purpose of API is data exchange and integration, whereas Software Development Kit (SDK) is customization. The type of API varies based on the data exchange and transport mechanism. The transport mechanism may be web-based, source code, or binary function.
Following are the most common types of API:
In this chapter, we will cover the following topics:
Developers use APIs to accomplish many tasks; using PowerShell, we can only achieve our tasks to some extent. For example, let's consider a scenario where we need to interact with a Windows API; to achieve this, we can use the Windows API. Most IT professionals don't use API considering the fact that it's meant for development. However, we can leverage APIs in Windows PowerShell to perform administration as well as development tasks.
In the following example, we will try to use the Windows API in PowerShell as a jump start. Let's take a look at the PowerShell way of playing with the User32.dll
file.
You can also refer to https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx
The C++ code used is as follows:
BOOL WINAPI ShowWindow( _In_ HWND hWnd, _In_ int nCmdShow );
In Windows PowerShell, we will use the Add-type
command for this exercise. This is not a big deal! It's a pretty old concept. However, to begin, we need to know how PowerShell loads the standard assembly. Later in this topic, we will examine the managed assemblies as well. Run the following command:
Help Add-Type -Detailed
Yes, as like all other cmdlets, we need to first read the help document before we proceed further. Using the preceding result, we can explore the parameters and related information. Now, we will build a PowerShell script that performs a few actions on Windows, which is as follows:
Function Set-Window { Param( [Parameter(Mandatory = $true)] $ID ) $code = @" [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); "@ $demo = Add-Type -MemberDefinition $code -Name "Demo" -Namespace Win32Functions -PassThru $demo::ShowWindowAsync((Get-Process -id $ID).MainWindowHandle , 2) } Set-Window -ID $pid
How does this work? We created a PowerShell function named Set-Window
, which accepts a parameter called ID
. Run the following command:
$code = @" [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); "@
Using the previous snippet of code inside the here-string, we will import the user32.dll
file and consume the ShowWinodAsync
function. This accepts the following two overloads:
hWnd
: An example of this is process informationnCmdShow
: This is a set of valid parameters listed in the following table:
Value |
Meaning |
---|---|
SW_FORCEMINIMIZE 11 |
This minimizes a window even if the thread that owns the window is not responding. This flag should only be used while minimizing windows from a different thread. |
SW_HIDE 0 |
This hides the window and activates another window. |
SW_MAXIMIZE 3 |
This maximizes the specified window. |
SW_MINIMIZE 6 |
This minimizes the specified window and activates the next top-level window in the Z order. |
SW_RESTORE 9 |
This activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag while restoring a minimized window. |
SW_SHOW 5 |
This activates the window and displays it in its current size and position. |
SW_SHOWDEFAULT 10 |
This sets the show state based on the SW_ value specified in the |
SW_SHOWMAXIMIZED 3 |
This activates the window and displays it as a maximized window. |
SW_SHOWMINIMIZED 2 |
This activates the window and displays it as a minimized window. |
SW_SHOWMINNOACTIVE 7 |
This displays the window as a minimized window. This value is similar to SW_SHOWMINIMIZED, except that the window is not activated. |
SW_SHOWNA 8 |
This displays the window in its current size and position. This value is similar to SW_SHOW, except that the window is not activated. |
SW_SHOWNOACTIVATE 4 |
This displays a window in its most recent size and position. This value is similar to SW_SHOWNORMAL, except that the window is not activated. |
SW_SHOWNORMAL 1 |
This activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag while displaying the window for the first time. |
To instantiate the ShowWindowAsnc
function, we will use the Add-Type
cmdlet and assign it to the $demo
variable, as shown in the following command line:
$demo = Add-Type -MemberDefinition $code -Name "Demo" -Namespace Win32Functions -PassThru $demo::ShowWindowAsync((Get-Process -id $ID).MainWindowHandle , 2) Set-Window -ID $pid
As we used $pid
while executing the function, the code will affect the current PowerShell console and minimize it. We can perform the advanced functions as required and build a PowerShell code with reference to the User32.dll
file.
In this chapter, we will cover PowerShell and API use for Microsoft technologies such as SharePoint, Exchange, and Lync; so, for another short example of .NET, let's take a look at the speech API.
Refer to the following URL for the System.Speech
API:
https://msdn.microsoft.com/en-us/library/gg145021%28v=vs.110%29.aspx
Run the following command:
Add-Type -AssemblyName System.Speech $speak = New-Object System.Speech.Synthesis.SpeechSynthesizer $speak.Rate = 1 $speak.Speak("Welcome to PowerShell 5.0")
The preceding code will speak out the text in the last line:
"Welcome to PowerShell 5.0"
This command will perform the following functions:
System.Speech
assembly$speak
object1
We considered a basic example of using the Windows API. There are different ways to use APIs in PowerShell based on the requirements. Windows PowerShell has the feature of interacting with .NET DLL files as well. Let's consider that you have a code that simply performs addition operations that we can load in PowerShell and then explores the methods. But why are we discussing DLL now? Here is a comparison of API and DLL:
API |
DLL |
---|---|
Abstract |
Concrete |
In this, the interface is implemented by the software program |
This is a method of providing APIs |
An API is an interface to the library of code |
DLL is nothing but a library of code |
In short, DLL is a file format and a way to use API.
Let's take a look at a demo where we will use a custom DLL file in Windows PowerShell. For this, we will choose a class library in Visual C# and build a code that will simply add two given integers.
The C# code is as follows:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ClassLibrary2 { public class Class1 { public static int sum(int a,int b) { return a + b; } } }
Note that you can rename namespace and class, which helps others understand them. Here, we have selected the default options for demonstration purposes.
We can directly use this with the here-string, and with the help of the Add-Type
cmdlet, we can call the sum
function and add two integer values. Run the following command:
$code = @" using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ClassLibrary2 { public class Class1 { public static int sum(int a,int b) { return a + b; } } } "@ Add-Type -TypeDefinition $code [ClassLibrary2.Class1]::sum(34,45)
The output is illustrated in the following image:
Points marked in the figure can be explained as follows:
Add-Type
cmdletTypeDefinition
parameter, we specify the source code$code
variable, we have our source code in the here-string formatsum
methodNow, in Visual Studio, compile the code and find the DLL file; in our case, we have the DLL file in the Temp
folder, so we can use the following snippet of code in PowerShell:
Import-Module C:TempClassLibrary2ClassLibrary2inDebugClassLibrary2.dll [ClassLibrary2.Class1]::sum(45,56)
Take a look at the following image:
Note that we have a Class
keyword in WMF 5.0; we could use that. However, in PowerShell, we have a few constraints—refer to the WMF 5.0 release notes.
In the next topic, we will explore the EWS API for managing Exchange Online.
The Exchange Web Services (EWS) Managed API provides a managed interface for developing .NET client applications that use EWS. Using the EWS Managed API, we can access most of the information stored in Office 365, Exchange Online, or the Exchange Server mailbox.
The EWS Managed API is now available as an open source project on GitHub. You can share your contributions and bug report bugs in GitHub.
For the contributions, refer to the following link:
https://github.com/OfficeDev/ews-managed-api/blob/master/CONTRIBUTING.md
To report issues, refer to the following link:
https://github.com/OfficeDev/ews-managed-api/issues
For the MSDN documentation, refer to the following link:
https://msdn.microsoft.com/en-us/library/office/dd633710%28v=exchg.80%29.aspx
To download the EWS Managed API 2.2, refer to the following link:
http://www.microsoft.com/en-us/download/details.aspx?id=42951
The installation of the EWS Managed API is as simple as a click—click and it's done! For an easy reference and quick demo, we moved the DLL file to the C:TempEWS
folder.
One of the easiest methods to explore the items in the mailbox folder is using the MSOnline
module.
To connect to Exchange Online, we will use the following snippet of code:
Import-Module MSOnline $O365Cred = Get-Credential $O365Session = New-PSSession –ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/?proxymethod=rps -Credential $O365Cred -Authentication Basic -AllowRedirection Import-PSSession $O365Session Connect-MsolService –Credential $O365Cred
Here is the PowerShell code:
Get-MailboxFolderStatistics -Identity "TargetMailBoxID" | Select FolderType , Name , ItemsinFolder
Refer to the following image:
We installed the EWS API 2.2 and moved the DLL file to the desired location. On the EWS managed API, we can do this using the following code:
#Target MailboxID's $MailboxNames = "TargetMailID" #Any Exchange Admin ID with appropriate permissions $AdminID = "AdminID" #Fetch password as secure string $AdminPwd = Read-Host "Enter Password" -AsSecureString #Load the Exchange Web Service DLL $dllpath = "C:TempMicrosoft.Exchange.WebServices.dll" [Reflection.Assembly]::LoadFile($dllpath) #Create a Exchange Web Service $Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1) #Credentials to impersonate the mail box $Service.Credentials = New-Object System.Net.NetworkCredential($AdminID , $AdminPwd) foreach($MailboxName in $MailboxNames) { #Impersonate using Exchange WebService Class $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName) $Service.AutodiscoverUrl($MailboxName,{$true}) #Assing EWS URL $service.Url = 'https://outlook.office365.com/EWS/Exchange.asmx' Write-Host "Processing Mailbox: $MailboxName" -ForegroundColor Green #Fetch Root Folder ID $RootFolderID = New-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root, $MailboxName) $RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service,$RootFolderID) #Create a Folder View $FolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000) $FolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep #Retrive the Information $response = $RootFolder.FindFolders($FolderView) $response | Select DisplayName , TotalCount , FolderClass }
This returns the same output as shown in the following image, but provides in-depth information:
You can spin up PowerShell and customize the scripting according to your needs.
At times, we may consider building a unique interface to collect and audit organization information such as AD, Mailbox, SharePoint, Lync, and so on. Let's accommodate PowerShell in C# to call the Exchange Online cmdlets; the reason for this is the unique and clean interface, which helps us query multiple sources. In the following example, we will obtain information from Exchange Online.
Here is a demo code that will print your Exchange Online user's display name and SMTP address:
using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using System.Management.Automation.Remoting; using System.Management.Automation.Runspaces; using System.Security; using System.Collections.ObjectModel; using System.Text; namespace Office365 { class Program { static void Main(string[] args) { string username = "ExchangeAdminID"; string password = "Password"; System.Security.SecureString securepassword = new System.Security.SecureString(); foreach (char c in password) { securepassword.AppendChar(c); } PSCredential credential = new PSCredential(username, securepassword); WSManConnectionInfo connectioninfo = new WSManConnectionInfo(new Uri("https://ps.outlook.com/powershell"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential); connectioninfo.AuthenticationMechanism = AuthenticationMechanism.Basic; //connectioninfo.AuthenticationMechanism = AuthenticationMechanism.Basic; connectioninfo.MaximumConnectionRedirectionCount = 2; //connectioninfo.MaximumConnectionRedirectionCount = 2; using (Runspace runspace = RunspaceFactory.CreateRunspace(connectioninfo)) { runspace.Open(); using (PowerShell powershell = PowerShell.Create()) { powershell.Runspace = runspace; //Create the command and add a parameter powershell.AddCommand("Get-Mailbox"); powershell.AddParameter("RecipientTypeDetails", "UserMailbox"); //powershell. //Invoke the command and store the results in a PSObject collection Collection<PSObject> results = powershell.Invoke(); foreach (PSObject result in results) { string createText = string.Format("Name: {0} Alias: {1} Mail: {2}", result.Properties["DisplayName"].Value.ToString(), result.Properties["Alias"].Value.ToString(), result.Properties["PrimarySMTPAddress"].Value.ToString()); System.IO.File.WriteAllText("C:\User.txt", createText); } } } } } }
Most organizations are moving to cloud-based storage, and as we know, all Microsoft products have a PowerShell interface. This eventually helps both developers and administrators to perform tasks without using a GUI. Not only automating tasks, using PowerShell we troubleshoot issues, identify the root cause, and so on.
Let's consider an operational requirement to purge Lync contact entries. Ignore the technical background and requirements or designs such as unified contact stores and so on; all we need to know is how to consume the EWS API and explore objects. Perform the following steps:
#Target MailboxID's $MailboxNames = "TargetMailBoxID1" , "TargetMailBoxID1" #Any Exchange Admin ID with appropriate permissions $AdminID = "AdminID" #Fetch password as secure string $AdminPwd = Read-Host "Enter Password" -AsSecureString #Load the Exchange Web Service DLL $dllpath = "C:TempMicrosoft.Exchange.WebServices.dll" [Reflection.Assembly]::LoadFile($dllpath)
Now, perform the following steps:
#Create a Exchange Web Service $Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1) #Credentials to impersonate the mail box $Service.Credentials = New-Object System.Net.NetworkCredential($AdminID , $AdminPwd)
After this, perform the following steps:
Load
method. Run the following command:foreach($MailboxName in $MailboxNames) { #Impersonate using Exchange WebService Class $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName) $Service.AutodiscoverUrl($MailboxName,{$true}) #Assing EWS URL $service.Url = 'https://outlook.office365.com/EWS/Exchange.asmx' Write-Host "Processing Mailbox: $MailboxName" -ForegroundColor Green #Fetch Root Folder ID $RootFolderID = New-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root, $MailboxName) $RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service,$RootFolderID) #Create a Folder View $FolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000) $FolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep #Retrieve Folders from view $response = $RootFolder.FindFolders($FolderView) #Query Folder which has display name like Lync Contacts $Folder = $response | ? {$_.FolderClass -eq 'IPF.Contact.MOC.QuickContacts'} $Folder | Select DisplayName , TotalCount #Purge the items $Folder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete, $false) $Folder.Load() }
Deleting items from the mailbox folder is almost similar to purging. We can delete items using the following lines of code:
$Folder.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete) $Folder.Load()
We cannot remove the Lync contacts entries in Outlook by simply right–clicking and deleting. We need to purge the items and then delete the entries in the contact folders. However, we can't communicate to the users to perform the delete operations themselves after performing purge, so simply executing the preceding code will complete the task. Using the previously mentioned methods we can perform administration tasks remotely without the user's intervention.