最近因为疫情嘛,大家伙都在家里工作。老板隔三差五的让我给他一些报告,比如最近SharePoint的用户使用情况啦,比如Office365 邮箱的收发频率啦,还有Azure账号的risky signin等等,这样子可以让他更好地了解大家的工作强度(有没有偷懒!)和系统安全性等等。 Azure和Office365都有现成的报表和图像界面可以搜索和下载这些资料,但是比较繁琐,需要拿鼠标点半天,那么有没有自动化的可能呢?

在网上搜了搜,没有找到现成的PowerShell模块和函数可以用的,不过呢,豆子发现我们可以直接对Azure Graph 进行查询获取结果。 Azure Graph 是一个Azure 的 Rest API 接口,这个接口允许用户通过http/https 的 get或者post请求可以对O365,Azure AD,SharePoints,teams,Exchange等等服务直接进行读写操作。事实上,很多第三方的软件,比如Veeam 365的备份功能,比如一些Azure App的 SSO的功能,都是就是通过这个API来和Azure进行互动的。豆子自然也是可以用PowerShell来进行访问的。

PowerShell 访问 Azure Graph API 生成报表

Graph提供了一个接口 https://graph.microsoft.com 来接受 http/https 的get/post 的请求。首先,用户需要获取一个有效的授权token来执行相关的操作的权限。简单的说,为了获取这个Token,用户需要在Azure Portal里面创建一个application,然后授权相关的api 权限,然后用户发送一个post请求给微软的oauth服务,从而获得一个临时的Token。
api的权限分为application和 delegation的权限,前者表示我注册的applciation本身具有的权限,后者表示我在这个application上登陆的用户的权限,最终的有效权限则是两者的交集。很多常见服务的操作,既可以通过application进行权限,也可以通过delegation来授权,但是有些特殊的服务,比如安全方面的报告,Azure明确要求用户具有P2以上的license才可以操作,那么就只能通过delegation来授权。

关于具体的授权和验证, Azure提供了多种方式,每种方式都有自己的应用场景,这里豆子演示一个最常用的方式(也是我上面提到的通过delegation给指定用户来授权)。

首先 登陆 Azure Portal,新建一个application

PowerShell 访问 Azure Graph API 生成报表

注册一个新的 application, redirect url 可以随便写一个,这个主要是取决于验证方式,如果不用这种验证方式,可以不写

PowerShell 访问 Azure Graph API 生成报表

注册之后,记录一下 Application(Client) ID,这个相当于application的用户名
PowerShell 访问 Azure Graph API 生成报表

我们再给这个application 创建一个 secrets,这个相当于这个application的密码

PowerShell 访问 Azure Graph API 生成报表

接下来,在API permissions里面给这个application授权。他默认只有 Graph的 delegated的User.Read 权限

PowerShell 访问 Azure Graph API 生成报表

我们可以添加新的权限,选择 Microsoft Graph

PowerShell 访问 Azure Graph API 生成报表

这里我们选择 Delegation ,这样授权生成Token的时候,他会通过我的用户的权限进行授权

PowerShell 访问 Azure Graph API 生成报表

接下来是重点,到底需要什么权限呢?这个权限我们需要查看Graph的API 手册,他会告诉每一个操作对应的权限。比如我想查看安全警告,那么我搜索 Security的Alert的操作, 原来我需要添加下面的权限

https://docs.microsoft.com/en-us/graph/api/alert-get?view=graph-rest-1.0&tabs=http

PowerShell 访问 Azure Graph API 生成报表

添加之后,点击 Grant admin consent

PowerShell 访问 Azure Graph API 生成报表

准备工作就做好了,接下来看看我们的PowerShell 代码。

下面代码会生成对应的Token,替换掉对应的参数即可。这段代码其实就是把application的用户名和密码,我的用户的用户名和密码一起发送出去,获取到一个临时权限的Token。

$clientId = "f4b6b7ec-68f3-420a-bd7e-bc6fe5a918d1"
$tenantName = "abc.com.au"
$clientSecret = "~mFs5EX4Fv9-82OsseQxhH.n0Su8L_Ei-7"
$resource = "https://graph.microsoft.com/"
$username='admin@test.com'
$password='SafeappPassword@'

$ReqTokenBody = @{
    Grant_Type    = "Password"
    client_Id     = $clientID
    Client_Secret = $clientSecret
    Username      = $Username
    Password      = $Password
    Scope         = "https://graph.microsoft.com/.default"
} 

$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody

PowerShell 访问 Azure Graph API 生成报表

接下来的操作就很简单了

我想获取最新的安全警告,对应的API文档如下

https://docs.microsoft.com/en-us/graph/api/alert-list?view=graph-rest-1.0&tabs=http

PowerShell 访问 Azure Graph API 生成报表

PowerShell 发送请求,获得结果,我过滤一下,输出结果


$data=(Invoke-RestMethod -uri "https://graph.microsoft.com/v1.0/security/alerts" -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Method Get) 
$data.value | Where-Object {$_.severity -notlike '*low*'} | select eventDateTime,Category, severity, title, userStates | ft

PowerShell 访问 Azure Graph API 生成报表

再看几个例子

类似的操作,我们可以查看API的手册,添加对应的application permission,然后执行不同请求。

比如 我想查看90天内所有用户对SharePoint的访问情况,先添加Reports.Read.All的权限

PowerShell 访问 Azure Graph API 生成报表

然后执行

$data=(Invoke-RestMethod -uri "https://graph.microsoft.com/v1.0/reports/getSharePointActivityUserDetail(period='D90')" -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Method Get) -replace "", "" 
$data | ConvertFrom-Csv | ft

PowerShell 访问 Azure Graph API 生成报表

再比如,我想查看 所有人的邮箱使用情况,发送了多少邮件,接受了多少邮件,同样的先添加权限

PowerShell 访问 Azure Graph API 生成报表

然后执行代码,查看7天内的情况


$data=(Invoke-RestMethod -uri "https://graph.microsoft.com/v1.0/reports/getEmailActivityUserDetail(period='D7')" -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Method Get) -replace "", "" 
$data | ConvertFrom-Csv | out-gridview

PowerShell 访问 Azure Graph API 生成报表

有的时候,可能觉得查看API手册不是太方便,微软提供了一个更便捷的方式,用户可以直接访问 Graph Explorer。
https://developer.microsoft.com/en-us/graph/graph-explorer

这个界面可以直接执行对应的API操作,返回结果,还提供了大量的常用查询和需要的权限。这样子我的操作就更简单了,可以在这个网页进行测试,成功之后,再修改自己的脚本。
PowerShell 访问 Azure Graph API 生成报表

最后,附上一个完整的脚本

#获取Token

$clientId = "8d4538e6-b1a5-4c05-b449-54d62ss18413"
$tenantName = "test.com"
$clientSecret = ".2S_zsp.dVff~~5J_9_cxM1e1TRXrJ94"
$resource = "https://graph.microsoft.com/"
$username='test@abc.com'
$password = '@@222333' 

$ReqTokenBody = @{
    Grant_Type    = "Password"
    client_Id     = $clientID
    Client_Secret = $clientSecret
    Username      = $Username
    Password      = $Password
    Scope         = "https://graph.microsoft.com/.default"
} 

$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody

# 通过API获取安全警告

$data=(Invoke-RestMethod -uri "https://graph.microsoft.com/v1.0/security/alerts" -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Method Get) 
$all=$data.value | Where-Object {$_.severity -like '*medium*' -or $_.severity -like "*high"} 

$ret=@()

foreach($one in $all){

$obj=$one.userStates 
$category=$one.category
$Severity=$one.severity
$title=$one.title

$obj | Add-Member -NotePropertyName Category -NotePropertyValue $category
$obj | Add-Member -NotePropertyName Severity -NotePropertyValue $Severity
$obj | Add-Member -NotePropertyName Title -NotePropertyValue $title

$ret+=$obj
}

$ret

#自定义一个CSS界面,用于发送对应的邮件,显示一个比较好看些的table界面
$style=@"
<style>
body {
    color:#333333;
    font-family:Calibri,Tahoma;
    font-size: 10pt;
}
h1 {
    text-align:center;
}
h2 {
    border-top:1px solid #666666;
}
th {
    font-weight:bold;
    color:#eeeeee;
    background-color:#333333;
    cursor:pointer;
}
.odd  { background-color:#ffffff; }
.even { background-color:#dddddd; }
.paginate_enabled_next, .paginate_enabled_previous {
    cursor:pointer; 
    border:1px solid #222222; 
    background-color:#dddddd; 
    padding:2px; 
    margin:4px;
    border-radius:2px;
}
.paginate_disabled_previous, .paginate_disabled_next {
    color:#666666; 
    cursor:pointer;
    background-color:#dddddd; 
    padding:2px; 
    margin:4px;
    border-radius:2px;
}
.dataTables_info { margin-bottom:4px; }
.sectionheader { cursor:pointer; }
.sectionheader:hover { color:red; }
.grid { width:100% }
.red {
    color:red;
    font-weight:bold;
} 
.green{
    color:green;
    font-weight:bold;
}
</style>

"@

#发送邮件

$from = "test@abc.com"
$to = "aaa@abc.com"
$smtp = "smtp.office365.com" 
$sub = "Security Alerts Lists" 
$password = Get-Content "C:\temp\password.txt" | ConvertTo-SecureString 
$mycreds = New-Object System.Management.Automation.PsCredential("xyli@vet.partners",$password)

$htmlbody=$ret| select AccountName, DomainName,LogonDateTime, LogonIP, UserPrincipalName, Category, Severity, Title | sort Severity| ConvertTo-Html -Body "<H1> Secuirty Alerts </H1>" -Head $style

Send-MailMessage -To $to -From $from -Subject $sub -Body ($htmlbody|Out-String) -Credential $mycreds -SmtpServer $smtp -DeliveryNotificationOption Never -BodyAsHtml -UseSsl -port 587

收到的邮件通知

PowerShell 访问 Azure Graph API 生成报表

参考资料:

https://developer.microsoft.com/en-us/graph/graph-explorer
https://adamtheautomator.com/microsoft-graph-api-powershell/
https://docs.microsoft.com/en-us/graph/overview?view=graph-rest-1.0